[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#858016: marked as done (unblock: tomcat8/8.5.12-1)



Your message dated Thu, 20 Apr 2017 18:03:00 +0000
with message-id <61035952-6f50-b108-ee0d-d4384c85e024@thykier.net>
and subject line Re: Bug#858016: unblock: tomcat8/8.5.12-1
has caused the Debian Bug report #858016,
regarding unblock: tomcat8/8.5.12-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
858016: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=858016
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
User: release.debian.org@packages.debian.org
Usertags: unblock

This is a pre-upload request to update tomcat8 to the version 8.5.12-1
in stretch. This version is mostly a bug fix release, it also addresses
compatibility issues with Java 9 which will be released later this year.
The full changelog is available here:

http://tomcat.apache.org/tomcat-8.5-doc/changelog.html#Tomcat_8.5.12_(markt)

Backporting security fixes to the stable version of Tomcat has proven
to be a bit tedious in the past, so it would be appreciable to have
a version as fresh as possible in stretch to ease the maintenance work
on the long term.

Thank you,

Emmanuel Bourg

(include/attach the debdiff against the package in testing)
diff --git a/build.properties.default b/build.properties.default
index 56000f70..c5e67cdf 100644
--- a/build.properties.default
+++ b/build.properties.default
@@ -25,7 +25,7 @@
 # ----- Version Control Flags -----
 version.major=8
 version.minor=5
-version.build=11
+version.build=12
 version.patch=0
 version.suffix=
 
@@ -115,8 +115,8 @@ wsdl4j-lib.jar=${wsdl4j-lib.home}/wsdl4j-${wsdl4j-lib.version}.jar
 
 # ----- Eclipse JDT, version 4.5.1 or later -----#
 # See https://wiki.apache.org/tomcat/JDTCoreBatchCompiler before updating
-jdt.version=4.5.1
-jdt.release=R-4.5.1-201509040015
+jdt.version=4.6.1
+jdt.release=R-4.6.1-201609071200
 jdt.home=${base.path}/ecj-${jdt.version}
 jdt.jar=${jdt.home}/ecj-${jdt.version}.jar
 # The download will be moved to the archive area eventually. We are taking care of that in advance.
@@ -124,7 +124,7 @@ jdt.loc.1=http://archive.eclipse.org/eclipse/downloads/drops4/${jdt.release}/ecj
 jdt.loc.2=http://download.eclipse.org/eclipse/downloads/drops4/${jdt.release}/ecj-${jdt.version}.jar
 
 # ----- Tomcat native library -----
-tomcat-native.version=1.2.10
+tomcat-native.version=1.2.12
 tomcat-native.home=${base.path}/tomcat-native-${tomcat-native.version}
 tomcat-native.tar.gz=${tomcat-native.home}/tomcat-native.tar.gz
 tomcat-native.loc.1=${base-tomcat.loc.1}/tomcat-connectors/native/${tomcat-native.version}/source/tomcat-native-${tomcat-native.version}-src.tar.gz
@@ -132,13 +132,13 @@ tomcat-native.loc.2=${base-tomcat.loc.2}/tomcat-connectors/native/${tomcat-nativ
 tomcat-native.win.1=${base-tomcat.loc.1}/tomcat-connectors/native/${tomcat-native.version}/binaries/tomcat-native-${tomcat-native.version}-win32-bin.zip
 tomcat-native.win.2=${base-tomcat.loc.2}/tomcat-connectors/native/${tomcat-native.version}/binaries/tomcat-native-${tomcat-native.version}-win32-bin.zip
 
-# ----- NSIS, version 2.0 or later -----
-nsis.version=2.51
+# ----- NSIS, version 3.0 or later -----
+nsis.version=3.01
 nsis.home=${base.path}/nsis-${nsis.version}
 nsis.exe=${nsis.home}/makensis.exe
-nsis.installoptions.dll=${nsis.home}/Plugins/InstallOptions.dll
-nsis.nsexec.dll=${nsis.home}/Plugins/nsExec.dll
-nsis.nsisdl.dll=${nsis.home}/Plugins/NSISdl.dll
+nsis.installoptions.dll=${nsis.home}/Plugins/x86-unicode/InstallOptions.dll
+nsis.nsexec.dll=${nsis.home}/Plugins/x86-unicode/nsExec.dll
+nsis.nsisdl.dll=${nsis.home}/Plugins/x86-unicode/NSISdl.dll
 nsis.loc=${base-sf.loc}/nsis/nsis-${nsis.version}.zip
 
 # ----- Commons Daemon, version 1.0-Alpha or later -----
diff --git a/build.xml b/build.xml
index cc21c78a..d986ea3d 100644
--- a/build.xml
+++ b/build.xml
@@ -548,6 +548,7 @@
         <exclude name="output/**"/>
         <exclude name="modules/**"/>
         <exclude name="**/*.mdl"/>
+        <exclude name="**/*.pem"/>
         <exclude name="**/*.svg"/>
         <exclude name="**/*_2.xml"/>
         <exclude name="res/checkstyle/header-al2.txt"/>
diff --git a/conf/catalina.properties b/conf/catalina.properties
index f1913e7d..128dba10 100644
--- a/conf/catalina.properties
+++ b/conf/catalina.properties
@@ -146,3 +146,7 @@ tomcat.util.buf.StringCache.byte.enabled=true
 #tomcat.util.buf.StringCache.char.enabled=true
 #tomcat.util.buf.StringCache.trainThreshold=500000
 #tomcat.util.buf.StringCache.cacheSize=5000
+
+# Allow for changes to HTTP request validation
+# WARNING: Using this option will expose the server to CVE-2016-6816
+#tomcat.util.http.parser.HttpParser.requestTargetAllow=|
diff --git a/debian/changelog b/debian/changelog
index 0e70c823..5926602e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+tomcat8 (8.5.12-1) unstable; urgency=medium
+
+  * Team upload.
+  * New upstream release
+    - Refreshed the patches
+
+ -- Emmanuel Bourg <ebourg@apache.org>  Fri, 17 Mar 2017 09:38:49 +0100
+
 tomcat8 (8.5.11-1) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/patches/0004-split-deploy-webapps-target-from-deploy-target.patch b/debian/patches/0004-split-deploy-webapps-target-from-deploy-target.patch
index 2281c1eb..099c2cf3 100644
--- a/debian/patches/0004-split-deploy-webapps-target-from-deploy-target.patch
+++ b/debian/patches/0004-split-deploy-webapps-target-from-deploy-target.patch
@@ -3,7 +3,7 @@ Date: Mon, 28 Jun 2010 21:32:35 +0200
 Subject: [PATCH] split deploy-webapps target from deploy target
 --- a/build.xml
 +++ b/build.xml
-@@ -993,7 +993,7 @@
+@@ -994,7 +994,7 @@
      </xslt>
    </target>
  
@@ -12,7 +12,7 @@ Subject: [PATCH] split deploy-webapps target from deploy target
            description="Default. Builds a working Tomcat instance">
  
      <copy tofile="${tomcat.build}/bin/tomcat-native.tar.gz"
-@@ -1032,6 +1032,10 @@
+@@ -1033,6 +1033,10 @@
        </fileset>
      </copy>
  
diff --git a/debian/patches/0005-skip-test-failures.patch b/debian/patches/0005-skip-test-failures.patch
index b52a32f0..9d6d7720 100644
--- a/debian/patches/0005-skip-test-failures.patch
+++ b/debian/patches/0005-skip-test-failures.patch
@@ -3,7 +3,7 @@ Author: Emmanuel Bourg <ebourg@apache.org>
 Forwarded: not-needed
 --- a/build.xml
 +++ b/build.xml
-@@ -1370,8 +1370,10 @@
+@@ -1371,8 +1371,10 @@
        </filterchain>
      </concat>
  
diff --git a/debian/patches/0010-debianize-build-xml.patch b/debian/patches/0010-debianize-build-xml.patch
index d6c053df..092e0cfb 100644
--- a/debian/patches/0010-debianize-build-xml.patch
+++ b/debian/patches/0010-debianize-build-xml.patch
@@ -5,7 +5,7 @@ Last-Update: 2011-05-16
 
 --- a/build.xml
 +++ b/build.xml
-@@ -644,7 +644,7 @@
+@@ -645,7 +645,7 @@
      </copy>
    </target>
  
@@ -14,7 +14,7 @@ Last-Update: 2011-05-16
      <!-- Compile internal server components -->
      <javac srcdir="java" destdir="${tomcat.classes}"
             debug="${compile.debug}"
-@@ -996,12 +996,14 @@
+@@ -997,12 +997,14 @@
    <target name="deploy" depends="package,build-docs,build-tomcat-jdbc,compile-webapp-examples,deploy-webapps"
            description="Default. Builds a working Tomcat instance">
  
diff --git a/java/javax/servlet/http/HttpServlet.java b/java/javax/servlet/http/HttpServlet.java
index 16ebc26c..9f3fe92e 100644
--- a/java/javax/servlet/http/HttpServlet.java
+++ b/java/javax/servlet/http/HttpServlet.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.text.MessageFormat;
 import java.util.Enumeration;
@@ -489,6 +490,18 @@ public abstract class HttpServlet extends GenericServlet {
         boolean ALLOW_TRACE = true;
         boolean ALLOW_OPTIONS = true;
 
+        // Tomcat specific hack to see if TRACE is allowed
+        Class<?> clazz = null;
+        try {
+            clazz = Class.forName("org.apache.catalina.connector.RequestFacade");
+            Method getAllowTrace = clazz.getMethod("getAllowTrace", (Class<?>[]) null);
+            ALLOW_TRACE = ((Boolean) getAllowTrace.invoke(req, (Object[]) null)).booleanValue();
+        } catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
+                IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+            // Ignore. Not running on Tomcat. TRACE is always allowed.
+        }
+        // End of Tomcat specific hack
+
         for (int i=0; i<methods.length; i++) {
             Method m = methods[i];
 
diff --git a/java/org/apache/catalina/Context.java b/java/org/apache/catalina/Context.java
index 2b10be16..d36b977e 100644
--- a/java/org/apache/catalina/Context.java
+++ b/java/org/apache/catalina/Context.java
@@ -1820,4 +1820,32 @@ public interface Context extends Container, ContextBind {
      *         {@code false}
      */
     public boolean getDispatchersUseEncodedPaths();
+
+    /**
+     * Set the default request body encoding for this web application.
+     *
+     * @param encoding The default encoding
+     */
+    public void setRequestCharacterEncoding(String encoding);
+
+    /**
+     * Get the default request body encoding for this web application.
+     *
+     * @return The default request body encoding
+     */
+    public String getRequestCharacterEncoding();
+
+    /**
+     * Set the default response body encoding for this web application.
+     *
+     * @param encoding The default encoding
+     */
+    public void setResponseCharacterEncoding(String encoding);
+
+    /**
+     * Get the default response body encoding for this web application.
+     *
+     * @return The default response body encoding
+     */
+    public String getResponseCharacterEncoding();
 }
diff --git a/java/org/apache/catalina/authenticator/AuthenticatorBase.java b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
index 40ddccc7..cc40141d 100644
--- a/java/org/apache/catalina/authenticator/AuthenticatorBase.java
+++ b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
@@ -26,11 +26,13 @@ import java.util.Map;
 import java.util.Set;
 
 import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
 import javax.security.auth.message.AuthException;
 import javax.security.auth.message.AuthStatus;
 import javax.security.auth.message.MessageInfo;
 import javax.security.auth.message.config.AuthConfigFactory;
 import javax.security.auth.message.config.AuthConfigProvider;
+import javax.security.auth.message.config.ClientAuthConfig;
 import javax.security.auth.message.config.RegistrationListener;
 import javax.security.auth.message.config.ServerAuthConfig;
 import javax.security.auth.message.config.ServerAuthContext;
@@ -97,6 +99,8 @@ public abstract class AuthenticatorBase extends ValveBase
     private static final String DATE_ONE =
             (new SimpleDateFormat(FastHttpDateFormat.RFC1123_DATE, Locale.US)).format(new Date(1));
 
+    private static final AuthConfigProvider NO_PROVIDER_AVAILABLE = new NoOpAuthConfigProvider();
+
     /**
      * The string manager for this package.
      */
@@ -1188,22 +1192,32 @@ public abstract class AuthenticatorBase extends ValveBase
     private AuthConfigProvider getJaspicProvider() {
         AuthConfigProvider provider = jaspicProvider;
         if (provider == null) {
-            AuthConfigFactory factory = AuthConfigFactory.getFactory();
+            provider = findJaspicProvider();
+        }
+        if (provider == NO_PROVIDER_AVAILABLE) {
+            return null;
+        }
+        return provider;
+    }
+
+
+    private AuthConfigProvider findJaspicProvider() {
+        AuthConfigFactory factory = AuthConfigFactory.getFactory();
+        AuthConfigProvider provider = null;
+        if (factory != null) {
             provider = factory.getConfigProvider("HttpServlet", jaspicAppContextID, this);
-            if (provider != null) {
-                jaspicProvider = provider;
-            }
         }
+        if (provider == null) {
+            provider = NO_PROVIDER_AVAILABLE;
+        }
+        jaspicProvider = provider;
         return provider;
     }
 
 
     @Override
     public void notify(String layer, String appContext) {
-        AuthConfigFactory factory = AuthConfigFactory.getFactory();
-        AuthConfigProvider provider = factory.getConfigProvider("HttpServlet", jaspicAppContextID,
-                this);
-        jaspicProvider = provider;
+        findJaspicProvider();
     }
 
 
@@ -1211,4 +1225,24 @@ public abstract class AuthenticatorBase extends ValveBase
         public MessageInfo messageInfo = null;
         public ServerAuthContext serverAuthContext = null;
     }
-}
+
+
+    private static class NoOpAuthConfigProvider implements AuthConfigProvider {
+
+        @Override
+        public ClientAuthConfig getClientAuthConfig(String layer, String appContext, CallbackHandler handler)
+                throws AuthException {
+            return null;
+        }
+
+        @Override
+        public ServerAuthConfig getServerAuthConfig(String layer, String appContext, CallbackHandler handler)
+                throws AuthException {
+            return null;
+        }
+
+        @Override
+        public void refresh() {
+        }
+    }
+}
\ No newline at end of file
diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java b/java/org/apache/catalina/authenticator/FormAuthenticator.java
index e5f58542..b3056a03 100644
--- a/java/org/apache/catalina/authenticator/FormAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java
@@ -427,9 +427,9 @@ public class FormAuthenticator
         RequestDispatcher disp =
             context.getServletContext().getRequestDispatcher(loginPage);
         try {
-            if (context.fireRequestInitEvent(request)) {
+            if (context.fireRequestInitEvent(request.getRequest())) {
                 disp.forward(request.getRequest(), response);
-                context.fireRequestDestroyEvent(request);
+                context.fireRequestDestroyEvent(request.getRequest());
             }
         } catch (Throwable t) {
             ExceptionUtils.handleThrowable(t);
@@ -471,12 +471,11 @@ public class FormAuthenticator
         }
 
         RequestDispatcher disp =
-            context.getServletContext().getRequestDispatcher
-            (config.getErrorPage());
+                context.getServletContext().getRequestDispatcher(config.getErrorPage());
         try {
-            if (context.fireRequestInitEvent(request)) {
+            if (context.fireRequestInitEvent(request.getRequest())) {
                 disp.forward(request.getRequest(), response);
-                context.fireRequestDestroyEvent(request);
+                context.fireRequestDestroyEvent(request.getRequest());
             }
         } catch (Throwable t) {
             ExceptionUtils.handleThrowable(t);
diff --git a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
index cb57812c..4ca5ac08 100644
--- a/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java
@@ -56,6 +56,7 @@ import org.ietf.jgss.Oid;
 public class SpnegoAuthenticator extends AuthenticatorBase {
 
     private static final Log log = LogFactory.getLog(SpnegoAuthenticator.class);
+    private static final String AUTH_HEADER_VALUE_NEGOTIATE = "Negotiate";
 
     private String loginConfigName = Constants.DEFAULT_LOGIN_MODULE_NAME;
     public String getLoginConfigName() {
@@ -149,7 +150,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
             if (log.isDebugEnabled()) {
                 log.debug(sm.getString("authenticator.noAuthHeader"));
             }
-            response.setHeader("WWW-Authenticate", "Negotiate");
+            response.setHeader(AUTH_HEADER_NAME, AUTH_HEADER_VALUE_NEGOTIATE);
             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
             return false;
         }
@@ -162,7 +163,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
                 log.debug(sm.getString(
                         "spnegoAuthenticator.authHeaderNotNego"));
             }
-            response.setHeader("WWW-Authenticate", "Negotiate");
+            response.setHeader(AUTH_HEADER_NAME, AUTH_HEADER_VALUE_NEGOTIATE);
             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
             return false;
         }
@@ -182,7 +183,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
                 log.debug(sm.getString(
                         "spnegoAuthenticator.authHeaderNoToken"));
             }
-            response.setHeader("WWW-Authenticate", "Negotiate");
+            response.setHeader(AUTH_HEADER_NAME, AUTH_HEADER_VALUE_NEGOTIATE);
             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
             return false;
         }
@@ -235,7 +236,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
                             "spnegoAuthenticator.ticketValidateFail"));
                 }
                 // Start again
-                response.setHeader("WWW-Authenticate", "Negotiate");
+                response.setHeader(AUTH_HEADER_NAME, AUTH_HEADER_VALUE_NEGOTIATE);
                 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                 return false;
             }
@@ -247,7 +248,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
             if (log.isDebugEnabled()) {
                 log.debug(sm.getString("spnegoAuthenticator.ticketValidateFail"), e);
             }
-            response.setHeader("WWW-Authenticate", "Negotiate");
+            response.setHeader(AUTH_HEADER_NAME, AUTH_HEADER_VALUE_NEGOTIATE);
             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
             return false;
         } catch (PrivilegedActionException e) {
@@ -259,7 +260,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
             } else {
                 log.error(sm.getString("spnegoAuthenticator.serviceLoginFail"), e);
             }
-            response.setHeader("WWW-Authenticate", "Negotiate");
+            response.setHeader(AUTH_HEADER_NAME, AUTH_HEADER_VALUE_NEGOTIATE);
             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
             return false;
         } finally {
@@ -280,7 +281,7 @@ public class SpnegoAuthenticator extends AuthenticatorBase {
         }
 
         // Send response token on success and failure
-        response.setHeader("WWW-Authenticate", "Negotiate "
+        response.setHeader(AUTH_HEADER_NAME, AUTH_HEADER_VALUE_NEGOTIATE + " "
                 + Base64.encodeBase64String(outToken));
 
         if (principal != null) {
diff --git a/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java b/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java
index c378f9a3..fa5e9aac 100644
--- a/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java
+++ b/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java
@@ -22,7 +22,6 @@ import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -52,7 +51,18 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
 
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
-    private final Map<String,RegistrationContextImpl> registrations = new ConcurrentHashMap<>();
+    private static String DEFAULT_REGISTRATION_ID = getRegistrationID(null, null);
+
+    private final Map<String,RegistrationContextImpl> layerAppContextRegistrations =
+            new ConcurrentHashMap<>();
+    private final Map<String,RegistrationContextImpl> appContextRegistrations =
+            new ConcurrentHashMap<>();
+    private final Map<String,RegistrationContextImpl> layerRegistrations =
+            new ConcurrentHashMap<>();
+    // Note: Although there will only ever be a maximum of one entry in this
+    //       Map, use a ConcurrentHashMap for consistency
+    private volatile Map<String,RegistrationContextImpl> defaultRegistration =
+            new ConcurrentHashMap<>(1);
 
 
     public AuthConfigFactoryImpl() {
@@ -63,10 +73,12 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
     @Override
     public AuthConfigProvider getConfigProvider(String layer, String appContext,
             RegistrationListener listener) {
-        String registrationID = getRegistrationID(layer, appContext);
-        RegistrationContextImpl registrationContext = registrations.get(registrationID);
+        RegistrationContextImpl registrationContext =
+                findRegistrationContextImpl(layer, appContext);
         if (registrationContext != null) {
-            registrationContext.addListener(null);
+            RegistrationListenerWrapper wrapper = new RegistrationListenerWrapper(
+                    layer, appContext, listener);
+            registrationContext.addListener(wrapper);
             return registrationContext.getProvider();
         }
         return null;
@@ -109,8 +121,9 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
         }
 
         String registrationID = getRegistrationID(layer, appContext);
-        registrations.put(registrationID,
-                new RegistrationContextImpl(layer, appContext, description, true, provider, properties));
+        RegistrationContextImpl registrationContextImpl = new RegistrationContextImpl(
+                layer, appContext, description, true, provider, properties);
+        addRegistrationContextImpl(layer, appContext, registrationID, registrationContextImpl);
         return registrationID;
     }
 
@@ -123,22 +136,115 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
                     provider.getClass().getName(), layer, appContext));
         }
         String registrationID = getRegistrationID(layer, appContext);
-        registrations.put(registrationID,
-                new RegistrationContextImpl(layer, appContext, description, false, provider, null));
+        RegistrationContextImpl registrationContextImpl = new RegistrationContextImpl(
+                layer, appContext, description, false, provider, null);
+        addRegistrationContextImpl(layer, appContext, registrationID, registrationContextImpl);
         return registrationID;
     }
 
 
+    private void addRegistrationContextImpl(String layer, String appContext,
+            String registrationID, RegistrationContextImpl registrationContextImpl) {
+        RegistrationContextImpl previous = null;
+
+        // Add the registration, noting any registration it replaces
+        if (layer != null && appContext != null) {
+            previous = layerAppContextRegistrations.put(registrationID, registrationContextImpl);
+        } else if (layer == null && appContext != null) {
+            previous = appContextRegistrations.put(registrationID, registrationContextImpl);
+        } else if (layer != null && appContext == null) {
+            previous = layerRegistrations.put(registrationID, registrationContextImpl);
+        } else {
+            previous = defaultRegistration.put(registrationID, registrationContextImpl);
+        }
+
+        if (previous == null) {
+            // No match with previous registration so need to check listeners
+            // for all less specific registrations to see if they need to be
+            // notified of this new registration. That there is no exact match
+            // with a previous registration allows a few short-cuts to be taken
+            if (layer != null && appContext != null) {
+                // Need to check existing appContext registrations
+                // (and layer and default)
+                // appContext must match
+                RegistrationContextImpl registration =
+                        appContextRegistrations.get(getRegistrationID(null, appContext));
+                if (registration != null) {
+                    for (RegistrationListenerWrapper wrapper : registration.listeners) {
+                        if (layer.equals(wrapper.getMessageLayer()) &&
+                                appContext.equals(wrapper.getAppContext())) {
+                            registration.listeners.remove(wrapper);
+                            wrapper.listener.notify(wrapper.messageLayer, wrapper.appContext);
+                        }
+                    }
+                }
+            }
+            if (appContext != null) {
+                // Need to check existing layer registrations
+                // (and default)
+                // Need to check registrations for all layers
+                for (RegistrationContextImpl registration : layerRegistrations.values()) {
+                    for (RegistrationListenerWrapper wrapper : registration.listeners) {
+                        if (appContext.equals(wrapper.getAppContext())) {
+                            registration.listeners.remove(wrapper);
+                            wrapper.listener.notify(wrapper.messageLayer, wrapper.appContext);
+                        }
+                    }
+                }
+            }
+            if (layer != null || appContext != null) {
+                // Need to check default
+                for (RegistrationContextImpl registration : defaultRegistration.values()) {
+                    for (RegistrationListenerWrapper wrapper : registration.listeners) {
+                        if (appContext != null && appContext.equals(wrapper.getAppContext()) ||
+                                layer != null && layer.equals(wrapper.getMessageLayer())) {
+                            registration.listeners.remove(wrapper);
+                            wrapper.listener.notify(wrapper.messageLayer, wrapper.appContext);
+                        }
+                    }
+                }
+            }
+        } else {
+            // Replaced an existing registration so need to notify those listeners
+            for (RegistrationListenerWrapper wrapper : previous.listeners) {
+                previous.listeners.remove(wrapper);
+                wrapper.listener.notify(wrapper.messageLayer, wrapper.appContext);
+            }
+        }
+    }
+
+
     @Override
     public boolean removeRegistration(String registrationID) {
-        return registrations.remove(registrationID) != null;
+        RegistrationContextImpl registration = null;
+        if (DEFAULT_REGISTRATION_ID.equals(registrationID)) {
+            registration = defaultRegistration.remove(registrationID);
+        }
+        if (registration == null) {
+            registration = layerAppContextRegistrations.remove(registrationID);
+        }
+        if (registration == null) {
+            registration =  appContextRegistrations.remove(registrationID);
+        }
+        if (registration == null) {
+            registration = layerRegistrations.remove(registrationID);
+        }
+
+        if (registration == null) {
+            return false;
+        } else {
+            for (RegistrationListenerWrapper wrapper : registration.listeners) {
+                wrapper.getListener().notify(wrapper.getMessageLayer(), wrapper.getAppContext());
+            }
+            return true;
+        }
     }
 
 
     @Override
     public String[] detachListener(RegistrationListener listener, String layer, String appContext) {
         String registrationID = getRegistrationID(layer, appContext);
-        RegistrationContextImpl registrationContext = registrations.get(registrationID);
+        RegistrationContextImpl registrationContext = findRegistrationContextImpl(layer, appContext);
         if (registrationContext.removeListener(listener)) {
             return new String[] { registrationID };
         }
@@ -148,23 +254,47 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
 
     @Override
     public String[] getRegistrationIDs(AuthConfigProvider provider) {
+        List<String> result = new ArrayList<>();
         if (provider == null) {
-            return registrations.keySet().toArray(EMPTY_STRING_ARRAY);
+            result.addAll(layerAppContextRegistrations.keySet());
+            result.addAll(appContextRegistrations.keySet());
+            result.addAll(layerRegistrations.keySet());
+            if (defaultRegistration != null) {
+                result.add(DEFAULT_REGISTRATION_ID);
+            }
         } else {
-            List<String> results = new ArrayList<>();
-            for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) {
-                if (provider.equals(entry.getValue().getProvider())) {
-                    results.add(entry.getKey());
-                }
+            findProvider(provider, layerAppContextRegistrations, result);
+            findProvider(provider, appContextRegistrations, result);
+            findProvider(provider, layerRegistrations, result);
+            findProvider(provider, defaultRegistration, result);
+        }
+        return result.toArray(EMPTY_STRING_ARRAY);
+    }
+
+
+    private void findProvider(AuthConfigProvider provider,
+            Map<String,RegistrationContextImpl> registrations, List<String> result) {
+        for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) {
+            if (provider.equals(entry.getValue().getProvider())) {
+                result.add(entry.getKey());
             }
-            return results.toArray(EMPTY_STRING_ARRAY);
         }
     }
 
 
     @Override
     public RegistrationContext getRegistrationContext(String registrationID) {
-        return registrations.get(registrationID);
+        RegistrationContext result = defaultRegistration.get(registrationID);
+        if (result == null) {
+            result = layerAppContextRegistrations.get(registrationID);
+        }
+        if (result == null) {
+            result = appContextRegistrations.get(registrationID);
+        }
+        if (result == null) {
+            result = layerRegistrations.get(registrationID);
+        }
+        return result;
     }
 
 
@@ -174,8 +304,16 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
     }
 
 
-    private String getRegistrationID(String layer, String appContext) {
-        return layer + ":" + appContext;
+    private static String getRegistrationID(String layer, String appContext) {
+        if (layer != null && layer.length() == 0) {
+            throw new IllegalArgumentException(
+                    sm.getString("authConfigFactoryImpl.zeroLengthMessageLayer"));
+        }
+        if (appContext != null && appContext.length() == 0) {
+            throw new IllegalArgumentException(
+                    sm.getString("authConfigFactoryImpl.zeroLengthAppContext"));
+        }
+        return (layer == null ? "" : layer) + ":" + (appContext == null ? "" : appContext);
     }
 
 
@@ -200,24 +338,55 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
     private void savePersistentRegistrations() {
         synchronized (CONFIG_FILE_LOCK) {
             Providers providers = new Providers();
-            for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) {
-                if (entry.getValue().isPersistent()) {
-                    Provider provider = new Provider();
-                    provider.setAppContext(entry.getValue().getAppContext());
-                    provider.setClassName(entry.getValue().getProvider().getClass().getName());
-                    provider.setDescription(entry.getValue().getDescription());
-                    provider.setLayer(entry.getValue().getMessageLayer());
-                    for (Entry<String,String> property : entry.getValue().getProperties().entrySet()) {
-                        provider.addProperty(property.getKey(), property.getValue());
-                    }
-                    providers.addProvider(provider);
-                }
-            }
+            savePersistentProviders(providers, layerAppContextRegistrations);
+            savePersistentProviders(providers, appContextRegistrations);
+            savePersistentProviders(providers, layerRegistrations);
+            savePersistentProviders(providers, defaultRegistration);
             PersistentProviderRegistrations.writeProviders(providers, CONFIG_FILE);
         }
     }
 
 
+    private void savePersistentProviders(Providers providers,
+            Map<String,RegistrationContextImpl> registrations) {
+        for (Entry<String,RegistrationContextImpl> entry : registrations.entrySet()) {
+            savePersistentProvider(providers, entry.getValue());
+        }
+    }
+
+
+    private void savePersistentProvider(Providers providers,
+            RegistrationContextImpl registrationContextImpl) {
+        if (registrationContextImpl != null && registrationContextImpl.isPersistent()) {
+            Provider provider = new Provider();
+            provider.setAppContext(registrationContextImpl.getAppContext());
+            provider.setClassName(registrationContextImpl.getProvider().getClass().getName());
+            provider.setDescription(registrationContextImpl.getDescription());
+            provider.setLayer(registrationContextImpl.getMessageLayer());
+            for (Entry<String,String> property : registrationContextImpl.getProperties().entrySet()) {
+                provider.addProperty(property.getKey(), property.getValue());
+            }
+            providers.addProvider(provider);
+        }
+    }
+
+
+    private RegistrationContextImpl findRegistrationContextImpl(String layer, String appContext) {
+        RegistrationContextImpl result;
+        result = layerAppContextRegistrations.get(getRegistrationID(layer, appContext));
+        if (result == null) {
+            result = appContextRegistrations.get(getRegistrationID(null, appContext));
+        }
+        if (result == null) {
+            result = layerRegistrations.get(getRegistrationID(layer, null));
+        }
+        if (result == null) {
+            result = defaultRegistration.get(DEFAULT_REGISTRATION_ID);
+        }
+        return result;
+    }
+
+
     private static class RegistrationContextImpl implements RegistrationContext {
 
         private RegistrationContextImpl(String messageLayer, String appContext, String description,
@@ -240,7 +409,7 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
         private final boolean persistent;
         private final AuthConfigProvider provider;
         private final Map<String,String> properties;
-        private final List<RegistrationListener> listeners = new CopyOnWriteArrayList<>();
+        private final List<RegistrationListenerWrapper> listeners = new CopyOnWriteArrayList<>();
 
         @Override
         public String getMessageLayer() {
@@ -270,7 +439,7 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
         }
 
 
-        private void addListener(RegistrationListener listener) {
+        private void addListener(RegistrationListenerWrapper listener) {
             if (listener != null) {
                 listeners.add(listener);
             }
@@ -284,13 +453,43 @@ public class AuthConfigFactoryImpl extends AuthConfigFactory {
 
         private boolean removeListener(RegistrationListener listener) {
             boolean result = false;
-            Iterator<RegistrationListener> iter = listeners.iterator();
-            while (iter.hasNext()) {
-                if (iter.next().equals(listener)) {
-                    iter.remove();
+            for (RegistrationListenerWrapper wrapper : listeners) {
+                if (wrapper.getListener().equals(listener)) {
+                    listeners.remove(wrapper);
                 }
             }
             return result;
         }
     }
+
+
+    private static class RegistrationListenerWrapper {
+
+        private final String messageLayer;
+        private final String appContext;
+        private final RegistrationListener listener;
+
+
+        public RegistrationListenerWrapper(String messageLayer, String appContext,
+                RegistrationListener listener) {
+            this.messageLayer = messageLayer;
+            this.appContext = appContext;
+            this.listener = listener;
+        }
+
+
+        public String getMessageLayer() {
+            return messageLayer;
+        }
+
+
+        public String getAppContext() {
+            return appContext;
+        }
+
+
+        public RegistrationListener getListener() {
+            return listener;
+        }
+    }
 }
diff --git a/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties b/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties
index ac4b956a..744f079f 100644
--- a/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties
+++ b/java/org/apache/catalina/authenticator/jaspic/LocalStrings.properties
@@ -14,6 +14,8 @@
 # limitations under the License.
 
 authConfigFactoryImpl.load=Loading persistent provider registrations from [{0}]
+authConfigFactoryImpl.zeroLengthAppContext=A zero length application context name is not valid
+authConfigFactoryImpl.zeroLengthMessageLayer=A zero length message layer name is not valid
 authConfigFactoryImpl.registerClass=Registering class [{0}] for layer [{1}] and application context [{2}]
 authConfigFactoryImpl.registerInstance=Registering instance of type[{0}] for layer [{1}] and application context [{2}]
 
diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java b/java/org/apache/catalina/connector/CoyoteAdapter.java
index bf16b754..52064d92 100644
--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
+++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
@@ -126,28 +126,24 @@ public class CoyoteAdapter implements Adapter {
     // -------------------------------------------------------- Adapter Methods
 
     @Override
-    public boolean asyncDispatch(org.apache.coyote.Request req,
-            org.apache.coyote.Response res, SocketEvent status) throws Exception {
+    public boolean asyncDispatch(org.apache.coyote.Request req, org.apache.coyote.Response res,
+            SocketEvent status) throws Exception {
+
         Request request = (Request) req.getNote(ADAPTER_NOTES);
         Response response = (Response) res.getNote(ADAPTER_NOTES);
 
         if (request == null) {
-            throw new IllegalStateException(
-                    "Dispatch may only happen on an existing request.");
+            throw new IllegalStateException("Dispatch may only happen on an existing request.");
         }
+
         boolean success = true;
         AsyncContextImpl asyncConImpl = request.getAsyncContextInternal();
-        req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
+
+        req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
+
         try {
             if (!request.isAsync()) {
-                // Error or timeout - need to tell listeners the request is over
-                // Have to test this first since state may change while in this
-                // method and this is only required if entering this method in
-                // this state
-                Context ctxt = request.getMappingData().context;
-                if (ctxt != null) {
-                    ctxt.fireRequestDestroyEvent(request);
-                }
+                // Error or timeout
                 // Lift any suspension (e.g. if sendError() was used by an async
                 // request) to allow the response to be written to the client
                 response.setSuspended(false);
@@ -231,13 +227,14 @@ public class CoyoteAdapter implements Adapter {
             // if the application doesn't define one)?
             if (!request.isAsyncDispatching() && request.isAsync() &&
                     response.isErrorReportRequired()) {
-                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
+                connector.getService().getContainer().getPipeline().getFirst().invoke(
+                        request, response);
             }
 
             if (request.isAsyncDispatching()) {
-                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
-                Throwable t = (Throwable) request.getAttribute(
-                        RequestDispatcher.ERROR_EXCEPTION);
+                connector.getService().getContainer().getPipeline().getFirst().invoke(
+                        request, response);
+                Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
                 if (t != null) {
                     asyncConImpl.setErrorState(t, true);
                 }
@@ -297,19 +294,14 @@ public class CoyoteAdapter implements Adapter {
     }
 
 
-    /**
-     * Service method.
-     */
     @Override
-    public void service(org.apache.coyote.Request req,
-                        org.apache.coyote.Response res)
-        throws Exception {
+    public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
+            throws Exception {
 
         Request request = (Request) req.getNote(ADAPTER_NOTES);
         Response response = (Response) res.getNote(ADAPTER_NOTES);
 
         if (request == null) {
-
             // Create objects
             request = connector.createRequest();
             request.setCoyoteRequest(req);
@@ -325,9 +317,7 @@ public class CoyoteAdapter implements Adapter {
             res.setNote(ADAPTER_NOTES, response);
 
             // Set query string encoding
-            req.getParameters().setQueryStringEncoding
-                (connector.getURIEncoding());
-
+            req.getParameters().setQueryStringEncoding(connector.getURIEncoding());
         }
 
         if (connector.getXpoweredBy()) {
@@ -337,16 +327,19 @@ public class CoyoteAdapter implements Adapter {
         boolean async = false;
         boolean postParseSuccess = false;
 
+        req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
+
         try {
             // Parse and set Catalina and configuration specific
             // request parameters
-            req.getRequestProcessor().setWorkerThreadName(THREAD_NAME.get());
             postParseSuccess = postParseRequest(req, request, res, response);
             if (postParseSuccess) {
                 //check valves if we support async
-                request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
+                request.setAsyncSupported(
+                        connector.getService().getContainer().getPipeline().isAsyncSupported());
                 // Calling the container
-                connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
+                connector.getService().getContainer().getPipeline().getFirst().invoke(
+                        request, response);
             }
             if (request.isAsync()) {
                 async = true;
@@ -382,6 +375,17 @@ public class CoyoteAdapter implements Adapter {
         } catch (IOException e) {
             // Ignore
         } finally {
+            AtomicBoolean error = new AtomicBoolean(false);
+            res.action(ActionCode.IS_ERROR, error);
+
+            if (request.isAsyncCompleting() && error.get()) {
+                // Connection will be forcibly closed which will prevent
+                // completion happening at the usual point. Need to trigger
+                // call to onComplete() here.
+                res.action(ActionCode.ASYNC_POST_PROCESS,  null);
+                async = false;
+            }
+
             // Access log
             if (!async && postParseSuccess) {
                 // Log only if processing was invoked.
@@ -391,11 +395,9 @@ public class CoyoteAdapter implements Adapter {
             }
 
             req.getRequestProcessor().setWorkerThreadName(null);
-            AtomicBoolean error = new AtomicBoolean(false);
-            res.action(ActionCode.IS_ERROR, error);
 
             // Recycle the wrapper request and response
-            if (!async || error.get()) {
+            if (!async) {
                 request.recycle();
                 response.recycle();
             }
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index 19410f21..8d336a1f 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -80,8 +80,8 @@ import org.apache.catalina.core.ApplicationPushBuilder;
 import org.apache.catalina.core.ApplicationSessionCookieConfig;
 import org.apache.catalina.core.AsyncContextImpl;
 import org.apache.catalina.mapper.MappingData;
-import org.apache.catalina.servlet4preview.http.Mapping;
 import org.apache.catalina.servlet4preview.http.PushBuilder;
+import org.apache.catalina.servlet4preview.http.ServletMapping;
 import org.apache.catalina.util.ParameterMap;
 import org.apache.catalina.util.URLEncoder;
 import org.apache.coyote.ActionCode;
@@ -994,7 +994,14 @@ public class Request implements org.apache.catalina.servlet4preview.http.HttpSer
      */
     @Override
     public String getCharacterEncoding() {
-      return coyoteRequest.getCharacterEncoding();
+        String result = coyoteRequest.getCharacterEncoding();
+        if (result == null) {
+            Context context = getContext();
+            if (context != null) {
+                result =  context.getRequestCharacterEncoding();
+            }
+        }
+        return result;
     }
 
 
@@ -1884,24 +1891,35 @@ public class Request implements org.apache.catalina.servlet4preview.http.HttpSer
      *
      * @param principal The user Principal
      */
-    public void setUserPrincipal(Principal principal) {
-
-        if (Globals.IS_SECURITY_ENABLED){
-            HttpSession session = getSession(false);
-            if ( (subject != null) &&
-                 (!subject.getPrincipals().contains(principal)) ){
-                subject.getPrincipals().add(principal);
-            } else if (session != null &&
-                        session.getAttribute(Globals.SUBJECT_ATTR) == null) {
-                subject = new Subject();
+    public void setUserPrincipal(final Principal principal) {
+        if (Globals.IS_SECURITY_ENABLED) {
+            if (subject == null) {
+                final HttpSession session = getSession(false);
+                if (session == null) {
+                    // Cache the subject in the request
+                    subject = newSubject(principal);
+                } else {
+                    // Cache the subject in the request and the session
+                    subject = (Subject) session.getAttribute(Globals.SUBJECT_ATTR);
+                    if (subject == null) {
+                        subject = newSubject(principal);
+                        session.setAttribute(Globals.SUBJECT_ATTR, subject);
+                    } else {
+                        subject.getPrincipals().add(principal);
+                    }
+                }
+            } else {
                 subject.getPrincipals().add(principal);
             }
-            if (session != null){
-                session.setAttribute(Globals.SUBJECT_ATTR, subject);
-            }
         }
+        userPrincipal = principal;
+    }
+
 
-        this.userPrincipal = principal;
+    private Subject newSubject(final Principal principal) {
+        final Subject result = new Subject();
+        result.getPrincipals().add(principal);
+        return result;
     }
 
 
@@ -1911,28 +1929,21 @@ public class Request implements org.apache.catalina.servlet4preview.http.HttpSer
      * Pulled forward from Servlet 4.0. The method signature may be modified,
      * removed or replaced at any time until Servlet 4.0 becomes final.
      *
-     * @return {@code true} If this request supports server push
+     * @return A builder to use to construct the push request
      */
     @Override
-    public boolean isPushSupported() {
+    public PushBuilder getPushBuilder() {
         AtomicBoolean result = new AtomicBoolean();
         coyoteRequest.action(ActionCode.IS_PUSH_SUPPORTED, result);
-        return result.get();
+        if (result.get()) {
+            return new ApplicationPushBuilder(this);
+        } else {
+            return null;
+        }
     }
 
 
     /**
-     * Pulled forward from Servlet 4.0. The method signature may be modified,
-     * removed or replaced at any time until Servlet 4.0 becomes final.
-     *
-     * @return A builder to use to construct the push request
-     */
-    @Override
-    public PushBuilder getPushBuilder() {
-        return new ApplicationPushBuilder(this);
-    }
-
-    /**
      * {@inheritDoc}
      *
      * @since Servlet 3.1
@@ -2195,8 +2206,8 @@ public class Request implements org.apache.catalina.servlet4preview.http.HttpSer
 
 
     @Override
-    public Mapping getMapping() {
-        return applicationMapping.getMapping();
+    public ServletMapping getServletMapping() {
+        return applicationMapping.getServletMapping();
     }
 
 
diff --git a/java/org/apache/catalina/connector/RequestFacade.java b/java/org/apache/catalina/connector/RequestFacade.java
index 73975c31..d11eddde 100644
--- a/java/org/apache/catalina/connector/RequestFacade.java
+++ b/java/org/apache/catalina/connector/RequestFacade.java
@@ -42,8 +42,8 @@ import javax.servlet.http.Part;
 import org.apache.catalina.Globals;
 import org.apache.catalina.security.SecurityUtil;
 import org.apache.catalina.servlet4preview.http.HttpServletRequest;
-import org.apache.catalina.servlet4preview.http.Mapping;
 import org.apache.catalina.servlet4preview.http.PushBuilder;
+import org.apache.catalina.servlet4preview.http.ServletMapping;
 import org.apache.tomcat.util.res.StringManager;
 
 /**
@@ -1123,20 +1123,8 @@ public class RequestFacade implements HttpServletRequest {
      * removed or replaced at any time until Servlet 4.0 becomes final.
      */
     @Override
-    public Mapping getMapping() {
-        return request.getMapping();
-    }
-
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Pulled forward from Servlet 4.0. The method signature may be modified,
-     * removed or replaced at any time until Servlet 4.0 becomes final.
-     */
-    @Override
-    public boolean isPushSupported() {
-        return request.isPushSupported();
+    public ServletMapping getServletMapping() {
+        return request.getServletMapping();
     }
 
 
diff --git a/java/org/apache/catalina/connector/Response.java b/java/org/apache/catalina/connector/Response.java
index d00fd34c..d5528a74 100644
--- a/java/org/apache/catalina/connector/Response.java
+++ b/java/org/apache/catalina/connector/Response.java
@@ -556,7 +556,17 @@ public class Response implements HttpServletResponse {
      */
     @Override
     public String getCharacterEncoding() {
-        return (getCoyoteResponse().getCharacterEncoding());
+        String result = getCoyoteResponse().getCharacterEncoding();
+        if (result == null) {
+            Context context = getContext();
+            if (context != null) {
+                result =  context.getResponseCharacterEncoding();
+            }
+        }
+        if (result == null) {
+            result = org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING;
+        }
+        return result;
     }
 
 
diff --git a/java/org/apache/catalina/core/ApplicationContext.java b/java/org/apache/catalina/core/ApplicationContext.java
index 70d7ec41..42e486ca 100644
--- a/java/org/apache/catalina/core/ApplicationContext.java
+++ b/java/org/apache/catalina/core/ApplicationContext.java
@@ -47,6 +47,7 @@ import javax.servlet.ServletContextAttributeListener;
 import javax.servlet.ServletContextListener;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRegistration;
+import javax.servlet.ServletRegistration.Dynamic;
 import javax.servlet.ServletRequestAttributeListener;
 import javax.servlet.ServletRequestListener;
 import javax.servlet.SessionCookieConfig;
@@ -66,7 +67,7 @@ import org.apache.catalina.WebResourceRoot;
 import org.apache.catalina.Wrapper;
 import org.apache.catalina.connector.Connector;
 import org.apache.catalina.mapper.MappingData;
-import org.apache.catalina.servlet4preview.http.Mapping;
+import org.apache.catalina.servlet4preview.http.ServletMapping;
 import org.apache.catalina.util.ServerInfo;
 import org.apache.catalina.util.URLEncoder;
 import org.apache.tomcat.util.ExceptionUtils;
@@ -85,7 +86,7 @@ import org.apache.tomcat.util.res.StringManager;
  * @author Craig R. McClanahan
  * @author Remy Maucherat
  */
-public class ApplicationContext implements ServletContext {
+public class ApplicationContext implements org.apache.catalina.servlet4preview.ServletContext {
 
     protected static final boolean STRICT_SERVLET_COMPLIANCE;
 
@@ -482,7 +483,7 @@ public class ApplicationContext implements ServletContext {
         Wrapper wrapper = mappingData.wrapper;
         String wrapperPath = mappingData.wrapperPath.toString();
         String pathInfo = mappingData.pathInfo.toString();
-        Mapping mapping = (new ApplicationMapping(mappingData)).getMapping();
+        ServletMapping mapping = (new ApplicationMapping(mappingData)).getServletMapping();
 
         mappingData.recycle();
 
@@ -830,25 +831,61 @@ public class ApplicationContext implements ServletContext {
 
     @Override
     public ServletRegistration.Dynamic addServlet(String servletName, String className) {
-        return addServlet(servletName, className, null);
+        return addServlet(servletName, className, null, null);
     }
 
 
     @Override
     public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) {
-        return addServlet(servletName, null, servlet);
+        return addServlet(servletName, null, servlet, null);
     }
 
 
     @Override
     public ServletRegistration.Dynamic addServlet(String servletName,
             Class<? extends Servlet> servletClass) {
-        return addServlet(servletName, servletClass.getName(), null);
+        return addServlet(servletName, servletClass.getName(), null, null);
     }
 
 
-    private ServletRegistration.Dynamic addServlet(String servletName,
-            String servletClass, Servlet servlet) throws IllegalStateException {
+    @Override
+    public Dynamic addJspFile(String jspName, String jspFile) {
+
+        // jspName is validated in addServlet()
+        if (jspFile == null || !jspFile.startsWith("/")) {
+            throw new IllegalArgumentException(
+                    sm.getString("applicationContext.addJspFile.iae", jspFile));
+        }
+
+        String jspServletClassName = null;
+        Map<String,String> jspFileInitParams = new HashMap<>();
+
+        Wrapper jspServlet = (Wrapper) context.findChild("jsp");
+
+        if (jspServlet == null) {
+            // No JSP servlet currently defined.
+            // Use default JSP Servlet class name
+            jspServletClassName = Constants.JSP_SERVLET_CLASS;
+        } else {
+            // JSP Servlet defined.
+            // Use same JSP Servlet class name
+            jspServletClassName = jspServlet.getServletClass();
+            // Use same init parameters
+            String[] params = jspServlet.findInitParameters();
+            for (String param : params) {
+                jspFileInitParams.put(param, jspServlet.findInitParameter(param));
+            }
+        }
+
+        // Add init parameter to specify JSP file
+        jspFileInitParams.put("jspFile", jspFile);
+
+        return addServlet(jspName, jspServletClassName, null, jspFileInitParams);
+    }
+
+
+    private ServletRegistration.Dynamic addServlet(String servletName, String servletClass,
+            Servlet servlet, Map<String,String> initParams) throws IllegalStateException {
 
         if (servletName == null || servletName.equals("")) {
             throw new IllegalArgumentException(sm.getString(
@@ -888,6 +925,12 @@ public class ApplicationContext implements ServletContext {
             wrapper.setServlet(servlet);
         }
 
+        if (initParams != null) {
+            for (Map.Entry<String, String> initParam: initParams.entrySet()) {
+                wrapper.addInitParameter(initParam.getKey(), initParam.getValue());
+            }
+        }
+
         return context.dynamicServletAdded(wrapper);
     }
 
@@ -1228,6 +1271,60 @@ public class ApplicationContext implements ServletContext {
     }
 
 
+    @Override
+    public int getSessionTimeout() {
+        return context.getSessionTimeout();
+    }
+
+
+    @Override
+    public void setSessionTimeout(int sessionTimeout) {
+        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
+            throw new IllegalStateException(
+                    sm.getString("applicationContext.setSessionTimeout.ise",
+                            getContextPath()));
+        }
+
+        context.setSessionTimeout(sessionTimeout);
+    }
+
+
+    @Override
+    public String getRequestCharacterEncoding() {
+        return context.getRequestCharacterEncoding();
+    }
+
+
+    @Override
+    public void setRequestCharacterEncoding(String encoding) {
+        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
+            throw new IllegalStateException(
+                    sm.getString("applicationContext.setRequestEncoding.ise",
+                            getContextPath()));
+        }
+
+        context.setRequestCharacterEncoding(encoding);
+    }
+
+
+    @Override
+    public String getResponseCharacterEncoding() {
+        return context.getResponseCharacterEncoding();
+    }
+
+
+    @Override
+    public void setResponseCharacterEncoding(String encoding) {
+        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
+            throw new IllegalStateException(
+                    sm.getString("applicationContext.setResponseEncoding.ise",
+                            getContextPath()));
+        }
+
+        context.setResponseCharacterEncoding(encoding);
+    }
+
+
     // -------------------------------------------------------- Package Methods
     protected StandardContext getContext() {
         return this.context;
diff --git a/java/org/apache/catalina/core/ApplicationContextFacade.java b/java/org/apache/catalina/core/ApplicationContextFacade.java
index 0a0b57c1..5af105c9 100644
--- a/java/org/apache/catalina/core/ApplicationContextFacade.java
+++ b/java/org/apache/catalina/core/ApplicationContextFacade.java
@@ -41,6 +41,7 @@ import javax.servlet.Servlet;
 import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRegistration;
+import javax.servlet.ServletRegistration.Dynamic;
 import javax.servlet.SessionCookieConfig;
 import javax.servlet.SessionTrackingMode;
 import javax.servlet.descriptor.JspConfigDescriptor;
@@ -56,7 +57,7 @@ import org.apache.tomcat.util.ExceptionUtils;
  *
  * @author Remy Maucherat
  */
-public class ApplicationContextFacade implements ServletContext {
+public class ApplicationContextFacade implements org.apache.catalina.servlet4preview.ServletContext {
 
     // ---------------------------------------------------------- Attributes
     /**
@@ -538,6 +539,17 @@ public class ApplicationContextFacade implements ServletContext {
 
 
     @Override
+    public Dynamic addJspFile(String jspName, String jspFile) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (ServletRegistration.Dynamic) doPrivileged("addJspFile",
+                    new Object[]{jspName, jspFile});
+        } else {
+            return context.addJspFile(jspName, jspFile);
+        }
+    }
+
+
+    @Override
     @SuppressWarnings("unchecked") // doPrivileged() returns the correct type
     public <T extends Servlet> T createServlet(Class<T> c)
     throws ServletException {
@@ -769,6 +781,66 @@ public class ApplicationContextFacade implements ServletContext {
     }
 
 
+    @Override
+    public int getSessionTimeout() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return ((Integer) doPrivileged("getSessionTimeout", null)).intValue();
+        } else  {
+            return context.getSessionTimeout();
+        }
+    }
+
+
+    @Override
+    public void setSessionTimeout(int sessionTimeout) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("getSessionTimeout", new Object[] { Integer.valueOf(sessionTimeout) });
+        } else  {
+            context.setSessionTimeout(sessionTimeout);
+        }
+    }
+
+
+    @Override
+    public String getRequestCharacterEncoding() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getRequestCharacterEncoding", null);
+        } else  {
+            return context.getRequestCharacterEncoding();
+        }
+    }
+
+
+    @Override
+    public void setRequestCharacterEncoding(String encoding) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("setRequestCharacterEncoding", new Object[] { encoding });
+        } else  {
+            context.setRequestCharacterEncoding(encoding);
+        }
+    }
+
+
+    @Override
+    public String getResponseCharacterEncoding() {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            return (String) doPrivileged("getResponseCharacterEncoding", null);
+        } else  {
+            return context.getResponseCharacterEncoding();
+        }
+    }
+
+
+    @Override
+    public void setResponseCharacterEncoding(String encoding) {
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            doPrivileged("setResponseCharacterEncoding", new Object[] { encoding });
+        } else  {
+            context.setResponseCharacterEncoding(encoding);
+        }
+    }
+
+
     /**
      * Use reflection to invoke the requested method. Cache the method object
      * to speed up the process
diff --git a/java/org/apache/catalina/core/ApplicationDispatcher.java b/java/org/apache/catalina/core/ApplicationDispatcher.java
index 4b43dfd8..df71c3ea 100644
--- a/java/org/apache/catalina/core/ApplicationDispatcher.java
+++ b/java/org/apache/catalina/core/ApplicationDispatcher.java
@@ -44,7 +44,7 @@ import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.RequestFacade;
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.connector.ResponseFacade;
-import org.apache.catalina.servlet4preview.http.Mapping;
+import org.apache.catalina.servlet4preview.http.ServletMapping;
 import org.apache.tomcat.util.ExceptionUtils;
 import org.apache.tomcat.util.res.StringManager;
 
@@ -206,7 +206,7 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
      */
     public ApplicationDispatcher
         (Wrapper wrapper, String requestURI, String servletPath,
-         String pathInfo, String queryString, Mapping mapping, String name) {
+         String pathInfo, String queryString, ServletMapping mapping, String name) {
 
         super();
 
@@ -263,7 +263,7 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
     /**
      * The mapping for this RequestDispatcher.
      */
-    private final Mapping mapping;
+    private final ServletMapping mapping;
 
 
     /**
@@ -354,9 +354,7 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
         // Handle an HTTP path-based forward
         else {
 
-            ApplicationHttpRequest wrequest =
-                (ApplicationHttpRequest) wrapRequest(state);
-            String contextPath = context.getPath();
+            ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state);
             HttpServletRequest hrequest = state.hrequest;
             if (hrequest.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI) == null) {
                 wrequest.setAttribute(RequestDispatcher.FORWARD_REQUEST_URI,
@@ -369,19 +367,19 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
                                       hrequest.getPathInfo());
                 wrequest.setAttribute(RequestDispatcher.FORWARD_QUERY_STRING,
                                       hrequest.getQueryString());
-                Mapping mapping;
+                ServletMapping mapping;
                 if (hrequest instanceof org.apache.catalina.servlet4preview.http.HttpServletRequest) {
                     mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
-                            hrequest).getMapping();
+                            hrequest).getServletMapping();
                 } else {
-                    mapping = (new ApplicationMapping(null)).getMapping();
+                    mapping = (new ApplicationMapping(null)).getServletMapping();
                 }
                 wrequest.setAttribute(
                         org.apache.catalina.servlet4preview.RequestDispatcher.FORWARD_MAPPING,
                         mapping);
             }
 
-            wrequest.setContextPath(contextPath);
+            wrequest.setContextPath(context.getPath());
             wrequest.setRequestURI(requestURI);
             wrequest.setServletPath(servletPath);
             wrequest.setPathInfo(pathInfo);
@@ -623,17 +621,20 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
         // Create a wrapped response to use for this request
         wrapResponse(state);
 
-        ApplicationHttpRequest wrequest =
-            (ApplicationHttpRequest) wrapRequest(state);
+        ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state);
+        HttpServletRequest hrequest = state.hrequest;
 
-        if (queryString != null) {
-            wrequest.setQueryParams(queryString);
+        wrequest.setAttribute(Globals.DISPATCHER_TYPE_ATTR, DispatcherType.ASYNC);
+        wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, getCombinedPath());
+        ServletMapping mapping;
+        if (hrequest instanceof org.apache.catalina.servlet4preview.http.HttpServletRequest) {
+            mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
+                    hrequest).getServletMapping();
+        } else {
+            mapping = (new ApplicationMapping(null)).getServletMapping();
         }
-
-        wrequest.setAttribute(Globals.DISPATCHER_TYPE_ATTR,
-                DispatcherType.ASYNC);
-        wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
-                getCombinedPath());
+        wrequest.setAttribute(
+                org.apache.catalina.servlet4preview.AsyncContext.ASYNC_MAPPING, mapping);
 
         wrequest.setContextPath(context.getPath());
         wrequest.setRequestURI(requestURI);
@@ -643,6 +644,7 @@ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher
             wrequest.setQueryString(queryString);
             wrequest.setQueryParams(queryString);
         }
+        wrequest.setMapping(this.mapping);
 
         invoke(state.outerRequest, state.outerResponse, state);
     }
diff --git a/java/org/apache/catalina/core/ApplicationHttpRequest.java b/java/org/apache/catalina/core/ApplicationHttpRequest.java
index 279a73df..cad7091e 100644
--- a/java/org/apache/catalina/core/ApplicationHttpRequest.java
+++ b/java/org/apache/catalina/core/ApplicationHttpRequest.java
@@ -37,8 +37,8 @@ import org.apache.catalina.Context;
 import org.apache.catalina.Globals;
 import org.apache.catalina.Manager;
 import org.apache.catalina.Session;
-import org.apache.catalina.servlet4preview.http.Mapping;
 import org.apache.catalina.servlet4preview.http.PushBuilder;
+import org.apache.catalina.servlet4preview.http.ServletMapping;
 import org.apache.catalina.util.ParameterMap;
 import org.apache.tomcat.util.buf.B2CConverter;
 import org.apache.tomcat.util.buf.MessageBytes;
@@ -189,7 +189,7 @@ class ApplicationHttpRequest
     /**
      * The mapping for this request.
      */
-    private Mapping mapping = null;
+    private ServletMapping mapping = null;
 
 
     /**
@@ -523,7 +523,7 @@ class ApplicationHttpRequest
 
 
     @Override
-    public Mapping getMapping() {
+    public ServletMapping getServletMapping() {
         return mapping;
     }
 
@@ -707,9 +707,9 @@ class ApplicationHttpRequest
         requestURI = request.getRequestURI();
         servletPath = request.getServletPath();
         if (request instanceof org.apache.catalina.servlet4preview.http.HttpServletRequest) {
-            mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest) request).getMapping();
+            mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest) request).getServletMapping();
         } else {
-            mapping = (new ApplicationMapping(null)).getMapping();
+            mapping = (new ApplicationMapping(null)).getServletMapping();
         }
     }
 
@@ -769,7 +769,7 @@ class ApplicationHttpRequest
     }
 
 
-    void setMapping(Mapping mapping) {
+    void setMapping(ServletMapping mapping) {
         this.mapping = mapping;
     }
 
diff --git a/java/org/apache/catalina/core/ApplicationMapping.java b/java/org/apache/catalina/core/ApplicationMapping.java
index 17bcbe2b..dabf55e3 100644
--- a/java/org/apache/catalina/core/ApplicationMapping.java
+++ b/java/org/apache/catalina/core/ApplicationMapping.java
@@ -17,20 +17,20 @@
 package org.apache.catalina.core;
 
 import org.apache.catalina.mapper.MappingData;
-import org.apache.catalina.servlet4preview.http.Mapping;
 import org.apache.catalina.servlet4preview.http.MappingMatch;
+import org.apache.catalina.servlet4preview.http.ServletMapping;
 
 public class ApplicationMapping {
 
     private final MappingData mappingData;
 
-    private volatile Mapping mapping = null;
+    private volatile ServletMapping mapping = null;
 
     public ApplicationMapping(MappingData mappingData) {
         this.mappingData = mappingData;
     }
 
-    public Mapping getMapping() {
+    public ServletMapping getServletMapping() {
         if (mapping == null) {
             if (mappingData == null) {
                 mapping = new MappingImpl("", "", MappingMatch.UNKNOWN, "");
@@ -46,20 +46,20 @@ public class ApplicationMapping {
                         mapping = new MappingImpl("", "", mappingData.matchType, servletName);
                         break;
                     case DEFAULT:
-                        mapping = new MappingImpl("/", "/", mappingData.matchType, servletName);
+                        mapping = new MappingImpl("", "/", mappingData.matchType, servletName);
                         break;
                     case EXACT:
-                        mapping = new MappingImpl(mappingData.wrapperPath.toString(),
+                        mapping = new MappingImpl(mappingData.wrapperPath.toString().substring(1),
                                 mappingData.wrapperPath.toString(), mappingData.matchType, servletName);
                         break;
                     case EXTENSION:
                         String path = mappingData.wrapperPath.toString();
                         int extIndex = path.lastIndexOf('.');
-                        mapping = new MappingImpl(path.substring(0, extIndex),
+                        mapping = new MappingImpl(path.substring(1, extIndex),
                                 "*" + path.substring(extIndex), mappingData.matchType, servletName);
                         break;
                     case PATH:
-                        mapping = new MappingImpl(mappingData.pathInfo.toString(),
+                        mapping = new MappingImpl(mappingData.pathInfo.toString().substring(1),
                                 mappingData.wrapperPath.toString() + "/*",
                                 mappingData.matchType, servletName);
                         break;
@@ -77,7 +77,7 @@ public class ApplicationMapping {
         mapping = null;
     }
 
-    private static class MappingImpl implements Mapping {
+    private static class MappingImpl implements ServletMapping {
 
         private final String matchValue;
         private final String pattern;
diff --git a/java/org/apache/catalina/core/ApplicationPushBuilder.java b/java/org/apache/catalina/core/ApplicationPushBuilder.java
index 1c29eacd..6d907540 100644
--- a/java/org/apache/catalina/core/ApplicationPushBuilder.java
+++ b/java/org/apache/catalina/core/ApplicationPushBuilder.java
@@ -21,6 +21,7 @@ import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -38,16 +39,26 @@ import org.apache.catalina.connector.Request;
 import org.apache.catalina.servlet4preview.http.PushBuilder;
 import org.apache.catalina.util.SessionConfig;
 import org.apache.coyote.ActionCode;
-import org.apache.coyote.PushToken;
 import org.apache.tomcat.util.buf.B2CConverter;
 import org.apache.tomcat.util.buf.HexUtils;
 import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap;
 import org.apache.tomcat.util.http.CookieProcessor;
+import org.apache.tomcat.util.http.parser.HttpParser;
 import org.apache.tomcat.util.res.StringManager;
 
 public class ApplicationPushBuilder implements PushBuilder {
 
     private static final StringManager sm = StringManager.getManager(ApplicationPushBuilder.class);
+    private static final Set<String> DISALLOWED_METHODS = new HashSet<>();
+
+    static {
+        DISALLOWED_METHODS.add("POST");
+        DISALLOWED_METHODS.add("PUT");
+        DISALLOWED_METHODS.add("DELETE");
+        DISALLOWED_METHODS.add("CONNECT");
+        DISALLOWED_METHODS.add("OPTIONS");
+        DISALLOWED_METHODS.add("TRACE");
+    }
 
     private final HttpServletRequest baseRequest;
     private final Request catalinaRequest;
@@ -61,7 +72,7 @@ public class ApplicationPushBuilder implements PushBuilder {
     private final List<Cookie> cookies = new ArrayList<>();
     private String method = "GET";
     private String path;
-    private String etag;
+    private String eTag;
     private String lastModified;
     private String queryString;
     private String sessionId;
@@ -193,6 +204,18 @@ public class ApplicationPushBuilder implements PushBuilder {
 
     @Override
     public ApplicationPushBuilder method(String method) {
+        String upperMethod = method.trim().toUpperCase();
+        if (DISALLOWED_METHODS.contains(upperMethod)) {
+            throw new IllegalArgumentException(
+                    sm.getString("applicationPushBuilder.methodInvalid", upperMethod));
+        }
+        // Check a token was supplied
+        for (char c : upperMethod.toCharArray()) {
+            if (!HttpParser.isToken(c)) {
+                throw new IllegalArgumentException(
+                        sm.getString("applicationPushBuilder.methodNotToken", upperMethod));
+            }
+        }
         this.method = method;
         return this;
     }
@@ -205,15 +228,15 @@ public class ApplicationPushBuilder implements PushBuilder {
 
 
     @Override
-    public ApplicationPushBuilder etag(String etag) {
-        this.etag = etag;
+    public ApplicationPushBuilder eTag(String eTag) {
+        this.eTag = eTag;
         return this;
     }
 
 
     @Override
-    public String getEtag() {
-        return etag;
+    public String getETag() {
+        return eTag;
     }
 
 
@@ -323,7 +346,7 @@ public class ApplicationPushBuilder implements PushBuilder {
 
 
     @Override
-    public boolean push() {
+    public void push() {
         if (path == null) {
             throw new IllegalStateException(sm.getString("pushBuilder.noPath"));
         }
@@ -382,8 +405,8 @@ public class ApplicationPushBuilder implements PushBuilder {
         }
 
         if (conditional) {
-            if (etag != null) {
-                setHeader("if-none-match", etag);
+            if (eTag != null) {
+                setHeader("if-none-match", eTag);
             } else if (lastModified != null) {
                 setHeader("if-modified-since", lastModified);
             }
@@ -393,18 +416,15 @@ public class ApplicationPushBuilder implements PushBuilder {
         setHeader("cookie", generateCookieHeader(cookies,
                 catalinaRequest.getContext().getCookieProcessor()));
 
-        PushToken pushToken = new PushToken(pushTarget);
-        coyoteRequest.action(ActionCode.PUSH_REQUEST, pushToken);
+        coyoteRequest.action(ActionCode.PUSH_REQUEST, pushTarget);
 
         // Reset for next call to this method
         pushTarget = null;
         path = null;
-        etag = null;
+        eTag = null;
         lastModified = null;
         headers.remove("if-none-match");
         headers.remove("if-modified-since");
-
-        return pushToken.getResult();
     }
 
 
diff --git a/java/org/apache/catalina/core/AsyncContextImpl.java b/java/org/apache/catalina/core/AsyncContextImpl.java
index dcc09026..49718c29 100644
--- a/java/org/apache/catalina/core/AsyncContextImpl.java
+++ b/java/org/apache/catalina/core/AsyncContextImpl.java
@@ -41,6 +41,7 @@ import org.apache.catalina.Globals;
 import org.apache.catalina.Host;
 import org.apache.catalina.Valve;
 import org.apache.catalina.connector.Request;
+import org.apache.catalina.util.URLEncoder;
 import org.apache.coyote.ActionCode;
 import org.apache.coyote.AsyncContextCallback;
 import org.apache.coyote.RequestInfo;
@@ -111,6 +112,7 @@ public class AsyncContextImpl implements AsyncContext, AsyncContextCallback {
                 }
             }
         } finally {
+            context.fireRequestDestroyEvent(request.getRequest());
             clearServletRequestResponse();
             context.unbind(Globals.IS_SECURITY_ENABLED, oldCL);
         }
@@ -161,6 +163,9 @@ public class AsyncContextImpl implements AsyncContext, AsyncContextCallback {
         if (pathInfo != null) {
             path += pathInfo;
         }
+        if (this.context.getDispatchersUseEncodedPaths()) {
+            path = URLEncoder.DEFAULT.encode(path, "UTF-8");
+        }
         dispatch(path);
     }
 
diff --git a/java/org/apache/catalina/core/DefaultInstanceManager.java b/java/org/apache/catalina/core/DefaultInstanceManager.java
index 7b324d1b..a84164c6 100644
--- a/java/org/apache/catalina/core/DefaultInstanceManager.java
+++ b/java/org/apache/catalina/core/DefaultInstanceManager.java
@@ -34,7 +34,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
-import java.util.WeakHashMap;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
@@ -53,6 +52,7 @@ import org.apache.catalina.util.Introspection;
 import org.apache.juli.logging.Log;
 import org.apache.tomcat.InstanceManager;
 import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.collections.ManagedConcurrentWeakHashMap;
 import org.apache.tomcat.util.res.StringManager;
 
 public class DefaultInstanceManager implements InstanceManager {
@@ -74,8 +74,8 @@ public class DefaultInstanceManager implements InstanceManager {
     protected final boolean privileged;
     protected final boolean ignoreAnnotations;
     private final Set<String> restrictedClasses;
-    private final Map<Class<?>, AnnotationCacheEntry[]> annotationCache =
-        new WeakHashMap<>();
+    private final ManagedConcurrentWeakHashMap<Class<?>, AnnotationCacheEntry[]> annotationCache =
+            new ManagedConcurrentWeakHashMap<>();
     private final Map<String, String> postConstructMethods;
     private final Map<String, String> preDestroyMethods;
 
@@ -190,10 +190,7 @@ public class DefaultInstanceManager implements InstanceManager {
 
         // At the end the postconstruct annotated
         // method is invoked
-        AnnotationCacheEntry[] annotations;
-        synchronized (annotationCache) {
-            annotations = annotationCache.get(clazz);
-        }
+        AnnotationCacheEntry[] annotations = annotationCache.get(clazz);
         for (AnnotationCacheEntry entry : annotations) {
             if (entry.getType() == AnnotationCacheEntryType.POST_CONSTRUCT) {
                 Method postConstruct = getMethod(clazz, entry);
@@ -227,10 +224,7 @@ public class DefaultInstanceManager implements InstanceManager {
 
         // At the end the postconstruct annotated
         // method is invoked
-        AnnotationCacheEntry[] annotations = null;
-        synchronized (annotationCache) {
-            annotations = annotationCache.get(clazz);
-        }
+        AnnotationCacheEntry[] annotations = annotationCache.get(clazz);
         if (annotations == null) {
             // instance not created through the instance manager
             return;
@@ -249,6 +243,11 @@ public class DefaultInstanceManager implements InstanceManager {
     }
 
 
+    public void backgroundProcess() {
+        annotationCache.maintain();
+    }
+
+
     /**
      * Make sure that the annotations cache has been populated for the provided
      * class.
@@ -268,10 +267,7 @@ public class DefaultInstanceManager implements InstanceManager {
         List<AnnotationCacheEntry> annotations = null;
 
         while (clazz != null) {
-            AnnotationCacheEntry[] annotationsArray = null;
-            synchronized (annotationCache) {
-                annotationsArray = annotationCache.get(clazz);
-            }
+            AnnotationCacheEntry[] annotationsArray = annotationCache.get(clazz);
             if (annotationsArray == null) {
                 if (annotations == null) {
                     annotations = new ArrayList<>();
@@ -446,10 +442,7 @@ public class DefaultInstanceManager implements InstanceManager {
         Class<?> clazz = instance.getClass();
 
         while (clazz != null) {
-            AnnotationCacheEntry[] annotations;
-            synchronized (annotationCache) {
-                annotations = annotationCache.get(clazz);
-            }
+            AnnotationCacheEntry[] annotations = annotationCache.get(clazz);
             for (AnnotationCacheEntry entry : annotations) {
                 if (entry.getType() == AnnotationCacheEntryType.SETTER) {
                     lookupMethodResource(context, instance,
@@ -472,9 +465,7 @@ public class DefaultInstanceManager implements InstanceManager {
      * @return the cache size
      */
     protected int getAnnotationCacheSize() {
-        synchronized (annotationCache) {
-            return annotationCache.size();
-        }
+        return annotationCache.size();
     }
 
 
diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index 64c22d95..b4b74506 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -14,6 +14,7 @@
 # limitations under the License.
 
 applicationContext.addFilter.ise=Filters cannot be added to context {0} as the context has been initialised
+applicationContext.addJspFile.iae=The JSP file [{0}] is not valid
 applicationContext.addListener.iae.cnfe=Unable to create an instance of type [{0}]
 applicationContext.addListener.iae.init=Unable to add an instance of type [{0}] as a listener
 applicationContext.addListener.iae.wrongType=The type specified [{0}] is not one of the expected listener types
@@ -32,6 +33,9 @@ applicationContext.role.iae=An individual role to declare for context [{0}] may
 applicationContext.roles.iae=Array of roles to declare for context [{0}] cannot be null
 applicationContext.setAttribute.namenull=Name cannot be null
 applicationContext.setInitParam.ise=Initialization parameters cannot be set after the context has been initialized
+applicationContext.setRequestEncoding.ise=The request encoding cannot be set for context {0} as the context has been initialised
+applicationContext.setResponseEncoding.ise=The response encoding cannot be set for context {0} as the context has been initialised
+applicationContext.setSessionTimeout.ise=The session timeout cannot be set for context {0} as the context has been initialised
 applicationContext.setSessionTracking.ise=The session tracking modes for context {0} cannot be set whilst the context is running
 applicationContext.setSessionTracking.iae.invalid=The session tracking mode {0} requested for context {1} is not supported by that context
 applicationContext.setSessionTracking.iae.ssl=The session tracking modes requested for context {0} included SSL and at least one other mode. SSL may not be configured with other modes.
@@ -51,6 +55,8 @@ applicationFilterConfig.release=Failed to destroy the filter named [{0}] of type
 applicationFilterRegistration.nullInitParam=Unable to set initialisation parameter for filter due to null name and/or value. Name [{0}], Value [{1}]
 applicationFilterRegistration.nullInitParams=Unable to set initialisation parameters for filter due to null name and/or value. Name [{0}], Value [{1}]
 
+applicationPushBuilder.methodInvalid=The HTTP method for a push request must be both cacheable and safe but [{0}] is not
+applicationPushBuilder.methodNotToken=HTTP methods must be tokens but [{0}] contains a non-token character
 applicationPushBuilder.noCoyoteRequest=Unable to find the underlying Coyote request object (which is required to create a push request) from the request of type [{0}]
 
 applicationServletRegistration.setServletSecurity.iae=Null constraint specified for servlet [{0}] deployed to context with name [{1}]
@@ -112,6 +118,7 @@ pushBuilder.noPath=It is illegal to call push() before setting a path
 standardContext.invalidWrapperClass={0} is not a subclass of StandardWrapper
 standardContext.applicationListener=Error configuring application listener of class {0}
 standardContext.applicationSkipped=Skipped installing application listeners due to previous error(s)
+standardContext.backgroundProcess.instanceManager=Exception processing instance manager {0} background process
 standardContext.backgroundProcess.loader=Exception processing loader {0} background process
 standardContext.backgroundProcess.manager=Exception processing manager {0} background process
 standardContext.backgroundProcess.resources=Exception processing resources {0} background process
diff --git a/java/org/apache/catalina/core/StandardContext.java b/java/org/apache/catalina/core/StandardContext.java
index b38be35b..397749b3 100644
--- a/java/org/apache/catalina/core/StandardContext.java
+++ b/java/org/apache/catalina/core/StandardContext.java
@@ -809,10 +809,37 @@ public class StandardContext extends ContainerBase
 
     private boolean dispatchersUseEncodedPaths = true;
 
+    private String requestEncoding = null;
+
+    private String responseEncoding = null;
 
     // ----------------------------------------------------- Context Properties
 
     @Override
+    public String getRequestCharacterEncoding() {
+        return requestEncoding;
+    }
+
+
+    @Override
+    public void setRequestCharacterEncoding(String requestEncoding) {
+        this.requestEncoding = requestEncoding;
+    }
+
+
+    @Override
+    public String getResponseCharacterEncoding() {
+        return responseEncoding;
+    }
+
+
+    @Override
+    public void setResponseCharacterEncoding(String responseEncoding) {
+        this.responseEncoding = responseEncoding;
+    }
+
+
+    @Override
     public void setDispatchersUseEncodedPaths(boolean dispatchersUseEncodedPaths) {
         this.dispatchersUseEncodedPaths = dispatchersUseEncodedPaths;
     }
@@ -5568,6 +5595,16 @@ public class StandardContext extends ContainerBase
                         resources), e);
             }
         }
+        InstanceManager instanceManager = getInstanceManager();
+        if (instanceManager instanceof DefaultInstanceManager) {
+            try {
+                ((DefaultInstanceManager)instanceManager).backgroundProcess();
+            } catch (Exception e) {
+                log.warn(sm.getString(
+                        "standardContext.backgroundProcess.instanceManager",
+                        resources), e);
+            }
+        }
         super.backgroundProcess();
     }
 
@@ -6392,7 +6429,7 @@ public class StandardContext extends ContainerBase
 
 
     private static class NoPluggabilityServletContext
-            implements ServletContext {
+            implements org.apache.catalina.servlet4preview.ServletContext {
 
         private final ServletContext sc;
 
@@ -6567,6 +6604,12 @@ public class StandardContext extends ContainerBase
         }
 
         @Override
+        public Dynamic addJspFile(String jspName, String jspFile) {
+            throw new UnsupportedOperationException(
+                    sm.getString("noPluggabilityServletContext.notAllowed"));
+        }
+
+        @Override
         public <T extends Servlet> T createServlet(Class<T> c)
                 throws ServletException {
             throw new UnsupportedOperationException(
@@ -6697,5 +6740,41 @@ public class StandardContext extends ContainerBase
         public String getVirtualServerName() {
             return sc.getVirtualServerName();
         }
+
+        @Override
+        public int getSessionTimeout() {
+            throw new UnsupportedOperationException(
+                    sm.getString("noPluggabilityServletContext.notAllowed"));
+        }
+
+        @Override
+        public void setSessionTimeout(int sessionTimeout) {
+            throw new UnsupportedOperationException(
+                    sm.getString("noPluggabilityServletContext.notAllowed"));
+        }
+
+        @Override
+        public String getRequestCharacterEncoding() {
+            throw new UnsupportedOperationException(
+                    sm.getString("noPluggabilityServletContext.notAllowed"));
+        }
+
+        @Override
+        public void setRequestCharacterEncoding(String encoding) {
+            throw new UnsupportedOperationException(
+                    sm.getString("noPluggabilityServletContext.notAllowed"));
+        }
+
+        @Override
+        public String getResponseCharacterEncoding() {
+            throw new UnsupportedOperationException(
+                    sm.getString("noPluggabilityServletContext.notAllowed"));
+        }
+
+        @Override
+        public void setResponseCharacterEncoding(String encoding) {
+            throw new UnsupportedOperationException(
+                    sm.getString("noPluggabilityServletContext.notAllowed"));
+        }
     }
 }
diff --git a/java/org/apache/catalina/core/StandardHostValve.java b/java/org/apache/catalina/core/StandardHostValve.java
index 8fe3e1a9..6b164feb 100644
--- a/java/org/apache/catalina/core/StandardHostValve.java
+++ b/java/org/apache/catalina/core/StandardHostValve.java
@@ -123,7 +123,7 @@ final class StandardHostValve extends ValveBase {
         try {
             context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);
 
-            if (!asyncAtStart && !context.fireRequestInitEvent(request)) {
+            if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) {
                 // Don't fire listeners during async processing (the listener
                 // fired for the request that called startAsync()).
                 // If a request init listener throws an exception, the request
@@ -178,8 +178,8 @@ final class StandardHostValve extends ValveBase {
                 }
             }
 
-            if (!request.isAsync() && (!asyncAtStart || !response.isErrorReportRequired())) {
-                context.fireRequestDestroyEvent(request);
+            if (!request.isAsync() && !asyncAtStart) {
+                context.fireRequestDestroyEvent(request.getRequest());
             }
         } finally {
             // Access a session (if present) to update last accessed time, based
diff --git a/java/org/apache/catalina/filters/CorsFilter.java b/java/org/apache/catalina/filters/CorsFilter.java
index e9c3c751..fcb8d2d8 100644
--- a/java/org/apache/catalina/filters/CorsFilter.java
+++ b/java/org/apache/catalina/filters/CorsFilter.java
@@ -76,7 +76,7 @@ import org.apache.tomcat.util.res.StringManager;
  * @see <a href="http://www.w3.org/TR/cors/";>CORS specification</a>
  *
  */
-public final class CorsFilter implements Filter {
+public class CorsFilter implements Filter {
 
     private static final Log log = LogFactory.getLog(CorsFilter.class);
     private static final StringManager sm = StringManager.getManager(CorsFilter.class);
diff --git a/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java b/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java
index cfa9f3fe..495ce611 100644
--- a/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java
+++ b/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java
@@ -95,6 +95,14 @@ public class ClusterSingleSignOn extends SingleSignOn implements ClusterValve, M
         this.terminateOnStartFailure = terminateOnStartFailure;
     }
 
+    private long accessTimeout = 5000;
+    public long getAccessTimeout() {
+        return accessTimeout;
+    }
+
+    public void setAccessTimeout(long accessTimeout) {
+        this.accessTimeout = accessTimeout;
+    }
 
     // ---------------------------------------------------- SingleSignOn Methods
 
@@ -163,6 +171,7 @@ public class ClusterSingleSignOn extends SingleSignOn implements ClusterValve, M
                     this, cluster.getChannel(), rpcTimeout, cluster.getClusterName() + "-SSO-cache",
                     cls, terminateOnStartFailure);
             cache.setChannelSendOptions(mapSendOptions);
+            cache.setAccessTimeout(accessTimeout);
             this.cache = cache;
         } catch (Throwable t) {
             ExceptionUtils.handleThrowable(t);
diff --git a/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml b/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml
index 593bc527..72a3d978 100644
--- a/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/ha/authenticator/mbeans-descriptors.xml
@@ -53,5 +53,9 @@
       name="terminateOnStartFailure"
       description="Flag for whether to terminate this map that failed to start."
       type="boolean"/>
+    <attribute
+      name="accessTimeout"
+      description="The timeout for a ping message in replication map."
+      type="long"/>
   </mbean>
 </mbeans-descriptors>
diff --git a/java/org/apache/catalina/ha/context/ReplicatedContext.java b/java/org/apache/catalina/ha/context/ReplicatedContext.java
index 34ac9f74..9c9eab06 100644
--- a/java/org/apache/catalina/ha/context/ReplicatedContext.java
+++ b/java/org/apache/catalina/ha/context/ReplicatedContext.java
@@ -56,19 +56,16 @@ public class ReplicatedContext extends StandardContext implements MapOwner {
      */
     @Override
     protected synchronized void startInternal() throws LifecycleException {
-
+        super.startInternal();
         try {
             CatalinaCluster catclust = (CatalinaCluster)this.getCluster();
-            if (this.context == null) this.context = new ReplApplContext(this);
             if ( catclust != null ) {
                 ReplicatedMap<String,Object> map = new ReplicatedMap<>(
                         this, catclust.getChannel(),DEFAULT_REPL_TIMEOUT,
                         getName(),getClassLoaders());
                 map.setChannelSendOptions(mapSendOptions);
                 ((ReplApplContext)this.context).setAttributeMap(map);
-                if (getAltDDName() != null) context.setAttribute(Globals.ALT_DD_ATTR, getAltDDName());
             }
-            super.startInternal();
         }  catch ( Exception x ) {
             log.error(sm.getString("replicatedContext.startUnable", getName()),x);
             throw new LifecycleException(sm.getString("replicatedContext.startFailed", getName()),x);
diff --git a/java/org/apache/catalina/ha/session/BackupManager.java b/java/org/apache/catalina/ha/session/BackupManager.java
index 5fb90e8a..07730172 100644
--- a/java/org/apache/catalina/ha/session/BackupManager.java
+++ b/java/org/apache/catalina/ha/session/BackupManager.java
@@ -69,6 +69,11 @@ public class BackupManager extends ClusterManagerBase
     private boolean terminateOnStartFailure = false;
 
     /**
+     * The timeout for a ping message in replication map.
+     */
+    private long accessTimeout = 5000;
+
+    /**
      * Constructor, just calls super()
      *
      */
@@ -144,6 +149,7 @@ public class BackupManager extends ClusterManagerBase
                     this, cluster.getChannel(), rpcTimeout, getMapName(),
                     getClassLoaders(), terminateOnStartFailure);
             map.setChannelSendOptions(mapSendOptions);
+            map.setAccessTimeout(accessTimeout);
             this.sessions = map;
         }  catch ( Exception x ) {
             log.error(sm.getString("backupManager.startUnable", getName()),x);
@@ -215,6 +221,14 @@ public class BackupManager extends ClusterManagerBase
         return terminateOnStartFailure;
     }
 
+    public long getAccessTimeout() {
+        return accessTimeout;
+    }
+
+    public void setAccessTimeout(long accessTimeout) {
+        this.accessTimeout = accessTimeout;
+    }
+
     @Override
     public String[] getInvalidatedSessions() {
         return new String[0];
@@ -227,6 +241,7 @@ public class BackupManager extends ClusterManagerBase
         result.mapSendOptions = mapSendOptions;
         result.rpcTimeout = rpcTimeout;
         result.terminateOnStartFailure = terminateOnStartFailure;
+        result.accessTimeout = accessTimeout;
         return result;
     }
 
diff --git a/java/org/apache/catalina/ha/session/mbeans-descriptors.xml b/java/org/apache/catalina/ha/session/mbeans-descriptors.xml
index 0b6cf22f..33bdba08 100644
--- a/java/org/apache/catalina/ha/session/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/ha/session/mbeans-descriptors.xml
@@ -536,6 +536,10 @@
       name="warnOnSessionAttributeFilterFailure"
       description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?"
       type="boolean"/>
+    <attribute
+      name="accessTimeout"
+      description="The timeout for a ping message in replication map."
+      type="long"/>
     <operation
       name="expireSession"
       description="Expired the given session"
diff --git a/java/org/apache/catalina/realm/RealmBase.java b/java/org/apache/catalina/realm/RealmBase.java
index 080d6a10..19a009ba 100644
--- a/java/org/apache/catalina/realm/RealmBase.java
+++ b/java/org/apache/catalina/realm/RealmBase.java
@@ -910,32 +910,30 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
 
 
     /**
-     * Return <code>true</code> if the specified Principal has the specified
-     * security role, within the context of this Realm; otherwise return
-     * <code>false</code>.  This method can be overridden by Realm
-     * implementations, but the default is adequate when an instance of
-     * <code>GenericPrincipal</code> is used to represent authenticated
-     * Principals from this Realm.
+     * {@inheritDoc}
      *
-     * @param principal Principal for whom the role is to be checked
-     * @param role Security role to be checked
+     * This method or {@link #hasRoleInternal(Principal,
+     * String)} can be overridden by Realm implementations, but the default is
+     * adequate when an instance of <code>GenericPrincipal</code> is used to
+     * represent authenticated Principals from this Realm.
      */
     @Override
     public boolean hasRole(Wrapper wrapper, Principal principal, String role) {
         // Check for a role alias defined in a <security-role-ref> element
         if (wrapper != null) {
             String realRole = wrapper.findSecurityReference(role);
-            if (realRole != null)
+            if (realRole != null) {
                 role = realRole;
+            }
         }
 
         // Should be overridden in JAASRealm - to avoid pretty inefficient conversions
-        if ((principal == null) || (role == null) ||
-            !(principal instanceof GenericPrincipal))
+        if (principal == null || role == null) {
             return false;
+        }
+
+        boolean result = hasRoleInternal(principal, role);
 
-        GenericPrincipal gp = (GenericPrincipal) principal;
-        boolean result = gp.hasRole(role);
         if (log.isDebugEnabled()) {
             String name = principal.getName();
             if (result)
@@ -943,8 +941,35 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
             else
                 log.debug(sm.getString("realmBase.hasRoleFailure", name, role));
         }
-        return (result);
 
+        return result;
+    }
+
+
+    /**
+     * Check if the specified Principal has the specified
+     * security role, within the context of this Realm.
+     *
+     * This method or {@link #hasRoleInternal(Principal,
+     * String)} can be overridden by Realm implementations, but the default is
+     * adequate when an instance of <code>GenericPrincipal</code> is used to
+     * represent authenticated Principals from this Realm.
+     *
+     * @param principal Principal for whom the role is to be checked
+     * @param role Security role to be checked
+     *
+     * @return <code>true</code> if the specified Principal has the specified
+     *         security role, within the context of this Realm; otherwise return
+     *         <code>false</code>.
+     */
+    protected boolean hasRoleInternal(Principal principal, String role) {
+        // Should be overridden in JAASRealm - to avoid pretty inefficient conversions
+        if (!(principal instanceof GenericPrincipal)) {
+            return false;
+        }
+
+        GenericPrincipal gp = (GenericPrincipal) principal;
+        return gp.hasRole(role);
     }
 
 
diff --git a/java/org/apache/catalina/security/SecurityClassLoad.java b/java/org/apache/catalina/security/SecurityClassLoad.java
index e63f9d77..36bb021a 100644
--- a/java/org/apache/catalina/security/SecurityClassLoad.java
+++ b/java/org/apache/catalina/security/SecurityClassLoad.java
@@ -166,7 +166,6 @@ public final class SecurityClassLoad {
     private static final void loadCoyotePackage(ClassLoader loader)
             throws Exception {
         final String basePackage = "org.apache.coyote.";
-        loader.loadClass(basePackage + "PushToken");
         loader.loadClass(basePackage + "http11.Constants");
         // Make sure system property is read at this point
         Class<?> clazz = loader.loadClass(basePackage + "Constants");
@@ -271,7 +270,9 @@ public final class SecurityClassLoad {
             throws Exception {
         final String basePackage = "org.apache.tomcat.";
         // buf
+        loader.loadClass(basePackage + "util.buf.B2CConverter");
         loader.loadClass(basePackage + "util.buf.ByteBufferUtils");
+        loader.loadClass(basePackage + "util.buf.C2BConverter");
         loader.loadClass(basePackage + "util.buf.HexUtils");
         loader.loadClass(basePackage + "util.buf.StringCache");
         loader.loadClass(basePackage + "util.buf.StringCache$ByteEntry");
diff --git a/java/org/apache/catalina/servlet4preview/AsyncContext.java b/java/org/apache/catalina/servlet4preview/AsyncContext.java
new file mode 100644
index 00000000..8f09306f
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/AsyncContext.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.servlet4preview;
+
+public interface AsyncContext extends javax.servlet.AsyncContext {
+
+    public static final String ASYNC_MAPPING = "javax.servlet.async.mapping";
+}
diff --git a/java/org/apache/catalina/servlet4preview/ServletContext.java b/java/org/apache/catalina/servlet4preview/ServletContext.java
new file mode 100644
index 00000000..9278bec1
--- /dev/null
+++ b/java/org/apache/catalina/servlet4preview/ServletContext.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.servlet4preview;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletRegistration;
+
+public interface ServletContext extends javax.servlet.ServletContext {
+
+    /**
+     * Get the default session timeout.
+     *
+     * @throws UnsupportedOperationException    If called from a
+     *    {@link ServletContextListener#contextInitialized(ServletContextEvent)}
+     *    method of a {@link ServletContextListener} that was not defined in a
+     *    web.xml file, a web-fragment.xml file nor annotated with
+     *    {@link javax.servlet.annotation.WebListener}. For example, a
+     *    {@link ServletContextListener} defined in a TLD would not be able to
+     *    use this method.
+     *
+     * @return The current default session timeout in minutes
+     *
+     * @since Servlet 4.0
+     */
+    public int getSessionTimeout();
+
+    /**
+     * Set the default session timeout. This method may only be called before
+     * the ServletContext is initialised.
+     *
+     * @param sessionTimeout The new default session timeout in minutes.
+     *
+     * @throws UnsupportedOperationException    If called from a
+     *    {@link ServletContextListener#contextInitialized(ServletContextEvent)}
+     *    method of a {@link ServletContextListener} that was not defined in a
+     *    web.xml file, a web-fragment.xml file nor annotated with
+     *    {@link javax.servlet.annotation.WebListener}. For example, a
+     *    {@link ServletContextListener} defined in a TLD would not be able to
+     *    use this method.
+     * @throws IllegalStateException If the ServletContext has already been
+     *         initialised
+     *
+     * @since Servlet 4.0
+     */
+    public void setSessionTimeout(int sessionTimeout);
+
+    /**
+     *
+     * @param jspName   The servlet name under which this JSP file should be
+     *                  registered
+     * @param jspFile   The path, relative to the web application root, for the
+     *                  JSP file to be used for this servlet
+     *
+     * @return  a {@link javax.servlet.ServletRegistration.Dynamic} object
+     *          that can be used to further configure the servlet
+     *
+     * @since Servlet 4.0
+     */
+    public ServletRegistration.Dynamic addJspFile(String jspName, String jspFile);
+
+    /**
+     * Get the default character encoding for reading request bodies.
+     *
+     * @return The character encoding name or {@code null} if no default has
+     *         been specified
+     *
+     * @throws UnsupportedOperationException    If called from a
+     *    {@link ServletContextListener#contextInitialized(ServletContextEvent)}
+     *    method of a {@link ServletContextListener} that was not defined in a
+     *    web.xml file, a web-fragment.xml file nor annotated with
+     *    {@link javax.servlet.annotation.WebListener}. For example, a
+     *    {@link ServletContextListener} defined in a TLD would not be able to
+     *    use this method.
+     *
+     * @since Servlet 4.0
+     */
+    public String getRequestCharacterEncoding();
+
+    /**
+     * Set the default character encoding to use for reading request bodies.
+     * Calling this method will over-ride any value set in the deployment
+     * descriptor.
+     *
+     * @param encoding The name of the character encoding to use
+     *
+     * @throws UnsupportedOperationException    If called from a
+     *    {@link ServletContextListener#contextInitialized(ServletContextEvent)}
+     *    method of a {@link ServletContextListener} that was not defined in a
+     *    web.xml file, a web-fragment.xml file nor annotated with
+     *    {@link javax.servlet.annotation.WebListener}. For example, a
+     *    {@link ServletContextListener} defined in a TLD would not be able to
+     *    use this method.
+     * @throws IllegalStateException If the ServletContext has already been
+     *         initialised
+     *
+     * @since Servlet 4.0
+     */
+    public void setRequestCharacterEncoding(String encoding);
+
+    /**
+     * Get the default character encoding for writing response bodies.
+     *
+     * @return The character encoding name or {@code null} if no default has
+     *         been specified
+     *
+     * @throws UnsupportedOperationException    If called from a
+     *    {@link ServletContextListener#contextInitialized(ServletContextEvent)}
+     *    method of a {@link ServletContextListener} that was not defined in a
+     *    web.xml file, a web-fragment.xml file nor annotated with
+     *    {@link javax.servlet.annotation.WebListener}. For example, a
+     *    {@link ServletContextListener} defined in a TLD would not be able to
+     *    use this method.
+     *
+     * @since Servlet 4.0
+     */
+    public String getResponseCharacterEncoding();
+
+    /**
+     * Set the default character encoding to use for writing response bodies.
+     * Calling this method will over-ride any value set in the deployment
+     * descriptor.
+     *
+     * @param encoding The name of the character encoding to use
+     *
+     * @throws UnsupportedOperationException    If called from a
+     *    {@link ServletContextListener#contextInitialized(ServletContextEvent)}
+     *    method of a {@link ServletContextListener} that was not defined in a
+     *    web.xml file, a web-fragment.xml file nor annotated with
+     *    {@link javax.servlet.annotation.WebListener}. For example, a
+     *    {@link ServletContextListener} defined in a TLD would not be able to
+     *    use this method.
+     * @throws IllegalStateException If the ServletContext has already been
+     *         initialised
+     *
+     * @since Servlet 4.0
+     */
+    public void setResponseCharacterEncoding(String encoding);
+}
diff --git a/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java b/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java
index 5751226b..b55ec06b 100644
--- a/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java
+++ b/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java
@@ -21,17 +21,7 @@ package org.apache.catalina.servlet4preview.http;
  */
 public interface HttpServletRequest extends javax.servlet.http.HttpServletRequest {
 
-    public Mapping getMapping();
-
-    /**
-     * Does the current request allow push requests. This will return {@code
-     * true} only if the underlying protocol supports server push and if pushes
-     * are permitted from the current request.
-     *
-     * @return {@code true} if server push is supported for the current request
-     *         otherwise {@code false}
-     */
-    public boolean isPushSupported();
+    public ServletMapping getServletMapping();
 
     /**
      * Obtain a builder for generating push requests. {@link PushBuilder}
@@ -40,7 +30,10 @@ public interface HttpServletRequest extends javax.servlet.http.HttpServletReques
      * previous instance obtained.
      *
      * @return A builder that can be used to generate push requests based on
-     *         this request.
+     *         this request or {@code null} if push is not supported. Note that
+     *         even if a PushBuilder instance is returned, by the time that
+     *         {@link PushBuilder#push()} is called, it may no longer be valid
+     *         to push a request and the push request will be ignored.
      *
      * @since Servlet 4.0
      */
diff --git a/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java b/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java
index c6f66cc7..7904efa7 100644
--- a/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java
+++ b/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java
@@ -43,33 +43,19 @@ public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletReq
      * {@inheritDoc}
      * <p>
      * The default behavior of this method is to return
-     * {@link HttpServletRequest#getMapping()} on the wrapped request object.
+     * {@link HttpServletRequest#getServletMapping()} on the wrapped request object.
      *
      * @since Servlet 4.0
      */
     @Override
-    public Mapping getMapping() {
-        return this._getHttpServletRequest().getMapping();
+    public ServletMapping getServletMapping() {
+        return this._getHttpServletRequest().getServletMapping();
     }
 
     /**
      * {@inheritDoc}
      * <p>
      * The default behavior of this method is to return
-     * {@link HttpServletRequest#isPushSupported()} on the wrapped request object.
-     *
-     * @since Servlet 4.0
-     */
-    @Override
-    public boolean isPushSupported() {
-        return this._getHttpServletRequest().isPushSupported();
-    }
-
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * The default behavior of this method is to return
      * {@link HttpServletRequest#getPushBuilder()} on the wrapped request object.
      *
      * @since Servlet 4.0
diff --git a/java/org/apache/catalina/servlet4preview/http/PushBuilder.java b/java/org/apache/catalina/servlet4preview/http/PushBuilder.java
index fe45c8d6..c5ab5dbc 100644
--- a/java/org/apache/catalina/servlet4preview/http/PushBuilder.java
+++ b/java/org/apache/catalina/servlet4preview/http/PushBuilder.java
@@ -49,6 +49,12 @@ public interface PushBuilder {
      * @param method The method to use for the push request
      *
      * @return This builder instance
+     *
+     * @throws IllegalArgumentException if an HTTP method is specified that is
+     *         known not to be <a
+     *         href="https://tools.ietf.org/html/rfc7540#section-8.2";>cacheable
+     *         and safe</a>. POST, PUT, DELETE, CONNECT, OPTIONS and TRACE will
+     *         trigger the exception.
      */
     PushBuilder method(String method);
 
@@ -82,7 +88,7 @@ public interface PushBuilder {
 
     /**
      * Sets if the request will be conditional. If {@code true} the values from
-     * {@link #getEtag()} and {@link #getLastModified()} will be used to
+     * {@link #getETag()} and {@link #getLastModified()} will be used to
      * construct appropriate headers.
      *
      * @param conditional Should generated push requests be conditional
@@ -137,15 +143,15 @@ public interface PushBuilder {
     PushBuilder path(String path);
 
     /**
-     * Sets the etag to be used for conditional push requests. This will be
+     * Sets the eTag to be used for conditional push requests. This will be
      * set to {@code null} after a call to {@link #push()} so it must be
      * explicitly set for every push request that requires it.
      *
-     * @param etag The etag use for the push request
+     * @param eTag The eTag use for the push request
      *
      * @return This builder instance
      */
-    PushBuilder etag(String etag);
+    PushBuilder eTag(String eTag);
 
     /**
      * Sets the last modified to be used for conditional push requests. This
@@ -164,18 +170,15 @@ public interface PushBuilder {
      * fields are set to {@code null}:
      * <ul>
      * <li>{@code path}</li>
-     * <li>{@code etag}</li>
+     * <li>{@code eTag}</li>
      * <li>{@code lastModified}</li>
      * </ul>
      *
-     * @return {@code true} if the push request was sent to the client,
-     *         otherwise {@code false}
-     *
      * @throws IllegalStateException If this method is called when {@code path}
      *         is {@code null}
      * @throws IllegalArgumentException If the request to push requires a body
      */
-    boolean push();
+    void push();
 
     /**
      * Obtain the name of the HTTP method that will be used for push requests
@@ -236,12 +239,12 @@ public interface PushBuilder {
     String getPath();
 
     /**
-     * Obtain the etag that will be used for the push request that will be
+     * Obtain the eTag that will be used for the push request that will be
      * generated by the next call to {@code push()}.
      *
-     * @return The etag value that will be associated with the next push request
+     * @return The eTag value that will be associated with the next push request
      */
-    String getEtag();
+    String getETag();
 
     /**
      * Obtain the last modified that will be used for the push request that will
diff --git a/java/org/apache/catalina/servlet4preview/http/Mapping.java b/java/org/apache/catalina/servlet4preview/http/ServletMapping.java
similarity index 98%
rename from java/org/apache/catalina/servlet4preview/http/Mapping.java
rename to java/org/apache/catalina/servlet4preview/http/ServletMapping.java
index 5e44d7fb..adb70c3f 100644
--- a/java/org/apache/catalina/servlet4preview/http/Mapping.java
+++ b/java/org/apache/catalina/servlet4preview/http/ServletMapping.java
@@ -22,7 +22,7 @@ package org.apache.catalina.servlet4preview.http;
  *
  * @since 4.0
  */
-public interface Mapping {
+public interface ServletMapping {
 
     /**
      * @return The value that was matched or the empty String if not known.
diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/apache/catalina/servlets/DefaultServlet.java
index 5416f748..c3fbf862 100644
--- a/java/org/apache/catalina/servlets/DefaultServlet.java
+++ b/java/org/apache/catalina/servlets/DefaultServlet.java
@@ -1913,14 +1913,16 @@ public class DefaultServlet extends HttpServlet {
                                   HttpServletResponse response,
                                   WebResource resource,
                                   long length, Range range) {
+        String canonicalPath;
         if (sendfileSize > 0
-            && resource.isFile()
             && length > sendfileSize
-            && (resource.getCanonicalPath() != null)
             && (Boolean.TRUE.equals(request.getAttribute(Globals.SENDFILE_SUPPORTED_ATTR)))
             && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade"))
-            && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))) {
-            request.setAttribute(Globals.SENDFILE_FILENAME_ATTR, resource.getCanonicalPath());
+            && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))
+            && resource.isFile()
+            && ((canonicalPath = resource.getCanonicalPath()) != null)
+            ) {
+            request.setAttribute(Globals.SENDFILE_FILENAME_ATTR, canonicalPath);
             if (range == null) {
                 request.setAttribute(Globals.SENDFILE_FILE_START_ATTR, Long.valueOf(0L));
                 request.setAttribute(Globals.SENDFILE_FILE_END_ATTR, Long.valueOf(length));
diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java b/java/org/apache/catalina/servlets/WebdavServlet.java
index 4eeeeba4..d96f9363 100644
--- a/java/org/apache/catalina/servlets/WebdavServlet.java
+++ b/java/org/apache/catalina/servlets/WebdavServlet.java
@@ -40,6 +40,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
 import org.apache.catalina.WebResource;
+import org.apache.catalina.connector.RequestFacade;
 import org.apache.catalina.util.ConcurrentDateFormat;
 import org.apache.catalina.util.DOMWriter;
 import org.apache.catalina.util.XMLWriter;
@@ -2303,7 +2304,12 @@ public class WebdavServlet
             return methodsAllowed;
         }
 
-        methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE");
+        methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE");
+        // Trace - assume disabled unless we can prove otherwise
+        if (req instanceof RequestFacade &&
+                ((RequestFacade) req).getAllowTrace()) {
+            methodsAllowed.append(", TRACE");
+        }
         methodsAllowed.append(", PROPPATCH, COPY, MOVE, LOCK, UNLOCK");
 
         if (listings) {
diff --git a/java/org/apache/catalina/startup/FailedContext.java b/java/org/apache/catalina/startup/FailedContext.java
index 8d7c5a8d..6ededcd9 100644
--- a/java/org/apache/catalina/startup/FailedContext.java
+++ b/java/org/apache/catalina/startup/FailedContext.java
@@ -796,4 +796,14 @@ public class FailedContext extends LifecycleMBeanBase implements Context {
     public void setDispatchersUseEncodedPaths(boolean dispatchersUseEncodedPaths) { /* NO-OP */ }
     @Override
     public boolean getDispatchersUseEncodedPaths() { return true; }
+
+    @Override
+    public void setRequestCharacterEncoding(String encoding) { /* NO-OP */ }
+    @Override
+    public String getRequestCharacterEncoding() { return null; }
+
+    @Override
+    public void setResponseCharacterEncoding(String encoding) { /* NO-OP */ }
+    @Override
+    public String getResponseCharacterEncoding() { return null; }
 }
\ No newline at end of file
diff --git a/java/org/apache/catalina/startup/Tomcat.java b/java/org/apache/catalina/startup/Tomcat.java
index e4404da6..7285c527 100644
--- a/java/org/apache/catalina/startup/Tomcat.java
+++ b/java/org/apache/catalina/startup/Tomcat.java
@@ -615,7 +615,7 @@ public class Tomcat {
         Context ctx = createContext(host, contextPath);
         ctx.setPath(contextPath);
         ctx.setDocBase(docBase);
-        ctx.addLifecycleListener(new DefaultWebXmlListener());
+        ctx.addLifecycleListener(getDefaultWebXmlListener());
         ctx.setConfigFile(getWebappConfigFile(docBase, contextPath));
 
         ctx.addLifecycleListener(config);
diff --git a/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java b/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java
index 4b6edfbb..1dda36d0 100644
--- a/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java
+++ b/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java
@@ -196,7 +196,7 @@ public class ConnectorStoreAppender extends StoreAppender {
      * @param aDesc The connector description
      * @throws Exception Store error occurred
      */
-    protected void storeConnectorAttribtues(PrintWriter aWriter, int indent,
+    protected void storeConnectorAttributes(PrintWriter aWriter, int indent,
             Object bean, StoreDescription aDesc) throws Exception {
         if (aDesc.isAttributes()) {
             printAttributes(aWriter, indent, false, bean, aDesc);
@@ -215,7 +215,7 @@ public class ConnectorStoreAppender extends StoreAppender {
             StoreDescription aDesc) throws Exception {
         aWriter.print("<");
         aWriter.print(aDesc.getTag());
-        storeConnectorAttribtues(aWriter, indent, bean, aDesc);
+        storeConnectorAttributes(aWriter, indent, bean, aDesc);
         aWriter.println(">");
     }
 
@@ -231,7 +231,7 @@ public class ConnectorStoreAppender extends StoreAppender {
             StoreDescription aDesc) throws Exception {
         aWriter.print("<");
         aWriter.print(aDesc.getTag());
-        storeConnectorAttribtues(aWriter, indent, bean, aDesc);
+        storeConnectorAttributes(aWriter, indent, bean, aDesc);
         aWriter.println("/>");
     }
 
diff --git a/java/org/apache/catalina/tribes/group/RpcChannel.java b/java/org/apache/catalina/tribes/group/RpcChannel.java
index fc564515..9798f407 100644
--- a/java/org/apache/catalina/tribes/group/RpcChannel.java
+++ b/java/org/apache/catalina/tribes/group/RpcChannel.java
@@ -112,7 +112,8 @@ public class RpcChannel implements ChannelListener {
         if ( rmsg.reply ) {
             RpcCollector collector = responseMap.get(key);
             if (collector == null) {
-                callback.leftOver(rmsg.message, sender);
+                if (!(rmsg instanceof RpcMessage.NoRpcChannelReply))
+                    callback.leftOver(rmsg.message, sender);
             } else {
                 synchronized (collector) {
                     //make sure it hasn't been removed
diff --git a/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java b/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
index 4255f83a..0529bb64 100644
--- a/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
+++ b/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
@@ -619,9 +619,16 @@ public abstract class AbstractReplicatedMap<K,V>
             mapmsg.deserialize(getExternalLoaders());
             if (mapmsg.getMsgType() == MapMessage.MSG_START) {
                 mapMemberAdded(mapmsg.getPrimary());
-            } else if (mapmsg.getMsgType() == MapMessage.MSG_INIT
-                    || mapmsg.getMsgType() == MapMessage.MSG_PING) {
+            } else if (mapmsg.getMsgType() == MapMessage.MSG_INIT) {
                 memberAlive(mapmsg.getPrimary());
+            } else if (mapmsg.getMsgType() == MapMessage.MSG_PING) {
+                Member member = mapmsg.getPrimary();
+                if (log.isInfoEnabled())
+                    log.info(sm.getString("abstractReplicatedMap.leftOver.pingMsg", member));
+                State state = (State) mapmsg.getValue();
+                if (state.isAvailable()) {
+                    memberAlive(member);
+                }
             } else {
                 // other messages are ignored.
                 if (log.isInfoEnabled())
diff --git a/java/org/apache/catalina/tribes/tipis/LocalStrings.properties b/java/org/apache/catalina/tribes/tipis/LocalStrings.properties
index 16d9ba38..070395c4 100644
--- a/java/org/apache/catalina/tribes/tipis/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/tipis/LocalStrings.properties
@@ -36,6 +36,7 @@ abstractReplicatedMap.unable.put=Unable to replicate out data for a AbstractRepl
 abstractReplicatedMap.unsupport.operation=This operation is not valid on a replicated map
 abstractReplicatedMap.mapMemberAdded.nullMember=Notified member is not registered in the membership:{0}.
 abstractReplicatedMap.mapMemberAdded.added=Map member added:{0}
+abstractReplicatedMap.leftOver.pingMsg=PING message has been received beyond the timeout period. The map member[{0}] might have been removed from the map membership.
 abstractReplicatedMap.leftOver.ignored=Message[{0}] is ignored.
 abstractReplicatedMap.mapMember.unavailable=Member[{0}] is not available yet.
 abstractReplicatedMap.ping.timeout=Member[{0}] in the Map[{1}] has timed-out in the ping processing.
diff --git a/java/org/apache/catalina/util/ParameterMap.java b/java/org/apache/catalina/util/ParameterMap.java
index a1668188..96bc0145 100644
--- a/java/org/apache/catalina/util/ParameterMap.java
+++ b/java/org/apache/catalina/util/ParameterMap.java
@@ -16,13 +16,17 @@
  */
 package org.apache.catalina.util;
 
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.tomcat.util.res.StringManager;
 
 /**
- * Extended implementation of <strong>HashMap</strong> that includes a
+ * Implementation of <strong>java.util.Map</strong> that includes a
  * <code>locked</code> property.  This class can be used to safely expose
  * Catalina internal parameter map objects to user classes without having
  * to clone them in order to avoid modifications.  When first created, a
@@ -33,20 +37,22 @@ import org.apache.tomcat.util.res.StringManager;
  *
  * @author Craig R. McClanahan
  */
-public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
+public final class ParameterMap<K,V> implements Map<K,V>, Serializable {
 
-    private static final long serialVersionUID = 1L;
+    private static final long serialVersionUID = 2L;
+
+    private final Map<K,V> delegatedMap;
+
+    private final Map<K,V> unmodifiableDelegatedMap;
 
 
-    // ----------------------------------------------------------- Constructors
     /**
      * Construct a new, empty map with the default initial capacity and
      * load factor.
      */
     public ParameterMap() {
-
-        super();
-
+        delegatedMap = new LinkedHashMap<>();
+        unmodifiableDelegatedMap = Collections.unmodifiableMap(delegatedMap);
     }
 
 
@@ -57,9 +63,8 @@ public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
      * @param initialCapacity The initial capacity of this map
      */
     public ParameterMap(int initialCapacity) {
-
-        super(initialCapacity);
-
+        delegatedMap = new LinkedHashMap<>(initialCapacity);
+        unmodifiableDelegatedMap = Collections.unmodifiableMap(delegatedMap);
     }
 
 
@@ -71,9 +76,8 @@ public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
      * @param loadFactor The load factor of this map
      */
     public ParameterMap(int initialCapacity, float loadFactor) {
-
-        super(initialCapacity, loadFactor);
-
+        delegatedMap = new LinkedHashMap<>(initialCapacity, loadFactor);
+        unmodifiableDelegatedMap = Collections.unmodifiableMap(delegatedMap);
     }
 
 
@@ -83,15 +87,11 @@ public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
      * @param map Map whose contents are duplicated in the new map
      */
     public ParameterMap(Map<K,V> map) {
-
-        super(map);
-
+        delegatedMap = new LinkedHashMap<>(map);
+        unmodifiableDelegatedMap = Collections.unmodifiableMap(delegatedMap);
     }
 
 
-    // ------------------------------------------------------------- Properties
-
-
     /**
      * The current lock state of this parameter map.
      */
@@ -102,9 +102,7 @@ public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
      * @return the locked state of this parameter map.
      */
     public boolean isLocked() {
-
-        return (this.locked);
-
+        return locked;
     }
 
 
@@ -114,102 +112,145 @@ public final class ParameterMap<K,V> extends LinkedHashMap<K,V> {
      * @param locked The new locked state
      */
     public void setLocked(boolean locked) {
-
         this.locked = locked;
-
     }
 
 
     /**
      * The string manager for this package.
      */
-    private static final StringManager sm =
-        StringManager.getManager("org.apache.catalina.util");
-
-
-    // --------------------------------------------------------- Public Methods
-
+    private static final StringManager sm = StringManager.getManager("org.apache.catalina.util");
 
 
     /**
-     * Remove all mappings from this map.
+     * {@inheritDoc}
      *
      * @exception IllegalStateException if this map is currently locked
      */
     @Override
     public void clear() {
-
-        if (locked)
-            throw new IllegalStateException
-                (sm.getString("parameterMap.locked"));
-        super.clear();
-
+        checkLocked();
+        delegatedMap.clear();
     }
 
 
     /**
-     * Associate the specified value with the specified key in this map.  If
-     * the map previously contained a mapping for this key, the old value is
-     * replaced.
-     *
-     * @param key Key with which the specified value is to be associated
-     * @param value Value to be associated with the specified key
-     *
-     * @return The previous value associated with the specified key, or
-     *  <code>null</code> if there was no mapping for key
+     * {@inheritDoc}
      *
      * @exception IllegalStateException if this map is currently locked
      */
     @Override
     public V put(K key, V value) {
-
-        if (locked)
-            throw new IllegalStateException
-                (sm.getString("parameterMap.locked"));
-        return (super.put(key, value));
-
+        checkLocked();
+        return delegatedMap.put(key, value);
     }
 
 
     /**
-     * Copy all of the mappings from the specified map to this one.  These
-     * mappings replace any mappings that this map had for any of the keys
-     * currently in the specified Map.
-     *
-     * @param map Mappings to be stored into this map
+     * {@inheritDoc}
      *
      * @exception IllegalStateException if this map is currently locked
      */
     @Override
     public void putAll(Map<? extends K,? extends V> map) {
-
-        if (locked)
-            throw new IllegalStateException
-                (sm.getString("parameterMap.locked"));
-        super.putAll(map);
-
+        checkLocked();
+        delegatedMap.putAll(map);
     }
 
 
     /**
-     * Remove the mapping for this key from the map if present.
-     *
-     * @param key Key whose mapping is to be removed from the map
-     *
-     * @return The previous value associated with the specified key, or
-     *  <code>null</code> if there was no mapping for that key
+     * {@inheritDoc}
      *
      * @exception IllegalStateException if this map is currently locked
      */
     @Override
     public V remove(Object key) {
+        checkLocked();
+        return delegatedMap.remove(key);
+    }
+
+
+    private void checkLocked() {
+        if (locked) {
+            throw new IllegalStateException(sm.getString("parameterMap.locked"));
+        }
+    }
+
+
+    @Override
+    public int size() {
+        return delegatedMap.size();
+    }
+
+
+    @Override
+    public boolean isEmpty() {
+        return delegatedMap.isEmpty();
+    }
+
+
+    @Override
+    public boolean containsKey(Object key) {
+        return delegatedMap.containsKey(key);
+    }
 
-        if (locked)
-            throw new IllegalStateException
-                (sm.getString("parameterMap.locked"));
-        return (super.remove(key));
 
+    @Override
+    public boolean containsValue(Object value) {
+        return delegatedMap.containsValue(value);
     }
 
 
+    @Override
+    public V get(Object key) {
+        return delegatedMap.get(key);
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Returns an <strong>unmodifiable</strong> {@link Set} view of the keys
+     * contained in this map if it is locked.
+     */
+    @Override
+    public Set<K> keySet() {
+        if (locked) {
+            return unmodifiableDelegatedMap.keySet();
+        }
+
+        return delegatedMap.keySet();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Returns an <strong>unmodifiable</strong> {@link Collection} view of the
+     * values contained in this map if it is locked.
+     */
+    @Override
+    public Collection<V> values() {
+        if (locked) {
+            return unmodifiableDelegatedMap.values();
+        }
+
+        return delegatedMap.values();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Returns an <strong>unmodifiable</strong> {@link Set} view of the mappings
+     * contained in this map if it is locked.
+     */
+    @Override
+    public Set<java.util.Map.Entry<K, V>> entrySet() {
+        if (locked) {
+            return unmodifiableDelegatedMap.entrySet();
+        }
+
+        return delegatedMap.entrySet();
+    }
 }
diff --git a/java/org/apache/catalina/valves/ErrorReportValve.java b/java/org/apache/catalina/valves/ErrorReportValve.java
index c67a4d44..aa7f1969 100644
--- a/java/org/apache/catalina/valves/ErrorReportValve.java
+++ b/java/org/apache/catalina/valves/ErrorReportValve.java
@@ -28,6 +28,7 @@ import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.util.RequestUtil;
 import org.apache.catalina.util.ServerInfo;
+import org.apache.catalina.util.TomcatCSS;
 import org.apache.coyote.ActionCode;
 import org.apache.tomcat.util.ExceptionUtils;
 import org.apache.tomcat.util.res.StringManager;
@@ -161,67 +162,67 @@ public class ErrorReportValve extends ValveBase {
             }
         }
 
-        // Do nothing if there is no report for the specified status code and
+        // Do nothing if there is no reason phrase for the specified status code and
         // no error message provided
-        String report = null;
+        String reason = null;
+        String description = null;
         StringManager smClient = StringManager.getManager(
                 Constants.Package, request.getLocales());
         response.setLocale(smClient.getLocale());
         try {
-            report = smClient.getString("http." + statusCode);
+            reason = smClient.getString("http." + statusCode + ".reason");
+            description = smClient.getString("http." + statusCode + ".desc");
         } catch (Throwable t) {
             ExceptionUtils.handleThrowable(t);
         }
-        if (report == null) {
-            if (message.length() == 0) {
+        if (reason == null || description == null) {
+            if (message.isEmpty()) {
                 return;
             } else {
-                report = smClient.getString("errorReportValve.noDescription");
+                reason = smClient.getString("errorReportValve.unknownReason");
+                description = smClient.getString("errorReportValve.noDescription");
             }
         }
 
         StringBuilder sb = new StringBuilder();
 
-        sb.append("<!DOCTYPE html><html><head>");
-        if(showServerInfo || showReport){
-            sb.append("<title>");
-            if(showServerInfo) {
-                sb.append(ServerInfo.getServerInfo()).append(" - ");
-            }
-            sb.append(smClient.getString("errorReportValve.errorReport"));
-            sb.append("</title>");
-            sb.append("<style type=\"text/css\">");
-            sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS);
-            sb.append("</style> ");
-        } else {
-            sb.append("<title>");
-            sb.append(smClient.getString("errorReportValve.errorReport"));
-            sb.append("</title>");
-        }
+        sb.append("<!doctype html><html lang=\"");
+        sb.append(smClient.getLocale().getLanguage()).append("\">");
+        sb.append("<head>");
+        sb.append("<title>");
+        sb.append(smClient.getString("errorReportValve.statusHeader",
+                String.valueOf(statusCode), reason));
+        sb.append("</title>");
+        sb.append("<style type=\"text/css\">");
+        sb.append(TomcatCSS.TOMCAT_CSS);
+        sb.append("</style>");
         sb.append("</head><body>");
         sb.append("<h1>");
         sb.append(smClient.getString("errorReportValve.statusHeader",
-                String.valueOf(statusCode), message)).append("</h1>");
-        if (showReport) {
-            sb.append("<div class=\"line\"></div>");
-            sb.append("<p><b>type</b> ");
+                String.valueOf(statusCode), reason)).append("</h1>");
+        if (isShowReport()) {
+            sb.append("<hr class=\"line\" />");
+            sb.append("<p><b>");
+            sb.append(smClient.getString("errorReportValve.type"));
+            sb.append("</b> ");
             if (throwable != null) {
                 sb.append(smClient.getString("errorReportValve.exceptionReport"));
             } else {
                 sb.append(smClient.getString("errorReportValve.statusReport"));
             }
             sb.append("</p>");
-            sb.append("<p><b>");
-            sb.append(smClient.getString("errorReportValve.message"));
-            sb.append("</b> <u>");
-            sb.append(message).append("</u></p>");
+            if (!message.isEmpty()) {
+                sb.append("<p><b>");
+                sb.append(smClient.getString("errorReportValve.message"));
+                sb.append("</b> ");
+                sb.append(message).append("</p>");
+            }
             sb.append("<p><b>");
             sb.append(smClient.getString("errorReportValve.description"));
-            sb.append("</b> <u>");
-            sb.append(report);
-            sb.append("</u></p>");
+            sb.append("</b> ");
+            sb.append(description);
+            sb.append("</p>");
             if (throwable != null) {
-
                 String stackTrace = getPartialServletStackTrace(throwable);
                 sb.append("<p><b>");
                 sb.append(smClient.getString("errorReportValve.exception"));
@@ -245,15 +246,14 @@ public class ErrorReportValve extends ValveBase {
 
                 sb.append("<p><b>");
                 sb.append(smClient.getString("errorReportValve.note"));
-                sb.append("</b> <u>");
-                sb.append(smClient.getString("errorReportValve.rootCauseInLogs",
-                        showServerInfo?ServerInfo.getServerInfo():""));
-                sb.append("</u></p>");
+                sb.append("</b> ");
+                sb.append(smClient.getString("errorReportValve.rootCauseInLogs"));
+                sb.append("</p>");
 
             }
-            sb.append("<hr class=\"line\">");
+            sb.append("<hr class=\"line\" />");
         }
-        if (showServerInfo) {
+        if (isShowServerInfo()) {
             sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
         }
         sb.append("</body></html>");
@@ -292,7 +292,7 @@ public class ErrorReportValve extends ValveBase {
      */
     protected String getPartialServletStackTrace(Throwable t) {
         StringBuilder trace = new StringBuilder();
-        trace.append(t.toString()).append('\n');
+        trace.append(t.toString()).append(System.lineSeparator());
         StackTraceElement[] elements = t.getStackTrace();
         int pos = elements.length;
         for (int i = elements.length - 1; i >= 0; i--) {
@@ -306,7 +306,7 @@ public class ErrorReportValve extends ValveBase {
         for (int i = 0; i < pos; i++) {
             if (!(elements[i].getClassName().startsWith
                   ("org.apache.catalina.core."))) {
-                trace.append('\t').append(elements[i].toString()).append('\n');
+                trace.append('\t').append(elements[i].toString()).append(System.lineSeparator());
             }
         }
         return trace.toString();
diff --git a/java/org/apache/catalina/valves/LocalStrings.properties b/java/org/apache/catalina/valves/LocalStrings.properties
index f3ae05d9..15fe5820 100644
--- a/java/org/apache/catalina/valves/LocalStrings.properties
+++ b/java/org/apache/catalina/valves/LocalStrings.properties
@@ -29,16 +29,17 @@ accessLogValve.invalidPortType=Invalid port type [{0}], using server (local) por
 accessLogValve.writeFail=Failed to write log message [{0}]
 
 # Error report valve
-errorReportValve.errorReport=Error report
-errorReportValve.statusHeader=HTTP Status {0} - {1}
-errorReportValve.exceptionReport=Exception report
-errorReportValve.statusReport=Status report
-errorReportValve.message=message
-errorReportValve.description=description
-errorReportValve.exception=exception
-errorReportValve.rootCause=root cause
-errorReportValve.note=note
-errorReportValve.rootCauseInLogs=The full stack trace of the root cause is available in the {0} logs.
+errorReportValve.statusHeader=HTTP Status {0} \u2013 {1}
+errorReportValve.type=Type
+errorReportValve.exceptionReport=Exception Report
+errorReportValve.statusReport=Status Report
+errorReportValve.message=Message
+errorReportValve.description=Description
+errorReportValve.exception=Exception
+errorReportValve.rootCause=Root Cause
+errorReportValve.note=Note
+errorReportValve.rootCauseInLogs=The full stack trace of the root cause is available in the server logs.
+errorReportValve.unknownReason=Unknown Reason
 errorReportValve.noDescription=No description available
 
 # Remote IP valve
@@ -56,63 +57,83 @@ stuckThreadDetectionValve.notifyStuckThreadDetected=Thread "{0}" (id={6}) has be
 stuckThreadDetectionValve.notifyStuckThreadCompleted=Thread "{0}" (id={3}) was previously reported to be stuck but has completed. It was active for approximately {1} milliseconds.{2,choice,0#|0< There is/are still {2} thread(s) that are monitored by this Valve and may be stuck.}
 stuckThreadDetectionValve.notifyStuckThreadInterrupted=Thread "{0}" (id={5}) has been interrupted because it was active for {1} milliseconds (since {2}) to serve the same request for {3} and was probably stuck (configured interruption threshold for this StuckThreadDetectionValve is {4} seconds).
 
-# HTTP status reports
+# HTTP status reports (error codes only)
 # All status codes registered with IANA can be found at
 # http://www.iana.org/assignments/http-status-codes/http-status-codes.xml
-http.100=The client may continue.
-http.101=The server is switching protocols according to the "Upgrade" header.
-http.102=The server has accepted the complete request, but has not yet completed it.
-http.201=The request succeeded and a new resource has been created on the server.
-http.202=This request was accepted for processing, but has not been completed.
-http.203=The meta information presented by the client did not originate from the server.
-http.204=The request succeeded but there is no information to return.
-http.205=The client should reset the document view which caused this request to be sent.
-http.206=The server has fulfilled a partial GET request for this resource.
-http.207=Multiple status values have been returned.
-http.208=This collection binding was already reported.
-http.226=The response is a representation of the result of one or more instance-manipulations applied to the current instance.
-http.300=The requested resource corresponds to any one of a set of representations, each with its own specific location.
-http.301=The requested resource has moved permanently to a new location.
-http.302=The requested resource has moved temporarily to a new location.
-http.303=The response to this request can be found under a different URI.
-http.304=The requested resource is available and has not been modified.
-http.305=The requested resource must be accessed through the proxy given by the "Location" header.
-http.307=The requested resource resides temporarily under a different URI.
-http.308=The target resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs.
-http.400=The request sent by the client was syntactically incorrect.
-http.401=This request requires HTTP authentication.
-http.402=Payment is required for access to this resource.
-http.403=Access to the specified resource has been forbidden.
-http.404=The requested resource is not available.
-http.405=The specified HTTP method is not allowed for the requested resource.
-http.406=The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.
-http.407=The client must first authenticate itself with the proxy.
-http.408=The client did not produce a request within the time that the server was prepared to wait.
-http.409=The request could not be completed due to a conflict with the current state of the resource.
-http.410=The requested resource is no longer available, and no forwarding address is known.
-http.411=This request cannot be handled without a defined content length.
-http.412=A specified precondition has failed for this request.
-http.413=The request entity is larger than the server is willing or able to process.
-http.414=The server refused this request because the request URI was too long.
-http.415=The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
-http.416=The requested byte range cannot be satisfied.
-http.417=The expectation given in the "Expect" request header could not be fulfilled.
-http.422=The server understood the content type and syntax of the request but was unable to process the contained instructions.
-http.423=The source or destination resource of a method is locked.
-http.424=The method could not be performed on the resource because the requested action depended on another action and that action failed.
-http.426=The request can only be completed after a protocol upgrade.
-http.428=The request is required to be conditional.
-http.429=The user has sent too many requests in a given amount of time.
-http.431=The server refused this request because the request header fields are too large.
-http.451=The server refused this request for legal reasons.
-http.500=The server encountered an internal error that prevented it from fulfilling this request.
-http.501=The server does not support the functionality needed to fulfill this request.
-http.502=This server received an invalid response from a server it consulted when acting as a proxy or gateway.
-http.503=The requested service is not currently available.
-http.504=The server received a timeout from an upstream server while acting as a gateway or proxy.
-http.505=The server does not support the requested HTTP protocol version.
-http.506=The chosen variant resource is configured to engage in transparent content negotiation itself, and is therefore not a proper end point in the negotiation process.
-http.507=The resource does not have sufficient space to record the state of the resource after execution of this method.
-http.508=The server terminated an operation because it encountered an infinite loop.
-http.510=The policy for accessing the resource has not been met in the request.
-http.511=The client needs to authenticate to gain network access.
+http.400.reason=Bad Request
+http.400.desc=The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
+http.401.reason=Unauthorized
+http.401.desc=The request has not been applied because it lacks valid authentication credentials for the target resource.
+http.402.reason=Payment Required
+http.402.desc=This status code is reserved for future use.
+http.403.reason=Forbidden
+http.403.desc=The server understood the request but refuses to authorize it.
+http.404.reason=Not Found
+http.404.desc=The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
+http.405.reason=Method Not Allowed
+http.405.desc=The method received in the request-line is known by the origin server but not supported by the target resource.
+http.406.reason=Not Acceptable
+http.406.desc= The target resource does not have a current representation that would be acceptable to the user agent, according to the proactive negotiation header fields received in the request, and the server is unwilling to supply a default representation.
+http.407.reason=Proxy Authentication Required
+http.407.desc=This status code is similar to 401 (Unauthorized), but it indicates that the client needs to authenticate itself in order to use a proxy.
+http.408.reason=Request Timeout
+http.408.desc= The server did not receive a complete request message within the time that it was prepared to wait.
+http.409.reason=Conflict
+http.409.desc=The request could not be completed due to a conflict with the current state of the target resource.
+http.410.reason=Gone
+http.410.desc= Access to the target resource is no longer available at the origin server and that this condition is likely to be permanent.
+http.411.reason=Length Required
+http.411.desc=The server refuses to accept the request without a defined Content-Length.
+http.412.reason=Precondition Failed
+http.412.desc=One or more conditions given in the request header fields evaluated to false when tested on the server.
+http.413.reason=Payload Too Large
+http.413.desc=The server is refusing to process a request because the request payload is larger than the server is willing or able to process.
+http.414.reason=URI Too Long
+http.414.desc=The server is refusing to service the request because the request-target is longer than the server is willing to interpret.
+http.415.reason=Unsupported Media Type
+http.415.desc=The origin server is refusing to service the request because the payload is in a format not supported by this method on the target resource.
+http.416.reason=Range Not Satisfiable
+http.416.desc=None of the ranges in the request's Range header field overlap the current extent of the selected resource or that the set of ranges requested has been rejected due to invalid ranges or an excessive request of small or overlapping ranges.
+http.417.reason=Expectation Failed
+http.417.desc=The expectation given in the request's Expect header field could not be met by at least one of the inbound servers.
+http.421.reason=Misdirected Request
+http.421.desc=The request was directed at a server that is not able to produce a response.
+http.422.reason=Unprocessable Entity
+http.422.desc=The server understands the content type of the request entity, and the syntax of the request entity is correct but was unable to process the contained instructions.
+http.423.reason=Locked
+http.423.desc=The source or destination resource of a method is locked.
+http.424.reason=Failed Dependency
+http.424.desc=The method could not be performed on the resource because the requested action depended on another action and that action failed.
+http.426.reason=Upgrade Required
+http.426.desc=the server refuses to perform the request using the current protocol but might be willing to do so after the client upgrades to a different protocol.
+http.428.reason=Precondition Required
+http.428.desc=The origin server requires the request to be conditional.
+http.429.reason=Too Many Requests
+http.429.desc=The user has sent too many requests in a given amount of time ("rate limiting").
+http.431.reason=Request Header Fields Too Large
+http.431.desc=The server is unwilling to process the request because its header fields are too large.
+http.451.reason=Unavailable For Legal Reasons
+http.451.desc=The server refused this request for legal reasons.
+http.500.reason=Internal Server Error
+http.500.desc=The server encountered an unexpected condition that prevented it from fulfilling the request.
+http.501.reason=Not Implemented
+http.501.desc=The server does not support the functionality required to fulfill the request.
+http.502.reason=Bad Gateway
+http.502.desc=The server, while acting as a gateway or proxy, received an invalid response from an inbound server it accessed while attempting to fulfill the request.
+http.503.reason=Service Unavailable
+http.503.desc=The server is currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay.
+http.504.reason=Gateway Timeout
+http.504.desc=The server, while acting as a gateway or proxy, did not receive a timely response from an upstream server it needed to access in order to complete the request.
+http.505.reason=HTTP Version Not Supported
+http.505.desc=The server does not support, or refuses to support, the major version of HTTP that was used in the request message.
+http.506.reason=Variant Also Negotiates
+http.506.desc=The server has an internal configuration error: the chosen variant resource is configured to engage in transparent content negotiation itself, and is thereforenot a proper end point in the negotiation process.
+http.507.reason=Insufficient Storage
+http.507.desc=The method could not be performed on the resource because the server is unable to store the representation needed to successfully complete the request.
+http.508.reason=Loop Detected
+http.508.desc=The server terminated an operation because it encountered an infinite loop while processing a request with "Depth: infinity".
+http.510.reason=Not Extended
+http.510.desc=The policy for accessing the resource has not been met in the request
+http.511.reason=Network Authentication Required
+http.511.desc=The client needs to authenticate to gain network access.
+
diff --git a/java/org/apache/catalina/valves/LocalStrings_es.properties b/java/org/apache/catalina/valves/LocalStrings_es.properties
index 412d12aa..e42a3dcf 100644
--- a/java/org/apache/catalina/valves/LocalStrings_es.properties
+++ b/java/org/apache/catalina/valves/LocalStrings_es.properties
@@ -17,8 +17,8 @@ accessLogValve.closeFail = No pude cerrar fichero de historial
 accessLogValve.openDirFail = No pude crear directorio [{0}] para historiales de acceso
 accessLogValve.rotateFail = No pude rotar historial de acceso
 # Error report valve
-errorReportValve.errorReport = Informe de Error
-errorReportValve.statusHeader = Estado HTTP {0} - {1}
+errorReportValve.statusHeader = Estado HTTP {0} \u2013 {1}
+errorReportValve.type=Tipo
 errorReportValve.exceptionReport = Informe de Excepci\u00F3n
 errorReportValve.statusReport = Informe de estado
 errorReportValve.message = mensaje
@@ -26,52 +26,39 @@ errorReportValve.description = descripci\u00F3n
 errorReportValve.exception = excepci\u00F3n
 errorReportValve.rootCause = causa ra\u00EDz
 errorReportValve.note = nota
-errorReportValve.rootCauseInLogs = La traza completa de la causa de este error se encuentra en los archivos de diario de {0}.
+errorReportValve.rootCauseInLogs = La traza completa de la causa de este error se encuentra en los archivos de diario del servidor.
+
 remoteIpValve.invalidPortHeader = Valor inv\u00E1lido [{0}] hallado para el puerto en cabecera HTTP [{1}]
 sslValve.certError = No pude procesar cadena de certificado [{0}] para crear un objeto  java.security.cert.X509Certificate
 sslValve.invalidProvider = El proveedor de SSL especificado en el conecto asociado con este requerimiento de [{0}] ies inv\u00E1lido. No se pueden procesar los datos del certificado.
 stuckThreadDetectionValve.notifyStuckThreadDetected = El hilo  "{0}" (id={6}) ha estado activo durante {1} miilisegundos (desde {2}) para servir el mismo requerimiento para {4} y puede estar atascado (el umbral configurado para este StuckThreadDetectionValve es de {5} segundos). Hay {3} hilo(s) en total que son monitorizados por esta V\u00E1lvula y pueden estar atascados.
 stuckThreadDetectionValve.notifyStuckThreadCompleted = El hilo "{0}" (id={3}), que previamente se report\u00F3 como atascado, se ha completado. Estuvo activo por aproximadamente {1} milisegundos. {2, choice,0\#|0< Hay a\u00FAn {2} hilo(s) que son monitorizados por esta V\u00E1lvula y pueden estar atascados.}
+
 # HTTP status reports
-http.100 = El cliente puede continuar.
-http.101 = El servidor est\u00E1 conmutando protocolos con arreglo a la cabecera "Upgrade".
-http.201 = El requerimiento tuvo \u00E9xito y un nuevo recurso ha sido creado en el servidor.
-http.202 = Este requerimiento ha sido aceptado para ser procesado, pero no ha sido completado.
-http.203 = La informaci\u00F3n meta presentada por el cliente no se origin\u00F3 desde el servidor.
-http.204 = El requerimiento tuvo \u00E9xito pero no hay informaci\u00F3n que devolver.
-http.205 = El cliente no deber\u00EDa de limpiar la vista del documento que caus\u00F3 que este requerimiento fuera enviado.
-http.206 = El servidor ha rellenado paci\u00E1lmente un requerimiento GET para este recurso.
-http.207 = Se han devuelto valores m\u00FAltiples de estado.
-http.300 = El recurso requerido corresponde a una cualquiera de un conjunto de representaciones, cada una con su propia localizaci\u00F3n espec\u00EDfica.
-http.301 = El recurso requerido ha sido movido perman\u00E9ntemente a una nueva localizaci\u00F3n.
-http.302 = El recurso requerido ha sido movido tempor\u00E1lmente a una nueva localizaci\u00F3n.
-http.303 = La respuesta a este requerimiento se puede hallar bajo una URI diferente.
-http.304 = El recurso requerido est\u00E1 disponible y no ha sido modificado.
-http.305 = El recurso requerido debe de ser accedido a trav\u00E9s del apoderado (proxy) dado mediante la cabecera "Location".
-http.400 = El requerimiento enviado por el cliente era sint\u00E1cticamente incorrecto.
-http.401 = Este requerimiento requiere autenticaci\u00F3n HTTP.
-http.402 = Se requiere pago para acceder a este recurso.
-http.403 = El acceso al recurso especificado ha sido prohibido.
-http.404 = El recurso requerido no est\u00E1 disponible.
-http.405 = El m\u00E9todo HTTP especificado no est\u00E1 permitido para el recurso requerido.
-http.406 = El recurso identificado por este requerimiento s\u00F3lo es capaz de generar respuestas con caracter\u00EDsticas no aceptables con arreglo a las cabeceras "accept" de requerimiento.
-http.407 = El cliente debe de ser primero autenticado en el apoderado.
-http.408 = El cliente no produjo un requerimiento dentro del tiempo en que el servidor estaba preparado esperando.
-http.409 = El requerimiento no pudo ser completado debido a un conflicto con el estado actual del recurso.
-http.410 = El recurso requerido ya no est\u00E1 disponible y no se conoce direcci\u00F3n de reenv\u00EDo.
-http.411 = Este requerimiento no puede ser manejado sin un tama\u00F1o definido de contenido.
-http.412 = Una precondici\u00F3n especificada ha fallado para este requerimiento.
-http.413 = La entidad de requerimiento es mayor de lo que el servidor quiere o puede procesar.
-http.414 = El servidor rechaz\u00F3 este requerimiento porque la URI requerida era demasiado larga.
-http.415 = El servidor rechaz\u00F3 este requerimiento porque la entidad requerida se encuentra en un formato no soportado por el recurso requerido para el m\u00E9todo requerido.
-http.416 = El rango de byte requerido no puede ser satisfecho.
-http.417 = Lo que se espera dado por la cabecera "Expect" de requerimiento no pudo ser completado.
-http.422 = El servidor entendi\u00F3 el tipo de contenido y la sint\u00E1xis del requerimiento pero no pudo procesar las instrucciones contenidas.
-http.423 = La fuente o recurso de destino de un m\u00E9todo est\u00E1 bloqueada.
-http.500 = El servidor encontr\u00F3 un error interno que hizo que no pudiera rellenar este requerimiento.
-http.501 = El servidor no soporta la funcionalidad necesaria para rellenar este requerimiento.
-http.502 = Este servidor recibi\u00F3 una respuesta inv\u00E1lida desde un servidor que consult\u00F3 cuando actuaba como apoderado o pasarela.
-http.503 = El servicio requerido no est\u00E1 disponible en este momento.
-http.504 = El servidor recibi\u00F3 un Tiempo Agotado desde un servidor superior cuando actuaba como pasarela o apoderado.
-http.505 = El servidor no soporta la versi\u00F3n de protocolo HTTP requerida.
-http.507 = El recurso no tiene espacio suficiente para registrar el estado del recurso tras la ejecuci\u00F3n de este m\u00E9todo.
+http.400.desc = El requerimiento enviado por el cliente era sint\u00E1cticamente incorrecto.
+http.401.desc = Este requerimiento requiere autenticaci\u00F3n HTTP.
+http.402.desc = Se requiere pago para acceder a este recurso.
+http.403.desc = El acceso al recurso especificado ha sido prohibido.
+http.404.desc = El recurso requerido no est\u00E1 disponible.
+http.405.desc = El m\u00E9todo HTTP especificado no est\u00E1 permitido para el recurso requerido.
+http.406.desc = El recurso identificado por este requerimiento s\u00F3lo es capaz de generar respuestas con caracter\u00EDsticas no aceptables con arreglo a las cabeceras "accept" de requerimiento.
+http.407.desc = El cliente debe de ser primero autenticado en el apoderado.
+http.408.desc = El cliente no produjo un requerimiento dentro del tiempo en que el servidor estaba preparado esperando.
+http.409.desc = El requerimiento no pudo ser completado debido a un conflicto con el estado actual del recurso.
+http.410.desc = El recurso requerido ya no est\u00E1 disponible y no se conoce direcci\u00F3n de reenv\u00EDo.
+http.411.desc = Este requerimiento no puede ser manejado sin un tama\u00F1o definido de contenido.
+http.412.desc = Una precondici\u00F3n especificada ha fallado para este requerimiento.
+http.413.desc = La entidad de requerimiento es mayor de lo que el servidor quiere o puede procesar.
+http.414.desc = El servidor rechaz\u00F3 este requerimiento porque la URI requerida era demasiado larga.
+http.415.desc = El servidor rechaz\u00F3 este requerimiento porque la entidad requerida se encuentra en un formato no soportado por el recurso requerido para el m\u00E9todo requerido.
+http.416.desc = El rango de byte requerido no puede ser satisfecho.
+http.417.desc = Lo que se espera dado por la cabecera "Expect" de requerimiento no pudo ser completado.
+http.422.desc = El servidor entendi\u00F3 el tipo de contenido y la sint\u00E1xis del requerimiento pero no pudo procesar las instrucciones contenidas.
+http.423.desc = La fuente o recurso de destino de un m\u00E9todo est\u00E1 bloqueada.
+http.500.desc = El servidor encontr\u00F3 un error interno que hizo que no pudiera rellenar este requerimiento.
+http.501.desc = El servidor no soporta la funcionalidad necesaria para rellenar este requerimiento.
+http.502.desc = Este servidor recibi\u00F3 una respuesta inv\u00E1lida desde un servidor que consult\u00F3 cuando actuaba como apoderado o pasarela.
+http.503.desc = El servicio requerido no est\u00E1 disponible en este momento.
+http.504.desc = El servidor recibi\u00F3 un Tiempo Agotado desde un servidor superior cuando actuaba como pasarela o apoderado.
+http.505.desc = El servidor no soporta la versi\u00F3n de protocolo HTTP requerida.
+http.507.desc = El recurso no tiene espacio suficiente para registrar el estado del recurso tras la ejecuci\u00F3n de este m\u00E9todo.
diff --git a/java/org/apache/catalina/valves/LocalStrings_fr.properties b/java/org/apache/catalina/valves/LocalStrings_fr.properties
index 7faaeca0..58068700 100644
--- a/java/org/apache/catalina/valves/LocalStrings_fr.properties
+++ b/java/org/apache/catalina/valves/LocalStrings_fr.properties
@@ -14,8 +14,8 @@
 # limitations under the License.
 
 # Error report valve
-errorReportValve.errorReport=Rapport d''erreur
-errorReportValve.statusHeader=Etat HTTP {0} - {1}
+errorReportValve.statusHeader=\u00c9tat HTTP {0} \u2013 {1}
+errorReportValve.type=Type
 errorReportValve.exceptionReport=Rapport d''exception
 errorReportValve.statusReport=Rapport d''\u00e9tat
 errorReportValve.message=message
@@ -23,48 +23,33 @@ errorReportValve.description=description
 errorReportValve.exception=exception
 errorReportValve.rootCause=cause m\u00e8re
 errorReportValve.note=note
-errorReportValve.rootCauseInLogs=La trace compl\u00e8te de la cause m\u00e8re de cette erreur est disponible dans les fichiers journaux de {0}.
+errorReportValve.rootCauseInLogs=La trace compl\u00e8te de la cause m\u00e8re de cette erreur est disponible dans les fichiers journaux de ce serveur.
 
 # HTTP status reports
-http.100=Le client peut continuer.
-http.101=Le serveur change de protocoles suivant la directive "Upgrade" de l''ent\u00eate.
-http.201=La requ\u00eate a r\u00e9ussi et une nouvelle ressource a \u00e9t\u00e9 cr\u00e9\u00e9e sur le serveur.
-http.202=La requ\u00eate a \u00e9t\u00e9 accept\u00e9e pour traitement, mais n''a pas \u00e9t\u00e9 termin\u00e9e.
-http.203=L''information meta pr\u00e9sent\u00e9e par le client n''a pas pour origine ce serveur.
-http.204=La requ\u00eate a r\u00e9ussi mais il n''y a aucune information \u00e0 retourner.
-http.205=Le client doit remettre \u00e0 z\u00e9ro la vue de document qui a caus\u00e9 l''envoi de cette requ\u00eate.
-http.206=Le serveur a satisfait une requ\u00eate GET partielle pour cette ressource.
-http.207=Plusieurs valeurs d''\u00e9tats ont \u00e9t\u00e9 retourn\u00e9es.
-http.300=La ressource demand\u00e9e correspond \u00e0 plusieurs repr\u00e9sentations, chacune avec sa propre localisation.
-http.301=La ressource demand\u00e9e a \u00e9t\u00e9 d\u00e9plac\u00e9e de fa\u00e7on permanente vers une nouvelle localisation.
-http.302=La ressource demand\u00e9e a \u00e9t\u00e9 d\u00e9plac\u00e9e de fa\u00e7on temporaire vers une nouvelle localisation.
-http.303=La r\u00e9ponse \u00e0 cette requ\u00eate peut \u00eatre trouv\u00e9e \u00e0 une URI diff\u00e9rente.
-http.304=La ressource demand\u00e9e est disponible et n''a pas \u00e9t\u00e9 modifi\u00e9e.
-http.305=La ressource demand\u00e9e doit \u00eatre acc\u00e9d\u00e9e au travers du relais indiqu\u00e9 par la directive "Location" de l''ent\u00eate.
-http.400=La requ\u00eate envoy\u00e9e par le client \u00e9tait syntaxiquement incorrecte.
-http.401=La requ\u00eate n\u00e9cessite une authentification HTTP.
-http.402=Un paiement est demand\u00e9 pour acc\u00e9der \u00e0 cette ressource.
-http.403=L''acc\u00e8s \u00e0 la ressource demand\u00e9e a \u00e9t\u00e9 interdit.
-http.404=La ressource demand\u00e9e n''est pas disponible.
-http.405=La m\u00e9thode HTTP sp\u00e9cifi\u00e9e n''est pas autoris\u00e9e pour la ressource demand\u00e9e.
-http.406=La ressource identifi\u00e9e par cette requ\u00eate n''est capable de g\u00e9n\u00e9rer des r\u00e9ponses qu''avec des caract\u00e9ristiques incompatible avec la directive "accept" pr\u00e9sente dans l''ent\u00eate de requ\u00eate.
-http.407=Le client doit d''abord s''authentifier aupr\u00e8s du relais.
-http.408=Le client n''a pas produit de requ\u00eate pendant le temps d''attente du serveur.
-http.409=La requ\u00eate ne peut \u00eatre finalis\u00e9e suite \u00e0 un conflit li\u00e9 \u00e0 l''\u00e9tat de la ressource.
-http.410=La ressource demand\u00e9e n''est pas disponible, et aucune addresse de rebond (forwarding) n''est connue.
-http.411=La requ\u00eate ne peut \u00eatre trait\u00e9e sans d\u00e9finition d''une taille de contenu (content length).
-http.412=Une condition pr\u00e9alable demand\u00e9e n''est pas satisfaite pour cette requ\u00eate.
-http.413=L''entit\u00e9 de requ\u00eate est plus importante que ce que le serveur veut ou peut traiter.
-http.414=Le serveur a refus\u00e9 cette requ\u00eate car l''URI de requ\u00eate est trop longue.
-http.415=Le serveur a refus\u00e9 cette requ\u00eate car l''entit\u00e9 de requ\u00eate est dans un format non support\u00e9 par la ressource demand\u00e9e avec la m\u00e9thode sp\u00e9cifi\u00e9e.
-http.416=La plage d''octets demand\u00e9e (byte range) ne peut \u00eatre satisfaite.
-http.417=L''attente indiqu\u00e9e dans la directive "Expect" de l''ent\u00eate de requ\u00eate ne peut \u00eatre satisfaite.
-http.422=Le serveur a compris le type de contenu (content type) ainsi que la syntaxe de la requ\u00eate mais a \u00e9t\u00e9 incapable de traiter les instructions contenues.
-http.423=La ressource source ou destination de la m\u00e9thode est verrouill\u00e9e.
-http.500=Le serveur a rencontr\u00e9 une erreur interne qui l''a emp\u00each\u00e9 de satisfaire la requ\u00eate.
-http.501=Le serveur ne supporte pas la fonctionnalit\u00e9 demand\u00e9e pour satisfaire cette requ\u00eate.
-http.502=Le serveur a re\u00e7u une r\u00e9ponse invalide d''un serveur qu''il consultait en tant que relais ou passerelle.
-http.503=Le service demand\u00e9 n''est pas disponible actuellement.
-http.504=Le serveur a re\u00e7u un d\u00e9passement de delai (timeout) d''un serveur amont qu''il consultait en tant que relais ou passerelle.
-http.505=Le serveur ne supporte pas la version demand\u00e9e du protocole HTTP.
-http.507=L''espace disponible est insuffisant pour enregistrer l''\u00e9tat de la ressource apr\u00e8s ex\u00e9cution de cette m\u00e9thode.
+http.400.desc=La requ\u00eate envoy\u00e9e par le client \u00e9tait syntaxiquement incorrecte.
+http.401.desc=La requ\u00eate n\u00e9cessite une authentification HTTP.
+http.402.desc=Un paiement est demand\u00e9 pour acc\u00e9der \u00e0 cette ressource.
+http.403.desc=L''acc\u00e8s \u00e0 la ressource demand\u00e9e a \u00e9t\u00e9 interdit.
+http.404.desc=La ressource demand\u00e9e n''est pas disponible.
+http.405.desc=La m\u00e9thode HTTP sp\u00e9cifi\u00e9e n''est pas autoris\u00e9e pour la ressource demand\u00e9e.
+http.406.desc=La ressource identifi\u00e9e par cette requ\u00eate n''est capable de g\u00e9n\u00e9rer des r\u00e9ponses qu''avec des caract\u00e9ristiques incompatible avec la directive "accept" pr\u00e9sente dans l''ent\u00eate de requ\u00eate.
+http.407.desc=Le client doit d''abord s''authentifier aupr\u00e8s du relais.
+http.408.desc=Le client n''a pas produit de requ\u00eate pendant le temps d''attente du serveur.
+http.409.desc=La requ\u00eate ne peut \u00eatre finalis\u00e9e suite \u00e0 un conflit li\u00e9 \u00e0 l''\u00e9tat de la ressource.
+http.410.desc=La ressource demand\u00e9e n''est pas disponible, et aucune addresse de rebond (forwarding) n''est connue.
+http.411.desc=La requ\u00eate ne peut \u00eatre trait\u00e9e sans d\u00e9finition d''une taille de contenu (content length).
+http.412.desc=Une condition pr\u00e9alable demand\u00e9e n''est pas satisfaite pour cette requ\u00eate.
+http.413.desc=L''entit\u00e9 de requ\u00eate est plus importante que ce que le serveur veut ou peut traiter.
+http.414.desc=Le serveur a refus\u00e9 cette requ\u00eate car l''URI de requ\u00eate est trop longue.
+http.415.desc=Le serveur a refus\u00e9 cette requ\u00eate car l''entit\u00e9 de requ\u00eate est dans un format non support\u00e9 par la ressource demand\u00e9e avec la m\u00e9thode sp\u00e9cifi\u00e9e.
+http.416.desc=La plage d''octets demand\u00e9e (byte range) ne peut \u00eatre satisfaite.
+http.417.desc=L''attente indiqu\u00e9e dans la directive "Expect" de l''ent\u00eate de requ\u00eate ne peut \u00eatre satisfaite.
+http.422.desc=Le serveur a compris le type de contenu (content type) ainsi que la syntaxe de la requ\u00eate mais a \u00e9t\u00e9 incapable de traiter les instructions contenues.
+http.423.desc=La ressource source ou destination de la m\u00e9thode est verrouill\u00e9e.
+http.500.desc=Le serveur a rencontr\u00e9 une erreur interne qui l''a emp\u00each\u00e9 de satisfaire la requ\u00eate.
+http.501.desc=Le serveur ne supporte pas la fonctionnalit\u00e9 demand\u00e9e pour satisfaire cette requ\u00eate.
+http.502.desc=Le serveur a re\u00e7u une r\u00e9ponse invalide d''un serveur qu''il consultait en tant que relais ou passerelle.
+http.503.desc=Le service demand\u00e9 n''est pas disponible actuellement.
+http.504.desc=Le serveur a re\u00e7u un d\u00e9passement de delai (timeout) d''un serveur amont qu''il consultait en tant que relais ou passerelle.
+http.505.desc=Le serveur ne supporte pas la version demand\u00e9e du protocole HTTP.
+http.507.desc=L''espace disponible est insuffisant pour enregistrer l''\u00e9tat de la ressource apr\u00e8s ex\u00e9cution de cette m\u00e9thode.
diff --git a/java/org/apache/catalina/valves/LocalStrings_ja.properties b/java/org/apache/catalina/valves/LocalStrings_ja.properties
index 8fe65f28..cc3d1fe4 100644
--- a/java/org/apache/catalina/valves/LocalStrings_ja.properties
+++ b/java/org/apache/catalina/valves/LocalStrings_ja.properties
@@ -16,11 +16,10 @@
 jdbcAccessLogValve.exception=\u30a2\u30af\u30bb\u30b9\u30a8\u30f3\u30c8\u30ea\u306e\u633f\u5165\u3092\u5b9f\u884c\u4e2d\u306e\u4f8b\u5916\u3067\u3059
 # Error report valve
 errorReportValve.statusHeader=HTTP\u30b9\u30c6\u30fc\u30bf\u30b9 {0} - {1}
-errorReportValve.exceptionReport=\u4f8b\u5916\u30ec\u30dd\u30fc\u30c8
 errorReportValve.statusReport=\u30b9\u30c6\u30fc\u30bf\u30b9\u30ec\u30dd\u30fc\u30c8
 errorReportValve.message=\u30e1\u30c3\u30bb\u30fc\u30b8
 errorReportValve.description=\u8aac\u660e
 errorReportValve.exception=\u4f8b\u5916
 errorReportValve.rootCause=\u539f\u56e0
 errorReportValve.note=\u6ce8\u610f
-errorReportValve.rootCauseInLogs=\u539f\u56e0\u306e\u3059\u3079\u3066\u306e\u30b9\u30bf\u30c3\u30af\u30c8\u30ec\u30fc\u30b9\u306f\u3001{0}\u306e\u30ed\u30b0\u306b\u8a18\u9332\u3055\u308c\u3066\u3044\u307e\u3059
+errorReportValve.rootCauseInLogs=\u539f\u56e0\u306e\u3059\u3079\u3066\u306e\u30b9\u30bf\u30c3\u30af\u30c8\u30ec\u30fc\u30b9\u306f\u3001\u306e\u30ed\u30b0\u306b\u8a18\u9332\u3055\u308c\u3066\u3044\u307e\u3059
diff --git a/java/org/apache/catalina/webresources/JarWarResource.java b/java/org/apache/catalina/webresources/JarWarResource.java
index 036378e0..90321ee8 100644
--- a/java/org/apache/catalina/webresources/JarWarResource.java
+++ b/java/org/apache/catalina/webresources/JarWarResource.java
@@ -24,6 +24,7 @@ import java.util.jar.JarInputStream;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.UriUtil;
 
 /**
  * Represents a single resource (file or directory) that is located within a
@@ -38,8 +39,9 @@ public class JarWarResource extends AbstractArchiveResource {
     public JarWarResource(AbstractArchiveResourceSet archiveResourceSet, String webAppPath,
             String baseUrl, JarEntry jarEntry, String archivePath) {
 
-        super(archiveResourceSet, webAppPath, "jar:war:" + baseUrl + "*/" + archivePath + "!/",
-                jarEntry, "war:" + baseUrl + "*/" + archivePath);
+        super(archiveResourceSet, webAppPath,
+                "jar:war:" + baseUrl + UriUtil.getWarSeparator() + archivePath + "!/",
+                jarEntry, "war:" + baseUrl + UriUtil.getWarSeparator() + archivePath);
         this.archivePath = archivePath;
     }
 
diff --git a/java/org/apache/catalina/webresources/StandardRoot.java b/java/org/apache/catalina/webresources/StandardRoot.java
index 8d99951e..cc389d6b 100644
--- a/java/org/apache/catalina/webresources/StandardRoot.java
+++ b/java/org/apache/catalina/webresources/StandardRoot.java
@@ -44,6 +44,7 @@ import org.apache.catalina.WebResourceSet;
 import org.apache.catalina.util.LifecycleMBeanBase;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.UriUtil;
 import org.apache.tomcat.util.http.RequestUtil;
 import org.apache.tomcat.util.res.StringManager;
 
@@ -806,7 +807,7 @@ public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot
                 if ("jar".equals(url.getProtocol())) {
                     endOfFileUrl = jarUrl.indexOf("!/");
                 } else {
-                    endOfFileUrl = jarUrl.indexOf("*/");
+                    endOfFileUrl = jarUrl.indexOf(UriUtil.getWarSeparator());
                 }
                 String fileUrl = jarUrl.substring(4, endOfFileUrl);
                 try {
diff --git a/java/org/apache/catalina/webresources/WarResource.java b/java/org/apache/catalina/webresources/WarResource.java
index 2d8ba8a4..c6425311 100644
--- a/java/org/apache/catalina/webresources/WarResource.java
+++ b/java/org/apache/catalina/webresources/WarResource.java
@@ -20,6 +20,7 @@ import java.util.jar.JarEntry;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.UriUtil;
 
 /**
  * Represents a single resource (file or directory) that is located within a
@@ -32,7 +33,8 @@ public class WarResource extends AbstractSingleArchiveResource {
 
     public WarResource(AbstractArchiveResourceSet archiveResourceSet, String webAppPath,
             String baseUrl, JarEntry jarEntry) {
-        super(archiveResourceSet, webAppPath, "war:" + baseUrl + "*/", jarEntry, baseUrl);
+        super(archiveResourceSet, webAppPath, "war:" + baseUrl + UriUtil.getWarSeparator(),
+                jarEntry, baseUrl);
     }
 
 
diff --git a/java/org/apache/coyote/AbstractProcessor.java b/java/org/apache/coyote/AbstractProcessor.java
index c01ae23e..4c93afef 100644
--- a/java/org/apache/coyote/AbstractProcessor.java
+++ b/java/org/apache/coyote/AbstractProcessor.java
@@ -84,19 +84,19 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
     protected void setErrorState(ErrorState errorState, Throwable t) {
         boolean blockIo = this.errorState.isIoAllowed() && !errorState.isIoAllowed();
         this.errorState = this.errorState.getMostSevere(errorState);
+        if (response.getStatus() < 400) {
+            response.setStatus(500);
+        }
+        if (t != null) {
+            request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
+        }
         if (blockIo && !ContainerThreadMarker.isContainerThread() && isAsync()) {
             // The error occurred on a non-container thread during async
             // processing which means not all of the necessary clean-up will
             // have been completed. Dispatch to a container thread to do the
             // clean-up. Need to do it this way to ensure that all the necessary
             // clean-up is performed.
-            if (response.getStatus() < 400) {
-                response.setStatus(500);
-            }
             getLog().info(sm.getString("abstractProcessor.nonContainerThreadError"), t);
-            // Set the request attribute so that the async onError() event is
-            // fired when the error event is processed
-            request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t);
             processSocketEvent(SocketEvent.ERROR, true);
         }
     }
@@ -478,7 +478,7 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
             break;
         }
         case PUSH_REQUEST: {
-            doPush((PushToken) param);
+            doPush((Request) param);
             break;
         }
         }
@@ -743,13 +743,13 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
      * Process a push. Processors that support push should override this method
      * and process the provided token.
      *
-     * @param pushToken Contains all the information necessary for the Processor
-     *                  to process the push request
+     * @param pushTarget Contains all the information necessary for the Processor
+     *                   to process the push request
      *
      * @throws UnsupportedOperationException if the protocol does not support
      *         push
      */
-    protected void doPush(PushToken pushToken) {
+    protected void doPush(Request pushTarget) {
         throw new UnsupportedOperationException(
                 sm.getString("abstractProcessor.pushrequest.notsupported"));
     }
diff --git a/java/org/apache/coyote/AsyncStateMachine.java b/java/org/apache/coyote/AsyncStateMachine.java
index e503427e..02da7a26 100644
--- a/java/org/apache/coyote/AsyncStateMachine.java
+++ b/java/org/apache/coyote/AsyncStateMachine.java
@@ -69,15 +69,15 @@ import org.apache.tomcat.util.security.PrivilegedSetTccl;
  * ERROR            - Something went wrong.
  *
  * |-----------------»------|
- * |                       \|/ /-----------------------------------«------------------------------|
- * |   |----------«-------ERROR----------------------------«-------------------------------|      |
- * |   |      complete() /|\/|\\                                                           |      |
- * |   |                  |  |  \                                                          |      |
- * |   |    |-----»-------|  |   \-----------»----------|                                  |      |
- * |   |    |                |                          |dispatch()                        |      |
- * |   |    |                |                         \|/                                 |      |
- * |   |    |                |          |--|timeout()   |                                  |      |
- * |   |    |     post()     |          | \|/           |     post()                       |      |
+ * |                       \|/ /---«-------------------------------«------------------------------|
+ * |   |----------«-----E R R O R--«-----------------------«-------------------------------|      |
+ * |   |      complete() /|\/|\\ \-«--------------------------------«-------|              |      |
+ * |   |                  |  |  \                                           |              |      |
+ * |   |    |-----»-------|  |   \-----------»----------|                   |              |      |
+ * |   |    |                |                          |dispatch()         |              |      |
+ * |   |    |                |                         \|/                  ^              |      |
+ * |   |    |                |          |--|timeout()   |                   |              |      |
+ * |   |    |     post()     |          | \|/           |     post()        |              |      |
  * |   |    |    |---------- | --»DISPATCHED«---------- | --------------COMPLETING«-----|  |      |
  * |   |    |    |           |   /|\/|\ |               |                | /|\ /|\      |  |      |
  * |   |    |    |    |---»- | ---|  |  |startAsync()   |       timeout()|--|   |       |  |      |
@@ -390,7 +390,8 @@ public class AsyncStateMachine {
                 state == AsyncState.DISPATCHED ||
                 state == AsyncState.TIMING_OUT ||
                 state == AsyncState.MUST_COMPLETE ||
-                state == AsyncState.READ_WRITE_OP) {
+                state == AsyncState.READ_WRITE_OP ||
+                state == AsyncState.COMPLETING) {
             clearNonBlockingListeners();
             state = AsyncState.ERROR;
         } else {
diff --git a/java/org/apache/coyote/PushToken.java b/java/org/apache/coyote/PushToken.java
deleted file mode 100644
index 83b5ba55..00000000
--- a/java/org/apache/coyote/PushToken.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You under the Apache License, Version 2.0
- *  (the "License"); you may not use this file except in compliance with
- *  the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- */
-package org.apache.coyote;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Token used during the HTTP/2 server push process.
- */
-public class PushToken {
-
-    private final AtomicBoolean result = new AtomicBoolean(false);
-    private final Request pushTarget;
-
-    public PushToken(Request pushTarget) {
-        this.pushTarget = pushTarget;
-    }
-
-    public Request getPushTarget() {
-        return pushTarget;
-    }
-
-    public void setResult(boolean result) {
-        this.result.set(result);
-    }
-
-    public boolean getResult() {
-        return result.get();
-    }
-}
diff --git a/java/org/apache/coyote/Response.java b/java/org/apache/coyote/Response.java
index 02de64c0..4cc44284 100644
--- a/java/org/apache/coyote/Response.java
+++ b/java/org/apache/coyote/Response.java
@@ -102,7 +102,7 @@ public final class Response {
      */
     String contentType = null;
     String contentLanguage = null;
-    String characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
+    String characterEncoding = null;
     long contentLength = -1;
     private Locale locale = DEFAULT_LOCALE;
 
@@ -525,7 +526,7 @@ public final class Response {
         contentType = null;
         contentLanguage = null;
         locale = DEFAULT_LOCALE;
-        characterEncoding = Constants.DEFAULT_CHARACTER_ENCODING;
+        characterEncoding = null;
         charsetSet = false;
         contentLength = -1;
         status = 200;
diff --git a/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java b/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
index 92be4176..d8204a4d 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11JsseProtocol.java
@@ -34,7 +34,7 @@ public abstract class AbstractHttp11JsseProtocol<S>
     }
 
 
-    protected String getSslImplemenationShortName() {
+    protected String getSslImplementationShortName() {
         if (OpenSSLImplementation.class.getName().equals(getSslImplementationName())) {
             return "openssl";
         }
diff --git a/java/org/apache/coyote/http11/Http11Nio2Protocol.java b/java/org/apache/coyote/http11/Http11Nio2Protocol.java
index 15c3b61b..de520ef2 100644
--- a/java/org/apache/coyote/http11/Http11Nio2Protocol.java
+++ b/java/org/apache/coyote/http11/Http11Nio2Protocol.java
@@ -44,7 +44,7 @@ public class Http11Nio2Protocol extends AbstractHttp11JsseProtocol<Nio2Channel>
     @Override
     protected String getNamePrefix() {
         if (isSSLEnabled()) {
-            return ("https-" + getSslImplemenationShortName()+ "-nio2");
+            return ("https-" + getSslImplementationShortName()+ "-nio2");
         } else {
             return ("http-nio2");
         }
diff --git a/java/org/apache/coyote/http11/Http11NioProtocol.java b/java/org/apache/coyote/http11/Http11NioProtocol.java
index 58052d0f..f872ffe0 100644
--- a/java/org/apache/coyote/http11/Http11NioProtocol.java
+++ b/java/org/apache/coyote/http11/Http11NioProtocol.java
@@ -76,7 +76,7 @@ public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {
     @Override
     protected String getNamePrefix() {
         if (isSSLEnabled()) {
-            return ("https-" + getSslImplemenationShortName()+ "-nio");
+            return ("https-" + getSslImplementationShortName()+ "-nio");
         } else {
             return ("http-nio");
         }
diff --git a/java/org/apache/coyote/http2/Http2Parser.java b/java/org/apache/coyote/http2/Http2Parser.java
index 10749155..54dd43b1 100644
--- a/java/org/apache/coyote/http2/Http2Parser.java
+++ b/java/org/apache/coyote/http2/Http2Parser.java
@@ -178,6 +178,11 @@ class Http2Parser {
             }
         } else {
             synchronized (dest) {
+                if (dest.remaining() < dataLength) {
+                    swallow(streamId, dataLength, false);
+                    // Client has sent more data than permitted by Window size
+                    throw new StreamException("Client sent more data than stream window allowed", Http2Error.FLOW_CONTROL_ERROR, streamId);
+                }
                 input.fill(true, dest, dataLength);
                 // Process padding before sending any notifications in case
                 // padding is invalid.
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index 03c5c250..15c44443 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -460,7 +460,7 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
 
         if (log.isDebugEnabled()) {
             log.debug(sm.getString("upgradeHandler.rst.debug", connectionId,
-                    Integer.toString(se.getStreamId()), se.getError()));
+                    Integer.toString(se.getStreamId()), se.getError(), se.getMessage()));
         }
 
         // Write a RST frame
diff --git a/java/org/apache/coyote/http2/LocalStrings.properties b/java/org/apache/coyote/http2/LocalStrings.properties
index 82b7ff57..d1ad5c44 100644
--- a/java/org/apache/coyote/http2/LocalStrings.properties
+++ b/java/org/apache/coyote/http2/LocalStrings.properties
@@ -114,7 +114,7 @@ upgradeHandler.pruneIncomplete=Connection [{0}] Failed to fully prune the connec
 upgradeHandler.pruneStart=Connection [{0}] Starting pruning of old streams. Limit is [{1}] + 10% and there are currently [{2}] streams.
 upgradeHandler.pruned=Connection [{0}] Pruned completed stream [{1}]
 upgradeHandler.prunedPriority=Connection [{0}] Pruned unused stream [{1}] that may have been part of the priority tree
-upgradeHandler.rst.debug=Connection [{0}], Stream [{1}], Error [{2}], RST (closing stream)
+upgradeHandler.rst.debug=Connection [{0}], Stream [{1}], Error [{2}], Message [{3}],  RST (closing stream)
 upgradeHandler.sendPrefaceFail=Connection [{0}], Failed to send preface to client
 upgradeHandler.socketCloseFailed=Error closing socket
 upgradeHandler.stream.closed=Stream [{0}] has been closed for some time
diff --git a/java/org/apache/coyote/http2/Stream.java b/java/org/apache/coyote/http2/Stream.java
index d5445e67..e18e4d04 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -471,7 +471,7 @@ public class Stream extends AbstractStream implements HeaderEmitter {
                 StreamException se = (StreamException) http2Exception;
                 if (log.isDebugEnabled()) {
                     log.debug(sm.getString("stream.reset.send", getConnectionId(), getIdentifier(),
-                            Long.toString(se.getError().getCode())));
+                            se.getError()));
                 }
                 state.sendReset();
                 handler.sendStreamReset(se);
@@ -492,9 +492,9 @@ public class Stream extends AbstractStream implements HeaderEmitter {
     }
 
 
-    boolean push(Request request) throws IOException {
+    final void push(Request request) throws IOException {
         if (!isPushSupported()) {
-            return false;
+            return;
         }
         // Set the special HTTP/2 headers
         request.getMimeHeaders().addValue(":method").duplicate(request.method());
@@ -517,8 +517,6 @@ public class Stream extends AbstractStream implements HeaderEmitter {
         }
 
         push(handler, request, this);
-
-        return true;
     }
 
 
@@ -751,7 +749,8 @@ public class Stream extends AbstractStream implements HeaderEmitter {
 
             // Ensure that only one thread accesses inBuffer at a time
             synchronized (inBuffer) {
-                while (inBuffer.position() == 0 && !isInputFinished()) {
+                boolean canRead = isActive() && !isInputFinished();
+                while (inBuffer.position() == 0 && canRead) {
                     // Need to block until some data is written
                     try {
                         if (log.isDebugEnabled()) {
@@ -781,7 +780,7 @@ public class Stream extends AbstractStream implements HeaderEmitter {
                     }
                     inBuffer.get(outBuffer, 0, written);
                     inBuffer.clear();
-                } else if (isInputFinished()) {
+                } else if (!canRead) {
                     return -1;
                 } else {
                     // Should never happen
@@ -807,7 +806,8 @@ public class Stream extends AbstractStream implements HeaderEmitter {
 
             // Ensure that only one thread accesses inBuffer at a time
             synchronized (inBuffer) {
-                while (inBuffer.position() == 0 && !isInputFinished()) {
+                boolean canRead = isActive() && !isInputFinished();
+                while (inBuffer.position() == 0 && canRead) {
                     // Need to block until some data is written
                     try {
                         if (log.isDebugEnabled()) {
@@ -837,7 +837,7 @@ public class Stream extends AbstractStream implements HeaderEmitter {
                     }
                     inBuffer.get(outBuffer, 0, written);
                     inBuffer.clear();
-                } else if (isInputFinished()) {
+                } else if (!canRead) {
                     return -1;
                 } else {
                     // Should never happen
diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java
index 88e206d6..3ccfd8a6 100644
--- a/java/org/apache/coyote/http2/StreamProcessor.java
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -24,7 +24,7 @@ import org.apache.coyote.ActionCode;
 import org.apache.coyote.Adapter;
 import org.apache.coyote.ContainerThreadMarker;
 import org.apache.coyote.ErrorState;
-import org.apache.coyote.PushToken;
+import org.apache.coyote.Request;
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.util.buf.ByteChunk;
@@ -203,9 +203,9 @@ class StreamProcessor extends AbstractProcessor {
 
 
     @Override
-    protected final void doPush(PushToken pushToken) {
+    protected final void doPush(Request pushTarget) {
         try {
-            pushToken.setResult(stream.push(pushToken.getPushTarget()));
+            stream.push(pushTarget);
         } catch (IOException ioe) {
             setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe);
             response.setErrorException(ioe);
diff --git a/java/org/apache/jasper/compiler/EncodingDetector.java b/java/org/apache/jasper/compiler/EncodingDetector.java
index 5843ef49..482dc55b 100644
--- a/java/org/apache/jasper/compiler/EncodingDetector.java
+++ b/java/org/apache/jasper/compiler/EncodingDetector.java
@@ -27,6 +27,9 @@ import javax.xml.stream.XMLStreamReader;
 /*
  * The BoM detection is derived from:
  * http://svn.us.apache.org/viewvc/tomcat/trunk/java/org/apache/jasper/xmlparser/XMLEncodingDetector.java?annotate=1742248
+ *
+ * The prolog is always at least as specific as the BOM therefore any encoding
+ * specified in the prolog should take priority over the BOM.
  */
 class EncodingDetector {
 
@@ -35,8 +38,9 @@ class EncodingDetector {
         XML_INPUT_FACTORY = XMLInputFactory.newFactory();
     }
 
-    private final BomResult bomResult;
-    private final String prologEncoding;
+    private final String encoding;
+    private final int skip;
+    private final boolean encodingSpecifiedInProlog;
 
 
     /*
@@ -50,7 +54,7 @@ class EncodingDetector {
         BufferedInputStream bis = new BufferedInputStream(is, 4);
         bis.mark(4);
 
-        bomResult = processBom(bis);
+        BomResult bomResult = processBom(bis);
 
         // Reset the stream back to the start to allow the XML prolog detection
         // to work. Skip any BoM we discovered.
@@ -59,22 +63,30 @@ class EncodingDetector {
             is.read();
         }
 
-        prologEncoding = getPrologEncoding(bis);
+        String prologEncoding = getPrologEncoding(bis);
+        if (prologEncoding == null) {
+            encodingSpecifiedInProlog = false;
+            encoding = bomResult.encoding;
+        } else {
+            encodingSpecifiedInProlog = true;
+            encoding = prologEncoding;
+        }
+        skip = bomResult.skip;
     }
 
 
-    String getBomEncoding() {
-        return bomResult.encoding;
+    String getEncoding() {
+        return encoding;
     }
 
 
     int getSkip() {
-        return bomResult.skip;
+        return skip;
     }
 
 
-    String getPrologEncoding() {
-        return prologEncoding;
+    boolean isEncodingSpecifiedInProlog() {
+        return encodingSpecifiedInProlog;
     }
 
 
diff --git a/java/org/apache/jasper/compiler/Generator.java b/java/org/apache/jasper/compiler/Generator.java
index 58069706..6869fdf6 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -2409,14 +2409,6 @@ class Generator {
             // includes setting the context
             generateSetters(n, tagHandlerVar, handlerInfo, false);
 
-            // JspIdConsumer (after context has been set)
-            if (n.implementsJspIdConsumer()) {
-                out.printin(tagHandlerVar);
-                out.print(".setJspId(\"");
-                out.print(createJspId());
-                out.println("\");");
-            }
-
             if (n.implementsTryCatchFinally()) {
                 out.printin("int[] ");
                 out.print(tagPushBodyCountVar);
@@ -2451,7 +2443,6 @@ class Generator {
                     out.println(" != javax.servlet.jsp.tagext.Tag.EVAL_BODY_INCLUDE) {");
                     // Assume EVAL_BODY_BUFFERED
                     out.pushIndent();
-                    out.printil("out = _jspx_page_context.pushBody();");
                     if (n.implementsTryCatchFinally()) {
                         out.printin(tagPushBodyCountVar);
                         out.println("[0]++;");
@@ -2459,11 +2450,10 @@ class Generator {
                         out.printin(pushBodyCountVar);
                         out.println("[0]++;");
                     }
-                    out.printin(tagHandlerVar);
-                    out.println(".setBodyContent((javax.servlet.jsp.tagext.BodyContent) out);");
-                    out.printin(tagHandlerVar);
-                    out.println(".doInitBody();");
-
+                    out.printin("out = org.apache.jasper.runtime.JspRuntimeLibrary.startBufferedBody(");
+                    out.print("_jspx_page_context, ");
+                    out.print(tagHandlerVar);
+                    out.println(");");
                     out.popIndent();
                     out.printil("}");
 
@@ -2487,24 +2477,21 @@ class Generator {
         }
 
         private void writeNewInstance(String tagHandlerVar, String tagHandlerClassName) {
+            out.printin(tagHandlerClassName);
+            out.print(" ");
+            out.print(tagHandlerVar);
+            out.print(" = ");
             if (Constants.USE_INSTANCE_MANAGER_FOR_TAGS) {
-                out.printin(tagHandlerClassName);
-                out.print(" ");
-                out.print(tagHandlerVar);
-                out.print(" = (");
+                out.print("(");
                 out.print(tagHandlerClassName);
                 out.print(")");
                 out.print("_jsp_getInstanceManager().newInstance(\"");
                 out.print(tagHandlerClassName);
                 out.println("\", this.getClass().getClassLoader());");
             } else {
-                out.printin(tagHandlerClassName);
-                out.print(" ");
-                out.print(tagHandlerVar);
-                out.print(" = (");
                 out.print("new ");
                 out.print(tagHandlerClassName);
-                out.println("());");
+                out.println("();");
                 out.printin("_jsp_getInstanceManager().newInstance(");
                 out.print(tagHandlerVar);
                 out.println(");");
@@ -2614,21 +2601,18 @@ class Generator {
             }
 
             // Ensure clean-up takes place
+            // Use JspRuntimeLibrary to minimise code in _jspService()
             out.popIndent();
             out.printil("} finally {");
             out.pushIndent();
+            out.printin("org.apache.jasper.runtime.JspRuntimeLibrary.releaseTag(");
+            out.print(tagHandlerVar);
+            out.print(", _jsp_getInstanceManager(), ");
             if (isPoolingEnabled && !(n.implementsJspIdConsumer())) {
-                out.printin("if (!");
                 out.print(tagHandlerVar);
-                out.println("_reused) {");
-                out.pushIndent();
-            }
-            out.printin(tagHandlerVar);
-            out.println(".release();");
-            writeDestroyInstance(tagHandlerVar);
-            if (isPoolingEnabled && !(n.implementsJspIdConsumer())) {
-                out.popIndent();
-                out.printil("}");
+                out.println("_reused);");
+            } else {
+                out.println("false);");
             }
             out.popIndent();
             out.printil("}");
@@ -2661,14 +2645,6 @@ class Generator {
 
             generateSetters(n, tagHandlerVar, handlerInfo, true);
 
-            // JspIdConsumer (after context has been set)
-            if (n.implementsJspIdConsumer()) {
-                out.printin(tagHandlerVar);
-                out.print(".setJspId(\"");
-                out.print(createJspId());
-                out.println("\");");
-            }
-
             // Set the body
             if (findJspBody(n) == null) {
                 /*
@@ -3263,6 +3239,14 @@ class Generator {
                     out.println(");");
                 }
             }
+
+            // JspIdConsumer (after context has been set)
+            if (n.implementsJspIdConsumer()) {
+                out.printin(tagHandlerVar);
+                out.print(".setJspId(\"");
+                out.print(createJspId());
+                out.println("\");");
+            }
         }
 
         /*
diff --git a/java/org/apache/jasper/compiler/ParserController.java b/java/org/apache/jasper/compiler/ParserController.java
index 54cf4908..c721d469 100644
--- a/java/org/apache/jasper/compiler/ParserController.java
+++ b/java/org/apache/jasper/compiler/ParserController.java
@@ -321,8 +321,8 @@ class ParserController implements TagConstants {
             InputStream inStream = JspUtil.getInputStream(absFileName, jar, ctxt);
             EncodingDetector encodingDetector = new EncodingDetector(inStream);
 
-            sourceEnc = encodingDetector.getBomEncoding();
-            isEncodingSpecifiedInProlog = (encodingDetector.getPrologEncoding() != null);
+            sourceEnc = encodingDetector.getEncoding();
+            isEncodingSpecifiedInProlog = encodingDetector.isEncodingSpecifiedInProlog();
             isBomPresent = (encodingDetector.getSkip() > 0);
             skip = encodingDetector.getSkip();
 
diff --git a/java/org/apache/jasper/runtime/JspApplicationContextImpl.java b/java/org/apache/jasper/runtime/JspApplicationContextImpl.java
index 10842d3c..ec41f26b 100644
--- a/java/org/apache/jasper/runtime/JspApplicationContextImpl.java
+++ b/java/org/apache/jasper/runtime/JspApplicationContextImpl.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import javax.el.CompositeELResolver;
+import javax.el.ELContext;
 import javax.el.ELContextEvent;
 import javax.el.ELContextListener;
 import javax.el.ELResolver;
@@ -101,12 +102,16 @@ public class JspApplicationContextImpl implements JspApplicationContext {
         ctx.putContext(JspContext.class, context);
 
         // alert all ELContextListeners
-        ELContextEvent event = new ELContextEvent(ctx);
+        fireListeners(ctx);
+
+        return ctx;
+    }
+
+    protected void fireListeners(ELContext elContext) {
+        ELContextEvent event = new ELContextEvent(elContext);
         for (int i = 0; i < this.contextListeners.size(); i++) {
             this.contextListeners.get(i).contextCreated(event);
         }
-
-        return ctx;
     }
 
     private ELResolver createELResolver() {
diff --git a/java/org/apache/jasper/runtime/JspContextWrapper.java b/java/org/apache/jasper/runtime/JspContextWrapper.java
index 9af183f4..5197ea0e 100644
--- a/java/org/apache/jasper/runtime/JspContextWrapper.java
+++ b/java/org/apache/jasper/runtime/JspContextWrapper.java
@@ -41,7 +41,9 @@ import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.JspApplicationContext;
 import javax.servlet.jsp.JspContext;
+import javax.servlet.jsp.JspFactory;
 import javax.servlet.jsp.JspWriter;
 import javax.servlet.jsp.PageContext;
 import javax.servlet.jsp.el.ELException;
@@ -508,6 +510,11 @@ public class JspContextWrapper extends PageContext implements VariableResolver {
     public ELContext getELContext() {
         if (elContext == null) {
             elContext = new ELContextWrapper(rootJspCtxt.getELContext(), jspTag, this);
+            JspFactory factory = JspFactory.getDefaultFactory();
+            JspApplicationContext jspAppCtxt = factory.getJspApplicationContext(servletContext);
+            if (jspAppCtxt instanceof JspApplicationContextImpl) {
+                ((JspApplicationContextImpl) jspAppCtxt).fireListeners(elContext);
+            }
         }
         return elContext;
     }
diff --git a/java/org/apache/jasper/runtime/JspRuntimeLibrary.java b/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
index fe4bbcf7..b51ddf67 100644
--- a/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
+++ b/java/org/apache/jasper/runtime/JspRuntimeLibrary.java
@@ -29,12 +29,18 @@ import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.jsp.JspException;
 import javax.servlet.jsp.JspWriter;
 import javax.servlet.jsp.PageContext;
 import javax.servlet.jsp.tagext.BodyContent;
+import javax.servlet.jsp.tagext.BodyTag;
+import javax.servlet.jsp.tagext.Tag;
 
 import org.apache.jasper.JasperException;
 import org.apache.jasper.compiler.Localizer;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.InstanceManager;
 
 /**
  * Bunch of util methods that are used by code generated for useBean,
@@ -50,6 +56,8 @@ import org.apache.jasper.compiler.Localizer;
  */
 public class JspRuntimeLibrary {
 
+    private static final Log log = LogFactory.getLog(JspRuntimeLibrary.class);
+
     /**
      * Returns the value of the javax.servlet.error.exception request
      * attribute value, if present, otherwise the value of the
@@ -963,4 +971,40 @@ public class JspRuntimeLibrary {
         return false;
     }
 
+
+    public static JspWriter startBufferedBody(PageContext pageContext, BodyTag tag)
+            throws JspException {
+        BodyContent out = pageContext.pushBody();
+        tag.setBodyContent(out);
+        tag.doInitBody();
+        return out;
+    }
+
+
+    public static void releaseTag(Tag tag, InstanceManager instanceManager, boolean reused) {
+        // Caller ensures pool is non-null if reuse is true
+        if (!reused) {
+            releaseTag(tag, instanceManager);
+        }
+    }
+
+
+    protected static void releaseTag(Tag tag, InstanceManager instanceManager) {
+        try {
+            tag.release();
+        } catch (Throwable t) {
+            ExceptionUtils.handleThrowable(t);
+            log.warn("Error processing release on tag instance of "
+                    + tag.getClass().getName(), t);
+        }
+        try {
+            instanceManager.destroyInstance(tag);
+        } catch (Exception e) {
+            Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
+            ExceptionUtils.handleThrowable(t);
+            log.warn("Error processing preDestroy on tag instance of "
+                    + tag.getClass().getName(), t);
+        }
+
+    }
 }
diff --git a/java/org/apache/jasper/runtime/TagHandlerPool.java b/java/org/apache/jasper/runtime/TagHandlerPool.java
index b730c4b6..00af0030 100644
--- a/java/org/apache/jasper/runtime/TagHandlerPool.java
+++ b/java/org/apache/jasper/runtime/TagHandlerPool.java
@@ -22,8 +22,6 @@ import javax.servlet.jsp.JspException;
 import javax.servlet.jsp.tagext.Tag;
 
 import org.apache.jasper.Constants;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
 import org.apache.tomcat.InstanceManager;
 
 /**
@@ -38,8 +36,6 @@ public class TagHandlerPool {
     public static final String OPTION_TAGPOOL = "tagpoolClassName";
     public static final String OPTION_MAXSIZE = "tagpoolMaxSize";
 
-    private static final Log log = LogFactory.getLog(TagHandlerPool.class);
-
     // index of next available tag handler
     private int current;
     protected InstanceManager instanceManager = null;
@@ -143,7 +139,7 @@ public class TagHandlerPool {
             }
         }
         // There is no need for other threads to wait for us to release
-        doRelease(handler);
+        JspRuntimeLibrary.releaseTag(handler, instanceManager);
     }
 
     /**
@@ -152,26 +148,7 @@ public class TagHandlerPool {
      */
     public synchronized void release() {
         for (int i = current; i >= 0; i--) {
-            doRelease(handlers[i]);
-        }
-    }
-
-
-    private void doRelease(Tag handler) {
-        try {
-            handler.release();
-        } catch (Throwable t) {
-            ExceptionUtils.handleThrowable(t);
-            log.warn("Error processing release on tag instance of "
-                    + handler.getClass().getName(), t);
-        }
-        try {
-            instanceManager.destroyInstance(handler);
-        } catch (Exception e) {
-            Throwable t = ExceptionUtils.unwrapInvocationTargetException(e);
-            ExceptionUtils.handleThrowable(t);
-            log.warn("Error processing preDestroy on tag instance of "
-                    + handler.getClass().getName(), t);
+            JspRuntimeLibrary.releaseTag(handlers[i], instanceManager);
         }
     }
 
diff --git a/java/org/apache/tomcat/util/bcel/Const.java b/java/org/apache/tomcat/util/bcel/Const.java
index 61b9355e..9948d967 100644
--- a/java/org/apache/tomcat/util/bcel/Const.java
+++ b/java/org/apache/tomcat/util/bcel/Const.java
@@ -114,6 +114,18 @@ public final class Const {
      * The Constant Pool in The Java Virtual Machine Specification</a> */
     public static final byte CONSTANT_InvokeDynamic    = 18;
 
+    /** Marks a constant pool entry as a Module Reference.
+     * Note: Early access Java 9 support- currently subject to change.
+     * @see <a href="http://cr.openjdk.java.net/~mr/jigsaw/spec/lang-vm.html#jigsaw-2.6";>
+     * JPMS: Modules in the Java Language and JVM</a> */
+    public static final byte CONSTANT_Module             = 19;
+
+    /** Marks a constant pool entry as a Package Reference.
+     * Note: Early access Java 9 support- currently subject to change.
+     * @see <a href="http://cr.openjdk.java.net/~mr/jigsaw/spec/lang-vm.html#jigsaw-2.6";>
+     * JPMS: Modules in the Java Language and JVM</a> */
+    public static final byte CONSTANT_Package            = 20;
+
     /**
      * The names of the types of entries in a constant pool.
      * Use getConstantName instead
@@ -124,7 +136,8 @@ public final class Const {
     "CONSTANT_Class", "CONSTANT_String", "CONSTANT_Fieldref",
     "CONSTANT_Methodref", "CONSTANT_InterfaceMethodref",
     "CONSTANT_NameAndType", "", "", "CONSTANT_MethodHandle",
-    "CONSTANT_MethodType", "", "CONSTANT_InvokeDynamic" };
+    "CONSTANT_MethodType", "", "CONSTANT_InvokeDynamic",
+    "CONSTANT_Module", "CONSTANT_Package"};
 
     public static String getConstantName(int index) {
         return CONSTANT_NAMES[index];
diff --git a/java/org/apache/tomcat/util/bcel/classfile/Constant.java b/java/org/apache/tomcat/util/bcel/classfile/Constant.java
index 51f75ced..bc31326d 100644
--- a/java/org/apache/tomcat/util/bcel/classfile/Constant.java
+++ b/java/org/apache/tomcat/util/bcel/classfile/Constant.java
@@ -81,6 +81,8 @@ public abstract class Constant {
                 return ConstantUtf8.getInstance(input);
             case Const.CONSTANT_String:
             case Const.CONSTANT_MethodType:
+            case Const.CONSTANT_Module:
+            case Const.CONSTANT_Package:
                 skipSize = 2; // unsigned short
                 break;
             case Const.CONSTANT_MethodHandle:
diff --git a/java/org/apache/tomcat/util/buf/UriUtil.java b/java/org/apache/tomcat/util/buf/UriUtil.java
index 9f16c139..acb2f0cb 100644
--- a/java/org/apache/tomcat/util/buf/UriUtil.java
+++ b/java/org/apache/tomcat/util/buf/UriUtil.java
@@ -26,9 +26,42 @@ import java.util.regex.Pattern;
  */
 public final class UriUtil {
 
-    private static Pattern PATTERN_EXCLAMATION_MARK = Pattern.compile("!/");
-    private static Pattern PATTERN_CARET = Pattern.compile("\\^/");
-    private static Pattern PATTERN_ASTERISK = Pattern.compile("\\*/");
+    private static final char[] HEX =
+        {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+    private static final Pattern PATTERN_EXCLAMATION_MARK = Pattern.compile("!/");
+    private static final Pattern PATTERN_CARET = Pattern.compile("\\^/");
+    private static final Pattern PATTERN_ASTERISK = Pattern.compile("\\*/");
+    private static final Pattern PATTERN_CUSTOM;
+    private static final String REPLACE_CUSTOM;
+
+    private static final String WAR_SEPARATOR;
+
+    static {
+        String custom = System.getProperty("org.apache.tomcat.util.buf.UriUtil.WAR_SEPARATOR");
+        if (custom == null) {
+            WAR_SEPARATOR = "*/";
+            PATTERN_CUSTOM = null;
+            REPLACE_CUSTOM = null;
+        } else {
+            WAR_SEPARATOR = custom + "/";
+            PATTERN_CUSTOM = Pattern.compile(Pattern.quote(WAR_SEPARATOR));
+            StringBuffer sb = new StringBuffer(custom.length() * 3);
+            // Deliberately use the platform's default encoding
+            byte[] ba = custom.getBytes();
+            for (int j = 0; j < ba.length; j++) {
+                // Converting each byte in the buffer
+                byte toEncode = ba[j];
+                sb.append('%');
+                int low = toEncode & 0x0f;
+                int high = (toEncode & 0xf0) >> 4;
+                sb.append(HEX[high]);
+                sb.append(HEX[low]);
+            }
+            REPLACE_CUSTOM = sb.toString();
+        }
+    }
+
 
     private UriUtil() {
         // Utility class. Hide default constructor
@@ -126,7 +159,11 @@ public final class UriUtil {
         String tmp = PATTERN_EXCLAMATION_MARK.matcher(input).replaceAll("%21/");
         // Tomcat's custom jar:war: URL handling treats */ and ^/ as special
         tmp = PATTERN_CARET.matcher(tmp).replaceAll("%5e/");
-        return PATTERN_ASTERISK.matcher(tmp).replaceAll("%2a/");
+        tmp = PATTERN_ASTERISK.matcher(tmp).replaceAll("%2a/");
+        if (PATTERN_CUSTOM != null) {
+            tmp = PATTERN_CUSTOM.matcher(tmp).replaceAll(REPLACE_CUSTOM);
+        }
+        return tmp;
     }
 
 
@@ -145,10 +182,17 @@ public final class UriUtil {
         String file = warUrl.getFile();
         if (file.contains("*/")) {
             file = file.replaceFirst("\\*/", "!/");
-        } else {
+        } else if (file.contains("^/")) {
             file = file.replaceFirst("\\^/", "!/");
+        } else if (PATTERN_CUSTOM != null) {
+            file = file.replaceFirst(PATTERN_CUSTOM.pattern(), "!/");
         }
 
         return new URL("jar", warUrl.getHost(), warUrl.getPort(), file);
     }
+
+
+    public static String getWarSeparator() {
+        return WAR_SEPARATOR;
+    }
 }
diff --git a/java/org/apache/tomcat/util/http/CookieProcessorBase.java b/java/org/apache/tomcat/util/http/CookieProcessorBase.java
new file mode 100644
index 00000000..dceb573c
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/CookieProcessorBase.java
@@ -0,0 +1,45 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.tomcat.util.http;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public abstract class CookieProcessorBase implements CookieProcessor {
+
+    private static final String COOKIE_DATE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z";
+
+    protected static final ThreadLocal<DateFormat> COOKIE_DATE_FORMAT =
+        new ThreadLocal<DateFormat>() {
+        @Override
+        protected DateFormat initialValue() {
+            DateFormat df =
+                new SimpleDateFormat(COOKIE_DATE_PATTERN, Locale.US);
+            df.setTimeZone(TimeZone.getTimeZone("GMT"));
+            return df;
+        }
+    };
+
+    protected static final String ANCIENT_DATE;
+
+    static {
+        ANCIENT_DATE = COOKIE_DATE_FORMAT.get().format(new Date(10000));
+    }
+}
diff --git a/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java b/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
index 2cf1b52c..16cee6c6 100644
--- a/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
+++ b/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
@@ -18,13 +18,9 @@ package org.apache.tomcat.util.http;
 
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
-import java.text.DateFormat;
 import java.text.FieldPosition;
-import java.text.SimpleDateFormat;
 import java.util.BitSet;
 import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
 
 import javax.servlet.http.Cookie;
 
@@ -44,7 +40,7 @@ import org.apache.tomcat.util.res.StringManager;
  * @author Costin Manolache
  * @author kevin seguin
  */
-public final class LegacyCookieProcessor implements CookieProcessor {
+public final class LegacyCookieProcessor extends CookieProcessorBase {
 
     private static final Log log = LogFactory.getLog(LegacyCookieProcessor.class);
 
@@ -62,26 +58,10 @@ public final class LegacyCookieProcessor implements CookieProcessor {
             '\t', ' ', '\"', '(', ')', ',', ':', ';', '<', '=', '>', '?', '@',
             '[', '\\', ']', '{', '}' };
 
-    private static final String COOKIE_DATE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z";
-    private static final ThreadLocal<DateFormat> COOKIE_DATE_FORMAT =
-        new ThreadLocal<DateFormat>() {
-        @Override
-        protected DateFormat initialValue() {
-            DateFormat df =
-                new SimpleDateFormat(COOKIE_DATE_PATTERN, Locale.US);
-            df.setTimeZone(TimeZone.getTimeZone("GMT"));
-            return df;
-        }
-    };
-
-    private static final String ANCIENT_DATE;
-
     static {
         for (char c : V0_SEPARATORS) {
             V0_SEPARATOR_FLAGS.set(c);
         }
-
-        ANCIENT_DATE = COOKIE_DATE_FORMAT.get().format(new Date(10000));
     }
 
     private final boolean STRICT_SERVLET_COMPLIANCE =
diff --git a/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java b/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
index 66068099..faa6ac4a 100644
--- a/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
+++ b/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
@@ -18,7 +18,9 @@ package org.apache.tomcat.util.http;
 
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.text.FieldPosition;
 import java.util.BitSet;
+import java.util.Date;
 
 import org.apache.juli.logging.Log;
 import org.apache.juli.logging.LogFactory;
@@ -27,7 +29,7 @@ import org.apache.tomcat.util.buf.MessageBytes;
 import org.apache.tomcat.util.http.parser.Cookie;
 import org.apache.tomcat.util.res.StringManager;
 
-public class Rfc6265CookieProcessor implements CookieProcessor {
+public class Rfc6265CookieProcessor extends CookieProcessorBase {
 
     private static final Log log = LogFactory.getLog(Rfc6265CookieProcessor.class);
 
@@ -98,7 +100,9 @@ public class Rfc6265CookieProcessor implements CookieProcessor {
     @Override
     public String generateHeader(javax.servlet.http.Cookie cookie) {
 
-        StringBuilder header = new StringBuilder();
+        // Can't use StringBuilder due to DateFormat
+        StringBuffer header = new StringBuffer();
+
         // TODO: Name validation takes place in Cookie and cannot be configured
         //       per Context. Moving it to here would allow per Context config
         //       but delay validation until the header is generated. However,
@@ -112,12 +116,28 @@ public class Rfc6265CookieProcessor implements CookieProcessor {
             header.append(value);
         }
 
-        // RFC 6265 prefers Max-Age to Expires so use Max-Age
+        // RFC 6265 prefers Max-Age to Expires but... (see below)
         int maxAge = cookie.getMaxAge();
         if (maxAge > -1) {
             // Negative Max-Age is equivalent to no Max-Age
             header.append(";Max-Age=");
             header.append(maxAge);
+
+            // Microsoft IE and Microsoft Edge don't understand Max-Age so send
+            // expires as well. Without this, persistent cookies fail with those
+            // browsers. See http://tomcat.markmail.org/thread/g6sipbofsjossacn
+
+            // Wdy, DD-Mon-YY HH:MM:SS GMT ( Expires Netscape format )
+            header.append (";Expires=");
+            // To expire immediately we need to set the time in past
+            if (maxAge == 0) {
+                header.append(ANCIENT_DATE);
+            } else {
+                COOKIE_DATE_FORMAT.get().format(
+                        new Date(System.currentTimeMillis() + maxAge * 1000L),
+                        header,
+                        new FieldPosition(0));
+            }
         }
 
         String domain = cookie.getDomain();
diff --git a/java/org/apache/tomcat/util/http/parser/Cookie.java b/java/org/apache/tomcat/util/http/parser/Cookie.java
index c0e277a3..a777cd15 100644
--- a/java/org/apache/tomcat/util/http/parser/Cookie.java
+++ b/java/org/apache/tomcat/util/http/parser/Cookie.java
@@ -138,12 +138,14 @@ public class Cookie {
 
         ByteBuffer value = readCookieValue(bb);
         if (value != null && value.remaining() == 1) {
-            if (value.get() == (byte) 49) {
+            int version = value.get() - '0';
+            if (version == 1 || version == 0) {
                 // $Version=1 -> RFC2109
+                // $Version=0 -> RFC2109
                 skipLWS(bb);
                 byte b = bb.get();
                 if (b == SEMICOLON_BYTE || b == COMMA_BYTE) {
-                    parseCookieRfc2109(bb, serverCookies);
+                    parseCookieRfc2109(bb, serverCookies, version);
                 }
                 return;
             } else {
@@ -242,7 +244,8 @@ public class Cookie {
     }
 
 
-    private static void parseCookieRfc2109(ByteBuffer bb, ServerCookies serverCookies) {
+    private static void parseCookieRfc2109(ByteBuffer bb, ServerCookies serverCookies,
+            int version) {
 
         boolean moreToProcess = true;
 
@@ -345,7 +348,7 @@ public class Cookie {
 
             if (name.hasRemaining() && value != null && value.hasRemaining()) {
                 ServerCookie sc = serverCookies.addCookie();
-                sc.setVersion(1);
+                sc.setVersion(version);
                 sc.getName().setBytes(name.array(), name.position(), name.remaining());
                 sc.getValue().setBytes(value.array(), value.position(), value.remaining());
                 if (domain != null) {
diff --git a/java/org/apache/tomcat/util/http/parser/HttpParser.java b/java/org/apache/tomcat/util/http/parser/HttpParser.java
index e0b36346..0804ae4f 100644
--- a/java/org/apache/tomcat/util/http/parser/HttpParser.java
+++ b/java/org/apache/tomcat/util/http/parser/HttpParser.java
@@ -19,6 +19,10 @@ package org.apache.tomcat.util.http.parser;
 import java.io.IOException;
 import java.io.StringReader;
 
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
+
 /**
  * HTTP header value parser implementation. Parsing HTTP headers as per RFC2616
  * is not always as simple as it first appears. For headers that only use tokens
@@ -34,6 +38,10 @@ import java.io.StringReader;
  */
 public class HttpParser {
 
+    private static final StringManager sm = StringManager.getManager(HttpParser.class);
+
+    private static final Log log = LogFactory.getLog(HttpParser.class);
+
     private static final int ARRAY_SIZE = 128;
 
     private static final boolean[] IS_CONTROL = new boolean[ARRAY_SIZE];
@@ -42,8 +50,22 @@ public class HttpParser {
     private static final boolean[] IS_HEX = new boolean[ARRAY_SIZE];
     private static final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE];
     private static final boolean[] IS_HTTP_PROTOCOL = new boolean[ARRAY_SIZE];
+    private static final boolean[] REQUEST_TARGET_ALLOW = new boolean[ARRAY_SIZE];
 
     static {
+        String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow");
+        if (prop != null) {
+            for (int i = 0; i < prop.length(); i++) {
+                char c = prop.charAt(i);
+                if (c == '{' || c == '}' || c == '|') {
+                    REQUEST_TARGET_ALLOW[c] = true;
+                } else {
+                    log.warn(sm.getString("httpparser.invalidRequestTargetCharacter",
+                            Character.valueOf(c)));
+                }
+            }
+        }
+
         for (int i = 0; i < ARRAY_SIZE; i++) {
             // Control> 0-31, 127
             if (i < 32 || i == 127) {
@@ -74,7 +96,9 @@ public class HttpParser {
             if (IS_CONTROL[i] || i > 127 ||
                     i == ' ' || i == '\"' || i == '#' || i == '<' || i == '>' || i == '\\' ||
                     i == '^' || i == '`'  || i == '{' || i == '|' || i == '}') {
-                IS_NOT_REQUEST_TARGET[i] = true;
+                if (!REQUEST_TARGET_ALLOW[i]) {
+                    IS_NOT_REQUEST_TARGET[i] = true;
+                }
             }
 
             // Not valid for HTTP protocol
diff --git a/java/org/apache/tomcat/util/http/parser/LocalStrings.properties b/java/org/apache/tomcat/util/http/parser/LocalStrings.properties
index 9051ddbf..e204972f 100644
--- a/java/org/apache/tomcat/util/http/parser/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/http/parser/LocalStrings.properties
@@ -17,4 +17,5 @@ authorization.unknownType=Unknown Type [{0}]
 cookie.fallToDebug=Note: further occurrences of this error will be logged at DEBUG level.
 cookie.invalidCookieValue=A cookie header was received [{0}] that contained an invalid cookie. That cookie will be ignored.
 cookie.invalidCookieVersion=A cookie header was received using an unrecognised cookie version of [{0}]. The header and the cookies it contains will be ignored.
-cookie.valueNotPresent=<not present>
\ No newline at end of file
+cookie.valueNotPresent=<not present>
+httpparser.invalidRequestTargetCharacter=Character [{0}] is not allowed and will continue to be rejected.
diff --git a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
index 2e131063..e1ad43fa 100644
--- a/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java
@@ -84,6 +84,13 @@ public abstract class AbstractJsseEndpoint<S> extends AbstractEndpoint<S> {
             for (SSLHostConfig sslHostConfig : sslHostConfigs.values()) {
                 createSSLContext(sslHostConfig);
             }
+
+            // Validate default SSLHostConfigName
+            if (sslHostConfigs.get(getDefaultSSLHostConfigName()) == null) {
+                throw new IllegalArgumentException(sm.getString("endpoint.noSslHostConfig",
+                        getDefaultSSLHostConfigName(), getName()));
+            }
+
         }
     }
 
diff --git a/java/org/apache/tomcat/util/net/AprEndpoint.java b/java/org/apache/tomcat/util/net/AprEndpoint.java
index 09d4af39..d89ad975 100644
--- a/java/org/apache/tomcat/util/net/AprEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AprEndpoint.java
@@ -373,6 +373,10 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack {
                 createSSLContext(sslHostConfig);
             }
             SSLHostConfig defaultSSLHostConfig = sslHostConfigs.get(getDefaultSSLHostConfigName());
+            if (defaultSSLHostConfig == null) {
+                throw new IllegalArgumentException(sm.getString("endpoint.noSslHostConfig",
+                        getDefaultSSLHostConfigName(), getName()));
+            }
             Long defaultSSLContext = defaultSSLHostConfig.getOpenSslContext();
             sslContext = defaultSSLContext.longValue();
             SSLContext.registerDefault(defaultSSLContext, this);
diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties b/java/org/apache/tomcat/util/net/LocalStrings.properties
index ba56d3c9..6acf4bbd 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -48,6 +48,7 @@ endpoint.init.bind=Socket bind failed: [{0}] {1}
 endpoint.init.listen=Socket listen failed: [{0}] {1}
 endpoint.init.notavail=APR not available
 endpoint.launch.fail=Failed to launch new runnable
+endpoint.noSslHostConfig=No SSLHostConfig element was found with the hostName [{0}] to match the defaultSSLHostConfigName for the connector [{1}]
 endpoint.noSslHostName=No host name was provided for the SSL host configuration
 endpoint.poll.limitedpollsize=Failed to create poller with specified size of {0}
 endpoint.poll.initfail=Poller creation failed
diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java b/java/org/apache/tomcat/util/net/NioEndpoint.java
index 87759192..0439844d 100644
--- a/java/org/apache/tomcat/util/net/NioEndpoint.java
+++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
@@ -219,7 +219,7 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
             //minimum one poller thread
             pollerThreadCount = 1;
         }
-        stopLatch = new CountDownLatch(pollerThreadCount);
+        setStopLatch(new CountDownLatch(pollerThreadCount));
 
         // Initialize SSL if needed
         initialiseSsl();
@@ -284,7 +284,7 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
                 pollers[i] = null;
             }
             try {
-                stopLatch.await(selectorTimeout + 100, TimeUnit.MILLISECONDS);
+                getStopLatch().await(selectorTimeout + 100, TimeUnit.MILLISECONDS);
             } catch (InterruptedException ignore) {
             }
             shutdownExecutor();
@@ -345,6 +345,16 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
     }
 
 
+    protected CountDownLatch getStopLatch() {
+        return stopLatch;
+    }
+
+
+    protected void setStopLatch(CountDownLatch stopLatch) {
+        this.stopLatch = stopLatch;
+    }
+
+
     /**
      * Process the specified connection.
      * @param socket The socket channel
@@ -815,7 +825,7 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
                 timeout(keyCount,hasEvents);
             }//while
 
-            stopLatch.countDown();
+            getStopLatch().countDown();
         }
 
         protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
@@ -871,9 +881,6 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
                 if (sd.fchannel == null) {
                     // Setup the file channel
                     File f = new File(sd.fileName);
-                    if (!f.exists()) {
-                        return SendfileState.ERROR;
-                    }
                     @SuppressWarnings("resource") // Closed when channel is closed
                     FileInputStream fis = new FileInputStream(f);
                     sd.fchannel = fis.getChannel();
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java b/java/org/apache/tomcat/util/net/SSLHostConfig.java
index 76a84c2a..bec3adf7 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfig.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -96,6 +96,7 @@ public class SSLHostConfig implements Serializable {
     private Set<String> protocols = new HashSet<>();
     // JSSE
     private String keyManagerAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
+    private boolean revocationEnabled = false;
     private int sessionCacheSize = 0;
     private int sessionTimeout = 86400;
     private String sslProtocol = Constants.SSL_PROTO_TLS;
@@ -486,6 +487,17 @@ public class SSLHostConfig implements Serializable {
     }
 
 
+    public void setRevocationEnabled(boolean revocationEnabled) {
+        setProperty("revocationEnabled", Type.JSSE);
+        this.revocationEnabled = revocationEnabled;
+    }
+
+
+    public boolean getRevocationEnabled() {
+        return revocationEnabled;
+    }
+
+
     public void setSessionCacheSize(int sessionCacheSize) {
         setProperty("sessionCacheSize", Type.JSSE);
         this.sessionCacheSize = sessionCacheSize;
diff --git a/java/org/apache/tomcat/util/net/SecureNio2Channel.java b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
index 4fc7225e..a53d8ec8 100644
--- a/java/org/apache/tomcat/util/net/SecureNio2Channel.java
+++ b/java/org/apache/tomcat/util/net/SecureNio2Channel.java
@@ -335,7 +335,7 @@ public class SecureNio2Channel extends Nio2Channel  {
 
         TLSClientHelloExtractor extractor = new TLSClientHelloExtractor(netInBuffer);
 
-        while (extractor.getResult() == ExtractorResult.UNDERFLOW &&
+        if (extractor.getResult() == ExtractorResult.UNDERFLOW &&
                 netInBuffer.capacity() < endpoint.getSniParseLimit()) {
             // extractor needed more data to process but netInBuffer was full so
             // expand the buffer and read some more data.
@@ -344,8 +344,8 @@ public class SecureNio2Channel extends Nio2Channel  {
                     Integer.toString(newLimit)));
 
             netInBuffer = ByteBufferUtils.expand(netInBuffer, newLimit);
-            sc.read(netInBuffer);
-            extractor = new TLSClientHelloExtractor(netInBuffer);
+            sc.read(netInBuffer, socket, handshakeReadCompletionHandler);
+            return 1;
         }
 
         String hostName = null;
diff --git a/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java b/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java
index 5965920c..e290caf0 100644
--- a/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java
+++ b/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java
@@ -258,10 +258,11 @@ public class JSSEUtil extends SSLUtilBase {
             checkTrustStoreEntries(trustStore);
             String algorithm = sslHostConfig.getTruststoreAlgorithm();
             String crlf = sslHostConfig.getCertificateRevocationListFile();
+            boolean revocationEnabled = sslHostConfig.getRevocationEnabled();
 
             if ("PKIX".equalsIgnoreCase(algorithm)) {
                 TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
-                CertPathParameters params = getParameters(crlf, trustStore);
+                CertPathParameters params = getParameters(crlf, trustStore, revocationEnabled);
                 ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(params);
                 tmf.init(mfp);
                 tms = tmf.getTrustManagers();
@@ -272,7 +273,7 @@ public class JSSEUtil extends SSLUtilBase {
                 if (crlf != null && crlf.length() > 0) {
                     throw new CRLException(sm.getString("jsseUtil.noCrlSupport", algorithm));
                 }
-                log.warn(sm.getString("jsseUtil.noVerificationDepth"));
+                log.warn(sm.getString("jsseUtil.noVerificationDepth", algorithm));
             }
         }
 
@@ -324,10 +325,15 @@ public class JSSEUtil extends SSLUtilBase {
      *
      * @param crlf The path to the CRL file.
      * @param trustStore The configured TrustStore.
+     * @param revocationEnabled Should the JSSE provider perform revocation
+     *                          checks? Ignored if {@code crlf} is non-null.
+     *                          Configuration of revocation checks are expected
+     *                          to be via proprietary JSSE provider methods.
      * @return The parameters including the CRLs and TrustStore.
      * @throws Exception An error occurred
      */
-    protected CertPathParameters getParameters(String crlf, KeyStore trustStore) throws Exception {
+    protected CertPathParameters getParameters(String crlf, KeyStore trustStore,
+            boolean revocationEnabled) throws Exception {
 
         PKIXBuilderParameters xparams =
                 new PKIXBuilderParameters(trustStore, new X509CertSelector());
@@ -338,7 +344,7 @@ public class JSSEUtil extends SSLUtilBase {
             xparams.addCertStore(store);
             xparams.setRevocationEnabled(true);
         } else {
-            xparams.setRevocationEnabled(false);
+            xparams.setRevocationEnabled(revocationEnabled);
         }
         xparams.setMaxPathLength(sslHostConfig.getCertificateVerificationDepth());
         return xparams;
diff --git a/java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java b/java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java
index d658353a..d9c6f537 100644
--- a/java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java
+++ b/java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java
@@ -93,6 +93,10 @@ public abstract class AbstractInputStreamJar implements Jar {
         if (entry == null) {
             return null;
         } else {
+            // Clear the entry so that multiple calls to this method for the
+            // same entry will result in a new InputStream for each call
+            // (BZ 60798)
+            entry = null;
             return jarInputStream;
         }
     }
diff --git a/java/org/apache/tomcat/util/scan/JarFactory.java b/java/org/apache/tomcat/util/scan/JarFactory.java
index b0631f7e..51c0011e 100644
--- a/java/org/apache/tomcat/util/scan/JarFactory.java
+++ b/java/org/apache/tomcat/util/scan/JarFactory.java
@@ -19,6 +19,7 @@ package org.apache.tomcat.util.scan;
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.util.regex.Matcher;
 
 import org.apache.tomcat.Jar;
 import org.apache.tomcat.util.buf.UriUtil;
@@ -61,7 +62,8 @@ public class JarFactory {
             // Assume this is pointing to a JAR file within a WAR. Java doesn't
             // support jar:jar:file:... so switch to Tomcat's war:file:...
             baseExternal = baseExternal.replaceFirst("^jar:", "war:");
-            baseExternal = baseExternal.replaceFirst("!/", "*/");
+            baseExternal = baseExternal.replaceFirst("!/",
+                    Matcher.quoteReplacement(UriUtil.getWarSeparator()));
         }
 
         return new URL("jar:" + baseExternal + "!/" + entryName);
diff --git a/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java b/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
index 179126be..1a70978e 100644
--- a/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
+++ b/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java
@@ -63,19 +63,23 @@ public class ThreadPoolExecutor extends java.util.concurrent.ThreadPoolExecutor
 
     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
+        prestartAllCoreThreads();
     }
 
     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
             RejectedExecutionHandler handler) {
         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
+        prestartAllCoreThreads();
     }
 
     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, new RejectHandler());
+        prestartAllCoreThreads();
     }
 
     public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new RejectHandler());
+        prestartAllCoreThreads();
     }
 
     public long getThreadRenewalDelay() {
diff --git a/java/org/apache/tomcat/websocket/WsSession.java b/java/org/apache/tomcat/websocket/WsSession.java
index 14fb41f7..766d3592 100644
--- a/java/org/apache/tomcat/websocket/WsSession.java
+++ b/java/org/apache/tomcat/websocket/WsSession.java
@@ -242,48 +235,44 @@ public class WsSession implements Session {
         // arbitrary objects with MessageHandlers and can wrap MessageHandlers
         // just as easily.
 
-        Set<MessageHandlerResult> mhResults =
-                Util.getMessageHandlers(target, listener, endpointConfig, this);
+        Set<MessageHandlerResult> mhResults = Util.getMessageHandlers(target, listener,
+                endpointConfig, this);
 
         for (MessageHandlerResult mhResult : mhResults) {
             switch (mhResult.getType()) {
-                case TEXT: {
-                    if (textMessageHandler != null) {
-                        throw new IllegalStateException(
-                                sm.getString("wsSession.duplicateHandlerText"));
-                    }
-                    textMessageHandler = mhResult.getHandler();
-                    break;
+            case TEXT: {
+                if (textMessageHandler != null) {
+                    throw new IllegalStateException(sm.getString("wsSession.duplicateHandlerText"));
                 }
-                case BINARY: {
-                    if (binaryMessageHandler != null) {
-                        throw new IllegalStateException(
-                                sm.getString("wsSession.duplicateHandlerBinary"));
-                    }
-                    binaryMessageHandler = mhResult.getHandler();
-                    break;
+                textMessageHandler = mhResult.getHandler();
+                break;
+            }
+            case BINARY: {
+                if (binaryMessageHandler != null) {
+                    throw new IllegalStateException(
+                            sm.getString("wsSession.duplicateHandlerBinary"));
                 }
-                case PONG: {
-                    if (pongMessageHandler != null) {
-                        throw new IllegalStateException(
-                                sm.getString("wsSession.duplicateHandlerPong"));
-                    }
-                    MessageHandler handler = mhResult.getHandler();
-                    if (handler instanceof MessageHandler.Whole<?>) {
-                        pongMessageHandler =
-                                (MessageHandler.Whole<PongMessage>) handler;
-                    } else {
-                        throw new IllegalStateException(
-                                sm.getString("wsSession.invalidHandlerTypePong"));
-                    }
-
-                    break;
+                binaryMessageHandler = mhResult.getHandler();
+                break;
+            }
+            case PONG: {
+                if (pongMessageHandler != null) {
+                    throw new IllegalStateException(sm.getString("wsSession.duplicateHandlerPong"));
                 }
-                default: {
-                    throw new IllegalArgumentException(sm.getString(
-                            "wsSession.unknownHandlerType", listener,
-                            mhResult.getType()));
+                MessageHandler handler = mhResult.getHandler();
+                if (handler instanceof MessageHandler.Whole<?>) {
+                    pongMessageHandler = (MessageHandler.Whole<PongMessage>) handler;
+                } else {
+                    throw new IllegalStateException(
+                            sm.getString("wsSession.invalidHandlerTypePong"));
                 }
+
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException(
+                        sm.getString("wsSession.unknownHandlerType", listener, mhResult.getType()));
+            }
             }
         }
     }
diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
index 66562212..b6b993a7 100644
--- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
@@ -236,18 +236,20 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
             }
         }
 
-        // If sa is null, no proxy is configured so need to create sa
-        if (sa == null) {
-            if (port == -1) {
-                if ("ws".equalsIgnoreCase(scheme)) {
-                    sa = new InetSocketAddress(host, 80);
-                } else {
-                    // Must be wss due to scheme validation above
-                    sa = new InetSocketAddress(host, 443);
-                }
+        // If the port is not explicitly specified, compute it based on the
+        // scheme
+        if (port == -1) {
+            if ("ws".equalsIgnoreCase(scheme)) {
+                port = 80;
             } else {
-                sa = new InetSocketAddress(host, port);
+                // Must be wss due to scheme validation above
+                port = 443;
             }
+        }
+
+        // If sa is null, no proxy is configured so need to create sa
+        if (sa == null) {
+            sa = new InetSocketAddress(host, port);
         } else {
             proxyConnect = createProxyRequest(host, port);
         }
@@ -450,16 +452,14 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
         StringBuilder request = new StringBuilder();
         request.append("CONNECT ");
         request.append(host);
-        if (port != -1) {
-            request.append(':');
-            request.append(port);
-        }
+        request.append(':');
+        request.append(port);
+
         request.append(" HTTP/1.1\r\nProxy-Connection: keep-alive\r\nConnection: keepalive\r\nHost: ");
         request.append(host);
-        if (port != -1) {
-            request.append(':');
-            request.append(port);
-        }
+        request.append(':');
+        request.append(port);
+
         request.append("\r\n\r\n");
 
         byte[] bytes = request.toString().getBytes(StandardCharsets.ISO_8859_1);
diff --git a/res/ide-support/eclipse/eclipse.classpath b/res/ide-support/eclipse/eclipse.classpath
index eafddd21..ef058eff 100644
--- a/res/ide-support/eclipse/eclipse.classpath
+++ b/res/ide-support/eclipse/eclipse.classpath
@@ -24,7 +24,7 @@
     <classpathentry kind="var" path="ANT_HOME/lib/ant.jar"/>
     <classpathentry kind="var" path="TOMCAT_LIBS_BASE/jaxrpc-1.1-rc4/geronimo-spec-jaxrpc-1.1-rc4.jar"/>
     <classpathentry kind="var" path="TOMCAT_LIBS_BASE/wsdl4j-1.6.2/wsdl4j-1.6.2.jar"/>
-    <classpathentry kind="var" path="TOMCAT_LIBS_BASE/ecj-4.5.1/ecj-4.5.1.jar"/>
+    <classpathentry kind="var" path="TOMCAT_LIBS_BASE/ecj-4.6.1/ecj-4.6.1.jar"/>
     <classpathentry kind="var" path="TOMCAT_LIBS_BASE/easymock-3.2/easymock-3.2.jar"/>
     <classpathentry kind="var" path="TOMCAT_LIBS_BASE/hamcrest-1.3/hamcrest-core-1.3.jar"/>
     <classpathentry kind="output" path=".settings/output"/>
diff --git a/res/ide-support/netbeans/nb-tomcat-build.properties b/res/ide-support/netbeans/nb-tomcat-build.properties
index bec1cd11..1d3fa37c 100644
--- a/res/ide-support/netbeans/nb-tomcat-build.properties
+++ b/res/ide-support/netbeans/nb-tomcat-build.properties
@@ -37,7 +37,7 @@ nb-test.io-method=org.apache.coyote.http11.Http11NioProtocol
 # it is not possible to retrieve the classpaths from the build to
 # use in the NetBeans targets, so they must be explicitly declared
 
-nb-test.classpath=${test.classes}:${tomcat.build}/webapps/examples/WEB-INF/classes:${base.path}/junit-4.11/junit-4.11.jar:${base.path}/easymock-3.2/easymock-3.2.jar:${base.path}/hamcrest-1.3/hamcrest-core-1.3.jar:${base.path}/ecj-4.5.1/ecj-4.5.1.jar:${tomcat.classes}
+nb-test.classpath=${test.classes}:${tomcat.build}/webapps/examples/WEB-INF/classes:${base.path}/junit-4.11/junit-4.11.jar:${base.path}/easymock-3.2/easymock-3.2.jar:${base.path}/hamcrest-1.3/hamcrest-core-1.3.jar:${base.path}/ecj-4.6.1/ecj-4.6.1.jar:${tomcat.classes}
 
 # Extra properties used by the Tomcat project additional NetBeans targets.
 
diff --git a/res/ide-support/netbeans/project.xml b/res/ide-support/netbeans/project.xml
index 3a04c943..3119ae3f 100644
--- a/res/ide-support/netbeans/project.xml
+++ b/res/ide-support/netbeans/project.xml
@@ -178,7 +178,7 @@
             -->
             <compilation-unit>
                 <package-root>java</package-root>
-                <classpath mode="compile">${base.path}/jaxrpc-1.1-rc4/geronimo-spec-jaxrpc-1.1-rc4.jar:${base.path}/wsdl4j-1.6.2/wsdl4j-1.6.2.jar:${base.path}/ecj-4.5.1/ecj-4.5.1.jar:${ant.includes}/</classpath>
+                <classpath mode="compile">${base.path}/jaxrpc-1.1-rc4/geronimo-spec-jaxrpc-1.1-rc4.jar:${base.path}/wsdl4j-1.6.2/wsdl4j-1.6.2.jar:${base.path}/ecj-4.6.1/ecj-4.6.1.jar:${ant.includes}/</classpath>
                 <source-level>1.7</source-level>
             </compilation-unit>
             <compilation-unit>
diff --git a/res/maven/mvn.properties.default b/res/maven/mvn.properties.default
index b831d22d..d0260b11 100644
--- a/res/maven/mvn.properties.default
+++ b/res/maven/mvn.properties.default
@@ -35,7 +35,7 @@ maven.asf.release.repo.url=https://repository.apache.org/service/local/staging/d
 maven.asf.release.repo.repositoryId=apache.releases
 
 # Release version info
-maven.asf.release.deploy.version=8.5.11
+maven.asf.release.deploy.version=8.5.12
 
 #Where do we load the libraries from
 tomcat.lib.path=../../output/build/lib
diff --git a/res/maven/tomcat-embed-jasper.pom b/res/maven/tomcat-embed-jasper.pom
index 6aad8997..6e5c3f8c 100644
--- a/res/maven/tomcat-embed-jasper.pom
+++ b/res/maven/tomcat-embed-jasper.pom
@@ -45,7 +45,7 @@
     <dependency>
       <groupId>org.eclipse.jdt.core.compiler</groupId>
       <artifactId>ecj</artifactId>
-      <version>4.5.1</version>
+      <version>4.6.1</version>
     </dependency>
   </dependencies>
 </project>
diff --git a/res/maven/tomcat-jasper.pom b/res/maven/tomcat-jasper.pom
index 7bb9ee5b..6e1f4bcd 100644
--- a/res/maven/tomcat-jasper.pom
+++ b/res/maven/tomcat-jasper.pom
@@ -57,7 +57,7 @@
     <dependency>
       <groupId>org.eclipse.jdt.core.compiler</groupId>
       <artifactId>ecj</artifactId>
-      <version>4.5.1</version>
+      <version>4.6.1</version>
     </dependency>
     <dependency>
       <groupId>org.apache.tomcat</groupId>
diff --git a/res/tomcat.nsi b/res/tomcat.nsi
index f2d7f87f..9f5ee91e 100644
--- a/res/tomcat.nsi
+++ b/res/tomcat.nsi
@@ -158,8 +158,8 @@ Var ServiceInstallLog
   InstType Minimum
   InstType Full
 
-  ReserveFile "${NSISDIR}\Plugins\System.dll"
-  ReserveFile "${NSISDIR}\Plugins\nsDialogs.dll"
+  ReserveFile "${NSISDIR}\Plugins\x86-unicode\System.dll"
+  ReserveFile "${NSISDIR}\Plugins\x86-unicode\nsDialogs.dll"
   ReserveFile confinstall\tomcat-users_1.xml
   ReserveFile confinstall\tomcat-users_2.xml
 
diff --git a/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java b/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java
new file mode 100644
index 00000000..90154108
--- /dev/null
+++ b/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java
@@ -0,0 +1,294 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.catalina.authenticator.jaspic;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.security.auth.message.config.AuthConfigFactory;
+import javax.security.auth.message.config.AuthConfigProvider;
+import javax.security.auth.message.config.RegistrationListener;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestAuthConfigFactoryImpl {
+
+    @Test
+    public void testRegistrationNullLayer() {
+        doTestResistration(null,  "AC_1",  ":AC_1");
+    }
+
+
+    @Test
+    public void testRegistrationNullAppContext() {
+        doTestResistration("L_1",  null,  "L_1:");
+    }
+
+
+    @Test
+    public void testRegistrationNullLayerAndNullAppContext() {
+        doTestResistration(null,  null,  ":");
+    }
+
+
+    @Test
+    public void testSearchNoMatch01() {
+        doTestSearchOrder("foo", "bar", 1);
+    }
+
+
+    @Test
+    public void testSearchNoMatch02() {
+        doTestSearchOrder(null, "bar", 1);
+    }
+
+
+    @Test
+    public void testSearchNoMatch03() {
+        doTestSearchOrder("foo", null, 1);
+    }
+
+
+    @Test
+    public void testSearchNoMatch04() {
+        doTestSearchOrder(null, null, 1);
+    }
+
+
+    @Test
+    public void testSearchOnlyAppContextMatch01() {
+        doTestSearchOrder("foo", "AC_1", 2);
+    }
+
+
+    @Test
+    public void testSearchOnlyAppContextMatch02() {
+        doTestSearchOrder(null, "AC_1", 2);
+    }
+
+
+    @Test
+    public void testSearchOnlyAppContextMatch03() {
+        doTestSearchOrder("L_2", "AC_1", 2);
+    }
+
+
+    @Test
+    public void testSearchOnlyLayerMatch01() {
+        doTestSearchOrder("L_1", "bar", 3);
+    }
+
+
+    @Test
+    public void testSearchOnlyLayerMatch02() {
+        doTestSearchOrder("L_1", null, 3);
+    }
+
+
+    @Test
+    public void testSearchOnlyLayerMatch03() {
+        doTestSearchOrder("L_1", "AC_2", 3);
+    }
+
+
+    @Test
+    public void testSearchBothMatch() {
+        doTestSearchOrder("L_2", "AC_2", 4);
+    }
+
+
+    private void doTestSearchOrder(String layer, String appContext, int expected) {
+        AuthConfigFactory factory = new AuthConfigFactoryImpl();
+        AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acp1, null, null, "1");
+        AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acp2, null, "AC_1", "2");
+        AuthConfigProvider acp3 = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acp3, "L_1", null, "3");
+        AuthConfigProvider acp4 = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acp4, "L_2", "AC_2", "4");
+
+        AuthConfigProvider searchResult = factory.getConfigProvider(layer, appContext, null);
+        int searchIndex;
+        if (searchResult == acp1) {
+            searchIndex = 1;
+        } else if (searchResult == acp2) {
+            searchIndex = 2;
+        } else if (searchResult == acp3) {
+            searchIndex = 3;
+        } else if (searchResult == acp4) {
+            searchIndex = 4;
+        } else {
+            searchIndex = -1;
+        }
+        Assert.assertEquals(expected, searchIndex);
+    }
+
+
+    private void doTestResistration(String layer, String appContext, String expectedRegId) {
+        AuthConfigFactory factory = new AuthConfigFactoryImpl();
+        AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
+        SimpleRegistrationListener listener = new SimpleRegistrationListener(layer, appContext);
+
+        String regId = factory.registerConfigProvider(acp1, layer, appContext, null);
+        Assert.assertEquals(expectedRegId, regId);
+
+        factory.getConfigProvider(layer, appContext, listener);
+        factory.removeRegistration(regId);
+        Assert.assertTrue(listener.wasCorrectlyCalled());
+
+        listener.reset();
+        factory.registerConfigProvider(acp1, layer, appContext, null);
+        factory.getConfigProvider(layer, appContext, listener);
+        // Replace it
+        AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acp2, layer, appContext, null);
+        Assert.assertTrue(listener.wasCorrectlyCalled());
+    }
+
+
+    @Test
+    public void testRegistrationInsertExact01() {
+        doTestRegistrationInsert("L_3", "AC_2", "L_3", "AC_2");
+    }
+
+
+    @Test
+    public void testRegistrationInsertExact02() {
+        doTestRegistrationInsert("L_2", "AC_3", "L_2", "AC_3");
+    }
+
+
+    @Test
+    public void testRegistrationInsertExact03() {
+        doTestRegistrationInsert("L_4", "AC_4", "L_4", "AC_4");
+    }
+
+
+    @Test
+    public void testRegistrationInsertAppContext01() {
+        doTestRegistrationInsert(null, "AC_3", "L_2", "AC_3");
+    }
+
+
+    @Test
+    public void testRegistrationInsertAppContext02() {
+        doTestRegistrationInsert(null, "AC_4", "L_4", "AC_4");
+    }
+
+
+    @Test
+    public void testRegistrationInsertLayer01() {
+        doTestRegistrationInsert("L_4", null, "L_4", "AC_4");
+    }
+
+
+    private void doTestRegistrationInsert(String newLayer, String newAppContext,
+            String expectedListenerLayer, String expectedListenerAppContext) {
+        // Set up
+        AuthConfigFactory factory = new AuthConfigFactoryImpl();
+        AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acp1, "L_1", "AC_1", null);
+        AuthConfigProvider acp2 = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acp2, null, "AC_2", null);
+        AuthConfigProvider acp3 = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acp3, "L_2", null, null);
+        AuthConfigProvider acp4 = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acp4, null, null, null);
+
+        SimpleRegistrationListener listener1 = new SimpleRegistrationListener("L_1", "AC_1");
+        factory.getConfigProvider("L_1", "AC_1", listener1);
+        SimpleRegistrationListener listener2 = new SimpleRegistrationListener("L_3", "AC_2");
+        factory.getConfigProvider("L_3", "AC_2", listener2);
+        SimpleRegistrationListener listener3 = new SimpleRegistrationListener("L_2", "AC_3");
+        factory.getConfigProvider("L_2", "AC_3", listener3);
+        SimpleRegistrationListener listener4 = new SimpleRegistrationListener("L_4", "AC_4");
+        factory.getConfigProvider("L_4", "AC_4", listener4);
+
+        List<SimpleRegistrationListener> listeners = new ArrayList<>();
+        listeners.add(listener1);
+        listeners.add(listener2);
+        listeners.add(listener3);
+        listeners.add(listener4);
+
+        // Register a new provider that will impact some existing registrations
+        AuthConfigProvider acpNew = new SimpleAuthConfigProvider(null, null);
+        factory.registerConfigProvider(acpNew, newLayer, newAppContext, null);
+
+        // Check to see if the expected listener fired.
+        for (SimpleRegistrationListener listener : listeners) {
+            if (listener.wasCalled()) {
+                Assert.assertEquals(listener.layer, expectedListenerLayer);
+                Assert.assertEquals(listener.appContext,  expectedListenerAppContext);
+                Assert.assertTrue(listener.wasCorrectlyCalled());
+            } else {
+                Assert.assertFalse((listener.layer.equals(expectedListenerLayer) &&
+                        listener.appContext.equals(expectedListenerAppContext)));
+            }
+        }
+    }
+
+
+    private static class SimpleRegistrationListener implements RegistrationListener {
+
+        private final String layer;
+        private final String appContext;
+
+        private boolean called = false;
+        private String layerNotified;
+        private String appContextNotified;
+
+        public SimpleRegistrationListener(String layer, String appContext) {
+            this.layer = layer;
+            this.appContext = appContext;
+        }
+
+        @Override
+        public void notify(String layer, String appContext) {
+            called = true;
+            layerNotified = layer;
+            appContextNotified = appContext;
+        }
+
+
+        public boolean wasCalled() {
+            return called;
+        }
+
+
+        public boolean wasCorrectlyCalled() {
+            return called && areTheSame(layer, layerNotified) &&
+                    areTheSame(appContext, appContextNotified);
+        }
+
+
+        public void reset() {
+            called = false;
+            layerNotified = null;
+            appContextNotified = null;
+        }
+
+
+        private static boolean areTheSame(String a, String b) {
+            if (a == null) {
+                return b == null;
+            }
+            return a.equals(b);
+        }
+    }
+}
diff --git a/test/org/apache/catalina/connector/TestConnector.java b/test/org/apache/catalina/connector/TestConnector.java
index 41e7a040..b67f1b25 100644
--- a/test/org/apache/catalina/connector/TestConnector.java
+++ b/test/org/apache/catalina/connector/TestConnector.java
@@ -16,15 +16,24 @@
  */
 package org.apache.catalina.connector;
 
+import java.io.File;
 import java.net.SocketTimeoutException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.Servlet;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import org.junit.Assert;
 import org.junit.Test;
 
 import org.apache.catalina.Context;
 import org.apache.catalina.Wrapper;
+import org.apache.catalina.servlets.DefaultServlet;
+import org.apache.catalina.servlets.WebdavServlet;
 import org.apache.catalina.startup.TesterServlet;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
@@ -92,4 +101,76 @@ public class TestConnector extends TomcatBaseTest {
         assertTrue(localPort1 > 0);
         assertTrue(localPort2 > 0);
     }
+
+
+    @Test
+    public void testTraceAllowedDefault() throws Exception {
+        doTestTrace(new DefaultServlet(), true);
+    }
+
+
+    @Test
+    public void testTraceNotAllowedDefault() throws Exception {
+        doTestTrace(new DefaultServlet(), false);
+    }
+
+
+    @Test
+    public void testTraceAllowedWebDav() throws Exception {
+        doTestTrace(new WebdavServlet(), true);
+    }
+
+
+    @Test
+    public void testTraceNotAllowedWebDav() throws Exception {
+        doTestTrace(new WebdavServlet(), false);
+    }
+
+
+    @Test
+    public void testTraceAllowedCustom() throws Exception {
+        doTestTrace(new TesterServlet(), true);
+    }
+
+
+    @Test
+    public void testTraceNotAllowedCustom() throws Exception {
+        doTestTrace(new TesterServlet(), false);
+    }
+
+
+    private void doTestTrace(Servlet servlet, boolean allowTrace) throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp");
+        Context root = tomcat.addContext("", appDir.getAbsolutePath());
+        Tomcat.addServlet(root, "default", servlet);
+        root.addServletMappingDecoded("/", "default");
+
+        Connector connector = tomcat.getConnector();
+        connector.setAllowTrace(allowTrace);
+
+        tomcat.start();
+
+        ByteChunk bc = new ByteChunk();
+        Map<String,List<String>> respHeaders = new HashMap<>();
+        int rc = methodUrl("http://localhost:"; + getPort() + "/index.html",
+                bc, 30000, null, respHeaders, "OPTIONS");
+
+        assertEquals(200, rc);
+
+        boolean foundTrace = false;
+        for (String header : respHeaders.get("Allow")) {
+            if (header.contains("TRACE")) {
+                foundTrace = true;
+                break;
+            }
+        }
+
+        if (allowTrace) {
+            Assert.assertTrue(foundTrace);
+        } else {
+            Assert.assertFalse(foundTrace);
+        }
+    }
 }
diff --git a/test/org/apache/catalina/connector/TestKeepAliveCount.java b/test/org/apache/catalina/connector/TestKeepAliveCount.java
index 07c37f64..067a5c97 100644
--- a/test/org/apache/catalina/connector/TestKeepAliveCount.java
+++ b/test/org/apache/catalina/connector/TestKeepAliveCount.java
@@ -109,7 +109,7 @@ public class TestKeepAliveCount extends TomcatBaseTest {
 
             for (int i=0; i<5; i++) {
                 processRequest(false); // blocks until response has been read
-                assertTrue(getResponseLine()!=null && getResponseLine().trim().startsWith("HTTP/1.1 200"));
+                assertTrue(getResponseLine()!=null && getResponseLine().startsWith("HTTP/1.1 200 "));
             }
             boolean passed = (this.readLine()==null);
             // Close the connection
diff --git a/test/org/apache/catalina/core/TestApplicationMapping.java b/test/org/apache/catalina/core/TestApplicationMapping.java
index 80726b8c..f6cff588 100644
--- a/test/org/apache/catalina/core/TestApplicationMapping.java
+++ b/test/org/apache/catalina/core/TestApplicationMapping.java
@@ -19,6 +19,7 @@ package org.apache.catalina.core;
 import java.io.IOException;
 import java.io.PrintWriter;
 
+import javax.servlet.AsyncContext;
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
@@ -29,7 +30,8 @@ import org.junit.Assert;
 import org.junit.Test;
 
 import org.apache.catalina.Context;
-import org.apache.catalina.servlet4preview.http.Mapping;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.servlet4preview.http.ServletMapping;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.tomcat.util.buf.ByteChunk;
@@ -43,22 +45,22 @@ public class TestApplicationMapping extends TomcatBaseTest {
 
     @Test
     public void testContextNonRootMappingDefault() throws Exception {
-        doTestMapping("/dummy", "/", "/foo", "/", "DEFAULT");
+        doTestMapping("/dummy", "/", "/foo", "", "DEFAULT");
     }
 
     @Test
     public void testContextNonRootMappingExtension() throws Exception {
-        doTestMapping("/dummy", "*.test", "/foo/bar.test", "/foo/bar", "EXTENSION");
+        doTestMapping("/dummy", "*.test", "/foo/bar.test", "foo/bar", "EXTENSION");
     }
 
     @Test
     public void testContextNonRootMappingExact() throws Exception {
-        doTestMapping("/dummy", "/foo/bar", "/foo/bar", "/foo/bar", "EXACT");
+        doTestMapping("/dummy", "/foo/bar", "/foo/bar", "foo/bar", "EXACT");
     }
 
     @Test
     public void testContextNonRootMappingPath() throws Exception {
-        doTestMapping("/dummy", "/foo/bar/*", "/foo/bar/foo2", "/foo2", "PATH");
+        doTestMapping("/dummy", "/foo/bar/*", "/foo/bar/foo2", "foo2", "PATH");
     }
 
     @Test
@@ -68,22 +70,22 @@ public class TestApplicationMapping extends TomcatBaseTest {
 
     @Test
     public void testContextRootMappingDefault() throws Exception {
-        doTestMapping("", "/", "/foo", "/", "DEFAULT");
+        doTestMapping("", "/", "/foo", "", "DEFAULT");
     }
 
     @Test
     public void testContextRootMappingExtension() throws Exception {
-        doTestMapping("", "*.test", "/foo/bar.test", "/foo/bar", "EXTENSION");
+        doTestMapping("", "*.test", "/foo/bar.test", "foo/bar", "EXTENSION");
     }
 
     @Test
     public void testContextRootMappingExact() throws Exception {
-        doTestMapping("", "/foo/bar", "/foo/bar", "/foo/bar", "EXACT");
+        doTestMapping("", "/foo/bar", "/foo/bar", "foo/bar", "EXACT");
     }
 
     @Test
     public void testContextRootMappingPath() throws Exception {
-        doTestMapping("", "/foo/bar/*", "/foo/bar/foo2", "/foo2", "PATH");
+        doTestMapping("", "/foo/bar/*", "/foo/bar/foo2", "foo2", "PATH");
     }
 
     private void doTestMapping(String contextPath, String mapping, String requestPath,
@@ -101,6 +103,9 @@ public class TestApplicationMapping extends TomcatBaseTest {
         tearDown();
         setUp();
         doTestMappingNamedForward(contextPath, mapping, requestPath, matchValue, matchType);
+        tearDown();
+        setUp();
+        doTestMappingAsync(contextPath, mapping, requestPath, matchValue, matchType);
     }
 
     private void doTestMappingDirect(String contextPath, String mapping, String requestPath,
@@ -146,7 +151,7 @@ public class TestApplicationMapping extends TomcatBaseTest {
         Assert.assertTrue(body, body.contains("MatchType=[" + matchType + "]"));
         Assert.assertTrue(body, body.contains("ServletName=[Include]"));
 
-        Assert.assertTrue(body, body.contains("IncludeMatchValue=[/mapping]"));
+        Assert.assertTrue(body, body.contains("IncludeMatchValue=[mapping]"));
         Assert.assertTrue(body, body.contains("IncludePattern=[/mapping]"));
         Assert.assertTrue(body, body.contains("IncludeMatchType=[EXACT]"));
         Assert.assertTrue(body, body.contains("IncludeServletName=[Mapping]"));
@@ -192,7 +197,7 @@ public class TestApplicationMapping extends TomcatBaseTest {
         ByteChunk bc = getUrl("http://localhost:"; + getPort() + contextPath + requestPath);
         String body = bc.toString();
 
-        Assert.assertTrue(body, body.contains("MatchValue=[/mapping]"));
+        Assert.assertTrue(body, body.contains("MatchValue=[mapping]"));
         Assert.assertTrue(body, body.contains("Pattern=[/mapping]"));
         Assert.assertTrue(body, body.contains("MatchType=[EXACT]"));
         Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
@@ -226,6 +231,35 @@ public class TestApplicationMapping extends TomcatBaseTest {
         Assert.assertTrue(body, body.contains("ServletName=[Forward]"));
     }
 
+    private void doTestMappingAsync(String contextPath, String mapping, String requestPath,
+            String matchValue, String matchType) throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        // No file system docBase required
+        Context ctx = tomcat.addContext(contextPath, null);
+
+        Wrapper w = Tomcat.addServlet(ctx, "Async", new AsyncServlet());
+        w.setAsyncSupported(true);
+        ctx.addServletMappingDecoded(mapping, "Async");
+        Tomcat.addServlet(ctx, "Mapping", new MappingServlet());
+        ctx.addServletMappingDecoded("/mapping", "Mapping");
+
+        tomcat.start();
+
+        ByteChunk bc = getUrl("http://localhost:"; + getPort() + contextPath + requestPath);
+        String body = bc.toString();
+
+        Assert.assertTrue(body, body.contains("MatchValue=[mapping]"));
+        Assert.assertTrue(body, body.contains("Pattern=[/mapping]"));
+        Assert.assertTrue(body, body.contains("MatchType=[EXACT]"));
+        Assert.assertTrue(body, body.contains("ServletName=[Mapping]"));
+
+        Assert.assertTrue(body, body.contains("AsyncMatchValue=[" + matchValue + "]"));
+        Assert.assertTrue(body, body.contains("AsyncPattern=[" + mapping + "]"));
+        Assert.assertTrue(body, body.contains("AsyncMatchType=[" + matchType + "]"));
+        Assert.assertTrue(body, body.contains("AsyncServletName=[Async]"));
+    }
+
 
     private static class IncludeServlet extends HttpServlet {
         private static final long serialVersionUID = 1L;
@@ -275,6 +309,18 @@ public class TestApplicationMapping extends TomcatBaseTest {
     }
 
 
+    private static class AsyncServlet extends HttpServlet {
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+            AsyncContext ac = req.startAsync();
+            ac.dispatch("/mapping");
+        }
+    }
+
+
     private static class MappingServlet extends HttpServlet {
 
         private static final long serialVersionUID = 1L;
@@ -284,13 +330,13 @@ public class TestApplicationMapping extends TomcatBaseTest {
                 throws ServletException, IOException {
             resp.setContentType("text/plain;charset=UTF-8");
             PrintWriter pw = resp.getWriter();
-            Mapping mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
-                    req).getMapping();
+            ServletMapping mapping = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
+                    req).getServletMapping();
             pw.println("MatchValue=[" + mapping.getMatchValue() + "]");
             pw.println("Pattern=[" + mapping.getPattern() + "]");
             pw.println("MatchType=[" + mapping.getMappingMatch() + "]");
             pw.println("ServletName=[" + mapping.getServletName() + "]");
-            Mapping includeMapping = (Mapping) req.getAttribute(
+            ServletMapping includeMapping = (ServletMapping) req.getAttribute(
                     org.apache.catalina.servlet4preview.RequestDispatcher.INCLUDE_MAPPING);
             if (includeMapping != null) {
                 pw.println("IncludeMatchValue=[" + includeMapping.getMatchValue() + "]");
@@ -299,7 +345,7 @@ public class TestApplicationMapping extends TomcatBaseTest {
                 pw.println("IncludeServletName=[" + includeMapping.getServletName() + "]");
 
             }
-            Mapping forwardMapping = (Mapping) req.getAttribute(
+            ServletMapping forwardMapping = (ServletMapping) req.getAttribute(
                     org.apache.catalina.servlet4preview.RequestDispatcher.FORWARD_MAPPING);
             if (forwardMapping != null) {
                 pw.println("ForwardMatchValue=[" + forwardMapping.getMatchValue() + "]");
@@ -307,6 +353,14 @@ public class TestApplicationMapping extends TomcatBaseTest {
                 pw.println("ForwardMatchType=[" + forwardMapping.getMappingMatch() + "]");
                 pw.println("ForwardServletName=[" + forwardMapping.getServletName() + "]");
             }
+            ServletMapping asyncMapping = (ServletMapping) req.getAttribute(
+                    org.apache.catalina.servlet4preview.AsyncContext.ASYNC_MAPPING);
+            if (asyncMapping != null) {
+                pw.println("AsyncMatchValue=[" + asyncMapping.getMatchValue() + "]");
+                pw.println("AsyncPattern=[" + asyncMapping.getPattern() + "]");
+                pw.println("AsyncMatchType=[" + asyncMapping.getMappingMatch() + "]");
+                pw.println("AsyncServletName=[" + asyncMapping.getServletName() + "]");
+            }
         }
     }
 }
diff --git a/test/org/apache/catalina/core/TestAsyncContextImpl.java b/test/org/apache/catalina/core/TestAsyncContextImpl.java
index 3bd46064..772d09b9 100644
--- a/test/org/apache/catalina/core/TestAsyncContextImpl.java
+++ b/test/org/apache/catalina/core/TestAsyncContextImpl.java
@@ -2225,7 +2225,19 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
 
     // https://bz.apache.org/bugzilla/show_bug.cgi?id=57559
     @Test
-    public void testAsyncRequestURI() throws Exception {
+    public void testAsyncRequestURI_24() throws Exception {
+        doTestAsyncRequestURI("/foo/%24/bar");
+    }
+
+
+    // https://bz.apache.org/bugzilla/show_bug.cgi?id=60722
+    @Test
+    public void testAsyncRequestURI_25() throws Exception {
+        doTestAsyncRequestURI("/foo/%25/bar");
+    }
+
+
+    private void doTestAsyncRequestURI(String uri) throws Exception{
         // Setup Tomcat instance
         Tomcat tomcat = getTomcatInstance();
 
@@ -2239,9 +2251,7 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
 
         tomcat.start();
 
-        String uri = "/foo/%24/bar";
-
-        ByteChunk body = getUrl("http://localhost:"; + getPort()+ uri);
+        ByteChunk body = getUrl("http://localhost:"; + getPort() + uri);
 
         Assert.assertEquals(uri, body.toString());
     }
diff --git a/test/org/apache/catalina/core/TestDefaultInstanceManager.java b/test/org/apache/catalina/core/TestDefaultInstanceManager.java
index c5c37b63..5b313d21 100644
--- a/test/org/apache/catalina/core/TestDefaultInstanceManager.java
+++ b/test/org/apache/catalina/core/TestDefaultInstanceManager.java
@@ -17,14 +17,22 @@
 package org.apache.catalina.core;
 
 import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.naming.NamingException;
 
 import static org.junit.Assert.assertEquals;
+
+import org.junit.Ignore;
 import org.junit.Test;
 
+import org.apache.catalina.Context;
 import org.apache.catalina.Wrapper;
+import org.apache.catalina.servlets.DefaultServlet;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.jasper.servlet.JasperInitializer;
+import org.apache.tomcat.InstanceManager;
 
 
 public class TestDefaultInstanceManager extends TomcatBaseTest {
@@ -53,6 +61,7 @@ public class TestDefaultInstanceManager extends TomcatBaseTest {
         // Spin a while until GC happens or we wait too long
         int loop = 0;
         while (loop < 10) {
+            instanceManager.backgroundProcess();
             if (instanceManager.getAnnotationCacheSize() == count) {
                 break;
             }
@@ -86,4 +95,70 @@ public class TestDefaultInstanceManager extends TomcatBaseTest {
 
         return (DefaultInstanceManager) ctxt.getInstanceManager();
     }
+
+
+    /*
+     * Performance test. Comment out @Ignore to run the test.
+     */
+    @Ignore
+    @Test
+    public void testConcurrency() throws Exception {
+        // Create a populated InstanceManager
+        Tomcat tomcat = getTomcatInstance();
+        Context ctx = tomcat.addContext(null, "", null);
+
+        tomcat.start();
+
+        InstanceManager im = ctx.getInstanceManager();
+
+        for (int i = 1; i < 9; i++) {
+            doTestConcurrency(im, i);
+        }
+    }
+
+
+    private void doTestConcurrency(InstanceManager im, int threadCount) throws Exception {
+        long start = System.nanoTime();
+
+        Thread[] threads = new Thread[threadCount];
+
+        for (int i = 0; i < threadCount; i++) {
+            threads[i] = new Thread(new InstanceManagerRunnable(im));
+        }
+
+        for (int i = 0; i < threadCount; i++) {
+            threads[i].start();
+        }
+
+        for (int i = 0; i < threadCount; i++) {
+            threads[i].join();
+        }
+
+        long duration = System.nanoTime() - start;
+
+        System.out.println(threadCount + " threads completed in " + duration + "ns");
+    }
+
+
+    private class InstanceManagerRunnable implements Runnable {
+
+        private final InstanceManager im;
+
+        private InstanceManagerRunnable(InstanceManager im) {
+            this.im = im;
+        }
+
+        @Override
+        public void run() {
+            try {
+                Object test = new DefaultServlet();
+                for (int i = 0; i < 200000; i++) {
+                    im.newInstance(test);
+                    im.destroyInstance(test);
+                }
+            } catch (NamingException | IllegalAccessException | InvocationTargetException ne) {
+                ne.printStackTrace();
+            }
+        }
+    }
 }
diff --git a/test/org/apache/catalina/startup/SimpleHttpClient.java b/test/org/apache/catalina/startup/SimpleHttpClient.java
index f366475b..2b3b0ea6 100644
--- a/test/org/apache/catalina/startup/SimpleHttpClient.java
+++ b/test/org/apache/catalina/startup/SimpleHttpClient.java
@@ -46,18 +46,18 @@ public abstract class SimpleHttpClient {
     public static final String LF = "\n";
     public static final String CRLF = CR + LF;
 
-    public static final String INFO_100 = "HTTP/1.1 100";
-    public static final String OK_200 = "HTTP/1.1 200";
-    public static final String REDIRECT_302 = "HTTP/1.1 302";
-    public static final String REDIRECT_303 = "HTTP/1.1 303";
-    public static final String FAIL_400 = "HTTP/1.1 400";
-    public static final String FAIL_404 = "HTTP/1.1 404";
-    public static final String TIMEOUT_408 = "HTTP/1.1 408";
-    public static final String FAIL_413 = "HTTP/1.1 413";
-    public static final String FAIL_417 = "HTTP/1.1 417";
+    public static final String INFO_100 = "HTTP/1.1 100 ";
+    public static final String OK_200 = "HTTP/1.1 200 ";
+    public static final String REDIRECT_302 = "HTTP/1.1 302 ";
+    public static final String REDIRECT_303 = "HTTP/1.1 303 ";
+    public static final String FAIL_400 = "HTTP/1.1 400 ";
+    public static final String FAIL_404 = "HTTP/1.1 404 ";
+    public static final String TIMEOUT_408 = "HTTP/1.1 408 ";
+    public static final String FAIL_413 = "HTTP/1.1 413 ";
+    public static final String FAIL_417 = "HTTP/1.1 417 ";
     public static final String FAIL_50X = "HTTP/1.1 50";
-    public static final String FAIL_500 = "HTTP/1.1 500";
-    public static final String FAIL_501 = "HTTP/1.1 501";
+    public static final String FAIL_500 = "HTTP/1.1 500 ";
+    public static final String FAIL_501 = "HTTP/1.1 501 ";
 
     private static final String CONTENT_LENGTH_HEADER_PREFIX =
             "Content-Length: ";
diff --git a/test/org/apache/catalina/util/TestParameterMap.java b/test/org/apache/catalina/util/TestParameterMap.java
new file mode 100644
index 00000000..45f2fa5f
--- /dev/null
+++ b/test/org/apache/catalina/util/TestParameterMap.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.util;
+
+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.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestParameterMap {
+
+    private static final String[] TEST_PARAM_VALUES_1 = { "value1" };
+    private static final String[] TEST_PARAM_VALUES_2 = { "value2" };
+    private static final String[] TEST_PARAM_VALUES_2_UPDATED = { "value2-updated" };
+    private static final String[] TEST_PARAM_VALUES_3 = { "value3" };
+
+    private Map<String, String[]> paramMap;
+
+    @Before
+    public void setUp() {
+        paramMap = new ParameterMap<>();
+
+        paramMap.put("param1", TEST_PARAM_VALUES_1);
+        paramMap.put("param2", TEST_PARAM_VALUES_2);
+        paramMap.put("param3", TEST_PARAM_VALUES_3);
+
+        Assert.assertTrue(paramMap.containsKey("param1"));
+        Assert.assertArrayEquals(TEST_PARAM_VALUES_1, paramMap.get("param1"));
+        Assert.assertTrue(paramMap.containsKey("param2"));
+        Assert.assertArrayEquals(TEST_PARAM_VALUES_2, paramMap.get("param2"));
+        Assert.assertTrue(paramMap.containsKey("param3"));
+        Assert.assertArrayEquals(TEST_PARAM_VALUES_3, paramMap.get("param3"));
+
+        final Set<String> keySet = paramMap.keySet();
+        Assert.assertTrue(keySet.contains("param1"));
+        Assert.assertTrue(keySet.contains("param2"));
+        Assert.assertTrue(keySet.contains("param3"));
+
+        paramMap.put("param2", TEST_PARAM_VALUES_2_UPDATED);
+        paramMap.remove("param3");
+
+        Assert.assertTrue(paramMap.containsKey("param1"));
+        Assert.assertArrayEquals(TEST_PARAM_VALUES_1, paramMap.get("param1"));
+        Assert.assertTrue(paramMap.containsKey("param2"));
+        Assert.assertArrayEquals(TEST_PARAM_VALUES_2_UPDATED, paramMap.get("param2"));
+        Assert.assertFalse(paramMap.containsKey("param3"));
+        Assert.assertNull(paramMap.get("param3"));
+
+        Assert.assertTrue(keySet.contains("param1"));
+        Assert.assertTrue(keySet.contains("param2"));
+        Assert.assertFalse(keySet.contains("param3"));
+    }
+
+    @After
+    public void tearDown() {
+        Assert.assertTrue(paramMap.containsKey("param1"));
+        Assert.assertArrayEquals(TEST_PARAM_VALUES_1, paramMap.get("param1"));
+        Assert.assertTrue(paramMap.containsKey("param2"));
+        Assert.assertArrayEquals(TEST_PARAM_VALUES_2_UPDATED, paramMap.get("param2"));
+        Assert.assertFalse(paramMap.containsKey("param3"));
+        Assert.assertNull(paramMap.get("param3"));
+    }
+
+    @Test
+    public void testMapImmutabilityAfterLocked() {
+        ((ParameterMap<String, String[]>) paramMap).setLocked(true);
+
+        try {
+            String[] updatedParamValues22 = new String[] { "value2-updated-2" };
+            paramMap.put("param2", updatedParamValues22);
+            Assert.fail("ParameterMap is not locked.");
+        } catch (IllegalStateException expectedException) {
+        }
+
+        try {
+            final Map<String, String[]> additionalParams = new HashMap<>();
+            additionalParams.put("param4", new String[] { "value4" });
+            paramMap.putAll(additionalParams);
+            Assert.fail("ParameterMap is not locked.");
+        } catch (IllegalStateException expectedException) {
+        }
+
+        try {
+            paramMap.remove("param2");
+            Assert.fail("ParameterMap is not locked.");
+        } catch (IllegalStateException expectedException) {
+        }
+
+        try {
+            paramMap.clear();
+            Assert.fail("ParameterMap is not locked.");
+        } catch (IllegalStateException expectedException) {
+        }
+    }
+
+    @Test
+    public void testKeySetImmutabilityAfterLocked() {
+        ((ParameterMap<String, String[]>) paramMap).setLocked(true);
+
+        final Set<String> keySet = paramMap.keySet();
+
+        try {
+            keySet.add("param4");
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            keySet.remove("param2");
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            keySet.removeAll(Arrays.asList("param1", "param2"));
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            keySet.retainAll(Collections.emptyList());
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            keySet.clear();
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+    }
+
+    @Test
+    public void testValuesImmutabilityAfterLocked() {
+        ((ParameterMap<String, String[]>) paramMap).setLocked(true);
+
+        final Collection<String[]> valuesCol = paramMap.values();
+
+        try {
+            valuesCol.add(new String[] { "value4" });
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            List<String[]> list = new ArrayList<>();
+            list.add(new String[] { "value4" });
+            valuesCol.addAll(list);
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            valuesCol.remove(TEST_PARAM_VALUES_1);
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            List<String[]> list = new ArrayList<>();
+            list.add(TEST_PARAM_VALUES_1);
+            valuesCol.removeAll(list);
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            valuesCol.retainAll(Collections.emptyList());
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            valuesCol.clear();
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+    }
+
+    @Test
+    public void testEntrySetImmutabilityAfterLocked() {
+        ((ParameterMap<String, String[]>) paramMap).setLocked(true);
+
+        final Set<Map.Entry<String, String[]>> entrySet = paramMap.entrySet();
+
+        try {
+            final Map<String, String[]> anotherParamsMap = new HashMap<>();
+            anotherParamsMap.put("param4", new String[] { "value4" });
+            Map.Entry<String, String[]> anotherEntry = anotherParamsMap.entrySet().iterator().next();
+            entrySet.add(anotherEntry);
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            final Map<String, String[]> anotherParamsMap = new HashMap<>();
+            anotherParamsMap.put("param4", new String[] { "value4" });
+            anotherParamsMap.put("param5", new String[] { "value5" });
+            entrySet.addAll(anotherParamsMap.entrySet());
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            final Map.Entry<String, String[]> entry = entrySet.iterator().next();
+            entrySet.remove(entry);
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            Set<Map.Entry<String, String[]>> anotherEntrySet = new HashSet<>(entrySet);
+            entrySet.removeAll(anotherEntrySet);
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            entrySet.retainAll(Collections.emptySet());
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+
+        try {
+            entrySet.clear();
+            Assert.fail("ParameterMap is not locked.");
+        } catch (UnsupportedOperationException expectedException) {
+        }
+    }
+}
\ No newline at end of file
diff --git a/test/org/apache/catalina/valves/TestErrorReportValve.java b/test/org/apache/catalina/valves/TestErrorReportValve.java
index e15aef98..da0a99fd 100644
--- a/test/org/apache/catalina/valves/TestErrorReportValve.java
+++ b/test/org/apache/catalina/valves/TestErrorReportValve.java
@@ -50,8 +50,8 @@ public class TestErrorReportValve extends TomcatBaseTest {
 
         ByteChunk res = getUrl("http://localhost:"; + getPort());
 
-        Assert.assertTrue(res.toString().contains("<p><b>message</b> <u>" +
-                ErrorServlet.ERROR_TEXT + "</u></p>"));
+        Assert.assertTrue(res.toString().contains("<p><b>Message</b> " +
+                ErrorServlet.ERROR_TEXT + "</p>"));
     }
 
 
diff --git a/test/org/apache/coyote/http2/Http2TestBase.java b/test/org/apache/coyote/http2/Http2TestBase.java
index 72762c81..fa509baf 100644
--- a/test/org/apache/coyote/http2/Http2TestBase.java
+++ b/test/org/apache/coyote/http2/Http2TestBase.java
@@ -545,7 +545,7 @@ public abstract class Http2TestBase extends TomcatBaseTest {
         if (responseHeaders.length < 3) {
             return false;
         }
-        if (!responseHeaders[0].startsWith("HTTP/1.1 101")) {
+        if (!responseHeaders[0].startsWith("HTTP/1.1 101 ")) {
             return false;
         }
 
@@ -617,7 +617,7 @@ public abstract class Http2TestBase extends TomcatBaseTest {
 
     void parseHttp11Response() throws IOException {
         String[] responseHeaders = readHttpResponseHeaders();
-        Assert.assertTrue(responseHeaders[0], responseHeaders[0].startsWith("HTTP/1.1 200"));
+        Assert.assertTrue(responseHeaders[0], responseHeaders[0].startsWith("HTTP/1.1 200 "));
 
         // Find the content length (chunked responses not handled)
         for (int i = 1; i < responseHeaders.length; i++) {
diff --git a/test/org/apache/coyote/http2/TestAbortedUpload.java b/test/org/apache/coyote/http2/TestAbortedUpload.java
new file mode 100644
index 00000000..403d0db7
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestAbortedUpload.java
@@ -0,0 +1,131 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.coyote.http2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.startup.Tomcat;
+
+public class TestAbortedUpload extends Http2TestBase {
+
+    @Test
+    public void testAbortedRequest() throws Exception {
+        http2Connect();
+
+        Http2Protocol http2Protocol =
+                (Http2Protocol) getTomcatInstance().getConnector().findUpgradeProtocols()[0];
+        http2Protocol.setAllowedTrailerHeaders(TRAILER_HEADER_NAME);
+
+        int bodySize = 8192;
+        int bodyCount = 20;
+
+        byte[] headersFrameHeader = new byte[9];
+        ByteBuffer headersPayload = ByteBuffer.allocate(128);
+        byte[] dataFrameHeader = new byte[9];
+        ByteBuffer dataPayload = ByteBuffer.allocate(bodySize);
+        byte[] trailerFrameHeader = new byte[9];
+        ByteBuffer trailerPayload = ByteBuffer.allocate(256);
+
+        buildPostRequest(headersFrameHeader, headersPayload, false, dataFrameHeader, dataPayload,
+                null, trailerFrameHeader, trailerPayload, 3);
+
+        // Write the headers
+        writeFrame(headersFrameHeader, headersPayload);
+        // Body
+        for (int i = 0; i < bodyCount; i++) {
+            writeFrame(dataFrameHeader, dataPayload);
+        }
+
+        // Trailers
+        writeFrame(trailerFrameHeader, trailerPayload);
+
+        // The actual response depends on timing issues. Particularly how much
+        // data is transferred in StreamInputBuffer inBuffer to outBuffer on the
+        // first read.
+        while (output.getTrace().length() == 0) {
+            try {
+                parser.readFrame(true);
+                if ("3-RST-[3]\n".equals(output.getTrace())) {
+                    output.clearTrace();
+                }
+            } catch (IOException ioe) {
+                // Might not be any further frames after the reset
+                break;
+            }
+        }
+
+        if (output.getTrace().startsWith("0-WindowSize-[")) {
+            String trace = output.getTrace();
+            int size = Integer.parseInt(trace.substring(14, trace.length() - 2));
+            output.clearTrace();
+            // Window updates always come in pairs
+            parser.readFrame(true);
+            Assert.assertEquals("3-WindowSize-[" + size + "]\n", output.getTrace());
+        }
+    }
+
+
+    @Override
+    protected void configureAndStartWebApplication() throws LifecycleException {
+        Tomcat tomcat = getTomcatInstance();
+
+        // Retain '/simple' url-pattern since it enables code re-use
+        Context ctxt = tomcat.addContext("", null);
+        Tomcat.addServlet(ctxt, "abort", new AbortServlet());
+        ctxt.addServletMappingDecoded("/simple", "abort");
+
+        tomcat.start();
+    }
+
+
+    private static class AbortServlet extends SimpleServlet {
+
+        private static final long serialVersionUID = 1L;
+
+        @Override
+        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+            // Read upto 128 bytes and then return a 403 response
+
+            InputStream is = req.getInputStream();
+            byte[] buf = new byte[128];
+            int toRead = 128;
+
+            int read = is.read(buf);
+            while (read != -1 && toRead > 0) {
+                toRead -= read;
+                read = is.read(buf);
+            }
+
+            if (toRead == 0) {
+                resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+            } else {
+                resp.setStatus(HttpServletResponse.SC_OK);
+            }
+        }
+    }
+}
diff --git a/test/org/apache/jasper/compiler/TestEncodingDetector.java b/test/org/apache/jasper/compiler/TestEncodingDetector.java
index 146a5e2a..4cc5016b 100644
--- a/test/org/apache/jasper/compiler/TestEncodingDetector.java
+++ b/test/org/apache/jasper/compiler/TestEncodingDetector.java
@@ -57,6 +57,8 @@ public class TestEncodingDetector extends TomcatBaseTest {
         result.add(new Object[] { "bom-utf16le-prolog-utf16be.jspx", Integer.valueOf(500), null });
         result.add(new Object[] { "bom-utf16le-prolog-utf16le.jspx", Integer.valueOf(200), Boolean.TRUE });
         result.add(new Object[] { "bom-utf16le-prolog-utf8.jspx",    Integer.valueOf(500), null });
+        result.add(new Object[] { "bug60769a.jspx",    Integer.valueOf(500), null });
+        result.add(new Object[] { "bug60769b.jspx",    Integer.valueOf(200), Boolean.TRUE });
         return result;
     }
 
diff --git a/test/org/apache/jasper/runtime/TestJspContextWrapper.java b/test/org/apache/jasper/runtime/TestJspContextWrapper.java
index f9affb0e..cfff4a3c 100644
--- a/test/org/apache/jasper/runtime/TestJspContextWrapper.java
+++ b/test/org/apache/jasper/runtime/TestJspContextWrapper.java
@@ -66,4 +66,20 @@ public class TestJspContextWrapper extends TomcatBaseTest {
         // Class import
         Assert.assertTrue(result, result.contains("02-" + Collections.EMPTY_LIST.size()));
     }
+
+    @Test
+    public void testELTagFileELContextListener() throws Exception {
+        getTomcatInstanceTestWebapp(false, true);
+
+        ByteChunk out = new ByteChunk();
+
+        int rc = getUrl("http://localhost:"; + getPort() + "/test/bug5nnnn/bug58178c.jsp", out, null);
+
+        Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+
+        String result = out.toString();
+
+        Assert.assertTrue(result, result.contains("JSP count: 1"));
+        Assert.assertTrue(result, result.contains("Tag count: 1"));
+    }
 }
diff --git a/test/org/apache/jasper/servlet/TestJspCServletContext.java b/test/org/apache/jasper/servlet/TestJspCServletContext.java
index de89b8d3..16c8b01a 100644
--- a/test/org/apache/jasper/servlet/TestJspCServletContext.java
+++ b/test/org/apache/jasper/servlet/TestJspCServletContext.java
@@ -40,7 +40,7 @@ public class TestJspCServletContext {
         Assert.assertTrue(jspConfigDescriptor.getTaglibs().isEmpty());
         Collection<JspPropertyGroupDescriptor> propertyGroups =
                 jspConfigDescriptor.getJspPropertyGroups();
-        Assert.assertEquals(2, propertyGroups.size());
+        Assert.assertEquals(4, propertyGroups.size());
         Iterator<JspPropertyGroupDescriptor> groupIterator =
                 propertyGroups.iterator();
         JspPropertyGroupDescriptor groupDescriptor;
diff --git a/test/org/apache/tomcat/unittest/TesterContext.java b/test/org/apache/tomcat/unittest/TesterContext.java
index 6e1ffb15..49e7ffa6 100644
--- a/test/org/apache/tomcat/unittest/TesterContext.java
+++ b/test/org/apache/tomcat/unittest/TesterContext.java
@@ -1267,4 +1267,14 @@ public class TesterContext implements Context {
     public void setDispatchersUseEncodedPaths(boolean dispatchersUseEncodedPaths) { /* NO-OP */ }
     @Override
     public boolean getDispatchersUseEncodedPaths() { return true; }
+
+    @Override
+    public void setRequestCharacterEncoding(String encoding) { /* NO-OP */ }
+    @Override
+    public String getRequestCharacterEncoding() { return null; }
+
+    @Override
+    public void setResponseCharacterEncoding(String encoding) { /* NO-OP */ }
+    @Override
+    public String getResponseCharacterEncoding() { return null; }
 }
diff --git a/test/org/apache/tomcat/util/buf/TestUriUtil24.java b/test/org/apache/tomcat/util/buf/TestUriUtil24.java
new file mode 100644
index 00000000..2f4ae76e
--- /dev/null
+++ b/test/org/apache/tomcat/util/buf/TestUriUtil24.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.buf;
+
+public class TestUriUtil24 extends TesterUriUtilBase {
+
+    public TestUriUtil24() {
+        super("$");
+    }
+}
diff --git a/test/org/apache/tomcat/util/buf/TestUriUtil26.java b/test/org/apache/tomcat/util/buf/TestUriUtil26.java
new file mode 100644
index 00000000..4e6a6d03
--- /dev/null
+++ b/test/org/apache/tomcat/util/buf/TestUriUtil26.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.buf;
+
+public class TestUriUtil26 extends TesterUriUtilBase {
+
+    public TestUriUtil26() {
+        super("&");
+    }
+}
diff --git a/test/org/apache/tomcat/util/buf/TestUriUtil2A.java b/test/org/apache/tomcat/util/buf/TestUriUtil2A.java
new file mode 100644
index 00000000..c328e9fa
--- /dev/null
+++ b/test/org/apache/tomcat/util/buf/TestUriUtil2A.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.buf;
+
+public class TestUriUtil2A extends TesterUriUtilBase {
+
+    public TestUriUtil2A() {
+        super("*");
+    }
+}
diff --git a/test/org/apache/tomcat/util/buf/TestUriUtil40.java b/test/org/apache/tomcat/util/buf/TestUriUtil40.java
new file mode 100644
index 00000000..10dcaf7a
--- /dev/null
+++ b/test/org/apache/tomcat/util/buf/TestUriUtil40.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.buf;
+
+public class TestUriUtil40 extends TesterUriUtilBase {
+
+    public TestUriUtil40() {
+        super("@");
+    }
+}
diff --git a/test/org/apache/tomcat/util/buf/TestUriUtil.java b/test/org/apache/tomcat/util/buf/TesterUriUtilBase.java
similarity index 65%
rename from test/org/apache/tomcat/util/buf/TestUriUtil.java
rename to test/org/apache/tomcat/util/buf/TesterUriUtilBase.java
index e612b86e..b176788b 100644
--- a/test/org/apache/tomcat/util/buf/TestUriUtil.java
+++ b/test/org/apache/tomcat/util/buf/TesterUriUtilBase.java
@@ -23,7 +23,18 @@ import java.net.URL;
 import org.junit.Assert;
 import org.junit.Test;
 
-public class TestUriUtil {
+import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
+
+public abstract class TesterUriUtilBase {
+
+    private final String separator;
+
+    protected TesterUriUtilBase(String separator) {
+        this.separator = separator;
+        TomcatURLStreamHandlerFactory.register();
+        System.setProperty("org.apache.tomcat.util.buf.UriUtil.WAR_SEPARATOR", separator);
+    }
+
 
     @Test
     public void testBuildJarUrl01() throws MalformedURLException {
@@ -61,6 +72,44 @@ public class TestUriUtil {
     }
 
 
+    @Test
+    public void testBuildJarUrl04() throws MalformedURLException {
+        File jarFile = new File("/patha/pathb" + separator + "/pathc");
+        String result = UriUtil.buildJarUrl(jarFile).toString();
+
+        int index = result.indexOf("!/");
+        Assert.assertEquals(result, result.length() - 2, index);
+
+        index = result.indexOf(separator + "/");
+        Assert.assertEquals(result, -1, index);
+    }
+
+
+    @Test
+    public void testWarToJar01() throws MalformedURLException {
+        doTestWarToJar("^");
+    }
+
+
+    @Test
+    public void testWarToJar02() throws MalformedURLException {
+        doTestWarToJar("*");
+    }
+
+
+    @Test
+    public void testWarToJar03() throws MalformedURLException {
+        doTestWarToJar(separator);
+    }
+
+
+    private void doTestWarToJar(String separator) throws MalformedURLException {
+        URL warUrl = new URL("war:file:/external/path" + separator + "/internal/path");
+        URL jarUrl = UriUtil.warToJar(warUrl);
+        Assert.assertEquals("jar:file:/external/path!/internal/path", jarUrl.toString());
+    }
+
+
     // @Test /* Uncomment to test performance for different implementations. */
     public void performanceTestBuildJarUrl() throws MalformedURLException {
         File jarFile = new File("/patha/pathb^/pathc");
diff --git a/test/org/apache/tomcat/util/http/TestCookieParsing.java b/test/org/apache/tomcat/util/http/TestCookieParsing.java
index 0d8a212a..b7361b1c 100644
--- a/test/org/apache/tomcat/util/http/TestCookieParsing.java
+++ b/test/org/apache/tomcat/util/http/TestCookieParsing.java
@@ -51,6 +51,11 @@ public class TestCookieParsing extends TomcatBaseTest {
     private static final String[] COOKIES_WITH_QUOTES = new String[] {
             "name=\"val\\\"ue\"", "name=\"value\"" };
 
+    private static final String[] COOKIES_V0 = new String[] {
+            "$Version=0;name=\"val ue\"", "$Version=0;name=\"val\tue\""};
+
+    private static final String COOKIES_V0_CONCAT = "name=\"val ue\"name=\"val\tue\"";
+
     private static final String[] COOKIES_V1 = new String[] {
             "$Version=1;name=\"val ue\"", "$Version=1;name=\"val\tue\""};
 
@@ -136,6 +141,14 @@ public class TestCookieParsing extends TomcatBaseTest {
 
 
     @Test
+    public void testRfc6265V0() throws Exception {
+        TestCookieParsingClient client = new TestCookieParsingClient(
+                new Rfc6265CookieProcessor(), COOKIES_V0, COOKIES_V0_CONCAT);
+        client.doRequest();
+    }
+
+
+    @Test
     public void testRfc6265V1() throws Exception {
         TestCookieParsingClient client = new TestCookieParsingClient(
                 new Rfc6265CookieProcessor(), COOKIES_V1, COOKIES_V1_CONCAT);
diff --git a/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java b/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java
index f97e877c..6c03e7f5 100644
--- a/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java
+++ b/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java
@@ -187,7 +187,7 @@ public class TestCookieProcessorGeneration {
 
     @Test
     public void v1TestMaxAgeZero() {
-        doV1TestMaxAge(0, "foo=bar; Version=1; Max-Age=0", "foo=bar;Max-Age=0");
+        doV1TestMaxAge(0, "foo=bar; Version=1; Max-Age=0", "foo=bar;Max-Age=0;Expires=Thu, 01-Jan-1970 00:00:10 GMT");
     }
 
     @Test
@@ -302,7 +302,14 @@ public class TestCookieProcessorGeneration {
             }
             Assert.assertNotNull("Failed to throw IAE", e);
         } else {
-            Assert.assertEquals(expected, cookieProcessor.generateHeader(cookie));
+            if (cookieProcessor instanceof Rfc6265CookieProcessor &&
+                    cookie.getMaxAge() > 0) {
+                // Expires attribute will depend on time cookie is generated so
+                // use a modified test
+                Assert.assertTrue(cookieProcessor.generateHeader(cookie).startsWith(expected));
+            } else {
+                Assert.assertEquals(expected, cookieProcessor.generateHeader(cookie));
+            }
         }
     }
 
diff --git a/test/org/apache/tomcat/util/http/TestCookies.java b/test/org/apache/tomcat/util/http/TestCookies.java
index 0f47297d..e0e95246 100644
--- a/test/org/apache/tomcat/util/http/TestCookies.java
+++ b/test/org/apache/tomcat/util/http/TestCookies.java
@@ -384,8 +384,8 @@ public class TestCookies {
 
     @Test
     public void rfc2109Version0Rfc6265() {
-        // Neither RFC2109 nor RFc6265 allow version 0
-        test(true, "$Version=0;foo=bar");
+        // RFC6265 will parse explicit version 0 using RFC2109
+        test(true, "$Version=0;foo=bar", FOO);
     }
 
     @Test
@@ -470,6 +470,33 @@ public class TestCookies {
         test(true, "$Version=1;x\tx=yyy,foo=bar;a=b", FOO, A);
     }
 
+    @Test
+    public void testBug60788Rfc6265() {
+        doTestBug60788(true);
+    }
+
+    @Test
+    public void testBug60788Rfc2109() {
+        doTestBug60788(false);
+    }
+
+    private void doTestBug60788(boolean useRfc6265) {
+        Cookie expected = new Cookie("userId", "foo");
+        expected.setVersion(1);
+        if (useRfc6265) {
+            expected.setDomain("\"www.example.org\"");
+            expected.setPath("\"/\"");
+        } else {
+            // The legacy processor removes the quotes for domain and path
+            expected.setDomain("www.example.org");
+            expected.setPath("/");
+        }
+
+        test(useRfc6265, "$Version=\"1\"; userId=\"foo\";$Path=\"/\";$Domain=\"www.example.org\"",
+                expected);
+    }
+
+
     private void test(boolean useRfc6265, String header, Cookie... expected) {
         MimeHeaders mimeHeaders = new MimeHeaders();
         ServerCookies serverCookies = new ServerCookies(4);
diff --git a/test/org/apache/tomcat/util/net/localhost-cert.pem b/test/org/apache/tomcat/util/net/localhost-cert.pem
index 2fb45961..1d567ca6 100644
--- a/test/org/apache/tomcat/util/net/localhost-cert.pem
+++ b/test/org/apache/tomcat/util/net/localhost-cert.pem
@@ -1,79 +1,79 @@
 Certificate:
     Data:
         Version: 3 (0x2)
-        Serial Number: 4102 (0x1006)
-    Signature Algorithm: sha1WithRSAEncryption
+        Serial Number: 4109 (0x100d)
+    Signature Algorithm: sha256WithRSAEncryption
         Issuer: C=US, CN=ca-test.tomcat.apache.org
         Validity
-            Not Before: Feb 28 16:57:14 2015 GMT
-            Not After : Feb 27 16:57:14 2017 GMT
+            Not Before: Feb 27 23:25:29 2017 GMT
+            Not After : Feb 27 23:25:29 2019 GMT
         Subject: C=US, CN=localhost
         Subject Public Key Info:
             Public Key Algorithm: rsaEncryption
                 Public-Key: (2048 bit)
                 Modulus:
-                    00:e7:6f:79:3f:18:87:91:dd:27:98:34:24:79:58:
-                    47:f9:c2:69:2b:d8:5b:c0:e0:bb:4a:57:d6:00:b5:
-                    bb:6a:b0:66:84:5c:b8:f0:12:0a:27:27:32:9c:82:
-                    2a:2f:0f:69:77:a6:e9:0d:df:64:31:51:c0:41:1e:
-                    dc:d4:74:51:9c:a3:b8:51:13:58:73:ee:21:9c:f9:
-                    63:82:1b:c2:2c:49:c3:09:70:ff:a9:f3:af:a2:0c:
-                    0b:60:2f:6a:db:a5:01:45:3e:34:90:8e:67:69:eb:
-                    45:f3:34:29:85:db:39:8a:99:c2:0f:72:15:21:fd:
-                    54:35:a6:7b:a7:30:cb:1e:4d:3d:32:24:c6:4b:84:
-                    4f:5f:60:ff:64:5e:68:ca:d8:fa:de:98:7d:40:04:
-                    60:b7:ae:50:ec:c8:8c:ae:dd:94:81:41:18:5b:03:
-                    63:0f:2b:02:63:0a:95:6a:ed:7e:68:e6:b6:d5:56:
-                    e9:4e:60:ea:1d:95:58:33:be:a2:12:55:cb:7f:9c:
-                    c4:97:0b:db:c0:94:09:2a:b3:9f:e1:6b:78:0d:63:
-                    1a:41:d5:6b:db:d8:48:59:04:88:d1:11:d5:e7:45:
-                    28:0e:7c:1b:78:75:20:7d:ff:7f:e1:d6:ea:e4:c5:
-                    51:77:41:42:30:4b:ff:29:33:3d:89:58:94:69:5b:
-                    70:27
+                    00:ba:d6:b2:32:de:10:53:1f:5d:af:da:d4:3f:64:
+                    b3:22:37:fd:4e:16:a3:f0:d6:9e:6e:d3:ee:47:ec:
+                    15:b4:b3:0d:80:bf:fc:21:96:8b:1d:40:16:6d:89:
+                    35:03:8a:45:8c:c6:6e:2b:66:67:0f:1c:19:cf:62:
+                    d5:e6:08:48:a8:df:10:da:4c:47:79:7c:02:97:54:
+                    f9:a8:e9:59:50:33:cd:a0:72:fd:e1:e7:5e:3a:43:
+                    5c:ff:0c:69:9e:f6:c2:86:71:07:a5:eb:b5:c7:61:
+                    f9:e9:fe:3f:26:55:2c:f4:04:7c:c0:bd:cd:2b:88:
+                    9c:69:4d:ce:3c:1e:ad:2e:18:96:aa:a0:eb:72:2b:
+                    95:99:47:16:90:b5:59:ed:f1:78:cc:8b:01:33:40:
+                    c4:e9:b0:3f:ec:89:04:13:5c:9b:22:01:cc:25:cf:
+                    40:c1:40:fa:04:a0:b9:b7:f7:d8:73:91:7f:b8:7e:
+                    e9:82:20:1f:e9:9c:89:25:28:b5:fa:6f:b7:4a:88:
+                    28:68:59:d5:30:52:f9:e4:5b:a6:b4:f8:e4:ed:2f:
+                    03:d8:50:61:9a:53:86:1f:ad:aa:0d:5f:f8:52:b5:
+                    27:dd:05:82:25:13:a0:d0:10:3c:dd:c0:70:15:24:
+                    63:89:22:0e:f0:5a:9a:fa:b0:75:56:06:aa:7f:b0:
+                    f7:9b
                 Exponent: 65537 (0x10001)
         X509v3 extensions:
-            X509v3 Basic Constraints:
+            X509v3 Basic Constraints: 
                 CA:FALSE
-            Netscape Comment:
+            Netscape Comment: 
                 OpenSSL Generated Certificate
-            X509v3 Subject Key Identifier:
-                30:DB:AB:70:94:34:CA:FD:75:46:AB:CE:E2:4A:A9:9E:74:BC:69:BB
-            X509v3 Authority Key Identifier:
+            X509v3 Subject Key Identifier: 
+                0B:37:2F:D6:48:9C:11:2F:28:AE:DC:47:E6:5E:3A:1D:24:12:0F:1A
+            X509v3 Authority Key Identifier: 
                 keyid:B0:3B:BC:C9:FA:28:5F:3E:04:1F:9B:6C:C7:8B:68:D8:01:B0:F8:3D
 
-    Signature Algorithm: sha1WithRSAEncryption
-         ac:e9:89:a0:fd:83:a7:aa:39:0b:08:f2:89:bc:64:e4:fa:3f:
-         7d:7a:5e:6d:79:98:34:31:19:ec:fb:e3:07:2b:ff:ab:2f:58:
-         7f:49:33:ca:d1:bb:36:9c:bd:3d:e2:3b:39:e9:a9:c2:b7:9e:
-         58:7d:5c:f4:9f:02:80:0b:e2:e2:d8:b8:3a:c0:76:c7:3b:33:
-         29:2a:61:02:ac:e0:23:aa:3e:a7:0d:0a:e9:8b:2d:4d:2a:ed:
-         59:0c:05:2d:40:86:ed:63:ad:fd:3c:a0:5e:4e:77:a6:f5:fe:
-         16:19:e5:bf:66:2f:c0:a3:21:25:65:a8:30:0b:25:9e:b4:67:
-         ad:9d:7a:33:c2:c7:c0:18:80:ef:f0:ea:1f:33:6b:f5:d6:b6:
-         7c:47:8d:99:b5:be:77:cd:61:ba:27:11:a0:8e:19:0f:8b:2d:
-         3d:70:ac:44:b3:f7:f5:a1:a7:a9:36:93:89:e4:63:cc:89:50:
-         ea:cc:c0:5a:c1:a7:41:7b:2f:64:c3:1e:e2:7f:62:72:3a:a1:
-         d5:9f:8d:83:bf:f4:10:5f:3b:e3:48:fd:2c:7c:55:7f:81:e2:
-         e3:2f:95:53:67:20:40:97:2a:cf:cf:f2:e0:13:0d:02:fe:9f:
-         43:93:01:55:22:5b:d9:b6:fd:a6:55:6c:c8:68:dc:3c:73:e7:
-         29:14:78:29
+    Signature Algorithm: sha256WithRSAEncryption
+         3b:0a:ad:f2:27:26:d4:db:bc:97:e7:4e:52:8b:6c:08:4d:7b:
+         e7:66:ec:81:0b:0c:04:f8:b9:92:35:12:c9:b9:ed:d2:5e:b7:
+         ac:89:67:72:7e:2b:4f:5b:e3:3a:d1:09:fe:e8:cf:33:ac:a5:
+         84:95:7f:48:4d:af:59:87:0b:4c:6f:6a:bf:6b:07:af:33:13:
+         19:fd:70:0d:fc:1c:92:04:be:05:b9:96:46:d5:82:a4:f8:3b:
+         b0:11:2d:f0:19:25:ba:d6:ce:1c:7a:17:76:c6:80:d2:73:a0:
+         1a:01:48:d6:0b:12:a9:3f:50:66:81:1b:e9:9f:1e:5b:6f:d1:
+         19:12:14:70:d3:de:4c:ab:d3:83:d6:e5:4f:bb:b3:e5:c6:87:
+         16:47:f7:59:4d:9d:52:9d:00:f0:24:7a:1e:6e:14:01:0d:07:
+         0c:b6:f7:4e:c0:40:77:65:fd:ac:c7:aa:73:77:f0:44:b1:30:
+         ad:65:83:1a:cc:bd:fa:9d:80:29:61:e9:b3:26:e8:3b:55:c7:
+         12:79:3e:4d:31:f1:21:d0:4e:5f:1f:73:c3:9f:ce:f9:6c:7e:
+         8e:11:10:8e:f6:60:d2:11:ae:0f:24:6e:10:71:42:05:ed:ea:
+         4b:41:86:86:84:26:74:ed:46:81:48:34:16:40:e6:df:64:c9:
+         c2:7d:6b:1b
 -----BEGIN CERTIFICATE-----
-MIIDSTCCAjGgAwIBAgICEAYwDQYJKoZIhvcNAQEFBQAwMTELMAkGA1UEBhMCVVMx
-IjAgBgNVBAMTGWNhLXRlc3QudG9tY2F0LmFwYWNoZS5vcmcwHhcNMTUwMjI4MTY1
-NzE0WhcNMTcwMjI3MTY1NzE0WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAxMJbG9j
-YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5295PxiHkd0n
-mDQkeVhH+cJpK9hbwOC7SlfWALW7arBmhFy48BIKJycynIIqLw9pd6bpDd9kMVHA
-QR7c1HRRnKO4URNYc+4hnPljghvCLEnDCXD/qfOvogwLYC9q26UBRT40kI5naetF
-8zQphds5ipnCD3IVIf1UNaZ7pzDLHk09MiTGS4RPX2D/ZF5oytj63ph9QARgt65Q
-7MiMrt2UgUEYWwNjDysCYwqVau1+aOa21VbpTmDqHZVYM76iElXLf5zElwvbwJQJ
-KrOf4Wt4DWMaQdVr29hIWQSI0RHV50UoDnwbeHUgff9/4dbq5MVRd0FCMEv/KTM9
-iViUaVtwJwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu
-U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUMNurcJQ0yv11RqvO
-4kqpnnS8abswHwYDVR0jBBgwFoAUsDu8yfooXz4EH5tsx4to2AGw+D0wDQYJKoZI
-hvcNAQEFBQADggEBAKzpiaD9g6eqOQsI8om8ZOT6P316Xm15mDQxGez74wcr/6sv
-WH9JM8rRuzacvT3iOznpqcK3nlh9XPSfAoAL4uLYuDrAdsc7MykqYQKs4COqPqcN
-CumLLU0q7VkMBS1Ahu1jrf08oF5Od6b1/hYZ5b9mL8CjISVlqDALJZ60Z62dejPC
-x8AYgO/w6h8za/XWtnxHjZm1vnfNYbonEaCOGQ+LLT1wrESz9/Whp6k2k4nkY8yJ
-UOrMwFrBp0F7L2TDHuJ/YnI6odWfjYO/9BBfO+NI/Sx8VX+B4uMvlVNnIECXKs/P
-8uATDQL+n0OTAVUiW9m2/aZVbMho3Dxz5ykUeCk=
+MIIDSTCCAjGgAwIBAgICEA0wDQYJKoZIhvcNAQELBQAwMTELMAkGA1UEBhMCVVMx
+IjAgBgNVBAMMGWNhLXRlc3QudG9tY2F0LmFwYWNoZS5vcmcwHhcNMTcwMjI3MjMy
+NTI5WhcNMTkwMjI3MjMyNTI5WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAxMJbG9j
+YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAutayMt4QUx9d
+r9rUP2SzIjf9Thaj8NaebtPuR+wVtLMNgL/8IZaLHUAWbYk1A4pFjMZuK2ZnDxwZ
+z2LV5ghIqN8Q2kxHeXwCl1T5qOlZUDPNoHL94edeOkNc/wxpnvbChnEHpeu1x2H5
+6f4/JlUs9AR8wL3NK4icaU3OPB6tLhiWqqDrciuVmUcWkLVZ7fF4zIsBM0DE6bA/
+7IkEE1ybIgHMJc9AwUD6BKC5t/fYc5F/uH7pgiAf6ZyJJSi1+m+3SogoaFnVMFL5
+5FumtPjk7S8D2FBhmlOGH62qDV/4UrUn3QWCJROg0BA83cBwFSRjiSIO8Fqa+rB1
+Vgaqf7D3mwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu
+U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUCzcv1kicES8ortxH
+5l46HSQSDxowHwYDVR0jBBgwFoAUsDu8yfooXz4EH5tsx4to2AGw+D0wDQYJKoZI
+hvcNAQELBQADggEBADsKrfInJtTbvJfnTlKLbAhNe+dm7IELDAT4uZI1Esm57dJe
+t6yJZ3J+K09b4zrRCf7ozzOspYSVf0hNr1mHC0xvar9rB68zExn9cA38HJIEvgW5
+lkbVgqT4O7ARLfAZJbrWzhx6F3bGgNJzoBoBSNYLEqk/UGaBG+mfHltv0RkSFHDT
+3kyr04PW5U+7s+XGhxZH91lNnVKdAPAkeh5uFAENBwy2907AQHdl/azHqnN38ESx
+MK1lgxrMvfqdgClh6bMm6DtVxxJ5Pk0x8SHQTl8fc8Ofzvlsfo4REI72YNIRrg8k
+bhBxQgXt6ktBhoaEJnTtRoFINBZA5t9kycJ9axs=
 -----END CERTIFICATE-----
diff --git a/test/org/apache/tomcat/util/net/localhost-copy1.jks b/test/org/apache/tomcat/util/net/localhost-copy1.jks
index 20b758a1..14716675 100644
Binary files a/test/org/apache/tomcat/util/net/localhost-copy1.jks and b/test/org/apache/tomcat/util/net/localhost-copy1.jks differ
diff --git a/test/org/apache/tomcat/util/net/localhost-key.pem b/test/org/apache/tomcat/util/net/localhost-key.pem
index 911476d1..527f77cf 100644
--- a/test/org/apache/tomcat/util/net/localhost-key.pem
+++ b/test/org/apache/tomcat/util/net/localhost-key.pem
@@ -1,28 +1,28 @@
 -----BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDnb3k/GIeR3SeY
-NCR5WEf5wmkr2FvA4LtKV9YAtbtqsGaEXLjwEgonJzKcgiovD2l3pukN32QxUcBB
-HtzUdFGco7hRE1hz7iGc+WOCG8IsScMJcP+p86+iDAtgL2rbpQFFPjSQjmdp60Xz
-NCmF2zmKmcIPchUh/VQ1pnunMMseTT0yJMZLhE9fYP9kXmjK2PremH1ABGC3rlDs
-yIyu3ZSBQRhbA2MPKwJjCpVq7X5o5rbVVulOYOodlVgzvqISVct/nMSXC9vAlAkq
-s5/ha3gNYxpB1Wvb2EhZBIjREdXnRSgOfBt4dSB9/3/h1urkxVF3QUIwS/8pMz2J
-WJRpW3AnAgMBAAECggEBALfaj6h3NSPEW7MHIT6gyjT4o5IoQ+O65C6QDrrrpCKC
-Vj6qZmSZACXQdt1sblSKWs+p8hSKNc8UUbWp7eZ9LWRRj5gg1TDrqRpJ6CoxIRUL
-1/wFH6WEqC8EgHj90lcBAzxdyWZZKoAzXpNxCdeDq3eW5Fpe17jzxdUFF9Be6816
-LbxTlrMisLp0u39v1GnE/vd2nemKUWY7uNSRRrtTi8mjAmXcnogxBmAyN7wjqVHt
-KKBYkyTRPCh8K6R+t/dqbZsv1k8fdS1/csXoiU2nJ86pdbSHOQ7aZMWXTZmAE5wb
-G4Oe9X61Hg5xgTDO/nEd8q2Sl6mMSx9L5IjdTm+WVRkCgYEA/CaXCzlUPUtVbkih
-bkDCQbYVarrRo90PskL2mgUQDUBgFOzSQXrJ6qEEYXy5PdOBhiaK3385wSXBX+nF
-7LOANzrgN9k+6Vt3fe20HrYltoc5e0lxvk4rdNSyrpe+E5bz8LtASlMBj2o6W6Md
-NkCAEKRp7wC2gb3MPeWhCpiJrLsCgYEA6vfthWHjL+yr6hFro/IsHnQdYSn4QPgn
-+WrOg6gGwhS5ZqJLK5jB95RFvgGhNBxh8nMCP0VnJYmM1pQtzR/YE7PU7SlRyAd/
-DVkyO8NRQTzA6z/mPoeGx4dtTwaye10c7KZxmZCXq1tB4D6gTDJUQF5gt8tbx4C8
-1mul1xQ0aYUCgYAFovMzbAenCx0QxwzcwxPUljZqWVzAVfu412hdzwkp0quTLCwT
-DKSg2xKW/0vAxw6ZKhlmn5hx6d8lvrsO7IBMO8OxW+jdHI9SQFMLcLTtHJ67U8v4
-HhU4mlyYLIoyM+imE/l+79YUF6LQU5gek1iJhrNbhV+PDOgY5h4wd3J0awKBgBfF
-OSwzOO6SPNoTJRaS20/BY29+9XRtJm4fFgFPsE9WFWOCq6Qfcg//2gZc19gTvvzu
-EZ4hAUxU3AChQPjtbcigerv7YCCiUYIiMejF26SD5uhlsH9G6qWo17AU911vkAuI
-0xk7/XwCYWm0LDdJKCjS42n0krZeGbx/a2mUy7CZAoGAGXq2r3INg0xiZa8mSeJB
-MeZswgGKxN7MHlKKcpELEWg97ev4NvTq4T+PEl+1miRtlyzcFgMXKe3oBU0Ru7Qu
-O2u3kfLTOPGHmo+KpFNJHE8g+PwoOheNis2JZkd3T9fX7JRRYCiBeW4nMYzP2yGS
-nlaUqPewhh5/1fRCmF36TyU=
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC61rIy3hBTH12v
+2tQ/ZLMiN/1OFqPw1p5u0+5H7BW0sw2Av/whlosdQBZtiTUDikWMxm4rZmcPHBnP
+YtXmCEio3xDaTEd5fAKXVPmo6VlQM82gcv3h5146Q1z/DGme9sKGcQel67XHYfnp
+/j8mVSz0BHzAvc0riJxpTc48Hq0uGJaqoOtyK5WZRxaQtVnt8XjMiwEzQMTpsD/s
+iQQTXJsiAcwlz0DBQPoEoLm399hzkX+4fumCIB/pnIklKLX6b7dKiChoWdUwUvnk
+W6a0+OTtLwPYUGGaU4YfraoNX/hStSfdBYIlE6DQEDzdwHAVJGOJIg7wWpr6sHVW
+Bqp/sPebAgMBAAECggEANW/iV5oboSp/aSR1QAxVD5xj3kmrad54QcprhDaJnrz3
+R7OhMRoVf7AsufdcKj8J1VGLgO4w26l12FK0Sq5j2aiy8TyP1LcbJp0vKphS6xVl
+4lYBNvMtiAxsNKYgL070I+9/+HyJ6RSkncAR36zMxp3h8F5Yp/LSiWuvaJJ9hYkU
+sH+svdnh3nvgNAnKHMEMRs+++CFZ5QL1Iv4WANTRyO3+ylDrwmWlNjzcwae1/789
+EaP5RX08E/yGJFwCFbpK04mZjwQZV3PS5V+TPCnVXLyAGFhBY8a5Faya9uNLItJ/
+oRJhqqMiUgUk3E6dpbSkM6SL0YHMv6jQuGXISDblgQKBgQD4ZBONARsaedr5jKPH
+HOCBSq2D2ROiv43DDl9a8LLjEHTwt5vjB7jYI5G1vGXXNOSYxNtBvtl6Jhzralmx
+TA282lV0Kr2xidZjpsbJb0Nwvw/bRS5GzwLv3eHt675zl9R2GJpqxnTxT2qyxHql
+RUJrCOwi6dmRMnCfBR72OccNYwKBgQDAj+qbVYybIaNq660T+9qKW1mkJE5SQxbg
+buPwK5V4Sl6ItxUEhZ0BtAO4gM76Qm2S6uJU7CkEJidNvPbrhv0fSgFbgwXmTlnr
+OtDzLs3MX7N4RQGhWXZvMMhWzFJGsfNYO7ReUjUuLPWkvR4ag5Y58dUfn5y0icj0
+kKAE73++aQKBgEzYESBXTj5DopX/h4+LBH2bT9DxcFyyLDZDFQb6Xi1XIwZxlz2G
+Xw4m+uYhcdRBXdNRW4+cboWWku3VygfKwplBZpx4uJTbUsMjjm41CMUxpsMsROPN
+GViB6WqEuzfhqe4bMHKGERmyewzxMgw9QA4DBaLLe+6qjyLzH6AHQeiHAoGANvWc
+f0M2g1xhePXVC1wZrt2NwzR9iisNz3a25eh6m7+WJa0jeYvtGPxZB4L0ECd/gKw7
+dwOEXWPKWfyx+N0pR5Hmu0i1GVuJfILn+lCEem1iGtSPN48CJ5Ajzeo5HWZSzv8T
+ZsxN02UPozo66lRePyGPs5vprPmaensAwQfeTOkCgYBaaJ6LC9slX4vEw28fO3xU
+4UbRKik4A3EbUlklXBXPY1eCDzI9r5IuiXD3aM/V3qeO7PGBUwW75Glu+fmmKXP8
+5R8beykoudKtiRTLIaEZHRfTNnaGmThDI0RrjkmKHt7AC5MBDTwajVL89T1H6Y0H
+tlo5AYv9klQDtpadOUZbhg==
 -----END PRIVATE KEY-----
diff --git a/test/org/apache/tomcat/util/net/localhost.jks b/test/org/apache/tomcat/util/net/localhost.jks
index d5ce763b..ac2cc2c8 100644
Binary files a/test/org/apache/tomcat/util/net/localhost.jks and b/test/org/apache/tomcat/util/net/localhost.jks differ
diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java
index 4d9d8367..74dd1be2 100644
--- a/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java
+++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java
@@ -77,6 +77,15 @@ public class TestCipher {
         //         EC alias. Use aRSA.
         // OpenSSL 1.0.0 onwards does not include eNULL in all.
         Set<String> availableCipherSuites = TesterOpenSSL.getOpenSSLCiphersAsSet("ALL:eNULL:aRSA");
+        // TODO
+        // Temporary removal of the TLS1.3 ciphers until the spec is final or an
+        // appropriate option is added to the ciphers command
+        availableCipherSuites.remove("TLS13-AES-128-GCM-SHA256+TLSv1.3");
+        availableCipherSuites.remove("TLS13-AES-128-CCM-8-SHA256+TLSv1.3");
+        availableCipherSuites.remove("TLS13-AES-128-CCM-SHA256+TLSv1.3");
+        availableCipherSuites.remove("TLS13-CHACHA20-POLY1305-SHA256+TLSv1.3");
+        availableCipherSuites.remove("TLS13-AES-256-GCM-SHA384+TLSv1.3");
+
         Set<String> expectedCipherSuites = new HashSet<>();
         for (Cipher cipher : Cipher.values()) {
             if (TesterOpenSSL.OPENSSL_UNIMPLEMENTED_CIPHERS.contains(cipher)) {
@@ -406,7 +415,12 @@ public class TestCipher {
                     "RC4-MD5+SSLv2",
                     "RSA-PSK-CAMELLIA128-SHA256+TLSv1",
                     "RSA-PSK-CAMELLIA256-SHA384+TLSv1",
-                    "RSA-PSK-CHACHA20-POLY1305+TLSv1.2")));
+                    "RSA-PSK-CHACHA20-POLY1305+TLSv1.2",
+                    "TLS13-AES-256-GCM-SHA384+TLSv1.3",
+                    "TLS13-CHACHA20-POLY1305-SHA256+TLSv1.3",
+                    "TLS13-AES-128-GCM-SHA256+TLSv1.3",
+                    "TLS13-AES-128-CCM-8-SHA256+TLSv1.3",
+                    "TLS13-AES-128-CCM-SHA256+TLSv1.3")));
 
 
     /**
@@ -692,7 +706,12 @@ public class TestCipher {
                     "SRP-DSS-AES-256-CBC-SHA+SSLv3",
                     "SRP-RSA-3DES-EDE-CBC-SHA+SSLv3",
                     "SRP-RSA-AES-128-CBC-SHA+SSLv3",
-                    "SRP-RSA-AES-256-CBC-SHA+SSLv3")));
+                    "SRP-RSA-AES-256-CBC-SHA+SSLv3",
+                    "TLS13-AES-256-GCM-SHA384+TLSv1.3",
+                    "TLS13-CHACHA20-POLY1305-SHA256+TLSv1.3",
+                    "TLS13-AES-128-GCM-SHA256+TLSv1.3",
+                    "TLS13-AES-128-CCM-8-SHA256+TLSv1.3",
+                    "TLS13-AES-128-CCM-SHA256+TLSv1.3")));
 
 
     private static JsseImpl ORACLE_JSSE_CIPHER_IMPL = new JsseImpl("Oracle",
diff --git a/test/org/apache/tomcat/util/scan/TestAbstractInputStreamJar.java b/test/org/apache/tomcat/util/scan/TestAbstractInputStreamJar.java
new file mode 100644
index 00000000..b6f96e2b
--- /dev/null
+++ b/test/org/apache/tomcat/util/scan/TestAbstractInputStreamJar.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.scan;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.catalina.util.IOTools;
+import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
+import org.apache.tomcat.Jar;
+
+public class TestAbstractInputStreamJar {
+
+    @Before
+    public void register() {
+        TomcatURLStreamHandlerFactory.register();
+    }
+
+
+    @Test
+    public void testNestedJarGetInputStream() throws Exception {
+        File f = new File("test/webresources/war-url-connection.war");
+        StringBuilder sb = new StringBuilder("war:");
+        sb.append(f.toURI().toURL());
+        sb.append("*/WEB-INF/lib/test.jar");
+
+        Jar jar = JarFactory.newInstance(new URL(sb.toString()));
+
+        InputStream is1 = jar.getInputStream("META-INF/resources/index.html");
+        ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
+        IOTools.flow(is1, baos1);
+
+        InputStream is2 = jar.getInputStream("META-INF/resources/index.html");
+        ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+        IOTools.flow(is2, baos2);
+
+        Assert.assertArrayEquals(baos1.toByteArray(), baos2.toByteArray());
+    }
+}
diff --git a/test/webapp/WEB-INF/web.xml b/test/webapp/WEB-INF/web.xml
index f29d2702..ff011515 100644
--- a/test/webapp/WEB-INF/web.xml
+++ b/test/webapp/WEB-INF/web.xml
@@ -129,6 +129,16 @@
       <include-coda>/bug5nnnn/bug55262-coda.jspf</include-coda>
       <default-content-type>text/plain</default-content-type>
     </jsp-property-group>
+    <jsp-property-group>
+      <url-pattern>/jsp/encoding/bug60769a.jspx</url-pattern>
+      <page-encoding>UTF-8</page-encoding>
+      <is-xml>true</is-xml>
+    </jsp-property-group>
+    <jsp-property-group>
+      <url-pattern>/jsp/encoding/bug60769b.jspx</url-pattern>
+      <page-encoding>ISO-8859-1</page-encoding>
+      <is-xml>true</is-xml>
+    </jsp-property-group>
   </jsp-config>
 
   <servlet>
diff --git a/test/webapp/bug5nnnn/bug58178c.jsp b/test/webapp/bug5nnnn/bug58178c.jsp
new file mode 100644
index 00000000..095f2d41
--- /dev/null
+++ b/test/webapp/bug5nnnn/bug58178c.jsp
@@ -0,0 +1,65 @@
+<%--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+--%>
+<%@ taglib prefix="tags" tagdir="/WEB-INF/tags" %>
+<%!
+static class TestListener implements javax.el.ELContextListener {
+
+    private int jspCount = 0;
+    private int tagCount = 0;
+
+    @Override
+    public void contextCreated(javax.el.ELContextEvent event) {
+        javax.el.ELContext elContext = event.getELContext();
+        if (elContext instanceof org.apache.jasper.el.ELContextImpl) {
+            jspCount++;
+        } else {
+            tagCount++;
+        }
+        (new Exception()).printStackTrace();
+    }
+
+    public int getJspCount() {
+        return jspCount;
+    }
+
+    public int getTagCount() {
+        return tagCount;
+    }
+}
+
+static TestListener listener = new TestListener();
+
+private boolean listenerAdded;
+%>
+<%
+synchronized(this) {
+    if (!listenerAdded) {
+        JspFactory factory = JspFactory.getDefaultFactory();
+        JspApplicationContext jspApplicationContext = factory.getJspApplicationContext(application);
+        jspApplicationContext.addELContextListener(listener);
+        listenerAdded = true;
+    }
+}
+%>
+<html>
+<body>
+<p>JSP count: <%= listener.getJspCount() %></p>
+<p>Tag count: <%= listener.getTagCount() %></p>
+<tags:bug58178b />
+<p>JSP count: <%= listener.getJspCount() %></p>
+<p>Tag count: <%= listener.getTagCount() %></p>
+</html>
\ No newline at end of file
diff --git a/test/webapp/jsp/encoding/README.txt b/test/webapp/jsp/encoding/README.txt
index 1cc4332a..0c4c10fc 100644
--- a/test/webapp/jsp/encoding/README.txt
+++ b/test/webapp/jsp/encoding/README.txt
@@ -17,7 +17,7 @@
 
 A number of the test files in this directory specify conflicting encoding
 in the BOM and and in the XML prolog. The rules for determining the actual
-encoding are as follows:
+encoding used in the file are as follows:
 
 1. If there is a BOM, use the encoding defined by the BOM.
 
diff --git a/test/webapp/jsp/encoding/bug60769a.jspx b/test/webapp/jsp/encoding/bug60769a.jspx
new file mode 100644
index 00000000..6cea958f
--- /dev/null
+++ b/test/webapp/jsp/encoding/bug60769a.jspx
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"; version="2.3">
+  <jsp:directive.page contentType="text/plain" />
+  <jsp:text>OK</jsp:text>
+</jsp:root>
\ No newline at end of file
diff --git a/test/webapp/jsp/encoding/bug60769b.jspx b/test/webapp/jsp/encoding/bug60769b.jspx
new file mode 100644
index 00000000..6cea958f
--- /dev/null
+++ b/test/webapp/jsp/encoding/bug60769b.jspx
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"; version="2.3">
+  <jsp:directive.page contentType="text/plain" />
+  <jsp:text>OK</jsp:text>
+</jsp:root>
\ No newline at end of file
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 3c530db4..ac98485c 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -44,10 +44,284 @@
   They eventually become mixed with the numbered issues. (I.e., numbered
   issues do not "pop up" wrt. others).
 -->
-<section name="Tomcat 8.5.11 (markt)">
+<section name="Tomcat 8.5.12 (markt)">
+  <subsection name="Catalina">
+    <changelog>
+      <fix>
+        <bug>60469</bug>: Refactor <code>RealmBase</code> for better code re-use
+        when implementing Realms that use a custom <code>Principal</code>.
+        (markt)
+      </fix>
+      <fix>
+        <bug>60490</bug>: Various formatting and layout improvements for the
+        <code>ErrorReportValve</code>. Patch provided by Michael Osipov. (markt)
+      </fix>
+      <update>
+        <bug>60596</bug>: Improve performance of DefaultServlet when sendfile
+        feature is disabled on connector. (kkolinko)
+      </update>
+      <scode>
+        Make it easier for sub-classes of <code>Tomcat</code> to modify the
+        default web.xml settings by over-riding
+        <code>getDefaultWebXmlListener()</code>. Patch provided by Aaron
+        Anderson. (markt)
+      </scode>
+      <fix>
+        Reduce the contention in the default <code>InstanceManager</code>
+        implementation when multiple threads are managing objects and need to
+        reference the annotation cache. (markt)
+      </fix>
+      <scode>
+        <bug>60674</bug>: Remove <code>final</code> marker from
+        <code>CorsFilter</code> to enable sub-classing. (markt)
+      </scode>
+      <fix>
+        <bug>60683</bug>: Security manager failure causing NPEs when doing IO
+        on some JVMs. (coty)
+      </fix>
+      <fix>
+        <bug>60688</bug>: Update the internal fork of Apache Commons BCEL to
+        r1782855 to add early access Java 9 support to the annotation scanning
+        code. (markt)
+      </fix>
+      <fix>
+        <bug>60694</bug>: Prevent NPE during authentication when no JASPIC
+        <code>AuthConfigFactory</code> is available. (markt)
+      </fix>
+      <fix>
+        <bug>60697</bug>: When HTTP TRACE requests are disabled on the
+        Connector, ensure that the HTTP OPTIONS response from custom servlets
+        does not include TRACE in the returned Allow header. (markt)
+      </fix>
+      <fix>
+        <bug>60718</bug>: Improve error handling for asynchronous processing and
+        correct a number of cases where the <code>requestDestroyed()</code>
+        event was not being fired and an entry wasn't being made in the access
+        logs. (markt)
+      </fix>
+      <fix>
+        <bug>60720</bug>: Replace "WWW-Authenticate" literal with static final
+        AUTH_HEADER_NAME in SpnegoAuthenticator. Patch provided by Michael
+        Osipov. (violetagg)
+      </fix>
+      <fix>
+        The default JASPIC <code>AuthConfigFactory</code> now correctly notifies
+        registered <code>RegistrationListener</code>s when a new
+        <code>AuthConfigProvider</code> is registered. (markt)
+      </fix>
+      <scode>
+        Improve the performance of <code>AuthenticatorBase</code> when there is
+        no JASPIC configuration available. (violetagg)
+      </scode>
+      <fix>
+        When HTTP TRACE requests are disabled on the Connector, ensure that the
+        HTTP OPTIONS response from custom the WebDAV servlet does not include
+        TRACE in the returned Allow header. (markt)
+      </fix>
+      <fix>
+        <bug>60722</bug>: Take account of the
+        <strong>dispatchersUseEncodedPaths</strong> setting on the current
+        <strong>Context</strong> when generating paths for dispatches triggered
+        by <code>AsyncContext.dispatch()</code>. (markt)
+      </fix>
+      <fix>
+        <bug>60728</bug>: Make the separator Tomcat uses in the Tomcat specific
+        <code>war:file:...</code> URL protocol customizable via a system
+        property. The separator is equivalent to the use of the <code>!</code>
+        character in <code>jar:file:...</code> URLs. The default separator of
+        <code>*</code> remains unchanged. (markt)
+      </fix>
+      <update>
+        Update the <code>org.apache.catalina.servlet4preview</code> package that
+        can be used to gain early access to Servlet 4.0 features to align with
+        the latest proposals from the Servlet 4.0 expert group. This includes
+        updates to the new Servlet mapping API, new methods on the
+        <code>ServletContext</code> to make the available API more equivalent to
+        the deployment descriptor, updates to the HTTP push API and the ability
+        to set default request and response character encoding per web
+        application. Note that the Servlet 4.0 API is still a work in progress
+        and further changes are likely. (markt)
+      </update>
+      <fix>
+        <bug>60798</bug>: Correct a bug in the handling of JARs in unpacked WARs
+        that meant multiple attempts to read the same entry from a JAR in
+        succession would fail for the second and subsequent attempts. (markt)
+      </fix>
+      <fix>
+        <bug>60808</bug>: Ensure that the <code>Map</code> returned by
+        <code>ServletRequest.getParameterMap()</code> is fully immutable. Based
+        on a patch provided by woosan. (markt)
+      </fix>
+      <fix>
+        <bug>60824</bug>: Correctly cache the <code>Subject</code> in the
+        session - if there is a session - when running under a
+        <code>SecurityManager</code>. Patch provided by Jan Engehausen. (markt)
+      </fix>
+      <fix>
+        Ensure request and response facades are used when firing application
+        listeners. (markt/remm)
+      </fix>
+    </changelog>
+  </subsection>
+  <subsection name="Coyote">
+    <changelog>
+      <fix>
+        Improve handling of case when an HTTP/2 client sends more data that is
+        subject to flow control than the current window size allows. (markt)
+      </fix>
+      <fix>
+        Improve NIO2 look-ahead parsing of TLS client hello for SNI with large
+        client hello messages. (markt)
+      </fix>
+      <fix>
+        <bug>59807</bug>: Provide a better error message when there is no
+        <strong>SSLHostConfig</strong> defined with a <code>hostName</code> that
+        matches the <code>defaultSSLHostConfigName</code> for the associated
+        <strong>Connector</strong>. (markt)
+      </fix>
+      <fix>
+        <bug>60627</bug>: Modify the <code>Rfc6265CookieProcessor</code> so that
+        in addition to cookie headers that start with an explicit RFC 2109
+        <code>$Version=1</code>, cookies that start with <code>$Version=0</code>
+        are also parsed as RFC 2109 cookies. (markt)
+      </fix>
+      <fix>
+        Include the value of <code>SslHostConfig.truststoreAlgorithm</code> when
+        warning that the algorithm does not support the
+        <code>certificateVerificationDepth</code> configuration option. (markt)
+      </fix>
+      <fix>
+        Ensure that executor thread pools used with connectors pre-start the
+        configured minimum number of idle threads. (markt)
+      </fix>
+      <add>
+        <bug>60594</bug>: Allow some invalid characters that were recently
+        restricted to be processed in requests by using the system property
+        <code>tomcat.util.http.parser.HttpParser.requestTargetAllow</code>.
+        (csutherl)
+      </add>
+      <fix>
+        <bug>60716</bug>: Add a new JSSE specific attribute,
+        <code>revocationEnabled</code>, to <code>SSLHostConfig</code> to permit
+        JSSE provider revocation checks to be enabled when no
+        <code>certificateRevocationListFile</code> has been configured. The
+        expectation is that configuration will be performed via a JSSE provider
+        specific mechanisms. (markt)
+      </fix>
+      <fix>
+        Modify the cookie header generated by the
+        <code>Rfc6265CookieProcessor</code> so it always sends an
+        <code>Expires</code> attribute as well as a <code>Max-Age</code>
+        attribute to avoid problems with Microsoft browsers that do not support
+        the <code>Max-Age</code> attribute. (markt)
+      </fix>
+      <fix>
+        <bug>60761</bug>: Expose a protected getter and setter for
+        <code>NioEndpoint.stopLatch</code> to make the class easier to extend.
+        (markt)
+      </fix>
+      <fix>
+        Prevent blocking reads after a stream exception occurs with HTTP/2.
+        (remm)
+      </fix>
+    </changelog>
+  </subsection>
+  <subsection name="Jasper">
+    <changelog>
+      <fix>
+        Follow up to the fix for <bug>58178</bug>. When creating the
+        <code>ELContext</code> for a tag file, ensure that any registered
+        <code>ELContextListener</code>s are fired. (markt)
+      </fix>
+      <fix>
+        Refactor code generated for JSPs to reduce the size of the code required
+        for tags. (markt)
+      </fix>
+      <fix>
+        <bug>60769</bug>: Correct a regression in the XML encoding detection
+        refactoring carried out for 8.5.10 that incorrectly always used the
+        detected BOM encoding in preference to any encoding specified in the
+        prolog. (markt)
+      </fix>
+      <update>
+        Update to the Eclipse JDT Compiler 4.6.1. (markt)
+      </update>
+    </changelog>
+  </subsection>
+  <subsection name="Cluster">
+    <changelog>
+      <add>
+        Make the <code>accessTimeout</code> configurable in
+        <code>BackupManager</code> and <code>ClusterSingleSignOn</code>. The
+        <code>accessTimeout</code> is used as a timeout period for PING in
+        replication map. (kfujino)
+      </add>
+      <fix>
+        <bug>60806</bug>: To avoid <code>ClassNotFoundException</code>, make
+        sure that the web application class loader is passed to
+        <code>ReplicatedContext</code>. (kfujino)
+      </fix>
+    </changelog>
+  </subsection>
+  <subsection name="WebSocket">
+    <changelog>
+      <fix>
+        <bug>60617</bug>: Correctly create a <code>CONNECT</code> request when
+        establishing a WebSocket connection via a proxy. Patch provided by
+        Svetlin Zarev. (markt)
+      </fix>
+    </changelog>
+  </subsection>
+  <subsection name="Tribes">
+    <changelog>
+      <add>
+        Add log message that PING message has received beyond the timeout
+        period. (kfujino)
+      </add>
+      <fix>
+        When a PING message that beyond the time-out period has been received,
+        make sure that valid member is added to the map membership. (kfujino)
+      </fix>
+      <fix>
+        Ensure that <code>NoRpcChannelReply</code> messages are not received on
+        <code>RpcCallback</code>. (kfujino)
+      </fix>
+    </changelog>
+  </subsection>
+  <subsection name="Web Applications">
+    <changelog>
+      <fix>
+        Add Specification and Javadoc references for JASPIC to the Docs
+        application. (csutherl)
+      </fix>
+    </changelog>
+  </subsection>
+  <subsection name="Other">
+    <changelog>
+      <fix>
+        Spelling corrections provided by Josh Soref. (violetagg)
+      </fix>
+      <update>
+        Update the packaged version of the Tomcat Native Library to 1.2.12 to
+        pick up the latest Windows binaries built with OpenSSL 1.0.2k. (violetagg)
+      </update>
+      <add>
+        <bug>60784</bug>: Update all unit tests that test the HTTP status line
+        to check for the required space after the status code. Patch provided by
+        Michael Osipov. (markt)
+      </add>
+      <update>
+        Update the NSIS Installer used to build the Windows installer to version
+        3.01. (markt)
+      </update>
+    </changelog>
+  </subsection>
+</section>
+<section name="Tomcat 8.5.11 (markt)" rtext="2017-01-16">
   <subsection name="Catalina">
     <changelog>
       <add>
+        <bug>60620</bug>:
         Extend the <code>JreMemoryLeakPreventionListener</code> to provide
         protection against <code>ForkJoinPool.commonPool()</code> related memory
         leaks. (markt)
@@ -574,13 +848,6 @@
       </add>
     </changelog>
   </subsection>
-  <subsection name="Jasper">
-    <changelog>
-      <update>
-        Update to the Eclipse JDT Compiler 4.6.1. (markt)
-      </update>
-    </changelog>
-  </subsection>
   <subsection name="Web applications">
     <changelog>
       <add>
diff --git a/webapps/docs/config/cluster-manager.xml b/webapps/docs/config/cluster-manager.xml
index cbcc24a7..7d742cbe 100644
--- a/webapps/docs/config/cluster-manager.xml
+++ b/webapps/docs/config/cluster-manager.xml
@@ -265,6 +265,11 @@
         <code>false</code> unless a <code>SecurityManager</code> is enabled in
         which case the default will be <code>true</code>.</p>
       </attribute>
+      <attribute name="accessTimeout" required="false">
+        The timeout for a ping message. If a remote map does not respond within
+        this timeout period, its regarded as disappeared.
+        Default value is <code>5000</code> milliseconds.
+      </attribute>
     </attributes>
   </subsection>
 </section>
diff --git a/webapps/docs/config/cluster-valve.xml b/webapps/docs/config/cluster-valve.xml
index 9778a268..1579a511 100644
--- a/webapps/docs/config/cluster-valve.xml
+++ b/webapps/docs/config/cluster-valve.xml
@@ -156,6 +156,11 @@
         part of the heartbeat process. If not specified, the default value of
         <code>false</code> is used.</p>
       </attribute>
+      <attribute name="accessTimeout" required="false">
+        The timeout for a ping message. If a remote map does not respond within
+        this timeout period, its regarded as disappeared.
+        Default value is <code>5000</code> milliseconds.
+      </attribute>
     </attributes>
   </subsection>
 </section>
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index 8f439e19..d726c0c1 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -1241,6 +1241,16 @@
       used.</p>
     </attribute>
 
+    <attribute name="revocationEnabled" required="false">
+      <p>JSSE only.</p>
+      <p>Should the JSSE provider enable certificate revocation checks? If
+      <strong>certificateRevocationListFile</strong> is set then this attribute
+      is ignored and revocation checks are always enabled. This attribute is
+      intended to enable revocation checks that have been configured for the
+      current JSSE provider via other means. If not specified, a default of
+      <code>false</code> is used.</p>
+    </attribute>
+
     <attribute name="sessionCacheSize" required="false">
       <p>JSSE only.</p>
       <p>The number of SSL sessions to maintain in the session cache. Use 0 to
diff --git a/webapps/docs/config/systemprops.xml b/webapps/docs/config/systemprops.xml
index d4e5a2df..38a2e097 100644
--- a/webapps/docs/config/systemprops.xml
+++ b/webapps/docs/config/systemprops.xml
@@ -606,6 +606,13 @@
       <p>If not specified, the default value of <code>200</code> will be used.</p>
     </property>
 
+    <property name="org.apache.tomcat.util.buf.UriUtil.WAR_SEPARATOR">
+      <p>The character to use to separate the WAR file and WAR content parts of
+      a WAR URL using the custom WAR scheme provided by Tomcat. This is
+      equivalent to how <code>!</code> is used in JAR URLs.</p>
+      <p>If not specified, the default value of <code>*</code> will be used.</p>
+    </property>
+
     <property name="tomcat.util.buf.StringCache.maxStringSize">
       <p>The maximum length of String that will be cached.</p>
       <p>If not specified, the default value of <code>128</code> will be used.</p>
@@ -639,6 +646,16 @@
       <p>If not specified, the default value of <code>3</code> will be used.</p>
     </property>
 
+    <property name="tomcat.util.http.parser.HttpParser.requestTargetAllow">
+      <p>A string comprised of characters the server should allow even when they are not encoded.
+      These characters would normally result in a 400 status.</p>
+      <p>The acceptable characters for this property are: <code>|</code>, <code>{</code>
+      , and <code>}</code></p>
+      <p><strong>WARNING</strong>: Use of this option will expose the server to CVE-2016-6816.
+      </p>
+      <p>If not specified, the default value of <code>null</code> will be used.</p>
+    </property>
+
   </properties>
 
 </section>
diff --git a/webapps/docs/index.xml b/webapps/docs/index.xml
index 9f1ecfbe..0a3e124e 100644
--- a/webapps/docs/index.xml
+++ b/webapps/docs/index.xml
@@ -185,6 +185,12 @@ are responsible for installing, configuring, and operating an Apache Tomcat serv
     <a href="http://docs.oracle.com/javaee/7/api/javax/websocket/package-summary.html";>
     <strong>Javadoc</strong></a>
     </li>
+<li>JASPIC 1.1
+    <a href="https://jcp.org/aboutJava/communityprocess/mrel/jsr196/index.html";>
+    <strong>Specification</strong></a> and
+    <a href="http://docs.oracle.com/javaee/7/api/javax/security/auth/message/package-summary.html";>
+    <strong>Javadoc</strong></a>
+    </li>
 </ul>
 
 </section>

--- End Message ---
--- Begin Message ---
Emmanuel Bourg:
> Contro: tags -1 - moreinfo
> 
> Le 12/04/2017 à 18:41, Ivo De Decker a écrit :
> 
>> Please go ahead once the security upload currently in unstable migrated to
>> testing (that should happen in a few days, I just unblocked and aged it).
>> Please remove the moreinfo tag from this bug once the new upload is in
>> unstable and built on the relevant architecture.
> 
> Hi,
> 
> Thanks a lot, tomcat8/8.5.12-1 is now in unstable. This version contains
> the fix for CVE-2017-5648 which was backported in 8.5.11-2.

Unblocked, thanks.

> If time
> allows I'll probably propose a last update to include the fixes for the
> other CVEs.
> 
> Emmanuel Bourg
> 

Ok, please file a new unblock request at that time. :)

Thanks,
~Niels

--- End Message ---

Reply to: