conditional request headers ({@code if-none-match} and
+ * {@code if-modified-since})
*
*
* @throws IllegalStateException If this method is called when {@code path}
diff -Nru tomcat9-9.0.27/java/javax/servlet/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/javax/servlet/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/javax/servlet/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/javax/servlet/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -20,3 +20,6 @@
httpMethodConstraintElement.invalidMethod=无效的HTTP.方法
value.true=true
+
+wrapper.nullRequest=请求不能为空
+wrapper.nullResponse=响应不能为空
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/AccessLog.java tomcat9-9.0.31/java/org/apache/catalina/AccessLog.java
--- tomcat9-9.0.27/java/org/apache/catalina/AccessLog.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/AccessLog.java 2020-02-05 19:26:48.000000000 +0000
@@ -81,22 +81,21 @@
public void log(Request request, Response response, long time);
/**
- * Should this valve set request attributes for IP address, hostname,
- * protocol and port used for the request? This are typically used in
- * conjunction with the {@link org.apache.catalina.valves.AccessLogValve}
- * which will otherwise log the original values.
+ * Should this valve use request attributes for IP address, hostname,
+ * protocol and port used for the request?
*
- * The attributes set are:
+ * The attributes used are:
*
*
org.apache.catalina.RemoteAddr
*
org.apache.catalina.RemoteHost
*
org.apache.catalina.Protocol
+ *
org.apache.catalina.ServerName
*
org.apache.catalina.ServerPost
*
*
* @param requestAttributesEnabled true causes the attributes
- * to be set, false disables
- * the setting of the attributes.
+ * to be used, false causes
+ * the original values to be used.
*/
public void setRequestAttributesEnabled(boolean requestAttributesEnabled);
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/ant/ServerinfoTask.java tomcat9-9.0.31/java/org/apache/catalina/ant/ServerinfoTask.java
--- tomcat9-9.0.27/java/org/apache/catalina/ant/ServerinfoTask.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/ant/ServerinfoTask.java 2020-02-05 19:26:48.000000000 +0000
@@ -14,8 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina.ant;
@@ -44,5 +42,4 @@
execute("/serverinfo");
}
-
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/authenticator/AuthenticatorBase.java tomcat9-9.0.31/java/org/apache/catalina/authenticator/AuthenticatorBase.java
--- tomcat9-9.0.27/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2020-02-05 19:26:48.000000000 +0000
@@ -19,6 +19,7 @@
import java.io.IOException;
import java.security.Principal;
import java.security.cert.X509Certificate;
+import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -33,6 +34,7 @@
import javax.security.auth.message.config.RegistrationListener;
import javax.security.auth.message.config.ServerAuthConfig;
import javax.security.auth.message.config.ServerAuthContext;
+import javax.servlet.DispatcherType;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
@@ -44,7 +46,6 @@
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleException;
-import org.apache.catalina.Manager;
import org.apache.catalina.Realm;
import org.apache.catalina.Session;
import org.apache.catalina.TomcatPrincipal;
@@ -53,6 +54,7 @@
import org.apache.catalina.authenticator.jaspic.MessageInfoImpl;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
+import org.apache.catalina.filters.CorsFilter;
import org.apache.catalina.filters.RemoteIpFilter;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.catalina.util.SessionIdGeneratorBase;
@@ -63,9 +65,12 @@
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.descriptor.web.FilterDef;
+import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.apache.tomcat.util.http.FastHttpDateFormat;
+import org.apache.tomcat.util.http.RequestUtil;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -237,12 +242,22 @@
*/
protected SingleSignOn sso = null;
+ private AllowCorsPreflight allowCorsPreflight = AllowCorsPreflight.NEVER;
+
private volatile String jaspicAppContextID = null;
private volatile Optional jaspicProvider = null;
// ------------------------------------------------------------- Properties
+ public String getAllowCorsPreflight() {
+ return allowCorsPreflight.name().toLowerCase(Locale.ENGLISH);
+ }
+
+ public void setAllowCorsPreflight(String allowCorsPreflight) {
+ this.allowCorsPreflight = AllowCorsPreflight.valueOf(allowCorsPreflight.trim().toUpperCase(Locale.ENGLISH));
+ }
+
public boolean getAlwaysUseSession() {
return alwaysUseSession;
}
@@ -521,7 +536,7 @@
if (constraints == null && !context.getPreemptiveAuthentication() && !authRequired) {
if (log.isDebugEnabled()) {
- log.debug(" Not subject to any constraint");
+ log.debug("Not subject to any constraint");
}
getNext().invoke(request, response);
return;
@@ -544,11 +559,11 @@
if (constraints != null) {
// Enforce any user data constraint for this security constraint
if (log.isDebugEnabled()) {
- log.debug(" Calling hasUserDataPermission()");
+ log.debug("Calling hasUserDataPermission()");
}
if (!realm.hasUserDataPermission(request, response, constraints)) {
if (log.isDebugEnabled()) {
- log.debug(" Failed hasUserDataPermission() test");
+ log.debug("Failed hasUserDataPermission() test");
}
/*
* ASSERT: Authenticator already set the appropriate HTTP status
@@ -593,9 +608,17 @@
JaspicState jaspicState = null;
+ if ((authRequired || constraints != null) && allowCorsPreflightBypass(request)) {
+ if (log.isDebugEnabled()) {
+ log.debug("CORS Preflight request bypassing authentication");
+ }
+ getNext().invoke(request, response);
+ return;
+ }
+
if (authRequired) {
if (log.isDebugEnabled()) {
- log.debug(" Calling authenticate()");
+ log.debug("Calling authenticate()");
}
if (jaspicProvider != null) {
@@ -609,7 +632,7 @@
jaspicProvider != null &&
!authenticateJaspic(request, response, jaspicState, false)) {
if (log.isDebugEnabled()) {
- log.debug(" Failed authenticate() test");
+ log.debug("Failed authenticate() test");
}
/*
* ASSERT: Authenticator already set the appropriate HTTP status
@@ -622,11 +645,11 @@
if (constraints != null) {
if (log.isDebugEnabled()) {
- log.debug(" Calling accessControl()");
+ log.debug("Calling accessControl()");
}
if (!realm.hasResourcePermission(request, response, constraints, this.context)) {
if (log.isDebugEnabled()) {
- log.debug(" Failed accessControl() test");
+ log.debug("Failed accessControl() test");
}
/*
* ASSERT: AccessControl method has already set the appropriate
@@ -638,7 +661,7 @@
// Any and all specified constraints have been satisfied
if (log.isDebugEnabled()) {
- log.debug(" Successfully passed all security constraints");
+ log.debug("Successfully passed all security constraints");
}
getNext().invoke(request, response);
@@ -648,6 +671,64 @@
}
+ protected boolean allowCorsPreflightBypass(Request request) {
+ boolean allowBypass = false;
+
+ if (allowCorsPreflight != AllowCorsPreflight.NEVER) {
+ // First check to see if this is a CORS Preflight request
+ // This is a subset of the tests in CorsFilter.checkRequestType
+ if ("OPTIONS".equals(request.getMethod())) {
+ String originHeader = request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN);
+ if (originHeader != null &&
+ !originHeader.isEmpty() &&
+ RequestUtil.isValidOrigin(originHeader) &&
+ !RequestUtil.isSameOrigin(request, originHeader)) {
+ String accessControlRequestMethodHeader =
+ request.getHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD);
+ if (accessControlRequestMethodHeader != null &&
+ !accessControlRequestMethodHeader.isEmpty()) {
+ // This appears to be a CORS Preflight request
+ if (allowCorsPreflight == AllowCorsPreflight.ALWAYS) {
+ allowBypass = true;
+ } else if (allowCorsPreflight == AllowCorsPreflight.FILTER) {
+ if (DispatcherType.REQUEST == request.getDispatcherType()) {
+ // Look at Filter configuration for the Context
+ // Can't cache this unless we add a listener to
+ // the Context to clear the cache on reload
+ for (FilterDef filterDef : request.getContext().findFilterDefs()) {
+ if (CorsFilter.class.getName().equals(filterDef.getFilterClass())) {
+ for (FilterMap filterMap : context.findFilterMaps()) {
+ if (filterMap.getFilterName().equals(filterDef.getFilterName())) {
+ if ((filterMap.getDispatcherMapping() & FilterMap.REQUEST) > 0) {
+ for (String urlPattern : filterMap.getURLPatterns()) {
+ if ("/*".equals(urlPattern)) {
+ allowBypass = true;
+ // No need to check other patterns
+ break;
+ }
+ }
+ }
+ // Found mappings for CORS filter.
+ // No need to look further
+ break;
+ }
+ }
+ // Found the CORS filter. No need to look further.
+ break;
+ }
+ }
+ }
+ } else {
+ // Unexpected enum type
+ }
+ }
+ }
+ }
+ }
+ return allowBypass;
+ }
+
+
@Override
public boolean authenticate(Request request, HttpServletResponse httpResponse)
throws IOException {
@@ -986,7 +1067,7 @@
associate(ssoId, request.getSessionInternal(true));
if (log.isDebugEnabled()) {
- log.debug(" Reauthenticated cached principal '" +
+ log.debug("Reauthenticated cached principal '" +
request.getUserPrincipal().getName() +
"' with auth type '" + request.getAuthType() + "'");
}
@@ -1020,7 +1101,31 @@
}
- private void register(Request request, HttpServletResponse response, Principal principal,
+ /**
+ * Register an authenticated Principal and authentication type in our
+ * request, in the current session (if there is one), and with our
+ * SingleSignOn valve, if there is one. Set the appropriate cookie to be
+ * returned.
+ *
+ * @param request
+ * The servlet request we are processing
+ * @param response
+ * The servlet response we are generating
+ * @param principal
+ * The authenticated Principal to be registered
+ * @param authType
+ * The authentication type to be registered
+ * @param username
+ * Username used to authenticate (if any)
+ * @param password
+ * Password used to authenticate (if any)
+ * @param alwaysUseSession
+ * Should a session always be used once a user is authenticated?
+ * @param cache
+ * Should we cache authenticated Principals if the request is part of an
+ * HTTP session?
+ */
+ protected void register(Request request, HttpServletResponse response, Principal principal,
String authType, String username, String password, boolean alwaysUseSession,
boolean cache) {
@@ -1044,17 +1149,11 @@
if (session != null) {
// If the principal is null then this is a logout. No need to change
// the session ID. See BZ 59043.
- if (changeSessionIdOnAuthentication && principal != null) {
- String oldId = null;
- if (log.isDebugEnabled()) {
- oldId = session.getId();
- }
- Manager manager = request.getContext().getManager();
- manager.changeSessionId(session);
- request.changeSessionId(session.getId());
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("authenticator.changeSessionId",
- oldId, session.getId()));
+ if (getChangeSessionIdOnAuthentication() && principal != null) {
+ String newSessionId = changeSessionID(request, session);
+ // If the current session ID is being tracked, update it.
+ if (session.getNote(Constants.SESSION_ID_NOTE) != null) {
+ session.setNote(Constants.SESSION_ID_NOTE, newSessionId);
}
}
} else if (alwaysUseSession) {
@@ -1062,21 +1161,9 @@
}
// Cache the authentication information in our session, if any
- if (cache) {
- if (session != null) {
- session.setAuthType(authType);
- session.setPrincipal(principal);
- if (username != null) {
- session.setNote(Constants.SESS_USERNAME_NOTE, username);
- } else {
- session.removeNote(Constants.SESS_USERNAME_NOTE);
- }
- if (password != null) {
- session.setNote(Constants.SESS_PASSWORD_NOTE, password);
- } else {
- session.removeNote(Constants.SESS_PASSWORD_NOTE);
- }
- }
+ if (session != null && cache) {
+ session.setAuthType(authType);
+ session.setPrincipal(principal);
}
// Construct a cookie to be returned to the client
@@ -1142,6 +1229,20 @@
}
+
+ protected String changeSessionID(Request request, Session session) {
+ String oldId = null;
+ if (log.isDebugEnabled()) {
+ oldId = session.getId();
+ }
+ String newId = request.changeSessionId();
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("authenticator.changeSessionId", oldId, newId));
+ }
+ return newId;
+ }
+
+
@Override
public void login(String username, String password, Request request) throws ServletException {
Principal principal = doLogin(request, username, password);
@@ -1301,4 +1402,11 @@
public MessageInfo messageInfo = null;
public ServerAuthContext serverAuthContext = null;
}
+
+
+ protected enum AllowCorsPreflight {
+ NEVER,
+ FILTER,
+ ALWAYS
+ }
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/authenticator/Constants.java tomcat9-9.0.31/java/org/apache/catalina/authenticator/Constants.java
--- tomcat9-9.0.27/java/org/apache/catalina/authenticator/Constants.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/authenticator/Constants.java 2020-02-05 19:26:48.000000000 +0000
@@ -14,11 +14,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-
package org.apache.catalina.authenticator;
-
public class Constants {
// Authentication methods for login configuration
// Servlet spec schemes are defined in HttpServletRequest
@@ -33,17 +30,13 @@
// SPNEGO authentication constants
public static final String KRB5_CONF_PROPERTY = "java.security.krb5.conf";
public static final String DEFAULT_KRB5_CONF = "conf/krb5.ini";
- public static final String JAAS_CONF_PROPERTY =
- "java.security.auth.login.config";
+ public static final String JAAS_CONF_PROPERTY = "java.security.auth.login.config";
public static final String DEFAULT_JAAS_CONF = "conf/jaas.conf";
- public static final String DEFAULT_LOGIN_MODULE_NAME =
- "com.sun.security.jgss.krb5.accept";
+ public static final String DEFAULT_LOGIN_MODULE_NAME = "com.sun.security.jgss.krb5.accept";
// Cookie name for single sign on support
- public static final String SINGLE_SIGN_ON_COOKIE =
- System.getProperty(
- "org.apache.catalina.authenticator.Constants.SSO_SESSION_COOKIE_NAME",
- "JSESSIONIDSSO");
+ public static final String SINGLE_SIGN_ON_COOKIE = System.getProperty(
+ "org.apache.catalina.authenticator.Constants.SSO_SESSION_COOKIE_NAME", "JSESSIONIDSSO");
// --------------------------------------------------------- Request Notes
@@ -52,16 +45,18 @@
* The notes key to track the single-sign-on identity with which this
* request is associated.
*/
- public static final String REQ_SSOID_NOTE =
- "org.apache.catalina.request.SSOID";
-
+ public static final String REQ_SSOID_NOTE = "org.apache.catalina.request.SSOID";
- public static final String REQ_JASPIC_SUBJECT_NOTE =
- "org.apache.catalina.authenticator.jaspic.SUBJECT";
+ public static final String REQ_JASPIC_SUBJECT_NOTE = "org.apache.catalina.authenticator.jaspic.SUBJECT";
// ---------------------------------------------------------- Session Notes
+ /**
+ * The session id used as a CSRF marker when redirecting a user's request.
+ */
+ public static final String SESSION_ID_NOTE = "org.apache.catalina.authenticator.SESSION_ID";
+
/**
* If the cache property of our authenticator is set, and
@@ -70,19 +65,15 @@
* Realm.authenticate(), under the following keys:
*/
-
/**
* The notes key for the password used to authenticate this user.
*/
- public static final String SESS_PASSWORD_NOTE =
- "org.apache.catalina.session.PASSWORD";
-
+ public static final String SESS_PASSWORD_NOTE = "org.apache.catalina.session.PASSWORD";
/**
* The notes key for the username used to authenticate this user.
*/
- public static final String SESS_USERNAME_NOTE =
- "org.apache.catalina.session.USERNAME";
+ public static final String SESS_USERNAME_NOTE = "org.apache.catalina.session.USERNAME";
/**
@@ -90,20 +81,17 @@
* cache required information prior to the completion of authentication.
*/
-
/**
* The previously authenticated principal (if caching is disabled).
+ *
+ * @deprecated Unused. Will be removed in Tomcat 10.
*/
- public static final String FORM_PRINCIPAL_NOTE =
- "org.apache.catalina.authenticator.PRINCIPAL";
-
+ @Deprecated
+ public static final String FORM_PRINCIPAL_NOTE = "org.apache.catalina.authenticator.PRINCIPAL";
/**
* The original request information, to which the user will be
* redirected if authentication succeeds.
*/
- public static final String FORM_REQUEST_NOTE =
- "org.apache.catalina.authenticator.REQUEST";
-
-
+ public static final String FORM_REQUEST_NOTE = "org.apache.catalina.authenticator.REQUEST";
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/authenticator/FormAuthenticator.java tomcat9-9.0.31/java/org/apache/catalina/authenticator/FormAuthenticator.java
--- tomcat9-9.0.27/java/org/apache/catalina/authenticator/FormAuthenticator.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/authenticator/FormAuthenticator.java 2020-02-05 19:26:48.000000000 +0000
@@ -28,7 +28,6 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.apache.catalina.Manager;
import org.apache.catalina.Realm;
import org.apache.catalina.Session;
import org.apache.catalina.connector.Request;
@@ -133,10 +132,6 @@
protected boolean doAuthenticate(Request request, HttpServletResponse response)
throws IOException {
- if (checkForCachedAuthentication(request, response, true)) {
- return true;
- }
-
// References to objects we will need later
Session session = null;
Principal principal = null;
@@ -147,22 +142,16 @@
if (log.isDebugEnabled()) {
log.debug("Checking for reauthenticate in session " + session);
}
- String username =
- (String) session.getNote(Constants.SESS_USERNAME_NOTE);
- String password =
- (String) session.getNote(Constants.SESS_PASSWORD_NOTE);
- if ((username != null) && (password != null)) {
+ String username = (String) session.getNote(Constants.SESS_USERNAME_NOTE);
+ String password = (String) session.getNote(Constants.SESS_PASSWORD_NOTE);
+ if (username != null && password != null) {
if (log.isDebugEnabled()) {
log.debug("Reauthenticating username '" + username + "'");
}
- principal =
- context.getRealm().authenticate(username, password);
+ principal = context.getRealm().authenticate(username, password);
if (principal != null) {
- session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
+ register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
if (!matchRequest(request)) {
- register(request, response, principal,
- HttpServletRequest.FORM_AUTH,
- username, password);
return true;
}
}
@@ -177,20 +166,7 @@
if (matchRequest(request)) {
session = request.getSessionInternal(true);
if (log.isDebugEnabled()) {
- log.debug("Restore request from session '"
- + session.getIdInternal()
- + "'");
- }
- principal = (Principal)
- session.getNote(Constants.FORM_PRINCIPAL_NOTE);
- register(request, response, principal, HttpServletRequest.FORM_AUTH,
- (String) session.getNote(Constants.SESS_USERNAME_NOTE),
- (String) session.getNote(Constants.SESS_PASSWORD_NOTE));
- // If we're caching principals we no longer need the username
- // and password in the session, so remove them
- if (cache) {
- session.removeNote(Constants.SESS_USERNAME_NOTE);
- session.removeNote(Constants.SESS_PASSWORD_NOTE);
+ log.debug("Restore request from session '" + session.getIdInternal() + "'");
}
if (restoreRequest(request, session)) {
if (log.isDebugEnabled()) {
@@ -206,14 +182,18 @@
}
}
+ // This check has to be after the previous check for a matching request
+ // because that matching request may also include a cached Principal.
+ if (checkForCachedAuthentication(request, response, true)) {
+ return true;
+ }
+
// Acquire references to objects we will need to evaluate
String contextPath = request.getContextPath();
String requestURI = request.getDecodedRequestURI();
// Is this the action request from the login page?
- boolean loginAction =
- requestURI.startsWith(contextPath) &&
- requestURI.endsWith(Constants.FORM_ACTION);
+ boolean loginAction = requestURI.startsWith(contextPath) && requestURI.endsWith(Constants.FORM_ACTION);
LoginConfig config = context.getLoginConfig();
@@ -241,8 +221,7 @@
saveRequest(request, session);
} catch (IOException ioe) {
log.debug("Request body too big to save during authentication");
- response.sendError(HttpServletResponse.SC_FORBIDDEN,
- sm.getString("authenticator.requestBodyTooBig"));
+ response.sendError(HttpServletResponse.SC_FORBIDDEN, sm.getString("authenticator.requestBodyTooBig"));
return false;
}
forwardToLoginPage(request, response, config);
@@ -274,14 +253,21 @@
if (session == null) {
session = request.getSessionInternal(false);
}
+ if (session != null && getChangeSessionIdOnAuthentication()) {
+ // Does session id match?
+ String expectedSessionId = (String) session.getNote(Constants.SESSION_ID_NOTE);
+ if (expectedSessionId == null || !expectedSessionId.equals(request.getRequestedSessionId())) {
+ session.expire();
+ session = null;
+ }
+ }
if (session == null) {
if (containerLog.isDebugEnabled()) {
- containerLog.debug
- ("User took so long to log on the session expired");
+ containerLog.debug("User took so long to log on the session expired");
}
if (landingPage == null) {
- response.sendError(HttpServletResponse.SC_REQUEST_TIMEOUT,
- sm.getString("authenticator.sessionExpired"));
+ response.sendError(
+ HttpServletResponse.SC_REQUEST_TIMEOUT, sm.getString("authenticator.sessionExpired"));
} else {
// Make the authenticator think the user originally requested
// the landing page
@@ -290,19 +276,13 @@
saved.setMethod("GET");
saved.setRequestURI(uri);
saved.setDecodedRequestURI(uri);
- request.getSessionInternal(true).setNote(
- Constants.FORM_REQUEST_NOTE, saved);
+ request.getSessionInternal(true).setNote(Constants.FORM_REQUEST_NOTE, saved);
response.sendRedirect(response.encodeRedirectURL(uri));
}
return false;
}
- // Save the authenticated Principal in our session
- session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
-
- // Save the username and password as well
- session.setNote(Constants.SESS_USERNAME_NOTE, username);
- session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+ register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
// Redirect the user to the original request URI (which will cause
// the original request to be restored)
@@ -312,8 +292,7 @@
}
if (requestURI == null) {
if (landingPage == null) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST,
- sm.getString("authenticator.formlogin"));
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, sm.getString("authenticator.formlogin"));
} else {
// Make the authenticator think the user originally requested
// the landing page
@@ -331,15 +310,12 @@
Response internalResponse = request.getResponse();
String location = response.encodeRedirectURL(requestURI);
if ("HTTP/1.1".equals(request.getProtocol())) {
- internalResponse.sendRedirect(location,
- HttpServletResponse.SC_SEE_OTHER);
+ internalResponse.sendRedirect(location, HttpServletResponse.SC_SEE_OTHER);
} else {
- internalResponse.sendRedirect(location,
- HttpServletResponse.SC_FOUND);
+ internalResponse.sendRedirect(location, HttpServletResponse.SC_FOUND);
}
}
return false;
-
}
@@ -380,6 +356,33 @@
}
+ @Override
+ protected void register(Request request, HttpServletResponse response,
+ Principal principal, String authType, String username,
+ String password, boolean alwaysUseSession, boolean cache) {
+
+ super.register(request, response, principal, authType, username, password, alwaysUseSession, cache);
+
+ // If caching an authenticated Principal is turned off,
+ // store username and password as session notes to use them for re-authentication.
+ if (!cache) {
+ Session session = request.getSessionInternal(false);
+ if (session != null) {
+ if (username != null) {
+ session.setNote(Constants.SESS_USERNAME_NOTE, username);
+ } else {
+ session.removeNote(Constants.SESS_USERNAME_NOTE);
+ }
+ if (password != null) {
+ session.setNote(Constants.SESS_PASSWORD_NOTE, password);
+ } else {
+ session.removeNote(Constants.SESS_PASSWORD_NOTE);
+ }
+ }
+ }
+ }
+
+
/**
* Called to forward to the login page
*
@@ -414,9 +417,8 @@
if (getChangeSessionIdOnAuthentication()) {
Session session = request.getSessionInternal(false);
if (session != null) {
- Manager manager = request.getContext().getManager();
- manager.changeSessionId(session);
- request.changeSessionId(session.getId());
+ String newSessionId = changeSessionID(request, session);
+ session.setNote(Constants.SESSION_ID_NOTE, newSessionId);
}
}
@@ -503,17 +505,24 @@
}
// Is there a saved request?
- SavedRequest sreq =
- (SavedRequest) session.getNote(Constants.FORM_REQUEST_NOTE);
+ SavedRequest sreq = (SavedRequest) session.getNote(Constants.FORM_REQUEST_NOTE);
if (sreq == null) {
return false;
}
// Is there a saved principal?
- if (session.getNote(Constants.FORM_PRINCIPAL_NOTE) == null) {
+ if (cache && session.getPrincipal() == null || !cache && request.getPrincipal() == null) {
return false;
}
+ // Does session id match?
+ if (getChangeSessionIdOnAuthentication()) {
+ String expectedSessionId = (String) session.getNote(Constants.SESSION_ID_NOTE);
+ if (expectedSessionId == null || !expectedSessionId.equals(request.getRequestedSessionId())) {
+ return false;
+ }
+ }
+
// Does the request URI match?
String decodedRequestURI = request.getDecodedRequestURI();
if (decodedRequestURI == null) {
@@ -538,10 +547,9 @@
throws IOException {
// Retrieve and remove the SavedRequest object from our session
- SavedRequest saved = (SavedRequest)
- session.getNote(Constants.FORM_REQUEST_NOTE);
+ SavedRequest saved = (SavedRequest) session.getNote(Constants.FORM_REQUEST_NOTE);
session.removeNote(Constants.FORM_REQUEST_NOTE);
- session.removeNote(Constants.FORM_PRINCIPAL_NOTE);
+ session.removeNote(Constants.SESSION_ID_NOTE);
if (saved == null) {
return false;
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/authenticator/jaspic/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/authenticator/jaspic/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/authenticator/jaspic/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/authenticator/jaspic/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+authConfigFactoryImpl.load=从[{0}]加载持久化提供者注册信息
authConfigFactoryImpl.zeroLengthAppContext=:)应用上下文名称的长度为0是无效的
authConfigFactoryImpl.zeroLengthMessageLayer=零长度的消息层名称是无效的
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/authenticator/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/authenticator/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/authenticator/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/authenticator/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -34,13 +34,20 @@
digestAuthenticator.cacheRemove=已从客户端 nonce 缓存中删除有效条目,以便为新条目腾出空间。重播攻击现在是可能的。为防止重播攻击的可能性,请降低nonceValidity或增加nonceCacheSize。此类型的进一步警告将被抑制5分钟。
+formAuthenticator.noErrorPage=没有为上下文[{0}]中的表单身份验证定义错误页
+
+singleSignOn.debug.associate=SSO将应用程序会话[{1}]与SSO会话[{0}]关联
singleSignOn.debug.cookieCheck=SSO检查SSO cookie
singleSignOn.debug.cookieNotFound=SSO没有找到SSO cookie
+singleSignOn.debug.deregisterFail=SSO撤销登记SSO会话[{0}]失败,因为缓存中不包含这个SSO会话
+singleSignOn.debug.hasPrincipal=找到以前经过身份验证的主体[{0}]
+singleSignOn.debug.invoke=SSO为[{0}]处理请求
singleSignOn.debug.principalCheck=SSO为SSO会话[{0}]寻找缓存的Principal
singleSignOn.debug.principalFound=SSO 找到了带着认证类型的缓存代理
singleSignOn.debug.removeSession=SSO 从 SSO session [{1}] 中删除应用程序会话 [{0}]
singleSignOn.debug.update=SSO 更新SSO 会话[{0}] 对认证 类型[{1}]
singleSignOn.sessionExpire.hostNotFound=因为 Host 丢失,SSO 无法使 session [{0}] 失效
singleSignOn.sessionExpire.managerError=由于会话管理器在检索会话时抛出异常,导致单点登录无法使会话[{0}]失效
+singleSignOn.sessionExpire.managerNotFound=SSO无法使会话[{0}]过期,因为找不到管理器
spnegoAuthenticator.authHeaderNoToken=客户端发送的协商授权 header 未包含 token
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/connector/Connector.java tomcat9-9.0.31/java/org/apache/catalina/connector/Connector.java
--- tomcat9-9.0.27/java/org/apache/catalina/connector/Connector.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/connector/Connector.java 2020-02-05 19:26:48.000000000 +0000
@@ -25,6 +25,7 @@
import javax.management.ObjectName;
+import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Service;
@@ -165,6 +166,16 @@
/**
+ * The flag that controls recycling of the facades of the request
+ * processing objects. If set to true the object facades
+ * will be discarded when the request is recycled. If the security
+ * manager is enabled, this setting is ignored and object facades are
+ * always discarded.
+ */
+ protected boolean discardFacades = RECYCLE_FACADES;
+
+
+ /**
* The redirect port for non-SSL to SSL redirects.
*/
protected int redirectPort = 443;
@@ -372,6 +383,25 @@
}
+ /**
+ * @return true if the object facades are discarded, either
+ * when the discardFacades value is true or when the
+ * security manager is enabled.
+ */
+ public boolean getDiscardFacades() {
+ return discardFacades || Globals.IS_SECURITY_ENABLED;
+ }
+
+
+ /**
+ * Set the recycling strategy for the object facades.
+ * @param discardFacades the new value of the flag
+ */
+ public void setDiscardFacades(boolean discardFacades) {
+ this.discardFacades = discardFacades;
+ }
+
+
/**
* @return the "enable DNS lookups" flag.
*/
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/connector/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/connector/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/connector/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/connector/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+coyoteAdapter.authorize=(:使用Tomcat的领域授权用户[{0}]
coyoteAdapter.checkRecycled.response=遇到非回收的相应并强行回收。
coyoteAdapter.debug=变量[{0}]的值为[{1}]。
@@ -23,20 +24,28 @@
coyoteConnector.protocolHandlerNoAprLibrary=配置的协议[{0}]需要不可用的APR/本机库
coyoteConnector.protocolHandlerNoAprListener=配置的协议[{0}]需要不可用的aprlifecycleListener
coyoteConnector.protocolHandlerPauseFailed=协议处理程序暂停失败
+coyoteConnector.protocolHandlerStartFailed=协议处理器启动失败
coyoteConnector.protocolHandlerStopFailed=协议处理程序.停止失败
coyoteInputStream.nbNotready=在非阻塞模式下,只有之前的读数据完成,并且isReady()方法返回true,你才可以使用 ServletInputStream 读取数据
+coyoteRequest.attributeEvent=属性事件侦听器引发的异常
+coyoteRequest.authenticate.ise=):提交响应后无法调用authenticate()
coyoteRequest.changeSessionId=无法更改 session ID。 没有与此请求关联的 session。
coyoteRequest.chunkedPostTooLarge=由于请求参数数据太大,导致参数不能解析。因为当前请求是块状请求,后续也不会处理。如果应用程序需要接收大的POST请求,可以使用连接器的maxPostSize解决它。
coyoteRequest.filterAsyncSupportUnknown=无法确定是否有任何过滤器不支持异步处理
coyoteRequest.gssLifetimeFail=为用户主体 [{0}] 获取剩余生命期失败
+coyoteRequest.maxPostSizeExceeded=):大多部分请求包含的参数数据(不包括上载的文件)超过了关联连接器上设置的maxPostSize 的限制
coyoteRequest.noMultipartConfig=由于没有提供multi-part配置,无法处理parts
coyoteRequest.sendfileNotCanonical=无法确定指定用于sendfile的文件[{0}]的规范名称
+coyoteRequest.sessionCreateCommitted=提交响应后无法创建会话
coyoteRequest.sessionEndAccessFail=在回收请求时,异常触发了对会话的结束访问。
coyoteRequest.setAttribute.namenull=不能在一个空的名字上调用setAttribute
+coyoteRequest.uploadLocationInvalid=临时上传路径[{0}]无效
coyoteResponse.encoding.invalid=JRE无法识别编码[{0}]
+coyoteResponse.getWriter.ise=当前响应已经调用了方法getOutputStream()
+coyoteResponse.reset.ise=已经提交响应后无法调用reset()
coyoteResponse.sendError.ise=响应提交后无法调用sendError()
coyoteResponse.sendRedirect.note=
* Returns the expiration date of the given {@link XHttpServletResponse} or
* {@code null} if no expiration date has been configured for the
* declared content type.
- *
*
* {@code protected} for extension.
- *
*
- * @param response The Servlet response
+ * @param response The wrapped HTTP response
+ *
* @return the expiration date
* @see HttpServletResponse#getContentType()
+ *
+ * @deprecated Will be removed in Tomcat 10.
+ * Use {@link #getExpirationDate(HttpServletRequest, XHttpServletResponse)}
*/
+ @Deprecated
protected Date getExpirationDate(XHttpServletResponse response) {
+ return getExpirationDate((HttpServletRequest) null, response);
+ }
+
+
+ /**
+ * Returns the expiration date of the given {@link XHttpServletResponse} or
+ * {@code null} if no expiration date has been configured for the
+ * declared content type.
+ *
+ * {@code protected} for extension.
+ *
+ * @param request The HTTP request
+ * @param response The wrapped HTTP response
+ *
+ * @return the expiration date
+ * @see HttpServletResponse#getContentType()
+ */
+ protected Date getExpirationDate(HttpServletRequest request, XHttpServletResponse response) {
String contentType = response.getContentType();
+ if (contentType == null && request != null &&
+ request.getHttpServletMapping().getMappingMatch() == MappingMatch.DEFAULT &&
+ response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED) {
+ // Default servlet normally sets the content type but does not for
+ // 304 responses. Look it up.
+ String servletPath = request.getServletPath();
+ if (servletPath != null) {
+ int lastSlash = servletPath.lastIndexOf('/');
+ if (lastSlash > -1) {
+ String fileName = servletPath.substring(lastSlash + 1);
+ contentType = request.getServletContext().getMimeType(fileName);
+ }
+ }
+ }
if (contentType != null) {
contentType = contentType.toLowerCase(Locale.ENGLISH);
}
@@ -1485,7 +1521,7 @@
return;
}
- Date expirationDate = getExpirationDate(response);
+ Date expirationDate = getExpirationDate(request, response);
if (expirationDate == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("expiresFilter.noExpirationConfigured",
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/filters/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/filters/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/filters/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/filters/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -17,19 +17,32 @@
corsFilter.nullRequest=HttpServletRequest 对象为空
corsFilter.nullRequestType=CORSRequestType对象为空(null)
corsFilter.onlyHttp=CORS不支持非HTTP请求或响应
+corsFilter.wrongType1=期望类型为[{0}]的HttpServletRequest对象
+corsFilter.wrongType2=期望类型为[{0}]或[{1}]的HttpServletRequest对象
-csrfPrevention.invalidRandomClass=不能使用class [{0}]创建随机源.
+csrfPrevention.invalidRandomClass=不能使用class [{0}]创建随机源。
+expiresFilter.invalidDurationNumber=指令[{1}]中的持续时间(数字)[{0}]无效
+expiresFilter.noDurationFound=在指令[{0}]中找不到持续时间
expiresFilter.noExpirationConfigured=请求[{0}],其响应状态为[{1}]内容类型[{2}],未配置到期日期
expiresFilter.noExpirationConfiguredForContentType=没有为 content-type [{0}] 找到过期配置
+expiresFilter.responseAlreadyCommited=请求[{0}],无法对已提交的响应应用ExpiresFilter。
expiresFilter.startingPointInvalid=在指令[{1}]中无效的起点(访问|现在|修改|a<秒>|m<秒>)[{0}]
+expiresFilter.startingPointNotFound=起始点(access|now|modification|a|m)未在指令[{0}]中找到
expiresFilter.unsupportedStartingPoint=不支持的起始点 [{0}]
+expiresFilter.useDefaultConfiguration=对内容类型[{1}]使用默认的[{0}]返回[{2}]
expiresFilter.useMatchingConfiguration=对内容类型[{2}]返回[{3}]使用[{0}]匹配[{1}]
+filterbase.noSuchProperty=未为[{1}]类型的筛选器定义属性[{0}]
+
http.403=禁止访问指定资源 [{0}] 。
-httpHeaderSecurityFilter.clickjack.invalid=为防咔嗒顶部标题指定了无效值[{0}]
+httpHeaderSecurityFilter.clickjack.invalid=为防点击挟持的响应消息头指定了非法值 [{0}]
+httpHeaderSecurityFilter.committed=在进入HttpHeaderSecurityFilter的时候响应消息已经提交导致不能添加响应消息头
+
+remoteCidrFilter.noRemoteIp=客户端没有 IP 地址,请求被拒绝。
-remoteCidrFilter.noRemoteIp=客户端没有 IP 地址。请求被拒绝。
+remoteIpFilter.invalidHostHeader=HTTP头 [{1}]中,为Host找到一个无效值 [{0}]
+remoteIpFilter.invalidHostWithPort=HTTP头 [{1}]中的Host值 [{0}]包含一个被忽略的端口号
requestFilter.deny=基于属性:[{1}],[{0}]的请求被拒绝。
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java tomcat9-9.0.31/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java
--- tomcat9-9.0.27/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java 2020-02-05 19:26:48.000000000 +0000
@@ -25,6 +25,7 @@
import java.util.regex.Pattern;
import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
@@ -89,6 +90,17 @@
private String pathsDelimiter = ",";
@Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ // Set the parameters
+ super.init(filterConfig);
+
+ // Put the expected request header name into the application scope
+ filterConfig.getServletContext().setAttribute(
+ Constants.CSRF_REST_NONCE_HEADER_NAME_KEY,
+ Constants.CSRF_REST_NONCE_HEADER_NAME);
+ }
+
+ @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/ha/backend/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/ha/backend/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/ha/backend/LocalStrings_zh_CN.properties 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/ha/backend/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,16 @@
+# 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.
+
+multiCastSender.sendFailed=无法发送多播信息
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/ha/deploy/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/ha/deploy/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/ha/deploy/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/ha/deploy/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,7 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+farmWarDeployer.deleteFail=无法删除 [{0}]
farmWarDeployer.hostOnly=FarmWarDeployer 只有做为 host cluster 的子元素是才生效
+farmWarDeployer.mbeanNameFail=无法为引擎[{0}]和主机[{1}]构造MBean对象名
farmWarDeployer.modInstall=从 [{1}] 安装 webapp [{0}]
farmWarDeployer.modInstallFail=无法安装 WAR 文件
farmWarDeployer.msgIoe=无法读取服务器场部署文件消息。
@@ -22,7 +24,9 @@
farmWarDeployer.removeFailLocal=从[{0}]本地移除失败
farmWarDeployer.removeFailRemote=本地从[{0}]删除失败,其他经理有app在服务!
farmWarDeployer.removeLocalFail=无法移除WAR文件
+farmWarDeployer.renameFail=将 [{0}] 重命名为 [{1}] 失败
farmWarDeployer.sendFragment=将群集战争片段路径[{0}],战争[{1}]发送到[{2}]
+farmWarDeployer.servicingDeploy=应用程序[{0}]正在服务。再次触摸WAR文件[{1}]!
farmWarDeployer.servicingUndeploy=正在处理应用程序[{0}],无法从备份群集节点中删除它
farmWarDeployer.undeployEnd=从[{0}]取消部署完成。
farmWarDeployer.undeployLocal=不能部署本地上下文[{0}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/ha/session/DeltaManager.java tomcat9-9.0.31/java/org/apache/catalina/ha/session/DeltaManager.java
--- tomcat9-9.0.27/java/org/apache/catalina/ha/session/DeltaManager.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/ha/session/DeltaManager.java 2020-02-05 19:26:48.000000000 +0000
@@ -485,7 +485,12 @@
@Override
public void changeSessionId(Session session) {
- changeSessionId(session, true);
+ rotateSessionId(session);
+ }
+
+ @Override
+ public String rotateSessionId(Session session) {
+ return rotateSessionId(session, true);
}
@Override
@@ -493,12 +498,25 @@
changeSessionId(session, newId, true);
}
+ /**
+ * @param session The session
+ * @param notify Notify change
+ * @deprecated Will be removed in Tomcat 10
+ */
+ @Deprecated
protected void changeSessionId(Session session, boolean notify) {
String orgSessionID = session.getId();
super.changeSessionId(session);
if (notify) sendChangeSessionId(session.getId(), orgSessionID);
}
+ protected String rotateSessionId(Session session, boolean notify) {
+ String orgSessionID = session.getId();
+ String newId = super.rotateSessionId(session);
+ if (notify) sendChangeSessionId(session.getId(), orgSessionID);
+ return newId;
+ }
+
protected void changeSessionId(Session session, String newId, boolean notify) {
String orgSessionID = session.getId();
super.changeSessionId(session, newId);
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/ha/session/DeltaSession.java tomcat9-9.0.31/java/org/apache/catalina/ha/session/DeltaSession.java
--- tomcat9-9.0.27/java/org/apache/catalina/ha/session/DeltaSession.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/ha/session/DeltaSession.java 2020-02-05 19:26:48.000000000 +0000
@@ -846,7 +846,9 @@
if (exclude(name, value)) {
continue;
}
- attributes.put(name, value);
+ // ConcurrentHashMap does not allow null keys or values
+ if(null != value)
+ attributes.put(name, value);
}
isValid = isValidSave;
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/ha/session/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/ha/session/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/ha/session/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/ha/session/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -18,12 +18,17 @@
backupManager.startUnable=无法启动BackupManager: [{0}]
backupManager.stopped=管理者[{0}]正在停止。
+deltaManager.createMessage.access=管理器[{0}]:创建会话为会话[{1}]存取消息
+deltaManager.createMessage.delta=管理器[{0}] ):为会话[{1}]创建增量请求消息
+deltaManager.createMessage.expire=管理器[{0}] (:为会话[{1}]创建会话过期消息
deltaManager.createSession.newSession=用id[{0}]创建一个扩展会话(DeltaSession),总数为 [{1}]
deltaManager.foundMasterMember=复制主master 成员在上下文中被发现.\n
deltaManager.loading.cnfe=加载持久化会话 [{0}] 时出现ClassNotFoundException
deltaManager.loading.ioe=加载持久 session 时出现 IOException:[{0}]
+deltaManager.managerLoad=从永久存储加载会话时发生异常
deltaManager.noContextManager=管理器[{0}]:回复[{1}]发送的“获取所有会话数据”消息,在[{2}] ms后收到“无匹配的上下文管理器”消息
deltaManager.noSessionState=管理者[{0}]:没有收到[{1}]发送的会话状态,在[{2}]毫秒之后超时。
+deltaManager.receiveMessage.accessed=管理器[{0}]:接收会话为会话[{1}]存取消息
deltaManager.receiveMessage.allSessionDataAfter=Manager [{0}]: session 状态反序列化
deltaManager.receiveMessage.allSessionDataBegin=管理者[{0}]:接收到所有会话数据状态
deltaManager.receiveMessage.delta.unknown=管理器[{0}]:未知会话的接收会话增量[{1}]
@@ -31,6 +36,7 @@
deltaManager.receiveMessage.unloadingBegin=管理器[{0}]: 开始卸载会话
deltaManager.registerCluster=将管理器[{0}]注册到名为[{2}]的集群元素[{1}]
deltaManager.sendMessage.newSession=\ 管理器 [{0}] 发送新的会话 [{1}]
+deltaManager.sessionReceived=管理器[{0}];在[{1}]发送的会话状态在[{2}]毫秒内收到。
deltaManager.unableSerializeSessionID=无法序列化会话ID [{0}]
deltaManager.unloading.ioe=当保存永久回话:[{0}] 时,抛出 IOException
@@ -43,3 +49,7 @@
jvmRoute.changeSession=会话从[{0}]切换到[{1}]
jvmRoute.missingJvmRouteAttribute=没有配置引擎jvmRoute属性!
jvmRoute.notFoundManager=没有在 [{0}] 找到Cluster Manager
+jvmRoute.set.orignalsessionid=在请求属性[{0}]值:[{1}]处设置原始会话ID
+jvmRoute.valve.started=JvmRouteBinderValve 启动
+
+standardSession.setAttribute.namenull=setAttribute:名称属性不能为空
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/loader/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/loader/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/loader/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/loader/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -14,14 +14,19 @@
# limitations under the License.
webappClassLoader.addTransformer=将类文件转换器[{0}]添加到Web应用程序[{1}]。
+webappClassLoader.addTransformer.illegalArgument=Web应用程序[{0}]试图添加空类文件转换器。
webappClassLoader.checkThreadLocalsForLeaks.badValue=无法确定类型为 [{0}] 的值的字符串表示形式
webappClassLoader.checkThreadLocalsForLeaks.unknown=:)未知
webappClassLoader.checkThreadLocalsForLeaksNone=web应用程序 [{0}] 创建了1个ThreadLocal变量(键:[{2}] (类型[{1}]) ,值:[{4}](类型[{3}]) )。键仅被ThreadLocal Map弱引用,所以不是内存泄露。
webappClassLoader.clearJdbc=Web应用程序 [{0}] 注册了JDBC驱动程序 [{1}],但在Web应用程序停止时无法注销它。 为防止内存泄漏,JDBC驱动程序已被强制取消注册。
+webappClassLoader.getThreadGroupError=无法获得线程组[{0}]的父级。不可能检查所有线程是否存在潜在的内存泄漏。
webappClassLoader.jarsRemoved=一个或多个 JAR 已被从 Web 应用程序 [{0}] 中删除
webappClassLoader.jdbcRemoveFailed=Web应用程序 [{0}] 的JDBC驱动程序注销失败
webappClassLoader.readError=资源读取错误:不能加载 [{0}].
+webappClassLoader.stackTrace=Web应用程序[{0}]似乎启动了一个名为[{1}]的线程,但未能停止它。这很可能会造成内存泄漏。线程的堆栈跟踪:[{2}]
+webappClassLoader.stopped=非法访问:此Web应用程序实例已停止。无法加载[{0}]。为了调试以及终止导致非法访问的线程,将抛出以下堆栈跟踪。
webappClassLoader.superCloseFail=调用父类的close()方法出现异常。
+webappClassLoader.transformError=检测错误:无法转换类[{0}],因为它的类文件格式是不合法的。
webappClassLoader.warnTimerThread=Web应用程序[{0}]似乎已通过java.util.Timer API启动了名为[{1}]的TimerThread,但未能将其停止。 为防止内存泄漏,计时器(以及相关联的线程)已被强制取消。
webappClassLoader.wrongVersion=(无法载入的.类 [{0}])
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/manager/Constants.java tomcat9-9.0.31/java/org/apache/catalina/manager/Constants.java
--- tomcat9-9.0.27/java/org/apache/catalina/manager/Constants.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/manager/Constants.java 2020-02-05 19:26:48.000000000 +0000
@@ -203,7 +203,7 @@
HTML_TAIL_SECTION =
"\n" +
"
\r\n");
- sb.append("");
+ sb.append("");
String readme = getReadme(resource, encoding);
if (readme!=null) {
sb.append(readme);
- sb.append("");
+ sb.append("");
}
if (showServerInfo) {
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -16,6 +16,7 @@
cgiServlet.expandFail=在路径[{0}] 到[{1}] 展开脚本失败.
cgiServlet.expandOk=从路径[{0}]到[{1}]扩展脚本
cgiServlet.find.location=在 [{0}] 查找文件
+cgiServlet.find.path=在相对于CGI位置[{1}]的路径[{0}]处请求的CGI脚本
cgiServlet.runHeaderReaderFail=I/O 问题关闭请求头读操作
cgiServlet.runInvalidStatus=无效状态 [{0}]
cgiServlet.runOutputStreamFail=关闭输出流时发生I/O问题
@@ -23,6 +24,7 @@
cgiServlet.runStdErrFail=I/O标准错误问题
defaultServlet.blockExternalSubset=用名称[{0}]和baseURI[{1}]阻止对外部子集的访问
+defaultServlet.noResources=找不到静态资源
directory.filename=文件名
directory.size=大小
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/servlets/WebdavServlet.java tomcat9-9.0.31/java/org/apache/catalina/servlets/WebdavServlet.java
--- tomcat9-9.0.27/java/org/apache/catalina/servlets/WebdavServlet.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/servlets/WebdavServlet.java 2020-02-05 19:26:48.000000000 +0000
@@ -61,7 +61,10 @@
import org.xml.sax.SAXException;
/**
- * Servlet which adds support for WebDAV level 2. All the basic HTTP requests
+ * Servlet which adds support for
+ * WebDAV
+ * level 2.
+ * All the basic HTTP requests
* are handled by the DefaultServlet. The WebDAVServlet must not be used as the
* default servlet (ie mapped to '/') as it will not work in this configuration.
*
@@ -120,6 +123,8 @@
* http://host:port/context/webdavedit/content
*
* @author Remy Maucherat
+ *
+ * @see RFC 4918
*/
public class WebdavServlet extends DefaultServlet {
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/session/FileStore.java tomcat9-9.0.31/java/org/apache/catalina/session/FileStore.java
--- tomcat9-9.0.27/java/org/apache/catalina/session/FileStore.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/session/FileStore.java 2020-02-05 19:26:48.000000000 +0000
@@ -127,17 +127,17 @@
@Override
public int getSize() throws IOException {
// Acquire the list of files in our storage directory
- File file = directory();
- if (file == null) {
+ File dir = directory();
+ if (dir == null) {
return 0;
}
- String files[] = file.list();
+ String files[] = dir.list();
// Figure out which files are sessions
int keycount = 0;
if (files != null) {
- for (int i = 0; i < files.length; i++) {
- if (files[i].endsWith(FILE_EXT)) {
+ for (String file : files) {
+ if (file.endsWith(FILE_EXT)) {
keycount++;
}
}
@@ -172,24 +172,23 @@
@Override
public String[] keys() throws IOException {
// Acquire the list of files in our storage directory
- File file = directory();
- if (file == null) {
+ File dir = directory();
+ if (dir == null) {
return new String[0];
}
-
- String files[] = file.list();
+ String files[] = dir.list();
// Bugzilla 32130
- if((files == null) || (files.length < 1)) {
+ if (files == null || files.length < 1) {
return new String[0];
}
// Build and return the list of session identifiers
List list = new ArrayList<>();
int n = FILE_EXT.length();
- for (int i = 0; i < files.length; i++) {
- if (files[i].endsWith(FILE_EXT)) {
- list.add(files[i].substring(0, files[i].length() - n));
+ for (String file : files) {
+ if (file.endsWith(FILE_EXT)) {
+ list.add (file.substring(0, file.length() - n));
}
}
return list.toArray(new String[list.size()]);
@@ -210,11 +209,7 @@
public Session load(String id) throws ClassNotFoundException, IOException {
// Open an input stream to the specified pathname, if any
File file = file(id);
- if (file == null) {
- return null;
- }
-
- if (!file.exists()) {
+ if (file == null || !file.exists()) {
return null;
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/session/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/session/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/session/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/session/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,14 +13,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+JDBCStore.checkConnectionClassNotFoundException=找不到 JDBC 驱动程序类 [{0}]
+JDBCStore.checkConnectionDBClosed=数据库连接为空或已关闭。正在尝试重新连接。
JDBCStore.checkConnectionDBReOpenFail=重新打开数据库失败,数据库可能已经宕机。
+JDBCStore.checkConnectionSQLException=发生 SQL 异常 [{0}]
+JDBCStore.loading=正在从数据库[{1}]加载会话[{0}]
JDBCStore.missingDataSourceName=没有给出有效的 JNDI 名称。
JDBCStore.saving=保存Session [{0}] 到数据库 [{1}]
+JDBCStore.wrongDataSource=无法打开 JNDI 数据源 [{0}]
fileStore.deleteFailed=无法删除阻止创建会话存储位置的文件 [{0}]
+fileStore.deleteSessionFailed=无法删除不再需要的文件[{0}]
managerBase.contextNull=使用 Manager 之前,必须将 Context 设置为非 null 值
managerBase.createSession.ise=createSession:活跃session过多
+managerBase.sessionNotFound=找不到会话 [{0}]
managerBase.setContextNotNew=如果Manager未处于NEW状态,则调用setContext()以更改与Manager关联的Context是非法的
persistentManager.deserializeError=错误反序列化会话[{0}]: [{1}]
@@ -34,11 +41,15 @@
standardManager.loading.exception=加载持久化会话时发生异常
standardManager.managerLoad=从持久化存储加载会话发生异常
standardManager.managerUnload=卸载会话到持久存储的异常
+standardManager.unloading.nosessions=没有要卸载的持久会话
+standardSession.attributeEvent=会话属性事件侦听器引发异常
+standardSession.getAttribute.ise=getAttribute: 会话已失效
standardSession.getAttributeNames.ise=getAttributeNames:会话已失效
standardSession.getCreationTime.ise=getCreataionTime:会话已经无效
standardSession.getIdleTime.ise=getIdleTime: 已失效的会话
standardSession.getLastAccessedTime.ise=getLastAccessedTime: 会话已失效
+standardSession.getThisAccessedTime.ise=getThisAccessedTime:会话已经失效
standardSession.getValueNames.ise=getValueNames:会话已经失效
standardSession.logoutfail=当回话将过期登出用户异常
standardSession.notDeserializable=无法反序列化会话 [{1}] 的属性 [{0}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/session/ManagerBase.java tomcat9-9.0.31/java/org/apache/catalina/session/ManagerBase.java
--- tomcat9-9.0.27/java/org/apache/catalina/session/ManagerBase.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/session/ManagerBase.java 2020-02-05 19:26:48.000000000 +0000
@@ -753,8 +753,15 @@
@Override
public void changeSessionId(Session session) {
+ rotateSessionId(session);
+ }
+
+
+ @Override
+ public String rotateSessionId(Session session) {
String newId = generateSessionId();
changeSessionId(session, newId, true, true);
+ return newId;
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/session/StandardSession.java tomcat9-9.0.31/java/org/apache/catalina/session/StandardSession.java
--- tomcat9-9.0.27/java/org/apache/catalina/session/StandardSession.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/session/StandardSession.java 2020-02-05 19:26:48.000000000 +0000
@@ -1595,7 +1595,9 @@
if (exclude(name, value)) {
continue;
}
- attributes.put(name, value);
+ // ConcurrentHashMap does not allow null keys or values
+ if(null != value)
+ attributes.put(name, value);
}
isValid = isValidSave;
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/ssi/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/ssi/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/ssi/LocalStrings_zh_CN.properties 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/ssi/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,16 @@
+# 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.
+
+expressionParseTree.extraNodes=创建额外节点
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/ssi/SSIServletExternalResolver.java tomcat9-9.0.31/java/org/apache/catalina/ssi/SSIServletExternalResolver.java
--- tomcat9-9.0.27/java/org/apache/catalina/ssi/SSIServletExternalResolver.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/ssi/SSIServletExternalResolver.java 2020-02-05 19:26:48.000000000 +0000
@@ -22,6 +22,7 @@
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
@@ -35,7 +36,6 @@
import org.apache.catalina.connector.Connector;
import org.apache.catalina.connector.Request;
-import org.apache.coyote.Constants;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.http.RequestUtil;
@@ -273,7 +273,7 @@
queryStringCharset = uriCharset;
} else {
// Use default as a last resort
- queryStringCharset = Constants.DEFAULT_URI_CHARSET;
+ queryStringCharset = StandardCharsets.UTF_8;
}
retVal = UDecoder.URLDecode(queryString, queryStringCharset);
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/startup/Bootstrap.java tomcat9-9.0.31/java/org/apache/catalina/startup/Bootstrap.java
--- tomcat9-9.0.27/java/org/apache/catalina/startup/Bootstrap.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/startup/Bootstrap.java 2020-02-05 19:26:48.000000000 +0000
@@ -34,7 +34,6 @@
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
-
/**
* Bootstrap loader for Catalina. This application constructs a class loader
* for use in loading the Catalina internal classes (by accumulating all of the
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/startup/CatalinaBaseConfigurationSource.java tomcat9-9.0.31/java/org/apache/catalina/startup/CatalinaBaseConfigurationSource.java
--- tomcat9-9.0.27/java/org/apache/catalina/startup/CatalinaBaseConfigurationSource.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/startup/CatalinaBaseConfigurationSource.java 2020-02-05 19:26:48.000000000 +0000
@@ -103,7 +103,12 @@
}
// Then try URI.
- URI uri = getURI(name);
+ URI uri = null;
+ try {
+ uri = getURI(name);
+ } catch (IllegalArgumentException e) {
+ throw new IOException(sm.getString("catalinaConfigurationSource.cannotObtainURL", name), e);
+ }
// Obtain the input stream we need
try {
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/startup/ContextConfig.java tomcat9-9.0.31/java/org/apache/catalina/startup/ContextConfig.java
--- tomcat9-9.0.27/java/org/apache/catalina/startup/ContextConfig.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/startup/ContextConfig.java 2020-02-05 19:26:48.000000000 +0000
@@ -1599,6 +1599,9 @@
entry = new DefaultWebXmlCacheEntry(webXmlDefaultFragment,
globalTimeStamp, hostTimeStamp);
hostWebXmlCache.put(host, entry);
+ // Add a Lifecycle listener to the Host that will remove it from
+ // the hostWebXmlCache once the Host is destroyed
+ host.addLifecycleListener(new HostWebXmlCacheCleaner());
}
return webXmlDefaultFragment;
@@ -1715,6 +1718,7 @@
}
}
+
/**
* Scan JARs that contain web-fragment.xml files that will be used to
* configure this application to see if they also contain static resources.
@@ -1784,6 +1788,7 @@
return getWebXmlSource(defaultWebXml, true);
}
+
/**
* Identify the host web.xml to be used and obtain an input source for
* it.
@@ -1956,6 +1961,7 @@
// validation is not enabled
parseRequired = false;
}
+
FragmentJarScannerCallback callback =
new FragmentJarScannerCallback(webXmlParser, delegate, parseRequired);
@@ -2681,6 +2687,18 @@
}
}
+ private static class HostWebXmlCacheCleaner implements LifecycleListener {
+
+ @Override
+ public void lifecycleEvent(LifecycleEvent event) {
+
+ if (Lifecycle.AFTER_DESTROY_EVENT.equals(event.getType())) {
+ Host host = (Host) event.getSource();
+ hostWebXmlCache.remove(host);
+ }
+ }
+ }
+
static class JavaClassCacheEntry {
public final String superclassName;
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/startup/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/startup/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/startup/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/startup/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -15,7 +15,10 @@
catalina.incorrectPermissions=权限错误,此文件没有读取权限
catalina.init=服务器在[{0}]毫秒内初始化
+catalina.initError=初始化 Catalina 时出错
+catalina.noCluster=由于[{0}]未找到群集Ruleset。已禁用群集配置。
catalina.serverStartFail=所必需的服务组件启动失败,所以无法启动Tomcat
+catalina.stopError=停止 Catalina 时出错
catalina.usage=用法: java org.apache.catalina.startup.Catalina [ -config {pathname} ] [ -nonaming ] { -help | start | stop }
catalinaConfigurationSource.cannotObtainURL=无法获取相对路径 [{0}] 的URL。 检查catalina.base是否已设置。
@@ -64,6 +67,9 @@
contextConfig.unavailable=由于之前的错误,标记当前应用程序不可用
contextConfig.unknownUrlProtocol=注解解析过程中,URL协议[{0}]未识别。忽略URL[{1}]。
contextConfig.urlPatternValue=类文件[{1}]的urlPatterns和值属性上同时设置了注解[{0}]
+contextConfig.xmlSettings=上下文[{0}]将解析web.xml和web-fragment.xml文件,验证为:[{1}],命名空间感知为(:[{2}]
+
+embedded.notmp=在[{0}]找不到指定的临时文件夹
engineConfig.cce=生命周期事件数据对象[{0}]不是一个引擎(Engine)
engineConfig.stop=配置引擎,处理进程停止。
@@ -76,13 +82,21 @@
expandWar.missingJarEntry=无法获得 JarEntry [{0}] 的输入流 - WAR 文件是否已损坏?
hostConfig.appBase=主机[{0}]的应用程序基础[{1}]不存在或不是目录。deployOnStartUp和autoDebug已设置为false,以防止部署错误。其他错误仍然可能发生。
+hostConfig.context.remove=移除上下文[{0}]错误
+hostConfig.deployDescriptor.blocked=(:未部署上下文路径为[{0}]的Web应用程序,因为它包含一个部署描述符[{1}],该描述符可能包含安全部署应用程序所需的配置,但此主机的DeployXML设置阻止了部署描述符的处理。应该在[{2}]创建适当的描述符来部署此应用程序。
hostConfig.deployDescriptor.error=部署描述符[{0}]时出错
+hostConfig.deployDescriptor.finished=部署描述符[{0}]的部署已在[{1}]ms内完成
+hostConfig.deployDescriptor.localDocBaseSpecified=(:在主机appBase 中指定了docBase [{0}],将被忽略
hostConfig.deployDir=把web 应用程序部署到目录 [{0}]
+hostConfig.deployDir.error=无法部署应用目录 [{0}]
hostConfig.deployWar.error=部署 Web 应用程序 archive [{0}] 时出错
hostConfig.deployWar.hiddenDir=将忽略目录[{0}],因为WAR [{1}]优先,unpackWAR为false
+hostConfig.deployWar.threaded.error=等待WAR文件的多线程部署完成时出错
hostConfig.docBaseUrlInvalid=所提供的部署目录无法用URL来表示
+hostConfig.expand.error=解压WEB应用程序文件[{0}]时异常
hostConfig.ignorePath=忽略appBase中的路径[{0}]以进行自动部署
hostConfig.jmx.unregister=移除注册上下文[{0}]失败
+hostConfig.start=HostConfig: 开始处理
hostConfig.stop=:)Host配置:停止处理
tomcat.addWebapp.conflictChild=无法在[{0}]处部署到上下文路径[{1}],因为存在上下文[{2}]
@@ -93,6 +107,7 @@
tomcat.noContextXml=不能找到web 应用的context.xml [{0}]
userConfig.database=加载用户数据库异常
+userConfig.deploy.threaded.error=等待用户目录的多线程部署完成时出错
userConfig.deploying=正在部署用户 web 应用程序
userConfig.error=为用户 [{0}]部署web应用发生错误
userConfig.start=用户配置:处理开始
@@ -103,6 +118,7 @@
versionLoggerListener.os.version=OS.版本: {0}
versionLoggerListener.prop=系统属性: {0} = {1}
versionLoggerListener.serverInfo.server.built=服务器构建: {0}
+versionLoggerListener.serverInfo.server.number=服务器版本号(:{0}
versionLoggerListener.serverInfo.server.version=Server.服务器版本: {0}
versionLoggerListener.vm.vendor=JVM.供应商: {0}
versionLoggerListener.vm.version=JVM 版本: {0}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/startup/Tomcat.java tomcat9-9.0.31/java/org/apache/catalina/startup/Tomcat.java
--- tomcat9-9.0.27/java/org/apache/catalina/startup/Tomcat.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/startup/Tomcat.java 2020-02-05 19:26:48.000000000 +0000
@@ -18,13 +18,11 @@
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
-import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.security.Principal;
@@ -119,12 +117,17 @@
* this class.
*
*
- * This class provides a set of convenience methods for configuring webapp
- * contexts, all overloads of the method addWebapp. These methods
- * create a webapp context, configure it, and then add it to a {@link Host}.
- * They do not use a global default web.xml; rather, they add a lifecycle
- * listener that adds the standard DefaultServlet, JSP processing, and welcome
- * files.
+ * This class provides a set of convenience methods for configuring web
+ * application contexts; all overloads of the method addWebapp().
+ * These methods are equivalent to adding a web application to the Host's
+ * appBase (normally the webapps directory). These methods create a Context,
+ * configure it with the equivalent of the defaults provided by
+ * conf/web.xml (see {@link #initWebappDefaults(String)} for
+ * details) and add the Context to a Host. These methods do not use a global
+ * default web.xml; rather, they add a {@link LifecycleListener} to configure
+ * the defaults. Any WEB-INF/web.xml and META-INF/context.xml packaged with the
+ * application will be processed normally. Normal web fragment and
+ * {@link javax.servlet.ServletContainerInitializer} processing will be applied.
*
*
* In complex cases, you may prefer to use the ordinary Tomcat API to create
@@ -223,17 +226,22 @@
hostname = s;
}
+
/**
- * This is equivalent to adding a web application to Tomcat's webapps
- * directory. The equivalent of the default web.xml will be applied to the
- * web application and any WEB-INF/web.xml and META-INF/context.xml packaged
- * with the application will be processed normally. Normal web fragment and
- * {@link javax.servlet.ServletContainerInitializer} processing will be
- * applied.
+ * This is equivalent to adding a web application to a Host's appBase
+ * (usually Tomcat's webapps directory). By default, the equivalent of the
+ * default web.xml will be applied to the web application (see
+ * {@link #initWebappDefaults(String)}). This may be prevented by calling
+ * {@link #setAddDefaultWebXmlToWebapp(boolean)} with {@code false}. Any
+ * WEB-INF/web.xml and META-INF/context.xml
+ * packaged with the application will always be processed and normal web
+ * fragment and {@link javax.servlet.ServletContainerInitializer} processing
+ * will always be applied.
*
* @param contextPath The context mapping to use, "" for root context.
- * @param docBase Base directory for the context, for static files.
- * Must exist, relative to the server home
+ * @param docBase Base directory for the context, for static files. Must
+ * exist, relative to the server home
+ *
* @return the deployed context
*/
public Context addWebapp(String contextPath, String docBase) {
@@ -676,13 +684,24 @@
return ctx;
}
+
/**
- * @param host The host in which the context will be deployed
+ * This is equivalent to adding a web application to a Host's appBase
+ * (usually Tomcat's webapps directory). By default, the equivalent of the
+ * default web.xml will be applied to the web application (see
+ * {@link #initWebappDefaults(String)}). This may be prevented by calling
+ * {@link #setAddDefaultWebXmlToWebapp(boolean)} with {@code false}. Any
+ * WEB-INF/web.xml and META-INF/context.xml
+ * packaged with the application will always be processed and normal web
+ * fragment and {@link javax.servlet.ServletContainerInitializer} processing
+ * will always be applied.
+ *
+ * @param host The host in which the context will be deployed
* @param contextPath The context mapping to use, "" for root context.
- * @param docBase Base directory for the context, for static files.
- * Must exist, relative to the server home
+ * @param docBase Base directory for the context, for static files. Must
+ * exist, relative to the server home
+ *
* @return the deployed context
- * @see #addWebapp(String, String)
*/
public Context addWebapp(Host host, String contextPath, String docBase) {
LifecycleListener listener = null;
@@ -698,14 +717,27 @@
return addWebapp(host, contextPath, docBase, listener);
}
+
/**
- * @param host The host in which the context will be deployed
+ * This is equivalent to adding a web application to a Host's appBase
+ * (usually Tomcat's webapps directory). By default, the equivalent of the
+ * default web.xml will be applied to the web application (see
+ * {@link #initWebappDefaults(String)}). This may be prevented by calling
+ * {@link #setAddDefaultWebXmlToWebapp(boolean)} with {@code false}. Any
+ * WEB-INF/web.xml and META-INF/context.xml
+ * packaged with the application will always be processed and normal web
+ * fragment and {@link javax.servlet.ServletContainerInitializer} processing
+ * will always be applied.
+ *
+ * @param host The host in which the context will be deployed
* @param contextPath The context mapping to use, "" for root context.
- * @param docBase Base directory for the context, for static files.
- * Must exist, relative to the server home
- * @param config Custom context configurator helper
+ * @param docBase Base directory for the context, for static files. Must
+ * exist, relative to the server home
+ * @param config Custom context configuration helper. Any configuration
+ * will be in addition to equivalent of the default
+ * web.xml configuration described above.
+ *
* @return the deployed context
- * @see #addWebapp(String, String)
*/
public Context addWebapp(Host host, String contextPath, String docBase,
LifecycleListener config) {
@@ -716,8 +748,9 @@
ctx.setPath(contextPath);
ctx.setDocBase(docBase);
- if (addDefaultWebXmlToWebapp)
+ if (addDefaultWebXmlToWebapp) {
ctx.addLifecycleListener(getDefaultWebXmlListener());
+ }
ctx.setConfigFile(getWebappConfigFile(docBase, contextPath));
@@ -1007,22 +1040,32 @@
}
}
+
/**
- * Provide default configuration for a context. This is the programmatic
- * equivalent of the default web.xml.
+ * Provide default configuration for a context. This is broadly the
+ * programmatic equivalent of the default web.xml and provides the following
+ * features:
+ *
+ *
Default servlet mapped to "/"
+ *
JSP servlet mapped to "*.jsp" and ""*.jspx"
+ *
Session timeout of 30 minutes
+ *
MIME mappings (subset of those in conf/web.xml)
+ *
Welcome files
+ *
+ * TODO: Align the MIME mappings with conf/web.xml - possibly via a common
+ * file.
*
- * TODO: in normal Tomcat, if default-web.xml is not found, use this
- * method
- *
- * @param contextPath The context to set the defaults for
+ * @param contextPath The path of the context to set the defaults for
*/
public void initWebappDefaults(String contextPath) {
Container ctx = getHost().findChild(contextPath);
initWebappDefaults((Context) ctx);
}
+
/**
- * Static version of {@link #initWebappDefaults(String)}
+ * Static version of {@link #initWebappDefaults(String)}.
+ *
* @param ctx The context to set the defaults for
*/
public static void initWebappDefaults(Context ctx) {
@@ -1415,33 +1458,8 @@
org.apache.catalina.startup.Tomcat tomcat = new org.apache.catalina.startup.Tomcat();
// Create a Catalina instance and let it parse the configuration files
// It will also set a shutdown hook to stop the Server when needed
- tomcat.init(new ConfigurationSource() {
- protected final File userDir = new File(System.getProperty("user.dir"));
- protected final URI userDirUri = userDir.toURI();
- @Override
- public Resource getResource(String name) throws IOException {
- File f = new File(name);
- if (!f.isAbsolute()) {
- f = new File(userDir, name);
- }
- if (f.isFile()) {
- return new Resource(new FileInputStream(f), f.toURI());
- } else {
- throw new FileNotFoundException(name);
- }
- }
- @Override
- public URI getURI(String name) {
- File f = new File(name);
- if (!f.isAbsolute()) {
- f = new File(userDir, name);
- }
- if (f.isFile()) {
- return f.toURI();
- }
- return userDirUri.resolve(name);
- }
- });
+ // Use the default configuration source
+ tomcat.init(null);
boolean await = false;
String path = "";
// Process command line parameters
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/startup/WebappServiceLoader.java tomcat9-9.0.31/java/org/apache/catalina/startup/WebappServiceLoader.java
--- tomcat9-9.0.27/java/org/apache/catalina/startup/WebappServiceLoader.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/startup/WebappServiceLoader.java 2020-02-05 19:26:48.000000000 +0000
@@ -34,6 +34,7 @@
import javax.servlet.ServletContext;
import org.apache.catalina.Context;
+import org.apache.catalina.WebResource;
import org.apache.tomcat.util.scan.JarFactory;
/**
@@ -58,6 +59,7 @@
* @see java.util.ServiceLoader
*/
public class WebappServiceLoader {
+ private static final String CLASSES = "/WEB-INF/classes/";
private static final String LIB = "/WEB-INF/lib/";
private static final String SERVICES = "META-INF/services/";
@@ -94,15 +96,28 @@
LinkedHashSet applicationServicesFound = new LinkedHashSet<>();
LinkedHashSet containerServicesFound = new LinkedHashSet<>();
- ClassLoader loader = servletContext.getClassLoader();
-
// if the ServletContext has ORDERED_LIBS, then use that to specify the
// set of JARs from WEB-INF/lib that should be used for loading services
@SuppressWarnings("unchecked")
- List orderedLibs =
- (List) servletContext.getAttribute(ServletContext.ORDERED_LIBS);
- if (orderedLibs != null) {
- // handle ordered libs directly, ...
+ List orderedLibs = (List) servletContext.getAttribute(ServletContext.ORDERED_LIBS);
+
+ // Handle application SCIs directly...
+ if (orderedLibs == null) {
+ // No ordered libs, so use every service definition we can find
+ WebResource[] resources = context.getResources().getClassLoaderResources("/" + configFile);
+ for (WebResource resource : resources) {
+ if (resource.isFile()) {
+ parseConfigFile(applicationServicesFound, resource.getURL());
+ }
+ }
+ } else {
+ // Ordered libs so only use services defined in those libs and any
+ // in WEB-INF/classes
+ URL unpacked = servletContext.getResource(CLASSES + configFile);
+ if (unpacked != null) {
+ parseConfigFile(applicationServicesFound, unpacked);
+ }
+
for (String lib : orderedLibs) {
URL jarUrl = servletContext.getResource(LIB + lib);
if (jarUrl == null) {
@@ -123,11 +138,11 @@
// no provider file found, this is OK
}
}
-
- // and the parent ClassLoader for all others
- loader = context.getParentClassLoader();
}
+ // and use the parent ClassLoader for all other SCIs
+ ClassLoader loader = context.getParentClassLoader();
+
Enumeration resources;
if (loader == null) {
resources = ClassLoader.getSystemResources(configFile);
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/storeconfig/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/storeconfig/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/storeconfig/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/storeconfig/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -14,3 +14,10 @@
# limitations under the License.
config.objectNameNotFound=目标[{0}]未找到
+
+registry.loadClassFailed=无法加载类 [{0}]
+
+standardContextSF.cannotWriteFile=无法在 [{0}] 写入文件
+
+storeFileMover.directoryCreationError=无法创建目录 [{0}]
+storeFileMover.renameError=无法将 [{0}] 重命名为 [{1}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/group/GroupChannel.java tomcat9-9.0.31/java/org/apache/catalina/tribes/group/GroupChannel.java
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/group/GroupChannel.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/group/GroupChannel.java 2020-02-05 19:26:48.000000000 +0000
@@ -166,7 +166,7 @@
* channel.addInterceptor(A);
* channel.addInterceptor(C);
* channel.addInterceptor(B);
- * Will result in a interceptor stack like this:
+ * Will result in an interceptor stack like this:
* A -> C -> B
* The complete stack will look like this:
* Channel -> A -> C -> B -> ChannelCoordinator
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java tomcat9-9.0.31/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java 2020-02-05 19:26:48.000000000 +0000
@@ -349,9 +349,6 @@
return new BaseEncryptionManager(algorithm,
new SecretKeySpec(encryptionKey, algorithmName),
providerName);
-// else if("ECB".equalsIgnoreCase(algorithmMode)) {
- // Note: ECB is not an appropriate mode for secure communications.
-// return new ECBEncryptionManager(algorithm, new SecretKeySpec(encryptionKey, algorithmName), providerName);
else
throw new IllegalArgumentException(sm.getString("encryptInterceptor.algorithm.unsupported-mode", algorithmMode));
}
@@ -601,32 +598,6 @@
}
}
- @SuppressWarnings("unused")
- private static class ECBEncryptionManager extends BaseEncryptionManager
- {
- public ECBEncryptionManager(String algorithm, SecretKeySpec secretKey, String providerName)
- throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException {
- super(algorithm, secretKey, providerName);
- }
-
- private static final byte[] EMPTY_IV = new byte[0];
-
- @Override
- protected int getIVSize() {
- return 0;
- }
-
- @Override
- protected byte[] generateIVBytes() {
- return EMPTY_IV;
- }
-
- @Override
- protected AlgorithmParameterSpec generateIV(byte[] bytes, int offset, int length) {
- return null;
- }
- }
-
static class ChannelConfigException
extends ChannelException
{
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -17,7 +17,9 @@
domainFilterInterceptor.message.refused=从集群[{0}]中接收的消息被拒绝
encryptInterceptor.decrypt.error.short-message=解密消息失败: 结尾消息提前结束
+encryptInterceptor.decrypt.failed=无法解密信息
encryptInterceptor.encrypt.failed=无法加密信息
+encryptInterceptor.init.failed=初始化EncryptInterceptor失败
encryptInterceptor.tcpFailureDetector.ordering=加密拦截器必须位于TCP故障检测器的上游。请重新订购加密拦截器,将其列在通道拦截器管道中的TCP故障检测器之前。
gzipInterceptor.report=:)GZip 拦截器报告[\n\
@@ -39,6 +41,7 @@
messageDispatchInterceptor.unableAdd.queue=无法将消息添加到异步队列,队列 bug?
messageDispatchInterceptor.warning.optionflag=警告!你正在覆盖异步选项标志,这将禁用其它程序可能用到的 Channel.SEND_OPTIONS_ASYNCHRONOUS。
+nonBlockingCoordinator.heartbeat.inconsistency=心跳发现不一致,重新启动选举
nonBlockingCoordinator.memberAlive.failed=无法执行成员活动检查,猜测成员下线。
nonBlockingCoordinator.processCoordinationMessage.failed=处理协调消息时出错。 可能是致命的。
@@ -49,6 +52,7 @@
tcpFailureDetector.failureDetection.failed=无法进行失败监测,假定成员宕机。[{0}]
tcpFailureDetector.heartbeat.failed=TCP心跳检测器无法执行心跳
tcpFailureDetector.member.disappeared=认证完成。成员消失[{0}]
+tcpFailureDetector.memberDisappeared.verify=(:收到的membermissed[{0}]消息。将验证。
tcpFailureDetector.still.alive=验证完成。成员 [{0}] 仍然存活
tcpFailureDetector.suspectMember.alive=验证可疑成员服务器还活着。[{0}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/group/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/tribes/group/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/group/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/group/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -17,5 +17,9 @@
channelCoordinator.invalid.startLevel=启动级别无效,有效级别为:SND_RX_SEQ,SND_TX_SEQ,MBR_TX_SEQ,MBR_RX_SEQ
groupChannel.listener.alreadyExist=侦听器已存在:[{0}][{1}]
+groupChannel.noDestination=没有指定目的地
+groupChannel.nullMessage=无法发送空消息
+groupChannel.optionFlag.conflict=拦截器选项标志冲突:[{0}]
+groupChannel.unable.deserialize=无法反序列化消息:[{0}]
groupChannel.unable.sendHeartbeat=无法通过Tribes拦截器堆栈发送心跳。 会再试一次。
groupChannel.warn.noUtilityExecutor=没有公共的executor 被设置时,创建一个
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java tomcat9-9.0.31/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java 2020-02-05 19:26:48.000000000 +0000
@@ -147,7 +147,7 @@
public void setLocalMemberProperties(String listenHost, int listenPort, int securePort, int udpPort) {
if (log.isDebugEnabled()) {
log.debug(String.format("setLocalMemberProperties(%s, %d, %d, %d)", listenHost,
- Integer.toString(listenPort), Integer.toString(securePort), Integer.toString(udpPort)));
+ Integer.valueOf(listenPort), Integer.valueOf(securePort), Integer.valueOf(udpPort)));
}
properties.setProperty("tcpListenHost", listenHost);
properties.setProperty("tcpListenPort", String.valueOf(listenPort));
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/membership/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/tribes/membership/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/membership/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/membership/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,12 +13,20 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+McastService.parseSoTimeout=无法解析SoTimeout:[{0}]
+McastService.payload=无法发送负载更新
+
mcastService.missing.property=McastService:缺少必需属性 [{0}]。
mcastServiceImpl.bind=尝试将多播套接字绑定到 [{0}:{1}]
+mcastServiceImpl.bind.failed=绑定到多播地址失败。仅绑定到端口。
+mcastServiceImpl.error.receiving=接收mcast包时出错。睡眠500毫秒
mcastServiceImpl.invalid.startLevel=无效的启动级别。只接受以下级别:Channel.MBR_RX_SEQ或 Channel.MBR_TX_SEQ
+mcastServiceImpl.invalid.stopLevel=无效的停止级别。只有Channel.MBR_RX_SEQ和Channel.MBR_TX_SEQ是可接受的级别
mcastServiceImpl.recovery=家族成员,运行恢复线程,广播不是功能。
mcastServiceImpl.recovery.stopFailed=恢复线程未能停止成员服务。
+mcastServiceImpl.recovery.successful=成员身份恢复成功。
+mcastServiceImpl.send.failed=无法发送多播信息
mcastServiceImpl.send.running=McastService.send已经运行
mcastServiceImpl.setInterface=设置多宿主多播接口为:[{0}]
mcastServiceImpl.setSoTimeout=设置集群多播超时时间:[{0}]
@@ -26,12 +34,14 @@
mcastServiceImpl.unable.join=无法加入多播组,请确保你的系统已启用多播。
mcastServiceImpl.unableReceive.broadcastMessage=无法接收广播消息。
-memberImpl.large.payload=负载太大对于处理许多...
+memberImpl.large.payload=负载太大以至于难以处理
memberImpl.notEnough.bytes=成员包中的字节不够。
+memberImpl.package.small=成员包太小以至于不能校验。
staticMember.invalid.uuidLength=UUID必须正好是16个字节,而不是:[{0}]
staticMembershipProvider.leftOver.ignored=消息 [{0}] 被忽略。
+staticMembershipProvider.pingThread.failed=无法发送ping。
staticMembershipProvider.startMembership.noReplies=0响应,可能超时
staticMembershipProvider.stopMembership.sendFailed=无法发送成员消息
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/tipis/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/tribes/tipis/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/tipis/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/tipis/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -15,14 +15,20 @@
abstractReplicatedMap.broadcast.noReplies=广播收到0回复,可能是超时了。
abstractReplicatedMap.leftOver.ignored=消息[{0}]被忽略
+abstractReplicatedMap.member.disappeared=成员[{0}]已消失。相关的映射项将重新定位到新节点。
abstractReplicatedMap.transferState.noReplies=传输状态,0响应,也许是超时。
abstractReplicatedMap.unable.get=无法复制 AbstractReplicatedMap.get 操作的数据
+abstractReplicatedMap.unable.put=无法复制AbstractReplicatedMap.Put操作的数据
+abstractReplicatedMap.unable.relocate=无法将[{0}]重新定位到新的备份节点
abstractReplicatedMap.unableSelect.backup=无法选择备用节点
abstractReplicatedMap.unableSend.startMessage=无法发送map启动消息。
+abstractReplicatedMap.unableStart=无法启动复制Map
lazyReplicatedMap.unableReplicate.proxy=不能复制proxy key:[{0}]到备份:[{1}]. 原因是:[{2}]
+mapMessage.deserialize.error.key=反序列化MapMessage主键失败
mapMessage.deserialize.error.value=MapMessage.value的反序列化误差
+replicatedMap.member.disappeared=成员[{0}]消失,关联的键值实体会重新关联到一个新的节点。
replicatedMap.relocate.complete=map 条目的重定位在 [{0}] ms内完成。
replicatedMap.unable.relocate=不能为一个新的备份节点重启定位[{0}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/bio/LocalStrings_pt_BR.properties tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/bio/LocalStrings_pt_BR.properties
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/bio/LocalStrings_pt_BR.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/bio/LocalStrings_pt_BR.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,4 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+bioReceiver.socket.closeFailed=Falha ao encerrar a conexão do socket
+
bioSender.send.again=Enviar dados novamente para [{0}:{1,number,integer}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/bio/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/bio/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/bio/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/bio/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -21,8 +21,12 @@
bioReplicationTask.socket.closeFailed=无法关闭套接字
bioReplicationTask.unable.service=不能服务bio套接字
+bioSender.ack.eof=在本地端口[{0}:{1,number,integer}]达到EOF
bioSender.ack.missing=不能读确认表格:[{0}] {1,number,integer}] in {2,number,integer} 毫秒, 失去socket连接, 重试连接.
bioSender.ack.wrong=在本地端口[{0}:{1,number,integer}]读取10个字节后丢失正确的ACK
bioSender.closeSocket=发件人关闭套接字到[{0}:{1,number,integer}](关闭计数{2,数字,整数})
+bioSender.fail.AckReceived=收到一个失败的 ack ):org.apache.catalina.tribes.transport.Constants.FAIL_ACK_DATA
bioSender.openSocket=发件人打开套接字到[{0}:{1,number,integer}](打开计数{2,数字,整数})
bioSender.send.again=再次发送数据到 [{0}:{1,number,integer}]
+
+pooledMultiSender.unable.retrieve.sender=无法获取数据发送器,超时([{0}] ms)错误
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/nio/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/nio/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/nio/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/nio/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -20,13 +20,17 @@
nioReceiver.run.fail=不能允许复制监听器
nioReceiver.start.fail=无法启动集群接收器
nioReceiver.stop.fail=无法关闭集群接收的选择器
+nioReceiver.stop.threadRunning=NioReceiver线程没有及时停止。关闭选择器时可能会观察到错误。
nioReceiver.threadpool.fail=ThreadPool 无法初始化。 监听器未启动。
nioReplicationTask.error.register.key=错误的注册key被读取:[{0}]
nioReplicationTask.process.clusterMsg.failed=处理集群消息失败
nioReplicationTask.unable.ack=不能通过channel发送ack,channel已经断开?[{0}]
+nioSender.datagram.already.established=数据报通道已经建立。连接可能正在进行中。
nioSender.not.connected=NioSender未连接,这是不应该发生的。
+nioSender.sender.disconnected=发件人已断开连接,无法处理选择密钥。
+nioSender.unable.receive.ack=无法接收确认消息。已到达套接字通道上的EOF。
nioSender.unknown.state=数据处于未知状态。readyOps = [{0}]
parallelNioSender.send.fail.retrying=成员发送失败:[{0}]; 设置为怀疑并重试。
@@ -35,4 +39,5 @@
parallelNioSender.sender.disconnected.sendFailed=发送失败且sender已断开连接,不再重试。
pooledParallelSender.sender.disconnected=sender 未连接。
+pooledParallelSender.unable.open=无法打开nio选择器。
pooledParallelSender.unable.retrieveSender=无法从sender池中获取一个sender
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java 2020-02-05 19:26:48.000000000 +0000
@@ -209,7 +209,6 @@
}
}
return result;
-
}
private static class SendResult {
@@ -372,4 +371,4 @@
if ( result ) try { selector.selectNow(); }catch (Exception e){/*Ignore*/}
return result;
}
-}
\ No newline at end of file
+}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/ReceiverBase.java tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/ReceiverBase.java
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/transport/ReceiverBase.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/transport/ReceiverBase.java 2020-02-05 19:26:48.000000000 +0000
@@ -220,10 +220,10 @@
/**
* Same as bind() except it does it for the UDP port
- * @param socket The socket to bind
- * @param portstart Starting port for bind attempts
- * @param retries Number of times to attempt to bind (port incremented
- * between attempts)
+ * @param socket The socket to bind
+ * @param portstart Starting port for bind attempts
+ * @param retries Number of times to attempt to bind (port incremented
+ * between attempts)
* @return int The retry count
* @throws IOException Socket bind error
*/
@@ -603,4 +603,4 @@
this.maxIdleTime = maxIdleTime;
}
-}
\ No newline at end of file
+}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/tribes/util/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/tribes/util/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/tribes/util/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/tribes/util/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -17,3 +17,4 @@
arrays.length.outOfBounds=当前key下没有足够的元素,长度越界
executorFactory.not.running=执行器没有运行,无法强制把命令送入队列
+executorFactory.queue.full=队列已满
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/util/LifecycleBase.java tomcat9-9.0.31/java/org/apache/catalina/util/LifecycleBase.java
--- tomcat9-9.0.27/java/org/apache/catalina/util/LifecycleBase.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/util/LifecycleBase.java 2020-02-05 19:26:48.000000000 +0000
@@ -432,8 +432,8 @@
private void handleSubClassException(Throwable t, String key, Object... args) throws LifecycleException {
- ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
+ ExceptionUtils.handleThrowable(t);
String msg = sm.getString(key, args);
if (getThrowOnFailure()) {
if (!(t instanceof LifecycleException)) {
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/util/LocalStrings_pt_BR.properties tomcat9-9.0.31/java/org/apache/catalina/util/LocalStrings_pt_BR.properties
--- tomcat9-9.0.27/java/org/apache/catalina/util/LocalStrings_pt_BR.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/util/LocalStrings_pt_BR.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-customObjectInputStream.nomatch=A classe [{0}] não casou com a expressão regular [{1}] para classes permitidas para deserialização
+customObjectInputStream.nomatch=A classe [{0}] não combina com a expressão regular [{1}] para classes permitidas para deserialização
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/util/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/util/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/util/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/util/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -23,9 +23,15 @@
introspection.classLoadFailed=加载 class [{0}] 失败
+lifecycleBase.alreadyStarted=在调用start()之后,在组件[{0}]上调用start()方法。第二个电话将被忽略。
+lifecycleBase.destroyStopFail=在失败组件[{0}]上调用Stop()以触发清理,但也失败了
lifecycleBase.initFail=初始化组件[{0}]失败。
+lifecycleBase.setState=设置状态从[{0}]到[{1}]
+
+lifecycleMBeanBase.registerFail=在组件初始化期间,无法注册名为{1}的对象{0}]
netmask.cidrNegative=CIDR [{0}]为负数。
netmask.invalidAddress=地址 [{0}] 无效
+sessionIdGeneratorBase.randomAlgorithm=使用算法[{0}]初始化随机数生成器时发生异常
sessionIdGeneratorBase.randomProvider=使用程序提供的初始化随机数生成器异常[{0}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/util/TomcatCSS.java tomcat9-9.0.31/java/org/apache/catalina/util/TomcatCSS.java
--- tomcat9-9.0.27/java/org/apache/catalina/util/TomcatCSS.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/util/TomcatCSS.java 2020-02-05 19:26:48.000000000 +0000
@@ -22,15 +22,13 @@
public class TomcatCSS {
public static final String TOMCAT_CSS =
- "h1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} " +
- "h2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} " +
- "h3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} " +
- "body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} " +
- "b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} " +
- "p {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;} " +
+ "body {font-family:Tahoma,Arial,sans-serif;} " +
+ "h1, h2, h3, b {color:white;background-color:#525D76;} " +
+ "h1 {font-size:22px;} " +
+ "h2 {font-size:16px;} " +
+ "h3 {font-size:14px;} " +
+ "p {font-size:12px;} " +
"a {color:black;} " +
- "a.name {color:black;} " +
".line {height:1px;background-color:#525D76;border:none;}";
-
-}
\ No newline at end of file
+}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/valves/AbstractAccessLogValve.java tomcat9-9.0.31/java/org/apache/catalina/valves/AbstractAccessLogValve.java
--- tomcat9-9.0.27/java/org/apache/catalina/valves/AbstractAccessLogValve.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/valves/AbstractAccessLogValve.java 2020-02-05 19:26:48.000000000 +0000
@@ -456,7 +456,13 @@
protected AccessLogElement[] logElements = null;
/**
- * Should this valve set request attributes for IP address, hostname,
+ * Array of elements where the value needs to be cached at the start of the
+ * request.
+ */
+ protected CachedElement[] cachedElements = null;
+
+ /**
+ * Should this valve use request attributes for IP address, hostname,
* protocol and port used for the request.
* Default is false.
* @see #setRequestAttributesEnabled(boolean)
@@ -563,6 +569,7 @@
this.pattern = pattern;
}
logElements = createLogElements();
+ cachedElements = createCachedElements(logElements);
}
/**
@@ -675,6 +682,9 @@
// to be cached in the request.
request.getAttribute(Globals.CERTIFICATES_ATTR);
}
+ for (CachedElement element : cachedElements) {
+ element.cache(request);
+ }
getNext().invoke(request, response);
}
@@ -797,7 +807,20 @@
protected interface AccessLogElement {
public void addElement(CharArrayWriter buf, Date date, Request request,
Response response, long time);
+ }
+ /**
+ * Marks an AccessLogElement as needing to be have the value cached at the
+ * start of the request rather than just recorded at the end as the source
+ * data for the element may not be available at the end of the request. This
+ * typically occurs for remote network information, such as ports, IP
+ * addresses etc. when the connection is closed unexpectedly. These elements
+ * take advantage of these values being cached elsewhere on first request
+ * and do not cache the value in the element since the elements are
+ * state-less.
+ */
+ protected interface CachedElement {
+ public void cache(Request request);
}
/**
@@ -849,7 +872,7 @@
/**
* write remote IP address - %a
*/
- protected class RemoteAddrElement implements AccessLogElement {
+ protected class RemoteAddrElement implements AccessLogElement, CachedElement {
@Override
public void addElement(CharArrayWriter buf, Date date, Request request,
Response response, long time) {
@@ -870,12 +893,19 @@
}
buf.append(value);
}
+
+ @Override
+ public void cache(Request request) {
+ if (!requestAttributesEnabled) {
+ request.getRemoteAddr();
+ }
+ }
}
/**
* write remote host name - %h
*/
- protected class HostElement implements AccessLogElement {
+ protected class HostElement implements AccessLogElement, CachedElement {
@Override
public void addElement(CharArrayWriter buf, Date date, Request request,
Response response, long time) {
@@ -898,6 +928,13 @@
}
buf.append(value);
}
+
+ @Override
+ public void cache(Request request) {
+ if (!requestAttributesEnabled) {
+ request.getRemoteHost();
+ }
+ }
}
/**
@@ -1183,7 +1220,7 @@
/**
* write local or remote port for request connection - %p and %{xxx}p
*/
- protected class PortElement implements AccessLogElement {
+ protected class PortElement implements AccessLogElement, CachedElement {
/**
* Type of port to log
@@ -1230,6 +1267,13 @@
}
}
}
+
+ @Override
+ public void cache(Request request) {
+ if (portType == PortType.REMOTE) {
+ request.getRemotePort();
+ }
+ }
}
/**
@@ -1668,6 +1712,18 @@
return list.toArray(new AccessLogElement[0]);
}
+
+ private CachedElement[] createCachedElements(AccessLogElement[] elements) {
+ List list = new ArrayList<>();
+ for (AccessLogElement element : elements) {
+ if (element instanceof CachedElement) {
+ list.add((CachedElement) element);
+ }
+ }
+ return list.toArray(new CachedElement[0]);
+ }
+
+
/**
* Create an AccessLogElement implementation which needs an element name.
* @param name Header name
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/valves/AccessLogValve.java tomcat9-9.0.31/java/org/apache/catalina/valves/AccessLogValve.java
--- tomcat9-9.0.27/java/org/apache/catalina/valves/AccessLogValve.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/valves/AccessLogValve.java 2020-02-05 19:26:48.000000000 +0000
@@ -610,10 +610,10 @@
// Log this message
try {
+ message.write(System.lineSeparator());
synchronized(this) {
if (writer != null) {
message.writeTo(writer);
- writer.println("");
if (!buffered) {
writer.flush();
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/valves/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/valves/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/valves/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/valves/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -18,10 +18,13 @@
accessLogValve.invalidPortType=端口类型 [{0}] 无效,使用服务器(本地)端口
accessLogValve.openFail=无法打开访问日志文件[{0}]。
accessLogValve.rotateFail=失败的循环切割访问日志.
+accessLogValve.writeFail=无法写入日志消息[{0}]
errorReportValve.description=描述
+errorReportValve.errorPageNotFound=在[{0}]无法找到静态错误页面
errorReportValve.exceptionReport=异常报告
errorReportValve.message=消息
+errorReportValve.note=):注意
errorReportValve.rootCauseInLogs=主要问题的全部 stack 信息可以在 server logs 里查看
errorReportValve.unknownReason=未知的原因
@@ -46,34 +49,48 @@
http.409.desc=由于和目标资源对当前状态发生冲突,所以请求无法完成。
http.409.reason=冲突
http.410.desc=原始服务器上不再可以访问目标资源,并且此条件可能是永久性的。
+http.411.reason=所需长度
http.412.desc=在服务器上测试时,请求头字段中给出的一个或多个条件被评估为false。
http.412.reason=前置条件失败
http.413.reason=有效载荷过大
http.414.desc=服务器拒绝为请求提供服务,因为请求目标比服务器愿意解释的要长。
http.415.desc=源服务器拒绝服务请求,因为有效负载的格式在目标资源上此方法不支持。
http.415.reason=不支持的媒体类型
+http.416.desc=(:请求的范围头字段中的任何范围都没有与选定资源的当前范围重叠,或者请求的范围集由于无效范围或小范围或重叠范围的过度请求而被拒绝。
http.416.reason=范围不满足
+http.417.desc=(:至少有一个入站服务器无法满足请求的Expect头字段中给定的期望。
http.417.reason=期望的失败
http.421.desc=请求被定向到一台无法响应的服务器
http.423.desc=源或目标资源的方法被锁
http.423.reason=已锁定
+http.424.desc=这个方法不能在这个资源上执行,因为请求操作依赖另一个操作,但是另一个操作失败了。
+http.424.reason=失败的依赖项
+http.426.desc=服务器拒绝使用当前协议执行请求,但可能愿意在客户端升级到其他协议后执行。
http.426.reason=需要升级
http.428.desc=原始服务器要求请求是有条件的。
http.429.reason=请求过多
http.431.reason=请求头的字段太大
+http.451.desc=服务器出于法律原因拒绝了此请求。
http.500.desc=服务器遇到一个意外的情况,阻止它完成请求。
http.502.desc=服务器在充当网关或代理时, 在尝试完成请求时, 从它访问的入站服务器收到无效响应。
http.503.desc=由于临时过载或计划维护,服务器当前无法处理请求,这可能会在一些延迟后得到缓解。
+http.504.desc=服务器在充当网关或代理时,没有从上游服务器接收到完成请求所需访问的及时响应。
+http.504.reason=网关超时
http.505.reason=HTTP 版本不支持
+http.506.desc=服务器内部配置错误:选取的变体资源配置为自身去处理透明的内容协商,因此在协商进程中不是一个合适的终点。
+http.507.desc=无法对资源执行该方法,因为服务器无法存储成功完成请求所需的表示。
http.507.reason=存储空间.不足
http.510.reason=没有.扩展
http.511.desc=客户端需要进行身份验证才能获得网络访问权限。
remoteCidrValve.noRemoteIp=客户端没有IP地址。请求被拒绝。
+remoteIpValve.invalidHostHeader=在HTTP请求头[{1}]中发现Host的无效值[{0}]
remoteIpValve.invalidPortHeader=HTTP标头[{1}]中的端口找到的值[{0}]无效
requestFilterValve.configInvalid=为Remote [Addr | Host]阀门提供了一个或多个无效配置设置,阻止Valve及其父容器启动
requestFilterValve.deny=根据[{1}]配置拒绝[{0}]的请求
sslValve.invalidProvider=与此{[0}]请求关联的连接器上指定的SSL提供程序无效。 无法处理证书数据。
+
+stuckThreadDetectionValve.notifyStuckThreadDetected=线程[{0}](id=[{6}])已处于活动状态[{1}]毫秒(自[{2}]起),以便为[{4}]提供相同的请求,并且可能被卡住(此StuckThreadDetectionValve的配置阈值为[{5}]秒)。总共有{3}个线程受此阀监视,可能被卡住。
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/valves/rewrite/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/valves/rewrite/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/valves/rewrite/LocalStrings_zh_CN.properties 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/valves/rewrite/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,16 @@
+# 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.
+
+rewriteValve.readError=读取配置时发生异常
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java tomcat9-9.0.31/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java
--- tomcat9-9.0.27/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,135 @@
+/*
+ * 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.valves.rewrite;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+public class QuotedStringTokenizer {
+
+ private Iterator tokenIterator;
+ private int tokenCount;
+ private int returnedTokens = 0;
+
+ enum WordMode {
+ SPACES, QUOTED, ESCAPED, SIMPLE, COMMENT
+ }
+
+ public QuotedStringTokenizer(String text) {
+ List tokens;
+ if (text != null) {
+ tokens = tokenizeText(text);
+ } else {
+ tokens = Collections.emptyList();
+ }
+ this.tokenCount = tokens.size();
+ this.tokenIterator = tokens.iterator();
+ }
+
+ private List tokenizeText(String inputText) {
+ List tokens = new ArrayList<>();
+ int pos = 0;
+ int length = inputText.length();
+ WordMode currentMode = WordMode.SPACES;
+ StringBuilder currentToken = new StringBuilder();
+ while (pos < length) {
+ char currentChar = inputText.charAt(pos);
+ switch (currentMode) {
+ case SPACES:
+ currentMode = handleSpaces(currentToken, currentChar);
+ break;
+ case QUOTED:
+ currentMode = handleQuoted(tokens, currentToken, currentChar);
+ break;
+ case ESCAPED:
+ currentToken.append(currentChar);
+ currentMode = WordMode.QUOTED;
+ break;
+ case SIMPLE:
+ currentMode = handleSimple(tokens, currentToken, currentChar);
+ break;
+ case COMMENT:
+ if (currentChar == '\r' || currentChar == '\n') {
+ currentMode = WordMode.SPACES;
+ }
+ break;
+ default:
+ throw new IllegalStateException(
+ "Couldn't tokenize text '" + inputText + "' after position " + pos + " from mode " + currentMode);
+ }
+ pos++;
+ }
+ String possibleLastToken = currentToken.toString();
+ if (!possibleLastToken.isEmpty()) {
+ tokens.add(possibleLastToken);
+ }
+ return tokens;
+ }
+
+ private WordMode handleSimple(List tokens, StringBuilder currentToken, char currentChar) {
+ if (Character.isWhitespace(currentChar)) {
+ tokens.add(currentToken.toString());
+ currentToken.setLength(0);
+ return WordMode.SPACES;
+ } else {
+ currentToken.append(currentChar);
+ }
+ return WordMode.SIMPLE;
+ }
+
+ private WordMode handleQuoted(List tokens, StringBuilder currentToken, char currentChar) {
+ if (currentChar == '"') {
+ tokens.add(currentToken.toString());
+ currentToken.setLength(0);
+ return WordMode.SPACES;
+ } else if (currentChar == '\\') {
+ return WordMode.ESCAPED;
+ } else {
+ currentToken.append(currentChar);
+ }
+ return WordMode.QUOTED;
+ }
+
+ private WordMode handleSpaces(StringBuilder currentToken, char currentChar) {
+ if (!Character.isWhitespace(currentChar)) {
+ if (currentChar == '"') {
+ return WordMode.QUOTED;
+ } else if (currentChar == '#') {
+ return WordMode.COMMENT;
+ } else {
+ currentToken.append(currentChar);
+ return WordMode.SIMPLE;
+ }
+ }
+ return WordMode.SPACES;
+ }
+
+ public boolean hasMoreTokens() {
+ return tokenIterator.hasNext();
+ }
+
+ public String nextToken() {
+ returnedTokens++;
+ return tokenIterator.next();
+ }
+
+ public int countTokens() {
+ return tokenCount - returnedTokens;
+ }
+}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/valves/rewrite/RewriteMap.java tomcat9-9.0.31/java/org/apache/catalina/valves/rewrite/RewriteMap.java
--- tomcat9-9.0.27/java/org/apache/catalina/valves/rewrite/RewriteMap.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/valves/rewrite/RewriteMap.java 2020-02-05 19:26:48.000000000 +0000
@@ -16,9 +16,58 @@
*/
package org.apache.catalina.valves.rewrite;
+/**
+ * Interface for user defined lookup/replacement logic that can be defined in
+ * a {@code rewrite.config} file by a {@code RewriteMap} directive. Such a map
+ * can then be used by a {@code RewriteRule} defined in the same file.
+ *
+ * An example {@code rewrite.config} file could look like:
+ *
+ *
+ * One parameter can be optionally appended to the {@code RewriteMap} directive.
+ * This could be used – for example – to specify a name of a file, that
+ * contains a lookup table used by the implementation of the map.
+ */
public interface RewriteMap {
+ /**
+ * Optional parameter that can be defined through the {@code RewriteMap}
+ * directive in the {@code rewrite.config} file.
+ *
+ * @param params the optional parameter
+ * @return value is currently ignored
+ */
public String setParameters(String params);
+ /**
+ * Optional parameters that can be defined through the {@code RewriteMap}
+ * directive in the {@code rewrite.config} file.
+ *
+ * This method will be called, if there are more than one parameters defined.
+ *
+ * @param params the optional parameters
+ */
+ default void setParameters(String... params) {
+ if (params == null) {
+ return;
+ }
+ if (params.length > 1) {
+ throw new IllegalArgumentException("Too many parameters for this map");
+ }
+ setParameters(params[0]);
+ }
+
+ /**
+ * Maps a key to a replacement value.
+ * The method is free to return {@code null} to indicate, that the default
+ * value from the {@code RewriteRule} directive should be used.
+ *
+ * @param key used by the actual implementation to generate a mapped value
+ * @return mapped value or {@code null}
+ */
public String lookup(String key);
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/valves/rewrite/RewriteValve.java tomcat9-9.0.31/java/org/apache/catalina/valves/rewrite/RewriteValve.java
--- tomcat9-9.0.27/java/org/apache/catalina/valves/rewrite/RewriteValve.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/valves/rewrite/RewriteValve.java 2020-02-05 19:26:48.000000000 +0000
@@ -572,7 +572,7 @@
* @return The condition, rule or map resulting from parsing the line
*/
public static Object parse(String line) {
- StringTokenizer tokenizer = new StringTokenizer(line);
+ QuotedStringTokenizer tokenizer = new QuotedStringTokenizer(line);
if (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (token.equals("RewriteCond")) {
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/WebResourceRoot.java tomcat9-9.0.31/java/org/apache/catalina/WebResourceRoot.java
--- tomcat9-9.0.27/java/org/apache/catalina/WebResourceRoot.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/WebResourceRoot.java 2020-02-05 19:26:48.000000000 +0000
@@ -140,7 +140,8 @@
* application. It must start with '/'.
*
* @return The objects that represents the class loader resources at the
- * given path
+ * given path. There will always be at least one element although
+ * that element may represent a resource that is not present.
*/
WebResource[] getClassLoaderResources(String path);
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/webresources/CachedResource.java tomcat9-9.0.31/java/org/apache/catalina/webresources/CachedResource.java
--- tomcat9-9.0.27/java/org/apache/catalina/webresources/CachedResource.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/webresources/CachedResource.java 2020-02-05 19:26:48.000000000 +0000
@@ -17,13 +17,28 @@
package org.apache.catalina.webresources;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
+import java.net.JarURLConnection;
+import java.net.MalformedURLException;
import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.nio.charset.Charset;
+import java.security.Permission;
import java.security.cert.Certificate;
+import java.text.Collator;
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
import java.util.jar.Manifest;
import org.apache.catalina.WebResource;
import org.apache.catalina.WebResourceRoot;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.res.StringManager;
/**
* This class is designed to wrap a 'raw' WebResource and providing caching for
@@ -32,6 +47,9 @@
*/
public class CachedResource implements WebResource {
+ private static final Log log = LogFactory.getLog(CachedResource.class);
+ private static final StringManager sm = StringManager.getManager(CachedResource.class);
+
// Estimate (on high side to be safe) of average size excluding content
// based on profiler data.
private static final long CACHE_ENTRY_SIZE = 500;
@@ -314,7 +332,48 @@
@Override
public URL getURL() {
- return webResource.getURL();
+ /*
+ * We don't want applications using this URL to access the resource
+ * directly as that could lead to inconsistent results when the resource
+ * is updated on the file system but the cache entry has not yet
+ * expired. We saw this, for example, in JSP compilation.
+ * - last modified time was obtained via
+ * ServletContext.getResource("path").openConnection().getLastModified()
+ * - JSP content was obtained via
+ * ServletContext.getResourceAsStream("path")
+ * The result was that the JSP modification was detected but the JSP
+ * content was read from the cache so the non-updated JSP page was
+ * used to generate the .java and .class file
+ *
+ * One option to resolve this issue is to use a custom URL scheme for
+ * resource URLs. This would allow us, via registration of a
+ * URLStreamHandlerFactory, to control how the resources are accessed
+ * and ensure that all access go via the cache We took this approach for
+ * war: URLs so we can use jar:war:file: URLs to reference resources in
+ * unpacked WAR files. However, because URL.setURLStreamHandlerFactory()
+ * may only be caused once, this can cause problems when using other
+ * libraries that also want to use a custom URL scheme.
+ *
+ * The approach below allows us to insert a custom URLStreamHandler
+ * without registering a custom protocol. The only limitation (compared
+ * to registering a custom protocol) is that if the application
+ * constructs the same URL from a String, they will access the resource
+ * directly and not via the cache.
+ */
+ URL resourceURL = webResource.getURL();
+ if (resourceURL == null) {
+ return null;
+ }
+ try {
+ CachedResourceURLStreamHandler handler =
+ new CachedResourceURLStreamHandler(resourceURL, root, webAppPath, usesClassLoaderResources);
+ URL result = new URL(null, resourceURL.toExternalForm(), handler);
+ handler.setAssociatedURL(result);
+ return result;
+ } catch (MalformedURLException e) {
+ log.error(sm.getString("cachedResource.invalidURL", resourceURL.toExternalForm()), e);
+ return null;
+ }
}
@Override
@@ -355,4 +414,188 @@
}
return result;
}
+
+
+ /*
+ * Mimics the behaviour of FileURLConnection.getInputStream for a directory.
+ * Deliberately uses default locale.
+ */
+ private static InputStream buildInputStream(String[] files) {
+ Arrays.sort(files, Collator.getInstance(Locale.getDefault()));
+ StringBuilder result = new StringBuilder();
+ for (String file : files) {
+ result.append(file);
+ // Every entry is followed by \n including the last
+ result.append('\n');
+ }
+ return new ByteArrayInputStream(result.toString().getBytes(Charset.defaultCharset()));
+ }
+
+
+ private static class CachedResourceURLStreamHandler extends URLStreamHandler {
+
+ private final URL resourceURL;
+ private final StandardRoot root;
+ private final String webAppPath;
+ private final boolean usesClassLoaderResources;
+
+ private URL associatedURL = null;
+
+ public CachedResourceURLStreamHandler(URL resourceURL, StandardRoot root, String webAppPath,
+ boolean usesClassLoaderResources) {
+ this.resourceURL = resourceURL;
+ this.root = root;
+ this.webAppPath = webAppPath;
+ this.usesClassLoaderResources = usesClassLoaderResources;
+ }
+
+ protected void setAssociatedURL(URL associatedURL) {
+ this.associatedURL = associatedURL;
+ }
+
+ @Override
+ protected URLConnection openConnection(URL u) throws IOException {
+ // This deliberately uses ==. If u isn't the URL object this
+ // URLStreamHandler was constructed for we do not want to use this
+ // URLStreamHandler to create a connection.
+ if (associatedURL != null && u == associatedURL) {
+ if ("jar".equals(associatedURL.getProtocol())) {
+ return new CachedResourceJarURLConnection(resourceURL, root, webAppPath, usesClassLoaderResources);
+ } else {
+ return new CachedResourceURLConnection(resourceURL, root, webAppPath, usesClassLoaderResources);
+ }
+ } else {
+ // The stream handler has been inherited by a URL that was
+ // constructed from a cache URL. We need to break that link.
+ URL constructedURL = new URL(u.toExternalForm());
+ return constructedURL.openConnection();
+ }
+ }
+ }
+
+
+ /*
+ * Keep this in sync with CachedResourceJarURLConnection.
+ */
+ private static class CachedResourceURLConnection extends URLConnection {
+
+ private final StandardRoot root;
+ private final String webAppPath;
+ private final boolean usesClassLoaderResources;
+ private final URL resourceURL;
+
+ protected CachedResourceURLConnection(URL resourceURL, StandardRoot root, String webAppPath,
+ boolean usesClassLoaderResources) {
+ super(resourceURL);
+ this.root = root;
+ this.webAppPath = webAppPath;
+ this.usesClassLoaderResources = usesClassLoaderResources;
+ this.resourceURL = resourceURL;
+ }
+
+ @Override
+ public void connect() throws IOException {
+ // NO-OP
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ WebResource resource = getResource();
+ if (resource.isDirectory()) {
+ return buildInputStream(resource.getWebResourceRoot().list(webAppPath));
+ } else {
+ return getResource().getInputStream();
+ }
+ }
+
+ @Override
+ public Permission getPermission() throws IOException {
+ // Doesn't trigger a call to connect for file:// URLs
+ return resourceURL.openConnection().getPermission();
+ }
+
+ @Override
+ public long getLastModified() {
+ return getResource().getLastModified();
+ }
+
+ @Override
+ public long getContentLengthLong() {
+ return getResource().getContentLength();
+ }
+
+ private WebResource getResource() {
+ return root.getResource(webAppPath, false, usesClassLoaderResources);
+ }
+ }
+
+
+ /*
+ * Keep this in sync with CachedResourceURLConnection.
+ */
+ private static class CachedResourceJarURLConnection extends JarURLConnection {
+
+ private final StandardRoot root;
+ private final String webAppPath;
+ private final boolean usesClassLoaderResources;
+ private final URL resourceURL;
+
+ protected CachedResourceJarURLConnection(URL resourceURL, StandardRoot root, String webAppPath,
+ boolean usesClassLoaderResources) throws IOException {
+ super(resourceURL);
+ this.root = root;
+ this.webAppPath = webAppPath;
+ this.usesClassLoaderResources = usesClassLoaderResources;
+ this.resourceURL = resourceURL;
+ }
+
+ @Override
+ public void connect() throws IOException {
+ // NO-OP
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ WebResource resource = getResource();
+ if (resource.isDirectory()) {
+ return buildInputStream(resource.getWebResourceRoot().list(webAppPath));
+ } else {
+ return getResource().getInputStream();
+ }
+ }
+
+ @Override
+ public Permission getPermission() throws IOException {
+ // Doesn't trigger a call to connect for jar:// URLs
+ return resourceURL.openConnection().getPermission();
+ }
+
+ @Override
+ public long getLastModified() {
+ return getResource().getLastModified();
+ }
+
+ @Override
+ public long getContentLengthLong() {
+ return getResource().getContentLength();
+ }
+
+ private WebResource getResource() {
+ return root.getResource(webAppPath, false, usesClassLoaderResources);
+ }
+
+ @Override
+ public JarFile getJarFile() throws IOException {
+ return ((JarURLConnection) resourceURL.openConnection()).getJarFile();
+ }
+
+ @Override
+ public JarEntry getJarEntry() throws IOException {
+ if (getEntryName() == null) {
+ return null;
+ } else {
+ return super.getJarEntry();
+ }
+ }
+ }
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/webresources/LocalStrings_ko.properties tomcat9-9.0.31/java/org/apache/catalina/webresources/LocalStrings_ko.properties
--- tomcat9-9.0.27/java/org/apache/catalina/webresources/LocalStrings_ko.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/webresources/LocalStrings_ko.properties 2020-02-05 19:26:48.000000000 +0000
@@ -25,6 +25,8 @@
cache.objectMaxSizeTooBig=objectMaxSize를 위한 값 [{0}]kB이, maxSize/20인 최대한계값 보다 커서, [{1}]kB로 줄여졌습니다.
cache.objectMaxSizeTooBigBytes=[{0}]kB를 캐시하기 위해, 최대 객체 크기로서 지정된 값이 Integer.MAX_VALUE 바이트보다 큰데, Integer.MAX_VALUE는 캐시될 수 있는 최대 크기입니다. 한계 값을 Integer.MAX_VALUE 바이트로 설정하겠습니다.
+cachedResource.invalidURL=URL [{0}]이(가) 유효하지 않기 때문에 CachedResourceURLStreamHandler 인스턴스를 생성할 수 없습니다.
+
classpathUrlStreamHandler.notFound=쓰레드 컨텍스트 클래스로더 또는 현재 클래스의 클래스로더를 사용하여, 리소스 [{0}]을(를) 로드할 수 없습니다.
dirResourceSet.manifestFail=[{0}](으)로부터 manifest를 읽지 못했습니다.
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/webresources/LocalStrings.properties tomcat9-9.0.31/java/org/apache/catalina/webresources/LocalStrings.properties
--- tomcat9-9.0.27/java/org/apache/catalina/webresources/LocalStrings.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/webresources/LocalStrings.properties 2020-02-05 19:26:48.000000000 +0000
@@ -25,6 +25,8 @@
cache.objectMaxSizeTooBig=The value of [{0}]kB for objectMaxSize is larger than the limit of maxSize/20 so has been reduced to [{1}]kB
cache.objectMaxSizeTooBigBytes=The value specified for the maximum object size to cache [{0}]kB is greater than Integer.MAX_VALUE bytes which is the maximum size that can be cached. The limit will be set to Integer.MAX_VALUE bytes.
+cachedResource.invalidURL=Unable to create an instance of CachedResourceURLStreamHandler because the URL [{0}] is malformed
+
classpathUrlStreamHandler.notFound=Unable to load the resource [{0}] using the thread context class loader or the current class''s class loader
dirResourceSet.manifestFail=Failed to read manifest from [{0}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/webresources/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/webresources/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/webresources/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/webresources/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+abstractArchiveResourceSet.setReadOnlyFalse=基于存档的WebResourceSets 如基于jar的WebResourceSets 硬编码为只读,并且不能配置为读写
+
cache.addFail=无法将位于[{0}]的资源添加到Web应用程序[{1}]的缓存中,因为在清除过期缓存条目后可用空间仍不足 - 请考虑增加缓存的最大空间。
dirResourceSet.notDirectory=基本和内部路径[{0}] {1} [{2}]指定的目录不存在。
@@ -20,9 +22,14 @@
extractingRoot.jarFailed=解压JAR文件[{0}]失败
extractingRoot.targetFailed=无法为提取的 JAR 文件创建目录 [{0}]
+fileResource.getCanonicalPathFail=不能判断资源的标准路径[{0}]
fileResource.getUrlFail=不能决定一个url 为资源[{0}]
+jarResource.getInputStreamFail=无法获取JAR[{1}]中的资源文件[{0}]的一个InputStream
+
standardRoot.checkStateNotStarted=如果当前未启动资源,则可能无法访问这些资源
standardRoot.createUnknownType=无法为未知类型[{0}]创建WebResourceSet。
+standardRoot.invalidPathNormal=资源路径[{0}]已规范化为无效的[{1}]
+standardRoot.lockedFile=Web应用程序[{0}]无法关闭通过以下堆栈跟踪打开的文件[{1}]
standardRoot.noContext=尚未为WebResourceRoot配置上下文
standardRoot.startInvalidMain=指定的主资源集 [{0}] 无效
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/webresources/StandardRoot.java tomcat9-9.0.31/java/org/apache/catalina/webresources/StandardRoot.java
--- tomcat9-9.0.27/java/org/apache/catalina/webresources/StandardRoot.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/webresources/StandardRoot.java 2020-02-05 19:26:48.000000000 +0000
@@ -207,7 +207,7 @@
return getResource(path, true, false);
}
- private WebResource getResource(String path, boolean validate,
+ protected WebResource getResource(String path, boolean validate,
boolean useClassLoaderResources) {
if (validate) {
path = validate(path);
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/AbstractProcessor.java tomcat9-9.0.31/java/org/apache/coyote/AbstractProcessor.java
--- tomcat9-9.0.27/java/org/apache/coyote/AbstractProcessor.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/AbstractProcessor.java 2020-02-05 19:26:48.000000000 +0000
@@ -98,7 +98,9 @@
* @param t The error which occurred
*/
protected void setErrorState(ErrorState errorState, Throwable t) {
- response.setError();
+ // Use the return value to avoid processing more than one async error
+ // in a single async cycle.
+ boolean setError = response.setError();
boolean blockIo = this.errorState.isIoAllowed() && !errorState.isIoAllowed();
this.errorState = this.errorState.getMostSevere(errorState);
// Don't change the status code for IOException since that is almost
@@ -110,17 +112,10 @@
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.
- asyncStateMachine.asyncMustError();
- if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString("abstractProcessor.nonContainerThreadError"), t);
+ if (blockIo && isAsync() && setError) {
+ if (asyncStateMachine.asyncError()) {
+ processSocketEvent(SocketEvent.ERROR, true);
}
- processSocketEvent(SocketEvent.ERROR, true);
}
}
@@ -252,15 +247,25 @@
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
+ SocketState state;
+
if (getErrorState().isError()) {
request.updateCounters();
- return SocketState.CLOSED;
+ state = SocketState.CLOSED;
} else if (isAsync()) {
- return SocketState.LONG;
+ state = SocketState.LONG;
} else {
request.updateCounters();
- return dispatchEndRequest();
+ state = dispatchEndRequest();
+ }
+
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Socket: [" + socketWrapper +
+ "], Status in: [" + status +
+ "], State out: [" + state + "]");
}
+
+ return state;
}
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/AbstractProcessorLight.java tomcat9-9.0.31/java/org/apache/coyote/AbstractProcessorLight.java
--- tomcat9-9.0.27/java/org/apache/coyote/AbstractProcessorLight.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/AbstractProcessorLight.java 2020-02-05 19:26:48.000000000 +0000
@@ -46,19 +46,18 @@
do {
if (dispatches != null) {
DispatchType nextDispatch = dispatches.next();
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Processing dispatch type: [" + nextDispatch + "]");
+ }
state = dispatch(nextDispatch.getSocketStatus());
+ if (!dispatches.hasNext()) {
+ state = checkForPipelinedData(state, socketWrapper);
+ }
} else if (status == SocketEvent.DISCONNECT) {
// Do nothing here, just wait for it to get recycled
} else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) {
state = dispatch(status);
- if (state == SocketState.OPEN) {
- // There may be pipe-lined data to read. If the data isn't
- // processed now, execution will exit this loop and call
- // release() which will recycle the processor (and input
- // buffer) deleting any pipe-lined data. To avoid this,
- // process it now.
- state = service(socketWrapper);
- }
+ state = checkForPipelinedData(state, socketWrapper);
} else if (status == SocketEvent.OPEN_WRITE) {
// Extra write event likely after async, ignore
state = SocketState.LONG;
@@ -98,6 +97,21 @@
}
+ private SocketState checkForPipelinedData(SocketState inState, SocketWrapperBase> socketWrapper)
+ throws IOException {
+ if (inState == SocketState.OPEN) {
+ // There may be pipe-lined data to read. If the data isn't
+ // processed now, execution will exit this loop and call
+ // release() which will recycle the processor (and input
+ // buffer) deleting any pipe-lined data. To avoid this,
+ // process it now.
+ return service(socketWrapper);
+ } else {
+ return inState;
+ }
+ }
+
+
public void addDispatch(DispatchType dispatchType) {
synchronized (dispatches) {
dispatches.add(dispatchType);
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/AbstractProtocol.java tomcat9-9.0.31/java/org/apache/coyote/AbstractProtocol.java
--- tomcat9-9.0.27/java/org/apache/coyote/AbstractProtocol.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/AbstractProtocol.java 2020-02-05 19:26:48.000000000 +0000
@@ -19,7 +19,7 @@
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.Collections;
-import java.util.Map;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
@@ -391,11 +391,17 @@
public void addWaitingProcessor(Processor processor) {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(sm.getString("abstractProcotol.waitingProcerssor.add", processor));
+ }
waitingProcessors.add(processor);
}
public void removeWaitingProcessor(Processor processor) {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(sm.getString("abstractProcotol.waitingProcerssor.remove", processor));
+ }
waitingProcessors.remove(processor);
}
@@ -731,7 +737,6 @@
private final AbstractProtocol proto;
private final RequestGroupInfo global = new RequestGroupInfo();
private final AtomicLong registerCount = new AtomicLong(0);
- private final Map connections = new ConcurrentHashMap<>();
private final RecycledProcessors recycledProcessors = new RecycledProcessors(this);
public ConnectionHandler(AbstractProtocol proto) {
@@ -770,7 +775,7 @@
S socket = wrapper.getSocket();
- Processor processor = connections.get(socket);
+ Processor processor = (Processor) wrapper.getCurrentProcessor();
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.connectionsGet",
processor, socket));
@@ -805,11 +810,12 @@
// OpenSSL typically returns null whereas JSSE typically
// returns "" when no protocol is negotiated
if (negotiatedProtocol != null && negotiatedProtocol.length() > 0) {
- UpgradeProtocol upgradeProtocol =
- getProtocol().getNegotiatedProtocol(negotiatedProtocol);
+ UpgradeProtocol upgradeProtocol = getProtocol().getNegotiatedProtocol(negotiatedProtocol);
if (upgradeProtocol != null) {
- processor = upgradeProtocol.getProcessor(
- wrapper, getProtocol().getAdapter());
+ processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(sm.getString("abstractConnectionHandler.processorCreate", processor));
+ }
} else if (negotiatedProtocol.equals("http/1.1")) {
// Explicitly negotiated the default protocol.
// Obtain a processor below.
@@ -822,9 +828,8 @@
// replace the code below with the commented out
// block.
if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString(
- "abstractConnectionHandler.negotiatedProcessor.fail",
- negotiatedProtocol));
+ getLog().debug(sm.getString("abstractConnectionHandler.negotiatedProcessor.fail",
+ negotiatedProtocol));
}
return SocketState.CLOSED;
/*
@@ -841,20 +846,22 @@
if (processor == null) {
processor = recycledProcessors.pop();
if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString("abstractConnectionHandler.processorPop",
- processor));
+ getLog().debug(sm.getString("abstractConnectionHandler.processorPop", processor));
}
}
if (processor == null) {
processor = getProtocol().createProcessor();
register(processor);
+ if (getLog().isDebugEnabled()) {
+ getLog().debug(sm.getString("abstractConnectionHandler.processorCreate", processor));
+ }
}
processor.setSslSupport(
wrapper.getSslSupport(getProtocol().getClientCertProvider()));
// Associate the processor with the connection
- connections.put(socket, processor);
+ wrapper.setCurrentProcessor(processor);
SocketState state = SocketState.CLOSED;
do {
@@ -873,7 +880,7 @@
wrapper, getProtocol().getAdapter());
wrapper.unRead(leftOverInput);
// Associate with the processor with the connection
- connections.put(socket, processor);
+ wrapper.setCurrentProcessor(processor);
} else {
if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString(
@@ -896,7 +903,7 @@
// Mark the connection as upgraded
wrapper.setUpgraded(true);
// Associate with the processor with the connection
- connections.put(socket, processor);
+ wrapper.setCurrentProcessor(processor);
// Initialise the upgrade handler (which may trigger
// some IO using the new protocol which is why the lines
// above are necessary)
@@ -934,7 +941,7 @@
} else if (state == SocketState.OPEN) {
// In keep-alive but between requests. OK to recycle
// processor. Continue to poll for the next request.
- connections.remove(socket);
+ wrapper.setCurrentProcessor(null);
release(processor);
wrapper.registerReadInterest();
} else if (state == SocketState.SENDFILE) {
@@ -960,7 +967,7 @@
// Connection closed. OK to recycle the processor.
// Processors handling upgrades require additional clean-up
// before release.
- connections.remove(socket);
+ wrapper.setCurrentProcessor(null);
if (processor.isUpgrade()) {
UpgradeToken upgradeToken = processor.getUpgradeToken();
HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
@@ -1020,7 +1027,7 @@
// Make sure socket/processor is removed from the list of current
// connections
- connections.remove(socket);
+ wrapper.setCurrentProcessor(null);
release(processor);
return SocketState.CLOSED;
}
@@ -1040,7 +1047,15 @@
@Override
public Set getOpenSockets() {
- return connections.keySet();
+ Set> set = proto.getEndpoint().getConnections();
+ Set result = new HashSet<>();
+ for (SocketWrapperBase socketWrapper : set) {
+ S socket = socketWrapper.getSocket();
+ if (socket != null) {
+ result.add(socket);
+ }
+ }
+ return result;
}
@@ -1071,7 +1086,9 @@
// recycledProcessors since that pool is only for AJP or
// HTTP processors
recycledProcessors.push(processor);
- getLog().debug("Pushed Processor [" + processor + "]");
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Pushed Processor [" + processor + "]");
+ }
}
}
}
@@ -1083,8 +1100,8 @@
*/
@Override
public void release(SocketWrapperBase socketWrapper) {
- S socket = socketWrapper.getSocket();
- Processor processor = connections.remove(socket);
+ Processor processor = (Processor) socketWrapper.getCurrentProcessor();
+ socketWrapper.setCurrentProcessor(null);
release(processor);
}
@@ -1104,13 +1121,13 @@
",name=" + getProtocol().getProtocolName() +
"Request" + count);
if (getLog().isDebugEnabled()) {
- getLog().debug("Register " + rpName);
+ getLog().debug("Register [" + processor + "] as [" + rpName + "]");
}
Registry.getRegistry(null, null).registerComponent(rp,
rpName, null);
rp.setRpName(rpName);
} catch (Exception e) {
- getLog().warn("Error registering request");
+ getLog().warn(sm.getString("abstractProtocol.processorRegisterError"), e);
}
}
}
@@ -1129,13 +1146,13 @@
rp.setGlobalProcessor(null);
ObjectName rpName = rp.getRpName();
if (getLog().isDebugEnabled()) {
- getLog().debug("Unregister " + rpName);
+ getLog().debug("Unregister [" + rpName + "]");
}
Registry.getRegistry(null, null).unregisterComponent(
rpName);
rp.setRpName(null);
} catch (Exception e) {
- getLog().warn("Error unregistering request", e);
+ getLog().warn(sm.getString("abstractProtocol.processorUnregisterError"), e);
}
}
}
@@ -1152,8 +1169,11 @@
* Note that even if the endpoint is resumed, there is (currently)
* no API to inform the Processors of this.
*/
- for (Processor processor : connections.values()) {
- processor.pause();
+ for (SocketWrapperBase wrapper : proto.getEndpoint().getConnections()) {
+ Processor processor = (Processor) wrapper.getCurrentProcessor();
+ if (processor != null) {
+ processor.pause();
+ }
}
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/ajp/AbstractAjpProtocol.java tomcat9-9.0.31/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
--- tomcat9-9.0.27/java/org/apache/coyote/ajp/AbstractAjpProtocol.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/ajp/AbstractAjpProtocol.java 2020-02-05 19:26:48.000000000 +0000
@@ -16,6 +16,9 @@
*/
package org.apache.coyote.ajp;
+import java.net.InetAddress;
+import java.util.regex.Pattern;
+
import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.Processor;
import org.apache.coyote.UpgradeProtocol;
@@ -46,6 +49,8 @@
setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
// AJP does not use Send File
getEndpoint().setUseSendfile(false);
+ // AJP listens on loopback by default
+ getEndpoint().setAddress(InetAddress.getLoopbackAddress());
ConnectionHandler cHandler = new ConnectionHandler<>(this);
setHandler(cHandler);
getEndpoint().setHandler(cHandler);
@@ -139,17 +144,60 @@
}
- private String requiredSecret = null;
+ private String secret = null;
+ /**
+ * Set the secret that must be included with every request.
+ *
+ * @param secret The required secret
+ */
+ public void setSecret(String secret) {
+ this.secret = secret;
+ }
+ protected String getSecret() {
+ return secret;
+ }
/**
* Set the required secret that must be included with every request.
*
* @param requiredSecret The required secret
+ *
+ * @deprecated Replaced by {@link #setSecret(String)}.
+ * Will be removed in Tomcat 11 onwards
*/
+ @Deprecated
public void setRequiredSecret(String requiredSecret) {
- this.requiredSecret = requiredSecret;
+ setSecret(requiredSecret);
}
+ /**
+ * @return The current secret
+ *
+ * @deprecated Replaced by {@link #getSecret()}.
+ * Will be removed in Tomcat 11 onwards
+ */
+ @Deprecated
protected String getRequiredSecret() {
- return requiredSecret;
+ return getSecret();
+ }
+
+
+ private boolean secretRequired = true;
+ public void setSecretRequired(boolean secretRequired) {
+ this.secretRequired = secretRequired;
+ }
+ public boolean getSecretRequired() {
+ return secretRequired;
+ }
+
+
+ private Pattern allowedRequestAttributesPattern;
+ public void setAllowedRequestAttributesPattern(String allowedRequestAttributesPattern) {
+ this.allowedRequestAttributesPattern = Pattern.compile(allowedRequestAttributesPattern);
+ }
+ public String getAllowedRequestAttributesPattern() {
+ return allowedRequestAttributesPattern.pattern();
+ }
+ protected Pattern getAllowedRequestAttributesPatternInternal() {
+ return allowedRequestAttributesPattern;
}
@@ -206,4 +254,16 @@
throw new IllegalStateException(sm.getString("ajpprotocol.noUpgradeHandler",
upgradeToken.getHttpUpgradeHandler().getClass().getName()));
}
+
+
+ @Override
+ public void start() throws Exception {
+ if (getSecretRequired()) {
+ String secret = getSecret();
+ if (secret == null || secret.length() == 0) {
+ throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
+ }
+ }
+ super.start();
+ }
}
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/ajp/AjpProcessor.java tomcat9-9.0.31/java/org/apache/coyote/ajp/AjpProcessor.java
--- tomcat9-9.0.27/java/org/apache/coyote/ajp/AjpProcessor.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/ajp/AjpProcessor.java 2020-02-05 19:26:48.000000000 +0000
@@ -25,6 +25,11 @@
import java.security.NoSuchProviderException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.servlet.http.HttpServletResponse;
@@ -78,6 +83,9 @@
private static final byte[] pongMessageArray;
+ private static final Set javaxAttributes;
+
+
static {
// Allocate the end message array
AjpMessage endMessage = new AjpMessage(16);
@@ -118,6 +126,14 @@
pongMessageArray = new byte[pongMessage.getLen()];
System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
0, pongMessage.getLen());
+
+ // Build the Set of javax attributes
+ Set s = new HashSet<>();
+ s.add("javax.servlet.request.cipher_suite");
+ s.add("javax.servlet.request.key_size");
+ s.add("javax.servlet.request.ssl_session");
+ s.add("javax.servlet.request.X509Certificate");
+ javaxAttributes= Collections.unmodifiableSet(s);
}
@@ -313,15 +329,18 @@
this.socketWrapper = socket;
boolean cping = false;
- boolean keptAlive = false;
+ // Expected to block on the first read as there should be at least one
+ // AJP message to read.
+ boolean firstRead = true;
while (!getErrorState().isError() && !protocol.isPaused()) {
// Parsing the request header
try {
// Get first message of the request
- if (!readMessage(requestHeaderMessage, !keptAlive)) {
+ if (!readMessage(requestHeaderMessage, firstRead)) {
break;
}
+ firstRead = false;
// Processing the request so make sure the connection rather
// than keep-alive timeout is used
@@ -340,6 +359,9 @@
socketWrapper.write(true, pongMessageArray, 0, pongMessageArray.length);
socketWrapper.flush(true);
} catch (IOException e) {
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Pong message failed", e);
+ }
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
}
recycle();
@@ -353,7 +375,6 @@
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, null);
break;
}
- keptAlive = true;
request.setStartTime(System.currentTimeMillis());
} catch (IOException e) {
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
@@ -693,8 +714,8 @@
}
// Decode extra attributes
- String requiredSecret = protocol.getRequiredSecret();
- boolean secret = false;
+ String secret = protocol.getSecret();
+ boolean secretPresentInRequest = false;
byte attributeCode;
while ((attributeCode = requestHeaderMessage.getByte())
!= Constants.SC_A_ARE_DONE) {
@@ -723,8 +744,26 @@
}
} else if(n.equals(Constants.SC_A_SSL_PROTOCOL)) {
request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, v);
+ } else if (n.equals("JK_LB_ACTIVATION")) {
+ request.setAttribute(n, v);
+ } else if (javaxAttributes.contains(n)) {
+ request.setAttribute(n, v);
} else {
- request.setAttribute(n, v );
+ // All 'known' attributes will be processed by the previous
+ // blocks. Any remaining attribute is an 'arbitrary' one.
+ Pattern pattern = protocol.getAllowedRequestAttributesPatternInternal();
+ if (pattern == null) {
+ response.setStatus(403);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ } else {
+ Matcher m = pattern.matcher(n);
+ if (m.matches()) {
+ request.setAttribute(n, v);
+ } else {
+ response.setStatus(403);
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
+ }
+ }
}
break;
@@ -796,9 +835,9 @@
case Constants.SC_A_SECRET:
requestHeaderMessage.getBytes(tmpMB);
- if (requiredSecret != null) {
- secret = true;
- if (!tmpMB.equals(requiredSecret)) {
+ if (secret != null) {
+ secretPresentInRequest = true;
+ if (!tmpMB.equals(secret)) {
response.setStatus(403);
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
@@ -814,7 +853,7 @@
}
// Check if secret was submitted if required
- if ((requiredSecret != null) && !secret) {
+ if ((secret != null) && !secretPresentInRequest) {
response.setStatus(403);
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/ajp/LocalStrings.properties tomcat9-9.0.31/java/org/apache/coyote/ajp/LocalStrings.properties
--- tomcat9-9.0.27/java/org/apache/coyote/ajp/LocalStrings.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/ajp/LocalStrings.properties 2020-02-05 19:26:48.000000000 +0000
@@ -28,5 +28,6 @@
ajpprocessor.request.process=Error processing request
ajpprotocol.noSSL=SSL is not supported with AJP. The SSL host configuration for [{0}] was ignored
+ajpprotocol.nosecret=The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
ajpprotocol.noUpgrade=Upgrade is not supported with AJP. The UpgradeProtocol configuration for [{0}] was ignored
ajpprotocol.noUpgradeHandler=Upgrade is not supported with AJP. The HttpUpgradeHandler [{0}] can not be processed
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/ajp/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/coyote/ajp/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/coyote/ajp/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/ajp/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -16,6 +16,8 @@
ajpmessage.null=不能赋空值
ajpmessage.overflow=在缓冲区[{1}]位置添加[{0}]字节时发生溢出错误
+ajpprocessor.certs.fail=):证书转换失败
ajpprocessor.header.error=头部信息解析失败
+ajpprocessor.readtimeout=从Socket读取数据超时
ajpprotocol.noUpgrade=AJP 不支持升级。[{0}] 的升级协议配置被忽略。
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/AsyncStateMachine.java tomcat9-9.0.31/java/org/apache/coyote/AsyncStateMachine.java
--- tomcat9-9.0.27/java/org/apache/coyote/AsyncStateMachine.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/AsyncStateMachine.java 2020-02-05 19:26:48.000000000 +0000
@@ -31,108 +31,91 @@
*
* The internal states that are used are:
* DISPATCHED - Standard request. Not in Async mode.
- * STARTING - ServletRequest.startAsync() has been called but the
- * request in which that call was made has not finished
- * processing.
- * STARTED - ServletRequest.startAsync() has been called and the
- * request in which that call was made has finished
- * processing.
+ * STARTING - ServletRequest.startAsync() has been called from
+ * Servlet.service() but service() has not exited.
+ * STARTED - ServletRequest.startAsync() has been called from
+ * Servlet.service() and service() has exited.
* READ_WRITE_OP - Performing an asynchronous read or write.
* MUST_COMPLETE - ServletRequest.startAsync() followed by complete() have
* been called during a single Servlet.service() method. The
- * complete() will be processed as soon as the request
- * finishes.
- * COMPLETE_PENDING - ServletRequest.startAsync() has been called and before the
- * request in which that call was had finished processing,
- * complete() was called for a non-container thread. The
- * complete() will be processed as soon as the request
- * finishes. This is different to MUST_COMPLETE because of
- * differences required to avoid race conditions during error
- * handling.
+ * complete() will be processed as soon as Servlet.service()
+ * exits.
+ * COMPLETE_PENDING - ServletRequest.startAsync() has been called from
+ * Servlet.service() but, before service() exited, complete()
+ * was called from another thread. The complete() will
+ * be processed as soon as Servlet.service() exits.
* COMPLETING - The call to complete() was made once the request was in
- * the STARTED state. May or may not be triggered by a
- * container thread - depends if start(Runnable) was used.
+ * the STARTED state.
* TIMING_OUT - The async request has timed out and is waiting for a call
- * to complete(). If that isn't made, the error state will
- * entered.
+ * to complete() or dispatch(). If that isn't made, the error
+ * state will be entered.
* MUST_DISPATCH - ServletRequest.startAsync() followed by dispatch() have
* been called during a single Servlet.service() method. The
- * dispatch() will be processed as soon as the request
- * finishes.
- * DISPATCH_PENDING - ServletRequest.startAsync() has been called and before the
- * request in which that call was had finished processing,
- * dispatch() was called for a non-container thread. The
- * dispatch() will be processed as soon as the request
- * finishes. This is different to MUST_DISPATCH because of
- * differences required to avoid race conditions during error
- * handling.
+ * dispatch() will be processed as soon as Servlet.service()
+ * exits.
+ * DISPATCH_PENDING - ServletRequest.startAsync() has been called from
+ * Servlet.service() but, before service() exited, dispatch()
+ * was called from another thread. The dispatch() will
+ * be processed as soon as Servlet.service() exits.
* DISPATCHING - The dispatch is being processed.
- * MUST_ERROR - ServletRequest.startAsync() has been called followed by an
- * I/O error on a non-container thread. The main purpose of
- * this state is to prevent additional async actions
- * (complete(), dispatch() etc.) on the non-container thread.
- * The container will perform the necessary error handling,
- * including ensuring that the AsyncLister.onError() method
- * is called.
+ * MUST_ERROR - ServletRequest.startAsync() has been called from
+ * Servlet.service() but, before service() exited, an I/O
+ * error occured on another thread. The container will
+ * perform the necessary error handling when
+ * Servlet.service() exits.
* ERROR - Something went wrong.
*
- * |-----«-------------------------------«------------------------------|
- * | |
- * | error() |
- * |-----------------»---| | |--«--------MUST_ERROR---------------«------------------------| |
- * | \|/ \|/\|/ | |
- * | |----------«-----E R R O R--«-----------------------«-------------------------------| | |
- * | | complete() /|\/|\\ \-«--------------------------------«-------| | | |
- * | | | | \ | | | |
- * | | |-----»-------| | \-----------»----------| | | | |
- * | | | | |dispatch() | | ^ |
- * | | | | \|/ ^ | | |
- * | | | | |--|timeout() | | | | |
- * | | | post() | | \|/ | post() | | | |
- * | | | |---------- | --»DISPATCHED«---------- | --------------COMPLETING«-----| | | |
- * | | | | | /|\/|\ | | | /|\ /|\ | | | |
- * | | | | |---»- | ---| | |startAsync() | timeout()|--| | | | | |
- * | | ^ ^ | | | | | | | ^ | |
- * | | | | | |-- \ -----| | complete() | |post() | | | |
- * | | | | | | \ | /--»----- | ---COMPLETE_PENDING-»-| ^ | | |
- * | | | | | | \ | / | | | | |
- * | | | | | ^ \ | / | complete() | | | |
- * | \|/ | | | | \ \|/ / post() | /---»-----| | ^ |
- * | MUST_COMPLETE-«- | - | --«----STARTING--»--------- | ------------| / | | |
- * | /|\ /|\ | | complete() | \ | | / error() | | ^
- * | | | | | | \ | | //---»----------| | |
- * | | | ^ | dispatch()| \ | post() | // | |
- * | | | | | | \ | |-----| | // nct-io-error | |
- * | | | | | | \ | | | | ///---»---------------| |
- * | | | | | \|/ \ | | \|/\| ||| |
- * | | | | |--«--MUST_DISPATCH-----«-----| |--«--STARTED«---------«---------| |
- * | | | | dispatched() /|\ | \ / | | post() | |
- * | | | | | | \ / | | | |
- * | | | | | | \ / | | | |
- * | | | | | |post() | | | | ^ |
- * ^ | ^ | | | \|/ | | |asyncOperation() | |
- * | | | ^ | | DISPATCH_PENDING | | | | |
- * | | | | | | |post() | | | | |
- * | | | | | | | |----------| | |»-READ_WRITE_OP--»---| |
- * | | | | | | | | dispatch() | | | | |
- * | | | | | | | | | | | | |
- * | | | |post() | | | | timeout()| | | | error()|
- * | | | |dispatched() | \|/\|/ \|/ | dispatch()| | |-»--------|
- * | | | |---«---------- | ---DISPATCHING«-----«------ | ------«----| |
- * | | | | | ^ | |
- * | | | | |----| | |
- * | | | | timeout() | |
- * | | | | | |
- * | | | | dispatch() \|/ |
- * | | | |-----------«-----------TIMING_OUT |
- * | | | | | |
- * | | |-------«----------------------------------«------| | |
- * | | complete() | |
- * | | | |
- * |«- | ----«-------------------«-------------------------------«--| |
- * | error() |
- * | complete() |
- * |----------------------------------------------------------------------------|
+ *
+ * The valid state transitions are:
+ *
+ * post() dispatched()
+ * |-------»------------------»---------| |-------«-----------------------«-----|
+ * | | | |
+ * | | | post() |
+ * | post() \|/ \|/ dispatched() |
+ * | |-----»----------------»DISPATCHED«-------------«-------------| |
+ * | | | /|\ | | |
+ * | | startAsync()| |--|timeout() | |
+ * ^ | | | |
+ * | | complete() | dispatch() ^ |
+ * | | |--«---------------«-- | ---«--MUST_ERROR--»-----| | |
+ * | | | | /|\ | | |
+ * | ^ | | | | | |
+ * | | | | /-----|error() | | |
+ * | | | | / | ^ |
+ * | | \|/ ST-complete() \|/ / ST-dispatch() \|/ | |
+ * | MUST_COMPLETE«--------«--------STARTING--------»---------»MUST_DISPATCH |
+ * | / | \ |
+ * | / | \ |
+ * | OT-complete() / | \ OT-dispatch() |
+ * | COMPLETE_PENDING«------«------/ | \-------»---------»DISPATCH_PENDING |
+ * | | | | |
+ * | post()| timeout() post()| post() post()| timeout() |
+ * | | |--| | |--| | |--| |
+ * | \|/ \|/ | complete() \|/\|/ | dispatch() \|/ \|/ | |
+ * |--«-----COMPLETING«--------«----------STARTED--------»---------»DISPATCHING----|
+ * /|\ /|\ /|\ | /|\ \ /|\ /|\ /|\
+ * | | | | \ \asyncOperation() | | |
+ * | | | timeout()| \ \ | | |
+ * | | | | \ \ | | |
+ * | | | | \ \ | | |
+ * | | | | \ \ | | |
+ * | | | | \ \ | | |
+ * | | | | post()\ \ dispatch()| | |
+ * | | | complete() | \ \|/ | | |
+ * | | |---«------------«-- | --«---READ_WRITE----»----| | |
+ * | | | | |
+ * | | complete() \|/ dispatch() | |
+ * | |------------«-------TIMING_OUT--------»----------------| |
+ * | |
+ * | complete() dispatch() |
+ * |---------------«-----------ERROR--------------»-----------------|
+ *
+ *
+ * Notes: * For clarity, the transitions to ERROR which are valid from every state apart from
+ * STARTING are not shown.
+ * * All transitions may happen on either the Servlet.service() thread (ST) or on any
+ * other thread (OT) unless explicitly marked.
*
*/
class AsyncStateMachine {
@@ -278,10 +261,12 @@
*/
synchronized SocketState asyncPostProcess() {
if (state == AsyncState.COMPLETE_PENDING) {
- doComplete();
+ clearNonBlockingListeners();
+ state = AsyncState.COMPLETING;
return SocketState.ASYNC_END;
} else if (state == AsyncState.DISPATCH_PENDING) {
- doDispatch();
+ clearNonBlockingListeners();
+ state = AsyncState.DISPATCHING;
return SocketState.ASYNC_END;
} else if (state == AsyncState.STARTING || state == AsyncState.READ_WRITE_OP) {
state = AsyncState.STARTED;
@@ -312,27 +297,42 @@
if (!ContainerThreadMarker.isContainerThread() && state == AsyncState.STARTING) {
state = AsyncState.COMPLETE_PENDING;
return false;
- } else {
- return doComplete();
}
- }
-
- private synchronized boolean doComplete() {
clearNonBlockingListeners();
- boolean doComplete = false;
- if (state == AsyncState.STARTING || state == AsyncState.TIMING_OUT ||
- state == AsyncState.ERROR || state == AsyncState.READ_WRITE_OP) {
+ boolean triggerDispatch = false;
+ if (state == AsyncState.STARTING || state == AsyncState.MUST_ERROR) {
+ // Processing is on a container thread so no need to transfer
+ // processing to a new container thread
state = AsyncState.MUST_COMPLETE;
- } else if (state == AsyncState.STARTED || state == AsyncState.COMPLETE_PENDING) {
+ } else if (state == AsyncState.STARTED) {
+ state = AsyncState.COMPLETING;
+ // A dispatch to a container thread is always required.
+ // If on a non-container thread, need to get back onto a container
+ // thread to complete the processing.
+ // If on a container thread the current request/response are not the
+ // request/response associated with the AsyncContext so need a new
+ // container thread to process the different request/response.
+ triggerDispatch = true;
+ } else if (state == AsyncState.READ_WRITE_OP || state == AsyncState.TIMING_OUT ||
+ state == AsyncState.ERROR) {
+ // Read/write operations can happen on or off a container thread but
+ // while in this state the call to listener that triggers the
+ // read/write will be in progress on a container thread.
+ // Processing of timeouts and errors can happen on or off a
+ // container thread (on is much more likely) but while in this state
+ // the call that triggers the timeout will be in progress on a
+ // container thread.
+ // The socket will be added to the poller when the container thread
+ // exits the AbstractConnectionHandler.process() method so don't do
+ // a dispatch here which would add it to the poller a second time.
state = AsyncState.COMPLETING;
- doComplete = true;
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncComplete()", state));
}
- return doComplete;
+ return triggerDispatch;
}
@@ -358,45 +358,42 @@
if (!ContainerThreadMarker.isContainerThread() && state == AsyncState.STARTING) {
state = AsyncState.DISPATCH_PENDING;
return false;
- } else {
- return doDispatch();
}
- }
-
- private synchronized boolean doDispatch() {
clearNonBlockingListeners();
- boolean doDispatch = false;
- if (state == AsyncState.STARTING ||
- state == AsyncState.TIMING_OUT ||
- state == AsyncState.ERROR) {
- // In these three cases processing is on a container thread so no
- // need to transfer processing to a new container thread
+ boolean triggerDispatch = false;
+ if (state == AsyncState.STARTING || state == AsyncState.MUST_ERROR) {
+ // Processing is on a container thread so no need to transfer
+ // processing to a new container thread
state = AsyncState.MUST_DISPATCH;
- } else if (state == AsyncState.STARTED || state == AsyncState.DISPATCH_PENDING) {
+ } else if (state == AsyncState.STARTED) {
state = AsyncState.DISPATCHING;
- // A dispatch is always required.
+ // A dispatch to a container thread is always required.
// If on a non-container thread, need to get back onto a container
// thread to complete the processing.
// If on a container thread the current request/response are not the
// request/response associated with the AsyncContext so need a new
// container thread to process the different request/response.
- doDispatch = true;
- } else if (state == AsyncState.READ_WRITE_OP) {
+ triggerDispatch = true;
+ } else if (state == AsyncState.READ_WRITE_OP || state == AsyncState.TIMING_OUT ||
+ state == AsyncState.ERROR) {
+ // Read/write operations can happen on or off a container thread but
+ // while in this state the call to listener that triggers the
+ // read/write will be in progress on a container thread.
+ // Processing of timeouts and errors can happen on or off a
+ // container thread (on is much more likely) but while in this state
+ // the call that triggers the timeout will be in progress on a
+ // container thread.
+ // The socket will be added to the poller when the container thread
+ // exits the AbstractConnectionHandler.process() method so don't do
+ // a dispatch here which would add it to the poller a second time.
state = AsyncState.DISPATCHING;
- // If on a container thread then the socket will be added to the
- // poller poller when the thread exits the
- // AbstractConnectionHandler.process() method so don't do a dispatch
- // here which would add it to the poller a second time.
- if (!ContainerThreadMarker.isContainerThread()) {
- doDispatch = true;
- }
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
"asyncDispatch()", state));
}
- return doDispatch;
+ return triggerDispatch;
}
@@ -412,36 +409,17 @@
}
- synchronized void asyncMustError() {
- if (state == AsyncState.STARTED) {
- clearNonBlockingListeners();
+ synchronized boolean asyncError() {
+ clearNonBlockingListeners();
+ if (state == AsyncState.STARTING) {
state = AsyncState.MUST_ERROR;
} else {
- throw new IllegalStateException(
- sm.getString("asyncStateMachine.invalidAsyncState",
- "asyncMustError()", state));
- }
- }
-
-
- synchronized void asyncError() {
- if (state == AsyncState.STARTING ||
- state == AsyncState.STARTED ||
- state == AsyncState.DISPATCHED ||
- state == AsyncState.TIMING_OUT ||
- state == AsyncState.MUST_COMPLETE ||
- state == AsyncState.READ_WRITE_OP ||
- state == AsyncState.COMPLETING ||
- state == AsyncState.MUST_ERROR) {
- clearNonBlockingListeners();
state = AsyncState.ERROR;
- } else {
- throw new IllegalStateException(
- sm.getString("asyncStateMachine.invalidAsyncState",
- "asyncError()", state));
}
+ return !ContainerThreadMarker.isContainerThread();
}
+
synchronized void asyncRun(Runnable runnable) {
if (state == AsyncState.STARTING || state == AsyncState.STARTED ||
state == AsyncState.READ_WRITE_OP) {
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/CompressionConfig.java tomcat9-9.0.31/java/org/apache/coyote/CompressionConfig.java
--- tomcat9-9.0.27/java/org/apache/coyote/CompressionConfig.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/CompressionConfig.java 2020-02-05 19:26:48.000000000 +0000
@@ -20,23 +20,33 @@
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.ResponseUtil;
import org.apache.tomcat.util.http.parser.AcceptEncoding;
+import org.apache.tomcat.util.http.parser.TokenList;
+import org.apache.tomcat.util.res.StringManager;
public class CompressionConfig {
+ private static final Log log = LogFactory.getLog(CompressionConfig.class);
+ private static final StringManager sm = StringManager.getManager(CompressionConfig.class);
+
private int compressionLevel = 0;
private Pattern noCompressionUserAgents = null;
private String compressibleMimeType = "text/html,text/xml,text/plain,text/css," +
"text/javascript,application/javascript,application/json,application/xml";
private String[] compressibleMimeTypes = null;
private int compressionMinSize = 2048;
+ private boolean noCompressionStrongETag = true;
/**
@@ -174,6 +184,35 @@
/**
+ * Determine if compression is disabled if the resource has a strong ETag.
+ *
+ * @return {@code true} if compression is disabled, otherwise {@code false}
+ *
+ * @deprecated Will be removed in Tomcat 10 where it will be hard-coded to
+ * {@code true}
+ */
+ @Deprecated
+ public boolean getNoCompressionStrongETag() {
+ return noCompressionStrongETag;
+ }
+
+
+ /**
+ * Set whether compression is disabled for resources with a strong ETag.
+ *
+ * @param noCompressionStrongETag {@code true} if compression is disabled,
+ * otherwise {@code false}
+ *
+ * @deprecated Will be removed in Tomcat 10 where it will be hard-coded to
+ * {@code true}
+ */
+ @Deprecated
+ public void setNoCompressionStrongETag(boolean noCompressionStrongETag) {
+ this.noCompressionStrongETag = noCompressionStrongETag;
+ }
+
+
+ /**
* Determines if compression should be enabled for the given response and if
* it is, sets any necessary headers to mark it as such.
*
@@ -193,10 +232,21 @@
// Check if content is not already compressed
MessageBytes contentEncodingMB = responseHeaders.getValue("Content-Encoding");
- if (contentEncodingMB != null &&
- (contentEncodingMB.indexOf("gzip") != -1 ||
- contentEncodingMB.indexOf("br") != -1)) {
- return false;
+ if (contentEncodingMB != null) {
+ // Content-Encoding values are ordered but order is not important
+ // for this check so use a Set rather than a List
+ Set tokens = new HashSet<>();
+ try {
+ TokenList.parseTokenList(responseHeaders.values("Content-Encoding"), tokens);
+ } catch (IOException e) {
+ // Because we are using StringReader, any exception here is a
+ // Tomcat bug.
+ log.warn(sm.getString("compressionConfig.ContentEncodingParseFail"), e);
+ return false;
+ }
+ if (tokens.contains("gzip") || tokens.contains("br")) {
+ return false;
+ }
}
// If force mode, the length and MIME type checks are skipped
@@ -214,6 +264,16 @@
return false;
}
}
+
+ // Check if the resource has a strong ETag
+ if (noCompressionStrongETag) {
+ String eTag = responseHeaders.getHeader("ETag");
+ if (eTag != null && !eTag.trim().startsWith("W/")) {
+ // Has an ETag that doesn't start with "W/..." so it must be a
+ // strong ETag
+ return false;
+ }
+ }
// If processing reaches this far, the response might be compressed.
// Therefore, set the Vary header to keep proxies happy
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/AbstractHttp11Protocol.java tomcat9-9.0.31/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
--- tomcat9-9.0.27/java/org/apache/coyote/http11/AbstractHttp11Protocol.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/AbstractHttp11Protocol.java 2020-02-05 19:26:48.000000000 +0000
@@ -95,6 +95,15 @@
// ------------------------------------------------ HTTP specific properties
// ------------------------------------------ managed in the ProtocolHandler
+ private boolean useKeepAliveResponseHeader = true;
+ public boolean getUseKeepAliveResponseHeader() {
+ return useKeepAliveResponseHeader;
+ }
+ public void setUseKeepAliveResponseHeader(boolean useKeepAliveResponseHeader) {
+ this.useKeepAliveResponseHeader = useKeepAliveResponseHeader;
+ }
+
+
private String relaxedPathChars = null;
public String getRelaxedPathChars() {
return relaxedPathChars;
@@ -136,27 +145,56 @@
}
- private boolean rejectIllegalHeaderName = true;
+ private boolean rejectIllegalHeader = true;
/**
- * If an HTTP request is received that contains an illegal header name (i.e.
- * the header name is not a token) will the request be rejected (with a 400
- * response) or will the illegal header be ignored.
+ * If an HTTP request is received that contains an illegal header name or
+ * value (e.g. the header name is not a token) will the request be rejected
+ * (with a 400 response) or will the illegal header be ignored?
*
* @return {@code true} if the request will be rejected or {@code false} if
* the header will be ignored
*/
- public boolean getRejectIllegalHeaderName() { return rejectIllegalHeaderName; }
+ public boolean getRejectIllegalHeader() { return rejectIllegalHeader; }
+ /**
+ * If an HTTP request is received that contains an illegal header name or
+ * value (e.g. the header name is not a token) should the request be
+ * rejected (with a 400 response) or should the illegal header be ignored?
+ *
+ * @param rejectIllegalHeader {@code true} to reject requests with illegal
+ * header names or values, {@code false} to
+ * ignore the header
+ */
+ public void setRejectIllegalHeader(boolean rejectIllegalHeader) {
+ this.rejectIllegalHeader = rejectIllegalHeader;
+ }
/**
- * If an HTTP request is received that contains an illegal header name (i.e.
- * the header name is not a token) should the request be rejected (with a
- * 400 response) or should the illegal header be ignored.
+ * If an HTTP request is received that contains an illegal header name or
+ * value (e.g. the header name is not a token) will the request be rejected
+ * (with a 400 response) or will the illegal header be ignored?
+ *
+ * @return {@code true} if the request will be rejected or {@code false} if
+ * the header will be ignored
+ *
+ * @deprecated Now an alias for {@link #getRejectIllegalHeader()}. Will be
+ * removed in Tomcat 10 onwards.
+ */
+ @Deprecated
+ public boolean getRejectIllegalHeaderName() { return rejectIllegalHeader; }
+ /**
+ * If an HTTP request is received that contains an illegal header name or
+ * value (e.g. the header name is not a token) should the request be
+ * rejected (with a 400 response) or should the illegal header be ignored?
*
* @param rejectIllegalHeaderName {@code true} to reject requests with
- * illegal header names, {@code false} to
- * ignore the header
+ * illegal header names or values,
+ * {@code false} to ignore the header
+ *
+ * @deprecated Now an alias for {@link #setRejectIllegalHeader(boolean)}.
+ * Will be removed in Tomcat 10 onwards.
*/
+ @Deprecated
public void setRejectIllegalHeaderName(boolean rejectIllegalHeaderName) {
- this.rejectIllegalHeaderName = rejectIllegalHeaderName;
+ this.rejectIllegalHeader = rejectIllegalHeaderName;
}
@@ -270,6 +308,16 @@
}
+ @Deprecated
+ public boolean getNoCompressionStrongETag() {
+ return compressionConfig.getNoCompressionStrongETag();
+ }
+ @Deprecated
+ public void setNoCompressionStrongETag(boolean noCompressionStrongETag) {
+ compressionConfig.setNoCompressionStrongETag(noCompressionStrongETag);
+ }
+
+
public boolean useCompression(Request request, Response response) {
return compressionConfig.useCompression(request, response);
}
@@ -479,6 +527,8 @@
}
}
}
+
+ upgradeProtocol.setHttp11Protocol(this);
}
@Override
public UpgradeProtocol getNegotiatedProtocol(String negotiatedName) {
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/Constants.java tomcat9-9.0.31/java/org/apache/coyote/http11/Constants.java
--- tomcat9-9.0.27/java/org/apache/coyote/http11/Constants.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/Constants.java 2020-02-05 19:26:48.000000000 +0000
@@ -103,12 +103,26 @@
/* Various constant "strings" */
public static final String CONNECTION = "Connection";
public static final String CLOSE = "close";
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 10.
+ */
+ @Deprecated
public static final byte[] CLOSE_BYTES = ByteChunk.convertToBytes(CLOSE);
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 10.
+ */
+ @Deprecated
public static final String KEEPALIVE = "keep-alive";
+ public static final String KEEP_ALIVE_HEADER_VALUE_TOKEN = "keep-alive";
+ /**
+ * @deprecated Unused. Will be removed in Tomcat 10.
+ */
+ @Deprecated
public static final byte[] KEEPALIVE_BYTES = ByteChunk.convertToBytes(KEEPALIVE);
public static final String CHUNKED = "chunked";
public static final byte[] ACK_BYTES = ByteChunk.convertToBytes("HTTP/1.1 100 " + CRLF + CRLF);
public static final String TRANSFERENCODING = "Transfer-Encoding";
+ public static final String KEEP_ALIVE_HEADER_NAME = "Keep-Alive";
public static final byte[] _200_BYTES = ByteChunk.convertToBytes("200");
public static final byte[] _400_BYTES = ByteChunk.convertToBytes("400");
public static final byte[] _404_BYTES = ByteChunk.convertToBytes("404");
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/filters/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/coyote/http11/filters/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/coyote/http11/filters/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/filters/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -18,6 +18,7 @@
chunkedInputFilter.error=没有数据可用由于先前的错误
chunkedInputFilter.invalidCrlfCRCR=无效的结束的行序列(CRCR)
chunkedInputFilter.invalidCrlfNoCR=无效的行尾结束符序列(LF前缺少CR)
+chunkedInputFilter.invalidCrlfNoData=无效的行尾序列(没有可读取的数据)
chunkedInputFilter.maxExtension=超过最大扩展数
chunkedInputFilter.maxTrailer=超过最大数
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/Http11InputBuffer.java tomcat9-9.0.31/java/org/apache/coyote/http11/Http11InputBuffer.java
--- tomcat9-9.0.27/java/org/apache/coyote/http11/Http11InputBuffer.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/Http11InputBuffer.java 2020-02-05 19:26:48.000000000 +0000
@@ -28,6 +28,7 @@
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.HeaderUtil;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.parser.HttpParser;
import org.apache.tomcat.util.net.ApplicationBufferHandler;
@@ -65,7 +66,7 @@
private final MimeHeaders headers;
- private final boolean rejectIllegalHeaderName;
+ private final boolean rejectIllegalHeader;
/**
* State.
@@ -151,13 +152,13 @@
// ----------------------------------------------------------- Constructors
public Http11InputBuffer(Request request, int headerBufferSize,
- boolean rejectIllegalHeaderName, HttpParser httpParser) {
+ boolean rejectIllegalHeader, HttpParser httpParser) {
this.request = request;
headers = request.getMimeHeaders();
this.headerBufferSize = headerBufferSize;
- this.rejectIllegalHeaderName = rejectIllegalHeaderName;
+ this.rejectIllegalHeader = rejectIllegalHeader;
this.httpParser = httpParser;
filterLibrary = new InputFilter[0];
@@ -761,6 +762,8 @@
//
byte chr = 0;
+ byte prevChr = 0;
+
while (headerParsePos == HeaderParsePosition.HEADER_START) {
// Read new bytes if needed
@@ -771,22 +774,29 @@
}
}
+ prevChr = chr;
chr = byteBuffer.get();
- if (chr == Constants.CR) {
- // Skip
- } else if (chr == Constants.LF) {
+ if (chr == Constants.CR && prevChr != Constants.CR) {
+ // Possible start of CRLF - process the next byte.
+ } else if (prevChr == Constants.CR && chr == Constants.LF) {
return HeaderParseStatus.DONE;
} else {
- byteBuffer.position(byteBuffer.position() - 1);
+ if (prevChr == 0) {
+ // Must have only read one byte
+ byteBuffer.position(byteBuffer.position() - 1);
+ } else {
+ // Must have read two bytes (first was CR, second was not LF)
+ byteBuffer.position(byteBuffer.position() - 2);
+ }
break;
}
-
}
if (headerParsePos == HeaderParsePosition.HEADER_START) {
// Mark the current buffer position
headerData.start = byteBuffer.position();
+ headerData.lineStart = headerData.start;
headerParsePos = HeaderParsePosition.HEADER_NAME;
}
@@ -877,11 +887,22 @@
}
}
+ prevChr = chr;
chr = byteBuffer.get();
if (chr == Constants.CR) {
- // Skip
- } else if (chr == Constants.LF) {
+ // Possible start of CRLF - process the next byte.
+ } else if (prevChr == Constants.CR && chr == Constants.LF) {
eol = true;
+ } else if (prevChr == Constants.CR) {
+ // Invalid value
+ // Delete the header (it will be the most recent one)
+ headers.removeHeader(headers.size() - 1);
+ return skipLine();
+ } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
+ // Invalid value
+ // Delete the header (it will be the most recent one)
+ headers.removeHeader(headers.size() - 1);
+ return skipLine();
} else if (chr == Constants.SP || chr == Constants.HT) {
byteBuffer.put(headerData.realPos, chr);
headerData.realPos++;
@@ -933,6 +954,9 @@
headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
boolean eol = false;
+ byte chr = 0;
+ byte prevChr = 0;
+
// Reading bytes until the end of the line
while (!eol) {
@@ -944,21 +968,21 @@
}
int pos = byteBuffer.position();
- byte chr = byteBuffer.get();
+ prevChr = chr;
+ chr = byteBuffer.get();
if (chr == Constants.CR) {
// Skip
- } else if (chr == Constants.LF) {
+ } else if (prevChr == Constants.CR && chr == Constants.LF) {
eol = true;
} else {
headerData.lastSignificantChar = pos;
}
}
- if (rejectIllegalHeaderName || log.isDebugEnabled()) {
+ if (rejectIllegalHeader || log.isDebugEnabled()) {
String message = sm.getString("iib.invalidheader",
- new String(byteBuffer.array(), headerData.start,
- headerData.lastSignificantChar - headerData.start + 1,
- StandardCharsets.ISO_8859_1));
- if (rejectIllegalHeaderName) {
+ HeaderUtil.toPrintableString(byteBuffer.array(), headerData.lineStart,
+ headerData.lastSignificantChar - headerData.lineStart + 1));
+ if (rejectIllegalHeader) {
throw new IllegalArgumentException(message);
}
log.debug(message);
@@ -1018,6 +1042,10 @@
private static class HeaderParseData {
/**
+ * The first character of the header line.
+ */
+ int lineStart = 0;
+ /**
* When parsing header name: first character of the header.
* When skipping broken header line: first character of the header.
* When parsing header value: first character after ':'.
@@ -1045,6 +1073,7 @@
*/
MessageBytes headerValue = null;
public void recycle() {
+ lineStart = 0;
start = 0;
realPos = 0;
lastSignificantChar = 0;
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/Http11NioProtocol.java tomcat9-9.0.31/java/org/apache/coyote/http11/Http11NioProtocol.java
--- tomcat9-9.0.27/java/org/apache/coyote/http11/Http11NioProtocol.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/Http11NioProtocol.java 2020-02-05 19:26:48.000000000 +0000
@@ -50,10 +50,21 @@
* NO-OP.
*
* @param count Unused
+ *
+ * @deprecated This setter will be removed in Tomcat 10.
*/
+ @Deprecated
public void setPollerThreadCount(int count) {
}
+ /**
+ * Always returns 1.
+ *
+ * @return 1
+ *
+ * @deprecated This getter will be removed in Tomcat 10.
+ */
+ @Deprecated
public int getPollerThreadCount() {
return 1;
}
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/Http11Processor.java tomcat9-9.0.31/java/org/apache/coyote/http11/Http11Processor.java
--- tomcat9-9.0.27/java/org/apache/coyote/http11/Http11Processor.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/Http11Processor.java 2020-02-05 19:26:48.000000000 +0000
@@ -19,8 +19,10 @@
import java.io.IOException;
import java.io.InterruptedIOException;
import java.nio.ByteBuffer;
-import java.util.Enumeration;
-import java.util.Locale;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletResponse;
@@ -46,12 +48,12 @@
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
-import org.apache.tomcat.util.buf.Ascii;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.FastHttpDateFormat;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.http.parser.HttpParser;
+import org.apache.tomcat.util.http.parser.TokenList;
import org.apache.tomcat.util.log.UserDataHelper;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.SSLSupport;
@@ -73,6 +75,7 @@
private final AbstractHttp11Protocol> protocol;
+
/**
* Input.
*/
@@ -153,7 +156,7 @@
protocol.getRelaxedQueryChars());
inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
- protocol.getRejectIllegalHeaderName(), httpParser);
+ protocol.getRejectIllegalHeader(), httpParser);
request.setInputBuffer(inputBuffer);
outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize());
@@ -185,39 +188,6 @@
/**
- * Specialized utility method: find a sequence of lower case bytes inside
- * a ByteChunk.
- */
- private static int findBytes(ByteChunk bc, byte[] b) {
-
- byte first = b[0];
- byte[] buff = bc.getBuffer();
- int start = bc.getStart();
- int end = bc.getEnd();
-
- // Look for first char
- int srcEnd = b.length;
-
- for (int i = start; i <= (end - srcEnd); i++) {
- if (Ascii.toLower(buff[i]) != first) {
- continue;
- }
- // found first char, now look for a match
- int myPos = i+1;
- for (int srcPos = 1; srcPos < srcEnd;) {
- if (Ascii.toLower(buff[myPos++]) != b[srcPos++]) {
- break;
- }
- if (srcPos == srcEnd) {
- return i - start; // found it
- }
- }
- }
- return -1;
- }
-
-
- /**
* Determine if we must drop the connection because of the HTTP status
* code. Use the same list of codes as Apache/httpd.
*/
@@ -239,9 +209,7 @@
*/
private void addInputFilter(InputFilter[] inputFilters, String encodingName) {
- // Trim provided encoding name and convert to lower case since transfer
- // encoding names are case insensitive. (RFC2616, section 3.6)
- encodingName = encodingName.trim().toLowerCase(Locale.ENGLISH);
+ // Parsing trims and converts to lower case.
if (encodingName.equals("identity")) {
// Skip
@@ -345,16 +313,7 @@
}
// Has an upgrade been requested?
- Enumeration connectionValues = request.getMimeHeaders().values("Connection");
- boolean foundUpgrade = false;
- while (connectionValues.hasMoreElements() && !foundUpgrade) {
- String connectionValue = connectionValues.nextElement();
- if (connectionValue != null) {
- foundUpgrade = connectionValue.toLowerCase(Locale.ENGLISH).contains("upgrade");
- }
- }
-
- if (foundUpgrade) {
+ if (isConnectionToken(request.getMimeHeaders(), "upgrade")) {
// Check the protocol
String requestedProtocol = request.getHeader("Upgrade");
@@ -565,7 +524,7 @@
/**
* After reading the request headers, we have to setup the request filters.
*/
- private void prepareRequest() {
+ private void prepareRequest() throws IOException {
http11 = true;
http09 = false;
@@ -603,11 +562,11 @@
// Check connection header
MessageBytes connectionValueMB = headers.getValue(Constants.CONNECTION);
if (connectionValueMB != null && !connectionValueMB.isNull()) {
- ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
- if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
+ Set tokens = new HashSet<>();
+ TokenList.parseTokenList(headers.values(Constants.CONNECTION), tokens);
+ if (tokens.contains(Constants.CLOSE)) {
keepAlive = false;
- } else if (findBytes(connectionValueBC,
- Constants.KEEPALIVE_BYTES) != -1) {
+ } else if (tokens.contains(Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN)) {
keepAlive = true;
}
}
@@ -615,7 +574,7 @@
if (http11) {
MessageBytes expectMB = headers.getValue("expect");
if (expectMB != null && !expectMB.isNull()) {
- if (expectMB.indexOfIgnoreCase("100-continue", 0) != -1) {
+ if (expectMB.toString().trim().equalsIgnoreCase("100-continue")) {
inputBuffer.setSwallowInput(false);
request.setExpectation(true);
} else {
@@ -762,20 +721,17 @@
// Parse transfer-encoding header
if (http11) {
MessageBytes transferEncodingValueMB = headers.getValue("transfer-encoding");
- if (transferEncodingValueMB != null && !transferEncodingValueMB.isNull()) {
- String transferEncodingValue = transferEncodingValueMB.toString();
- // Parse the comma separated list. "identity" codings are ignored
- int startPos = 0;
- int commaPos = transferEncodingValue.indexOf(',');
- String encodingName = null;
- while (commaPos != -1) {
- encodingName = transferEncodingValue.substring(startPos, commaPos);
- addInputFilter(inputFilters, encodingName);
- startPos = commaPos + 1;
- commaPos = transferEncodingValue.indexOf(',', startPos);
+ if (transferEncodingValueMB != null) {
+ List encodingNames = new ArrayList<>();
+ if (TokenList.parseTokenList(headers.values("transfer-encoding"), encodingNames)) {
+ for (String encodingName : encodingNames) {
+ // "identity" codings are ignored
+ addInputFilter(inputFilters, encodingName);
+ }
+ } else {
+ // Invalid transfer encoding
+ badRequest("http11processor.request.invalidTransferEncoding");
}
- encodingName = transferEncodingValue.substring(startPos);
- addInputFilter(inputFilters, encodingName);
}
}
@@ -879,7 +835,6 @@
}
// Check for compression
-
boolean useCompression = false;
if (entityBody && sendfileData == null) {
useCompression = protocol.useCompression(request, response);
@@ -900,7 +855,7 @@
}
long contentLength = response.getContentLengthLong();
- boolean connectionClosePresent = false;
+ boolean connectionClosePresent = isConnectionToken(headers, Constants.CLOSE);
if (http11 && response.getTrailerFields() != null) {
// If trailer fields are set, always use chunking
outputBuffer.addActiveFilter(outputFilters[Constants.CHUNKED_FILTER]);
@@ -913,7 +868,6 @@
} else {
// If the response code supports an entity body and we're on
// HTTP 1.1 then we chunk unless we have a Connection: close header
- connectionClosePresent = isConnectionClose(headers);
if (http11 && entityBody && !connectionClosePresent) {
outputBuffer.addActiveFilter(outputFilters[Constants.CHUNKED_FILTER]);
contentDelimitation = true;
@@ -957,8 +911,36 @@
headers.addValue(Constants.CONNECTION).setString(
Constants.CLOSE);
}
- } else if (!http11 && !getErrorState().isError()) {
- headers.addValue(Constants.CONNECTION).setString(Constants.KEEPALIVE);
+ } else if (!getErrorState().isError()) {
+ if (!http11) {
+ headers.addValue(Constants.CONNECTION).setString(Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
+ }
+
+ if (protocol.getUseKeepAliveResponseHeader()) {
+ boolean connectionKeepAlivePresent =
+ isConnectionToken(request.getMimeHeaders(), Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
+
+ if (connectionKeepAlivePresent) {
+ int keepAliveTimeout = protocol.getKeepAliveTimeout();
+
+ if (keepAliveTimeout > 0) {
+ String value = "timeout=" + keepAliveTimeout / 1000L;
+ headers.setValue(Constants.KEEP_ALIVE_HEADER_NAME).setString(value);
+
+ if (http11) {
+ // Append if there is already a Connection header,
+ // else create the header
+ MessageBytes connectionHeaderValue = headers.getValue(Constants.CONNECTION);
+ if (connectionHeaderValue == null) {
+ headers.addValue(Constants.CONNECTION).setString(Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
+ } else {
+ connectionHeaderValue.setString(
+ connectionHeaderValue.getString() + ", " + Constants.KEEP_ALIVE_HEADER_VALUE_TOKEN);
+ }
+ }
+ }
+ }
+ }
}
// Add server header
@@ -992,14 +974,18 @@
outputBuffer.commit();
}
- private static boolean isConnectionClose(MimeHeaders headers) {
+ private static boolean isConnectionToken(MimeHeaders headers, String token) throws IOException {
MessageBytes connection = headers.getValue(Constants.CONNECTION);
if (connection == null) {
return false;
}
- return connection.equals(Constants.CLOSE);
+
+ Set tokens = new HashSet<>();
+ TokenList.parseTokenList(headers.values(Constants.CONNECTION), tokens);
+ return tokens.contains(token);
}
+
private void prepareSendfile(OutputFilter[] outputFilters) {
String fileName = (String) request.getAttribute(
org.apache.coyote.Constants.SENDFILE_FILENAME_ATTR);
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/LocalStrings.properties tomcat9-9.0.31/java/org/apache/coyote/http11/LocalStrings.properties
--- tomcat9-9.0.27/java/org/apache/coyote/http11/LocalStrings.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/LocalStrings.properties 2020-02-05 19:26:48.000000000 +0000
@@ -23,6 +23,7 @@
http11processor.request.finish=Error finishing request
http11processor.request.inconsistentHosts=The host specified in the request line is not consistent with the host header
http11processor.request.invalidScheme=The HTTP request contained an absolute URI with an invalid scheme
+http11processor.request.invalidTransferEncoding=The HTTP request contained an invalid Transfer-Encoding header
http11processor.request.invalidUri=The HTTP request contained an invalid URI
http11processor.request.invalidUserInfo=The HTTP request contained an absolute URI with an invalid userinfo
http11processor.request.multipleContentLength=The request contained multiple content-length headers
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/coyote/http11/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/coyote/http11/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,9 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+abstractHttp11Protocol.alpnConfigured=[{0}]连接器已配置为支持通过ALPN与[{1}]协商。
+
http11processor.header.parse=解析 HTTP 请求 header 错误
http11processor.request.inconsistentHosts=请求行中指定的主机与主机头不一致。
+http11processor.request.invalidScheme=HTTP请求包含具有无效方案的绝对URL
http11processor.request.invalidUserInfo=HTTP 请求包含带有无效 userinfo 的绝对 URI
+http11processor.request.multipleContentLength=请求包含了多个content-length请求头参数
+http11processor.request.noHostHeader=http/1.1请求没有提供主机头
http11processor.request.prepare=准备请求时出错
http11processor.request.process=错误的处理请求
http11processor.response.finish=错误完成相应
@@ -30,4 +35,5 @@
iib.readtimeout=从套接字读取数据超时
iob.failedwrite=写入.失败
+iob.failedwrite.ack=无法发送HTTP 100继续响应
iob.responseheadertoolarge.error=尝试将更多数据写入响应标头,而不是缓冲区中有可用空间。 增加连接器上的maxHttpHeaderSize或将更少的数据写入响应头。
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/upgrade/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/coyote/http11/upgrade/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/coyote/http11/upgrade/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/upgrade/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -14,9 +14,13 @@
# limitations under the License.
upgrade.sis.isFinished.ise=当 ServletInputStream 不处于非阻塞模式时调用 isFinished() 是非法的(即必须首先调用 setReadListener())
+upgrade.sis.onErrorFail=对注册的readlistener的错误处理触发了这个进一步的错误,该错误被吞入
upgrade.sis.read.closed=InputStream 已被关闭
+upgrade.sis.read.ise=在非阻塞模式下调用任何read()方法而不首先通过调用isready()检查是否有可用的数据是非法的
upgrade.sis.readListener.set=在同一个upgraded连接上调用多次setReadListener()函数是非法的
+upgrade.sos.onErrorFail=对注册的WriteListener 的错误处理触发了这个进一步的错误,该错误被吞入
upgrade.sos.write.closed=输出流已被关闭
+upgrade.sos.write.ise=在非阻塞模式下调用任何写()方法都是非法的,而无需首先检查是否有可用的空间,只需调用isreadi()
upgrade.sos.writeListener.null=对setWriteListener()传递null是非法的
upgradeProcessor.unexpectedState=因传入套接字状态为[{0}]而意外关闭升级连接
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java tomcat9-9.0.31/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java
--- tomcat9-9.0.27/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java 2020-02-05 19:26:48.000000000 +0000
@@ -134,6 +134,6 @@
@Override
public void pause() {
- // NOOP for AJP
+ // NOOP
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http2/Http2Protocol.java tomcat9-9.0.31/java/org/apache/coyote/http2/Http2Protocol.java
--- tomcat9-9.0.27/java/org/apache/coyote/http2/Http2Protocol.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http2/Http2Protocol.java 2020-02-05 19:26:48.000000000 +0000
@@ -27,6 +27,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
+import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.Adapter;
import org.apache.coyote.CompressionConfig;
import org.apache.coyote.Processor;
@@ -91,6 +92,8 @@
private boolean useSendfile = true;
// Compression
private final CompressionConfig compressionConfig = new CompressionConfig();
+ // Reference to HTTP/1.1 protocol that this instance is configured under
+ private AbstractProtocol> http11Protocol = null;
@Override
public String getHttpUpgradeName(boolean isSSLEnabled) {
@@ -405,7 +408,26 @@
}
+ @Deprecated
+ public boolean getNoCompressionStrongETag() {
+ return compressionConfig.getNoCompressionStrongETag();
+ }
+ @Deprecated
+ public void setNoCompressionStrongETag(boolean noCompressionStrongETag) {
+ compressionConfig.setNoCompressionStrongETag(noCompressionStrongETag);
+ }
+
+
public boolean useCompression(Request request, Response response) {
return compressionConfig.useCompression(request, response);
}
+
+
+ public AbstractProtocol> getHttp11Protocol() {
+ return this.http11Protocol;
+ }
+ @Override
+ public void setHttp11Protocol(AbstractProtocol> http11Protocol) {
+ this.http11Protocol = http11Protocol;
+ }
}
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http2/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/coyote/http2/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/coyote/http2/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http2/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,11 +13,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+abstractStream.windowSizeDec=连接[{0}],流[{1}],将流控制窗口减少[{2}]到[{3}]
abstractStream.windowSizeInc=连接 [{0}], 流 [{1}], 增加流量控制窗口[{2}] 到 [{3}]
+abstractStream.windowSizeTooBig=连接[{0}],流[{1}],窗口大小从[{2}]增加到[{3}],超过了允许的最大值
connectionPrefaceParser.mismatch=请求了新的远程流ID[{0}],但所有远程流都必须使用奇数标识符。
connectionSettings.debug=连接[{0}],参数类型[{1}]设置为[{2}]
+connectionSettings.enablePushInvalid=连接[{0}],请求的enable push[{1}]值不是允许的值之一(零或一)
connectionSettings.headerTableSizeLimit=连接 [{0}],尝试将 header 表大小设置为 [{1}],但限制为 16k
connectionSettings.maxFrameSizeInvalid=连接[{0}],请求的最大帧大小[{1}]在[{2}]到[{3}]的允许范围之外
connectionSettings.unknown=连接[{0}],标识为[{1}]和值为[{2}]的未知设置被忽略
@@ -25,35 +28,49 @@
frameType.checkPayloadSize=对帧类型[{1}]来说,负载[{0}]是无效的
frameType.checkStream=无效的帧类型[{0}]
+hpack.integerEncodedOverTooManyOctets=HPACK 可变长度整数编码过多的八位字节,最大值为[{0}]
hpack.invalidCharacter=代码点[{1}]处的Unicode字符[{0}]无法编码,因为它超出了允许的0到255范围。
hpackdecoder.tableSizeUpdateNotAtStart=任何表大小的更新都必须在头块开始时发送。
http2Parser.error=Connection [{0}],Stream [{1}], 框架类型 [{2}], 错误
+http2Parser.headerLimitCount=连接[{0}],流[{1}],标题太多
http2Parser.headerLimitSize=连接[{0}],Stream[{1}],总的头信息尺寸太大
http2Parser.headers.wrongStream=连接[{0}], 头部信息对于流[{1}]正在进行但对于流[{2}]的一帧已经收到了。
+http2Parser.invalidBuffers=应使用两个缓冲区进行读取
http2Parser.nonZeroPadding=连接[{0}],流[{1}],非零填充
http2Parser.preface.invalid=出现无效连接
+http2Parser.processFrame.unexpectedType=需要帧类型[{0}],但收到帧类型[{1}]
http2Parser.processFrameData.window=连接[{0}],客户端发送的数据比流窗口允许的多
http2Parser.processFrameHeaders.decodingDataLeft=数据在HPACK解码后依然保留 - 它本应该被消费掉
+http2Parser.processFrameHeaders.payload=连接:[{0}],流:[{1}],正在处理[{1}]大小的头文件负载
+http2Parser.processFramePriority.invalidParent=连接[{0}],流[{1}],流可能不依赖于自身
http2Parser.processFramePushPromise=请求了新的远程流ID[{0}],但所有远程流都必须使用奇数标识符\n\
\n
+http2Parser.swallow.debug=连接:[{0}],流:[{1}],吞下[{2}]字节
stream.closed=连接[{0}],流[{1}],一旦关闭就无法写入流
stream.header.debug=连接[{0}],流[{1}],HTTP标头[{2}],值[{3}]
stream.header.noPath=连接[{0}],流[{1}],[:path]伪标头为空
+stream.header.required=连接 [{0}], 流 [{1}], 缺少一个或多个必要的头文件
stream.header.unknownPseudoHeader=收到连接[{0}],流[{1}],未知伪标头[{2}]
stream.inputBuffer.readTimeout=等待从客户端读取数据超时
stream.inputBuffer.reset=流.重置
stream.inputBuffer.signal=读线程在等待时,数据被添加到inBuffer中。 发信号通知该线程继续
stream.reprioritisation.debug=连接[{0}],流[{1}],独占[{2}],父[{3}],权重[{4}]
+stream.reset.fail=连接[{0}],流[{1}],重置流失败
stream.writeTimeout=等待客户端增加流控制窗口以允许写入流数据的超时
streamProcessor.cancel=连接到[{0}],Stream [{1}],
streamProcessor.error.connection=连接[{0}],Stream[{0}],处理中发生错误,对连接来说是致命的。
streamProcessor.service.error=请求处理期间出错
+streamStateMachine.debug.change=(:连接[{0}],流[{1}],状态从[{2}]更改为[{3}]
+
upgradeHandler.allocate.left=连接[{0}],流[{1}],[{2}]字节未分配 - 尝试分配给子项
+upgradeHandler.allocate.recipient=(:连接[{0}],流[{1}],潜在接收者[{2}],权重为[{3}]
+upgradeHandler.goaway.debug=连接[{0}],离开,最后的流[{1}],错误码[{2}],调试数据[{3}]
+upgradeHandler.init=连接[{0}],状态[{1}]
upgradeHandler.ioerror=连接[{0}]
upgradeHandler.pingFailed=对客户端发送ping 链接失败.
upgradeHandler.prefaceReceived=连接[{0}],从客户端收到连接准备。
@@ -62,13 +79,19 @@
upgradeHandler.rst.debug=连接[{0}],流[{1}],错误[{2}],消息[{3}],RST(关闭流)
upgradeHandler.sendPrefaceFail=连接[{0}],给客户端发送前言失败
upgradeHandler.socketCloseFailed=关闭 socket 错误
+upgradeHandler.stream.closed=流[{0}]已经关闭了一段时间
upgradeHandler.stream.even=\ 请求了新的远程流ID[{0}],但所有远程流都必须使用奇数标识符\n\
\n
upgradeHandler.tooMuchOverhead=连接[{0}],开销过大,连接将关闭
upgradeHandler.upgrade=连接[{0}], HTTP/1.1 升级到流[1]
+upgradeHandler.upgrade.fail=):连接[{0}],http/1.1升级失败
upgradeHandler.upgradeDispatch.entry=条目,连接[{0}],SocketStatus [{1}]
upgradeHandler.upgradeDispatch.exit=退出,连接[{0}], SocketState[{1}]
+upgradeHandler.windowSizeTooBig=连接[{0}],流[{1}],窗口太大
upgradeHandler.writeBody=连接 [{0}],数据流[{1}], 数据长度[{2}]
upgradeHandler.writeHeaders=连接 [{0}],流 [{1}]
+windowAllocationManager.notify=连接[{0}], 流[{1}], 等待类型[{2}], 通知类型[{3}]
+windowAllocationManager.waitFor.ise=连接[{0}], 流[{1}], 已经准备好
+
writeStateMachine.ise=处于 [{1}] 状态时调用 [{0}()] 方法是非法的
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/http2/StreamProcessor.java tomcat9-9.0.31/java/org/apache/coyote/http2/StreamProcessor.java
--- tomcat9-9.0.27/java/org/apache/coyote/http2/StreamProcessor.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/http2/StreamProcessor.java 2020-02-05 19:26:48.000000000 +0000
@@ -71,7 +71,10 @@
try {
state = process(socketWrapper, event);
- if (state == SocketState.CLOSED) {
+ if (state == SocketState.LONG) {
+ handler.getProtocol().getHttp11Protocol().addWaitingProcessor(this);
+ } else if (state == SocketState.CLOSED) {
+ handler.getProtocol().getHttp11Protocol().removeWaitingProcessor(this);
if (!getErrorState().isConnectionIoAllowed()) {
ConnectionException ce = new ConnectionException(sm.getString(
"streamProcessor.error.connection", stream.getConnectionId(),
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/LocalStrings_fr.properties tomcat9-9.0.31/java/org/apache/coyote/LocalStrings_fr.properties
--- tomcat9-9.0.27/java/org/apache/coyote/LocalStrings_fr.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/LocalStrings_fr.properties 2020-02-05 19:26:48.000000000 +0000
@@ -28,11 +28,12 @@
abstractProcessor.hostInvalid=L''hôte [{0}] n''est pas valide
abstractProcessor.httpupgrade.notsupported=La promotion (upgrade) HTTP n'est pas supporté par ce protocole
abstractProcessor.noExecute=Impossible de transférer l'exécution à un thread du conteneur parce que ce processeur n'est pas associé à un SocketWrapper
-abstractProcessor.nonContainerThreadError=Un erreur s'est produite hors d'un des fils d'exécution du conteneur, la connection sera immédiatement fermée
abstractProcessor.pushrequest.notsupported=Le requêtes push du serveur ne sont pas supportées par ce protocole
abstractProcessor.socket.ssl=Exception lors de l'obtention des attributs SSL
abstractProtocol.mbeanDeregistrationFailed=Erreur lors du désenregistrement du mbean [{0}] dans le serveur [{1}]
+abstractProtocol.processorRegisterError=Erreur lors de l'enregistrement du processeur de requêtes
+abstractProtocol.processorUnregisterError=Erreur lors du désenregistrement du processeur de requêtes
abstractProtocolHandler.asyncTimeoutError=Erreur de traitement du délai d'attente maximum en mode asynchrone
abstractProtocolHandler.destroy=Destruction du gestionnaire de protocole [{0}]
@@ -46,6 +47,8 @@
asyncStateMachine.invalidAsyncState=L''appel à [{0}] n''est pas valide pour une requête dans l''état Async [{1}]
+compressionConfig.ContentEncodingParseFail=Echec du traitement de l'en-tête Content-Encoding en vérifiant si la compression était déjà utilisée
+
request.notAsync=Il n'est possible de passer en mode d'entrée-sorties non bloquantes que lors de traitements asynchrones ou après mise à niveau depuis HTTP
request.nullReadListener=L'écouteur passé à setReadListener() ne peut pas être null
request.readListenerSet=L'écouteur des lectures non bloquantes a déjà été défini
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/LocalStrings_ja.properties tomcat9-9.0.31/java/org/apache/coyote/LocalStrings_ja.properties
--- tomcat9-9.0.27/java/org/apache/coyote/LocalStrings_ja.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/LocalStrings_ja.properties 2020-02-05 19:26:48.000000000 +0000
@@ -27,7 +27,6 @@
abstractProcessor.hostInvalid=ホスト名 [{0}] は不正なホスト名です。
abstractProcessor.httpupgrade.notsupported=このプロトコルは HTTP アップグレードに対応していません。
abstractProcessor.noExecute=このProcessor が現在SocketWrapperに関連付けられていないため、コンテナスレッドに処理を転送できません。
-abstractProcessor.nonContainerThreadError=非コンテナスレッドの処理中にエラーが発生しました。 すぐにコネクションが閉じられます。
abstractProcessor.pushrequest.notsupported=このプロトコルはサーバープッシュの要求に対応していません。
abstractProcessor.socket.ssl=SSL属性を取得する例外
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/LocalStrings_ko.properties tomcat9-9.0.31/java/org/apache/coyote/LocalStrings_ko.properties
--- tomcat9-9.0.27/java/org/apache/coyote/LocalStrings_ko.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/LocalStrings_ko.properties 2020-02-05 19:26:48.000000000 +0000
@@ -29,11 +29,12 @@
abstractProcessor.hostInvalid=호스트 [{0}]은(는) 유효하지 않습니다.
abstractProcessor.httpupgrade.notsupported=HTTP 업그레이드는 이 프로토콜에 의해 지원되지 않습니다.
abstractProcessor.noExecute=이 프로세서가 현재 SocketWrapper와 연관되어 있지 않기 때문에, 처리 작업을 컨테이너 쓰레드로 이관할 수 없습니다.
-abstractProcessor.nonContainerThreadError=컨테이너 쓰레드가 아닌 쓰레드에서, 처리 도중 오류가 발생했습니다. 연결을 즉시 닫을 것입니다.
abstractProcessor.pushrequest.notsupported=이 프로토콜은 서버 push 요청들을 지원하지 않습니다.
abstractProcessor.socket.ssl=SSL 속성들을 얻으려는 중 예외 발생
abstractProtocol.mbeanDeregistrationFailed=MBean 서버 [{1}](으)로부터, [{0}](이)라는 이름의 MBean의 등록을 제거하지 못했습니다.
+abstractProtocol.processorRegisterError=RequestProcessor 구성요소를 등록하는 중 오류 발생
+abstractProtocol.processorUnregisterError=RequestProcessor 구성요소를 등록 해제하는 중 오류 발생
abstractProtocolHandler.asyncTimeoutError=비동기 제한 시간 초과를 처리하는 동안 오류 발생
abstractProtocolHandler.destroy=프로토콜 핸들러 [{0}]을(를) 소멸시킵니다.
@@ -47,6 +48,8 @@
asyncStateMachine.invalidAsyncState=비동기 상태가 [{1}]인 요청에 대하여, [{0}]을(를) 호출하는 것은 유효하지 않습니다.
+compressionConfig.ContentEncodingParseFail=압축이 이미 사용되는지 여부를 점검하는 중, Content-Encoding 헤더를 파싱하지 못했습니다.
+
request.notAsync=오직 비동기 처리 또는 HTTP 업그레이드 처리 시에만, Non-blocking IO로의 전환이 유효합니다.
request.nullReadListener=setReadListener()에 전달된 리스너는 널일 수 없습니다.
request.readListenerSet=Non-blocking 읽기 리스너가 이미 설정되어 있습니다.
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/LocalStrings.properties tomcat9-9.0.31/java/org/apache/coyote/LocalStrings.properties
--- tomcat9-9.0.27/java/org/apache/coyote/LocalStrings.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/LocalStrings.properties 2020-02-05 19:26:48.000000000 +0000
@@ -19,6 +19,7 @@
abstractConnectionHandler.negotiatedProcessor.fail=Failed to create Processor for negotiated protocol [{0}]
abstractConnectionHandler.oome=Failed to complete processing of a request
abstractConnectionHandler.process=Processing socket [{0}] with status [{1}]
+abstractConnectionHandler.processorCreate=Created new processor [{0}]
abstractConnectionHandler.processorPop=Popped processor [{0}] from cache
abstractConnectionHandler.protocolexception.debug=ProtocolExceptions are normal, ignored
abstractConnectionHandler.socketexception.debug=SocketExceptions are normal, ignored
@@ -29,11 +30,14 @@
abstractProcessor.hostInvalid=The host [{0}] is not valid
abstractProcessor.httpupgrade.notsupported=HTTP upgrade is not supported by this protocol
abstractProcessor.noExecute=Unable to transfer processing to a container thread because this Processor is not currently associated with a SocketWrapper
-abstractProcessor.nonContainerThreadError=An error occurred in processing while on a non-container thread. The connection will be closed immediately
abstractProcessor.pushrequest.notsupported=Server push requests are not supported by this protocol
abstractProcessor.socket.ssl=Exception getting SSL attributes
abstractProtocol.mbeanDeregistrationFailed=Failed to deregister MBean named [{0}] from MBean server [{1}]
+abstractProtocol.processorRegisterError=Error registering request processor
+abstractProtocol.processorUnregisterError=Error unregistering request processor
+abstractProcotol.waitingProcerssor.add=Added processor [{0}] to waiting processors
+abstractProcotol.waitingProcerssor.remove=Removed processor [{0}] from waiting processors
abstractProtocolHandler.asyncTimeoutError=Error processing async timeouts
abstractProtocolHandler.destroy=Destroying ProtocolHandler [{0}]
@@ -47,6 +51,8 @@
asyncStateMachine.invalidAsyncState=Calling [{0}] is not valid for a request with Async state [{1}]
+compressionConfig.ContentEncodingParseFail=Failed to parse Content-Encoding header when checking to see if compression was already in use
+
request.notAsync=It is only valid to switch to non-blocking IO within async processing or HTTP upgrade processing
request.nullReadListener=The listener passed to setReadListener() may not be null
request.readListenerSet=The non-blocking read listener has already been set
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/coyote/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/coyote/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -15,16 +15,22 @@
abstractConnectionHandler.ioexception.debug=正常的 IOException,忽略
abstractConnectionHandler.processorPop=从缓存中弹出的处理器[{0}]
+abstractConnectionHandler.socketexception.debug=(:SocketException是正常的,忽略
+abstractConnectionHandler.upgradeCreate=为套接字包装程序[{1}]创建了升级处理器[{0}]
abstractProcessor.fallToDebug=注意:更多的请求解析错误将以DEBUG级别日志进行记录。
abstractProcessor.hostInvalid=[{0}] 是无效主机
abstractProcessor.httpupgrade.notsupported=此协议不支持HTTP升级(upgrade)。
abstractProcessor.socket.ssl=获取SSL属性异常
+abstractProtocol.processorRegisterError=注册请求处理器错误
+abstractProtocol.processorUnregisterError=注销请求处理器错误
+
abstractProtocolHandler.asyncTimeoutError=错误的处理异步超时
abstractProtocolHandler.destroy=正在摧毁协议处理器 [{0}]
abstractProtocolHandler.init=初始化协议处理器 [{0}]
abstractProtocolHandler.start=开始协议处理句柄[{0}]
+abstractProtocolHandler.stop=正在停止ProtocolHandler [{0}]
asyncStateMachine.invalidAsyncState=调用[{0}]对于具有异步状态[{1}]的请求无效
diff -Nru tomcat9-9.0.27/java/org/apache/coyote/UpgradeProtocol.java tomcat9-9.0.31/java/org/apache/coyote/UpgradeProtocol.java
--- tomcat9-9.0.27/java/org/apache/coyote/UpgradeProtocol.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/coyote/UpgradeProtocol.java 2020-02-05 19:26:48.000000000 +0000
@@ -91,4 +91,20 @@
* false
*/
public boolean accept(Request request);
+
+
+ /**
+ * Configure the HTTP/1.1 protocol that this UpgradeProcotol is nested
+ * under. Connections passed to this UpgradeProtocol via HTTP upgrade will
+ * have been initially handled by this HTTP/1.1 protocol implementation.
+ *
+ * The default implementation is a NO-OP.
+ *
+ * @param protocol The HTTP/1.1 protocol implementation that will initially
+ * handle any connections passed to this UpgradeProtocol via
+ * the HTTP upgrade mechanism
+ */
+ public default void setHttp11Protocol(AbstractProtocol> protocol) {
+ // NO-OP
+ }
}
diff -Nru tomcat9-9.0.27/java/org/apache/jasper/compiler/Compiler.java tomcat9-9.0.31/java/org/apache/jasper/compiler/Compiler.java
--- tomcat9-9.0.27/java/org/apache/jasper/compiler/Compiler.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/jasper/compiler/Compiler.java 2020-02-05 19:26:48.000000000 +0000
@@ -101,9 +101,9 @@
/**
* Compile the jsp file into equivalent servlet in .java file
*
- * @throws Exception Error generating Java source
- *
* @return A map of class names to JSR 045 source maps
+ *
+ * @throws Exception Error generating Java source
*/
protected Map generateJava() throws Exception {
@@ -382,9 +382,9 @@
}
try {
+ final Long jspLastModified = ctxt.getLastModified(ctxt.getJspFile());
Map smaps = generateJava();
File javaFile = new File(ctxt.getServletJavaFileName());
- Long jspLastModified = ctxt.getLastModified(ctxt.getJspFile());
if (!javaFile.setLastModified(jspLastModified.longValue())) {
throw new JasperException(Localizer.getMessage("jsp.error.setLastModified", javaFile));
}
diff -Nru tomcat9-9.0.27/java/org/apache/jasper/compiler/Generator.java tomcat9-9.0.31/java/org/apache/jasper/compiler/Generator.java
--- tomcat9-9.0.27/java/org/apache/jasper/compiler/Generator.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/jasper/compiler/Generator.java 2020-02-05 19:26:48.000000000 +0000
@@ -3273,12 +3273,18 @@
}
/*
- * @param c The target class to which to coerce the given string @param
- * s The string value @param attrName The name of the attribute whose
- * value is being supplied @param propEditorClass The property editor
- * for the given attribute @param isNamedAttribute true if the given
- * attribute is a named attribute (that is, specified using the
- * jsp:attribute standard action), and false otherwise
+ * @param c
+ * The target class to which to coerce the given string
+ * @param s
+ * The string value
+ * @param attrName
+ * The name of the attribute whose value is being supplied
+ * @param propEditorClass
+ * The property editor for the given attribute
+ * @param isNamedAttribute
+ * true if the given attribute is a named attribute (that
+ * is, specified using the jsp:attribute standard action),
+ * and false otherwise
*/
private String convertString(Class> c, String s, String attrName,
Class> propEditorClass, boolean isNamedAttribute) {
@@ -3695,8 +3701,8 @@
String className = tagInfo.getTagClassName();
int lastIndex = className.lastIndexOf('.');
if (lastIndex != -1) {
- String pkgName = className.substring(0, lastIndex);
- genPreamblePackage(pkgName);
+ String packageName = className.substring(0, lastIndex);
+ genPreamblePackage(packageName);
className = className.substring(lastIndex + 1);
}
diff -Nru tomcat9-9.0.27/java/org/apache/jasper/JspCompilationContext.java tomcat9-9.0.31/java/org/apache/jasper/JspCompilationContext.java
--- tomcat9-9.0.27/java/org/apache/jasper/JspCompilationContext.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/jasper/JspCompilationContext.java 2020-02-05 19:26:48.000000000 +0000
@@ -82,7 +82,9 @@
private volatile boolean removed = false;
- private URLClassLoader jspLoader;
+ // volatile so changes are visible when multiple threads request a JSP file
+ // that has been modified
+ private volatile URLClassLoader jspLoader;
private URL baseUrl;
private Class> servletClass;
@@ -454,11 +456,11 @@
if (isTagFile()) {
String className = tagInfo.getTagClassName();
int lastIndex = className.lastIndexOf('.');
- String pkgName = "";
+ String packageName = "";
if (lastIndex != -1) {
- pkgName = className.substring(0, lastIndex);
+ packageName = className.substring(0, lastIndex);
}
- return pkgName;
+ return packageName;
} else {
String dPackageName = getDerivedPackageName();
if (dPackageName.length() == 0) {
@@ -767,4 +769,3 @@
return result.toString();
}
}
-
diff -Nru tomcat9-9.0.27/java/org/apache/jasper/resources/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/jasper/resources/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/jasper/resources/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/jasper/resources/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -16,8 +16,11 @@
jasper.error.emptybodycontent.nonempty=根据 TLD,[{0}] 标签必须为空,但不是
jsp.error.action.isnottagfile=[{0}]行为只能用于标签文件
+jsp.error.action.istagfile=标签文件中不能使用[{0}]功能
jsp.error.attempt_to_clear_flushed_buffer=错误:尝试清空已刷新的缓冲区
+jsp.error.attr.quoted=应引用属性值
jsp.error.attribute.deferredmix=不能在同一属性值中同时使用 ${} 和 #{} EL 表达式
+jsp.error.attribute.duplicate=属性限定名在元素中必须是唯一的
jsp.error.attribute.noequal=期望的符号是等号
jsp.error.attribute.noescape=属性值[{0}]引用[{1}],在值内使用时必须被转义。
jsp.error.attribute.nowhitespace=JSP 规范要求一个属性名字前有空格
@@ -27,12 +30,15 @@
jsp.error.beans.noproperty=在[{1}]类型bean中找不到任何有关属性[{0}]的信息
jsp.error.beans.nullbean=尝试获取一个bean 操作在一个空对象上.
jsp.error.beans.property.conversion=无法将字符串[{0}]转换为属性[{2}]的类[{1}]:[{3}]
+jsp.error.beans.propertyeditor.notregistered=属性编辑器未注册到属性编辑管理器
jsp.error.cannotAddResolver=在第一次请求发生之后不能调用addELResolver
jsp.error.compilation.source=加载源文件时出错[{0}]
jsp.error.compiler=没有可用的Java编译器
+jsp.error.config_pagedir_encoding_mismatch=jsp属性组[{0}]中指定的页编码与page指令[{1}]中指定的页编码不同
jsp.error.corresponding.servlet=生成的servlet错误:\n
jsp.error.could.not.add.taglibraries=不能增加一个或者多个tag 库.
jsp.error.data.file.processing=处理文件 [{0}] 错误
+jsp.error.deferredvaluetypewithoutdeferredvalue=如果“deferredValue”的值不是“true”的话,不能指定一个值类型
jsp.error.duplicate.name.jspattribute=标准或自定义操作中指定的属性[{0}]也显示为随附的jsp:属性中name属性的值
jsp.error.el.template.deferred=#{...} 不允许出现在模板文本中
jsp.error.el_interpreter_class.instantiation=加载或实例化ELInterpreter类[{0}]失败
@@ -43,22 +49,28 @@
jsp.error.include.tag=无效的jsp:include标签
jsp.error.internal.filenotfound=内部错误:找不到文件 [{0}]
jsp.error.internal.unexpectedNodeType=节点类型不一致
+jsp.error.invalid.attribute=[{0}]有一个无效属性:[{1}]
jsp.error.invalid.tagdir=标签文件目录 [{0}] 不以"/WEB-INF/tags"开头
jsp.error.invalid.version=为标签 [{0}] 定义了无效的 JSP 版本号
jsp.error.ise_on_clear=当缓存大小等于0时调用clear()函数是非法的
jsp.error.jspbody.emptybody.only=标签[{}]的标签体内智能包含jsp:attribute
+jsp.error.jspbody.invalidUse=JSP:主体必须是标准或自定义操作的子元素
jsp.error.jspbody.required=如果使用 jsp:attribute,则必须使用 jsp:body 为 [{0}] 指定标记正文。
+jsp.error.jspc.missingTarget=缺少目标:必须指定-webapp或-uriroot或一个或多个jsp页
jsp.error.jspelement.missing.name=XML强制性约束:属性name缺失。
jsp.error.jspoutput.conflict=&lt; jsp:output&gt;:非法使多个[{0}]出现不同的值(旧:[{1}],新:[{2}])
jsp.error.jspoutput.doctypenamesystem=<jsp:output>: 'doctype-root-element' 和 'doctype-system' 必须一起出现
+jsp.error.jspoutput.nonemptybody=<jsp:output>不能有正文
jsp.error.jsproot.version.invalid=版本号 [{0}] 无效,版本号必须是"1.2"、"2.0"、"2.1"、"2.2"、"2.3"中的一个
jsp.error.jsptext.badcontent='&lt;',当出现在&lt; jsp:text&gt;的主体中时,必须封装在CDATA中
jsp.error.lastModified=无法确定文件 [{0}] 的最后修改日期
+jsp.error.library.invalid=根据库[{0}](:[{1}],jsp页无效
jsp.error.loadclass.taghandler=无法为TAG [{1}]加载标记处理程序类[{0}]
jsp.error.location=行.: [{0}], 列: [{1}]
jsp.error.mandatory.attribute=[{0}]: 强制性属性 [{1}] 缺失。
jsp.error.missing_attribute=根据TLD或标记文件,标记[{1}]必须使用属性[{0}]
jsp.error.negativeBufferSize=缓存大小是负数
+jsp.error.nested.jspattribute=jsp:attribute标准操作不能嵌套在另一个jsp:attribute标准操作中
jsp.error.nested.jspbody=JSP:体标准动作不能嵌套在另一个jsp:body 或者 jsp:属性标准动作中
jsp.error.nested_jsproot=嵌套的<jsp:root>
jsp.error.no.scratch.dir=JSP引擎未配置scratch文件夹。\n\
@@ -70,17 +82,22 @@
jsp.error.overflow=错误:JSP缓冲区溢出
jsp.error.page.conflict.contenttype=Page指令:非法出现多次出现的''contentType''具有不同的值(old:[{0}],new:[{1}])
jsp.error.page.conflict.errorpage=页指令:不同值的多次出现“errorPage”的非法值(旧:[{0}],新:[{1}])
+jsp.error.page.conflict.language=页指令:多次出现不同值的“语言”(旧:[{0}],新:[{1}])
jsp.error.page.conflict.session=Page指令:多次出现具有不同值的''session''非法(old:[{0}],new:[{1}])
+jsp.error.page.conflict.trimdirectivewhitespaces=页面指令:违法出现多个有不同的值(旧值:[{0}],新值:[{1}])的''trimDirectiveWhitespaces''
jsp.error.page.invalid.deferredsyntaxallowedasliteral=页面指令:deferredSyntaxAllowedAsLiteral的值无效
jsp.error.page.invalid.import=网页指令:无效引用
jsp.error.page.invalid.iselignored=页面指令:忽略的无效值
jsp.error.page.invalid.session=页面提示:session值无效
jsp.error.page.multi.pageencoding=页指令不能有多次出现的页编码
+jsp.error.page.noSession=无法访问不参与任何会话的页中的会话作用域
jsp.error.page.nullThrowable=空异常
jsp.error.param.invalidUse=jsp:param 不能在jsp:include、jsp:forward或jsp:params等元素外使用
jsp.error.paramexpected=使用“name”和“value”属性期望“jsp:param”标准操作
jsp.error.params.invalidUse=参数jsp:params必须是jsp:plugin的直接孩子参数
jsp.error.parse.xml=无法解析 XML 文件 [{0}]
+jsp.error.parse.xml.scripting.invalid.body=[{0}]元素的主体不能包含任何XML元素
+jsp.error.plugin.badtype=jsp:plugin中“type”属性的值非法:必须是“bean”或“applet”
jsp.error.plugin.nocode=代码未定义在jsp:plugin中
jsp.error.plugin.notype=jsp:plugin中未声明type
jsp.error.precompilation=无法预编译JSP[{0}]
@@ -97,8 +114,12 @@
jsp.error.single.line.number=JSP文件:[{1}] 的第 [{0}] 行发生了一个错误
jsp.error.stream.close.failed=流.关闭失败
jsp.error.stream.closed=流.关闭
+jsp.error.tag.conflict.iselignored=TAG指令:多次出现不同值的“isELIgnored”(旧:[{0}],New:[{1}])
jsp.error.tag.conflict.language=标签指令:非法出现多次出现的具有不同值的“语言”(旧:[{0}],新:[{1}])
+jsp.error.tag.conflict.trimdirectivewhitespaces=标签指令:非法地多次出现具有不同值的“trimDirectiveWhitespaces”(旧值:[{0}),新值:[{1}])
+jsp.error.tag.invalid.deferredsyntaxallowedasliteral=标签指令):deferredSyntaxAllowedAsLiteral的值无效
jsp.error.tag.invalid.trimdirectivewhitespaces=TAG指令:trimDirectiveWhitespaces的值无效
+jsp.error.tagdirective.badbodycontent=标签指令中的无效的内容体[{0}]
jsp.error.tagfile.nameFrom.badAttribute=属性指令(在行[{1}]中声明并且其name属性为[{0}],此name-from-attribute属性的值)必须是java.lang.String类型,是“required”而不是一个“rtexprvalue”。
jsp.error.tagfile.nameFrom.noAttribute=找不到具有值[{0}]的name属性的属性指令,该属性是name-from-attribute属性的值。
jsp.error.taglibDirective.absUriCannotBeResolved=无法在web.xml或使用此应用程序部署的jar文件中解析绝对uri:[{0}]
@@ -109,6 +130,7 @@
jsp.error.text.has_subelement=&LT; JSP:文本&GT; 不得有任何子元素
jsp.error.tld.fn.invalid.signature=TLD中函数签名的语法无效。 标签库:[{0}],功能:[{1}]
jsp.error.tld.mandatory.element.missing=TLD [{1}] 中强制 TLD 元素 [{0}] 不存在或为空
+jsp.error.tlv.invalid.page=):[{0}]和[{1}]的TagLibraryValidator的验证错误消息
jsp.error.unable.deleteClassFile=无法删除class文件[{0}]
jsp.error.unable.load=无法加载JSP的相关类
jsp.error.unable.renameClassFile=无法重命名类文件[{0}]为[{1}]
@@ -117,6 +139,7 @@
jsp.error.unbalanced.endtag=结束标签</{0}不对称
jsp.error.unknown_attribute_type=属性[{0}]的未知属性类型[{1}]。
jsp.error.unsupported.encoding=不支持的编码:[{0}]
+jsp.error.variable.both.name=不能在变量指令的属性属性中同时指定给定的名称和名称
jsp.error.variable.either.name=必须在变量指令中指定 name-given 或 name-from-attribute 属性
jsp.error.xml.badStandardAction=无效、标准的action: [{0}]
jsp.exception=在 [{1}] 行处理 [{0}] 时发生异常
@@ -124,6 +147,7 @@
jsp.message.jsp_added=增加JSP 为路径[{0}]为上下文[{1}]的队列
jsp.message.jsp_queue_update=在上下文[{1}]队列中更新路径为[{0}]的JSP
jsp.message.jsp_removed_excess=从上下文[{1}]的队列中移除额外在路径[{0}]中JSP,
+jsp.message.jsp_removed_idle=在[{2}]毫秒之后删除上下文[{1}]中路径[{0}]的空闲JSP
jsp.message.jsp_unload_check=在context[{0}]中检查未加载的jsp,jsp总共:[{1}]队列长度[{2}]
jsp.tldCache.noTldSummary=至少有一个JAR被扫描用于TLD但尚未包含TLD。 为此记录器启用调试日志记录,以获取已扫描但未在其中找到TLD的完整JAR列表。 在扫描期间跳过不需要的JAR可以缩短启动时间和JSP编译时间。
jsp.tldCache.tldInDir=在目录 [{0}]中找到了TLD文件。
@@ -132,6 +156,8 @@
jsp.warning.dumpSmap=警告:初始化堆内存的值无效。将使用“false”的默认值
jsp.warning.enablePooling=警告:initParam enablePooling的值无效。将使用默认值“true”
jsp.warning.fork=警告:initParam的值无效。将使用“true”的默认值
+jsp.warning.noJarScanner=警告:ServletContext中没有设置org.apache.tomcat.JarScaner。回到默认的JarScaner实现。
+jsp.warning.suppressSmap=警告:suppressSmap的初始化参数无效。将使用默认值“false”
jspc.delete.fail=无法删除文件 [{0}]
jspc.error.fileDoesNotExist=文件参数 [{0}] 不存在
diff -Nru tomcat9-9.0.27/java/org/apache/jasper/servlet/JspServletWrapper.java tomcat9-9.0.31/java/org/apache/jasper/servlet/JspServletWrapper.java
--- tomcat9-9.0.27/java/org/apache/jasper/servlet/JspServletWrapper.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/jasper/servlet/JspServletWrapper.java 2020-02-05 19:26:48.000000000 +0000
@@ -281,6 +281,7 @@
synchronized (this) {
if (getReloadInternal() || tagHandlerClass == null) {
tagHandlerClass = ctxt.load();
+ // Volatile 'reload' forces in order write of 'tagHandlerClass'
reload = false;
}
}
@@ -326,7 +327,7 @@
}
}
}
- target = tagHandlerClass.newInstance();
+ target = tagHandlerClass.getConstructor().newInstance();
} else {
target = getServlet();
}
diff -Nru tomcat9-9.0.27/java/org/apache/naming/factory/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/naming/factory/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/naming/factory/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/naming/factory/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,7 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+factoryBase.factoryClassError=无法加载资源工厂类
+factoryBase.factoryCreationError=无法创建资源工厂实例
+factoryBase.instanceCreationError=无法创建资源实例
+
lookupFactory.createFailed=无法创建JNDI查找工厂类实例
+lookupFactory.loadFailed=无法加载JNDI查找工厂类
lookupFactory.typeMismatch=期望JNDI引用[{0}]的类型为[{1}],但查找[{2}]返回类型为[{3}]的对象
resourceLinkFactory.nullType=引用全局资源 [{1}] 的本地资源链接 [{0}] 未指定所需的属性类型
diff -Nru tomcat9-9.0.27/java/org/apache/naming/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/naming/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/naming/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/naming/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,9 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+contextBindings.noContextBoundToCL=没有绑定到此类加载器的命名上下文
+contextBindings.noContextBoundToThread=没有绑定到此线程的命名上下文
contextBindings.unknownContext=未知.上下文名:[{0}]
namingContext.contextExpected=上下文Context未绑定名称name
namingContext.failResolvingReference=解析引用时意外异常
selectorContext.methodUsingName=用[{1}]的name属性调用方法[{0}]
+selectorContext.methodUsingString=使用字符串[{1}]调用方法[{0}]
+selectorContext.noJavaUrl=必须通过java:url访问此上下文
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/buildutil/SignCode.java tomcat9-9.0.31/java/org/apache/tomcat/buildutil/SignCode.java
--- tomcat9-9.0.27/java/org/apache/tomcat/buildutil/SignCode.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/buildutil/SignCode.java 2020-02-05 19:26:48.000000000 +0000
@@ -51,7 +51,11 @@
import org.w3c.dom.NodeList;
/**
- * Ant task that submits a file to the Symantec code-signing service.
+ * Ant task that submits a file to the Digicert (formally Symantec) code-signing
+ * service. The service is defined by the published
+ * WSDL.
+ * Note that while the service has migrated to a Digicert domain, the namespace
+ * continues to use a Symantec domain.
*/
public class SignCode extends Task {
@@ -64,7 +68,7 @@
static {
try {
SIGNING_SERVICE_URL = new URL(
- "https://api-appsec-cws.ws.symantec.com/webtrust/SigningService");
+ "https://api-appsec.pki.digicert.com/webtrust/SigningService");
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/buildutil/translate/Export.java tomcat9-9.0.31/java/org/apache/tomcat/buildutil/translate/Export.java
--- tomcat9-9.0.27/java/org/apache/tomcat/buildutil/translate/Export.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/buildutil/translate/Export.java 1970-01-01 00:00:00.000000000 +0000
@@ -1,56 +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.tomcat.buildutil.translate;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * Generates a single properties file per language for import into a translation
- * tool.
- */
-public class Export {
-
- private static final Map translations = new HashMap<>();
-
- public static void main(String... args) throws IOException {
- File root = new File(".");
- for (String dir : Constants.SEARCH_DIRS) {
- File directory = new File(dir);
- Utils.processDirectory(root, directory, translations);
- }
-
- outputTranslations();
- }
-
-
- private static void outputTranslations() {
-
- File storageDir = new File(Constants.STORAGE_DIR);
- if (!storageDir.exists()) {
- storageDir.mkdirs();
- }
-
- for (Map.Entry translationEntry : translations.entrySet()) {
- Utils.export(translationEntry.getKey(), translationEntry.getValue(), storageDir);
- }
- }
-}
-
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/buildutil/translate/Utils.java tomcat9-9.0.31/java/org/apache/tomcat/buildutil/translate/Utils.java
--- tomcat9-9.0.27/java/org/apache/tomcat/buildutil/translate/Utils.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/buildutil/translate/Utils.java 2020-02-05 19:26:48.000000000 +0000
@@ -78,7 +78,11 @@
static void processDirectory(File root, File dir, Map translations) throws IOException {
- for (File f : dir.listFiles()) {
+ File[] files = dir.listFiles();
+ if (files == null) {
+ throw new IllegalArgumentException("Not a directory [" + dir.getAbsolutePath() + "]");
+ }
+ for (File f : files) {
if (f.isDirectory()) {
processDirectory(root, f, translations);
} else if (f.isFile()) {
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/dbcp2/AbandonedTrace.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/dbcp2/AbandonedTrace.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/dbcp2/AbandonedTrace.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/dbcp2/AbandonedTrace.java 2020-02-05 19:26:48.000000000 +0000
@@ -58,45 +58,6 @@
}
/**
- * Initializes abandoned tracing for this object.
- *
- * @param parent
- * AbandonedTrace parent object.
- */
- private void init(final AbandonedTrace parent) {
- if (parent != null) {
- parent.addTrace(this);
- }
- }
-
- /**
- * Gets the last time this object was used in milliseconds.
- *
- * @return long time in milliseconds.
- */
- @Override
- public long getLastUsed() {
- return lastUsedMillis;
- }
-
- /**
- * Sets the time this object was last used to the current time in milliseconds.
- */
- protected void setLastUsed() {
- lastUsedMillis = System.currentTimeMillis();
- }
-
- /**
- * Sets the time in milliseconds this object was last used.
- *
- * @param lastUsedMillis
- * time in milliseconds.
- */
- protected void setLastUsed(final long lastUsedMillis) {
- this.lastUsedMillis = lastUsedMillis;
- }
-
- /**
* Adds an object to the list of objects being traced.
*
* @param trace
@@ -119,6 +80,16 @@
}
/**
+ * Gets the last time this object was used in milliseconds.
+ *
+ * @return long time in milliseconds.
+ */
+ @Override
+ public long getLastUsed() {
+ return lastUsedMillis;
+ }
+
+ /**
* Gets a list of objects being traced by this object.
*
* @return List of objects.
@@ -145,6 +116,30 @@
}
/**
+ * Initializes abandoned tracing for this object.
+ *
+ * @param parent
+ * AbandonedTrace parent object.
+ */
+ private void init(final AbandonedTrace parent) {
+ if (parent != null) {
+ parent.addTrace(this);
+ }
+ }
+
+ /**
+ * Removes this object the source object is tracing.
+ *
+ * @param source The object tracing
+ * @since 2.7.0
+ */
+ protected void removeThisTrace(final Object source) {
+ if (source instanceof AbandonedTrace) {
+ AbandonedTrace.class.cast(source).removeTrace(this);
+ }
+ }
+
+ /**
* Removes a child object this object is tracing.
*
* @param trace
@@ -167,14 +162,19 @@
}
/**
- * Removes this object the source object is tracing.
+ * Sets the time this object was last used to the current time in milliseconds.
+ */
+ protected void setLastUsed() {
+ lastUsedMillis = System.currentTimeMillis();
+ }
+
+ /**
+ * Sets the time in milliseconds this object was last used.
*
- * @param source The object tracing
- * @since 2.7.0
+ * @param lastUsedMillis
+ * time in milliseconds.
*/
- protected void removeThisTrace(final Object source) {
- if (source instanceof AbandonedTrace) {
- AbandonedTrace.class.cast(source).removeTrace(this);
- }
+ protected void setLastUsed(final long lastUsedMillis) {
+ this.lastUsedMillis = lastUsedMillis;
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java 2020-02-05 19:26:48.000000000 +0000
@@ -621,7 +621,7 @@
// Statement's when it is closed.
// DBCP-288. Not all the traced objects will be statements
final List traces = getTrace();
- if (traces != null && traces.isEmpty()) {
+ if (traces != null && !traces.isEmpty()) {
final List thrownList = new ArrayList<>();
final Iterator traceIter = traces.iterator();
while (traceIter.hasNext()) {
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java 2020-02-05 19:26:48.000000000 +0000
@@ -153,18 +153,18 @@
thrownList.add(e);
}
}
- clearTrace();
}
- if (statement != null) {
- try {
- statement.close();
- } catch (Exception e) {
- if (connection != null) {
- // Does not rethrow e.
- connection.handleExceptionNoThrow(e);
- }
- thrownList.add(e);
+ clearTrace();
+ }
+ if (statement != null) {
+ try {
+ statement.close();
+ } catch (Exception e) {
+ if (connection != null) {
+ // Does not rethrow e.
+ connection.handleExceptionNoThrow(e);
}
+ thrownList.add(e);
}
}
} finally {
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/BaseKeyedPooledObjectFactory.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/BaseKeyedPooledObjectFactory.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/BaseKeyedPooledObjectFactory.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/BaseKeyedPooledObjectFactory.java 2020-02-05 19:26:48.000000000 +0000
@@ -21,7 +21,9 @@
*
* All operations defined here are essentially no-op's.
*
+ *
* This class is immutable, and therefore thread-safe.
+ *
*
* @param Type of element pooled in this pool.
*
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java 2020-02-05 19:26:48.000000000 +0000
@@ -31,7 +31,9 @@
* {@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} /
* {@link GenericKeyedObjectPool#getSoftMinEvictableIdleTimeMillis()}
*
+ *
* This class is immutable and thread-safe.
+ *
*
* @param the type of objects in the pool
*
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java 2020-02-05 19:26:48.000000000 +0000
@@ -22,6 +22,7 @@
* its own specific configuration attributes.
*
* This class is immutable and thread-safe.
+ *
*
* @since 2.0
*/
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java 2020-02-05 19:26:48.000000000 +0000
@@ -120,12 +120,9 @@
public Thread newThread(final Runnable runnable) {
final Thread thread = new Thread(null, runnable, "commons-pool-evictor-thread");
thread.setDaemon(true); // POOL-363 - Required for applications using Runtime.addShutdownHook().
- AccessController.doPrivileged(new PrivilegedAction() {
- @Override
- public Void run() {
- thread.setContextClassLoader(EvictorThreadFactory.class.getClassLoader());
- return null;
- }
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ thread.setContextClassLoader(EvictorThreadFactory.class.getClassLoader());
+ return null;
});
return thread;
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java 2020-02-05 19:26:48.000000000 +0000
@@ -50,10 +50,12 @@
* {@link #borrowObject borrowObject} methods. Each time a new key value is
* provided to one of these methods, a sub-new pool is created under the given
* key to be managed by the containing GenericKeyedObjectPool.
+ *
*
* Note that the current implementation uses a ConcurrentHashMap which uses
* equals() to compare keys.
* This means that distinct instance keys must be distinguishable using equals.
+ *
*
* Optionally, one may configure the pool to examine and possibly evict objects
* as they sit idle in the pool and to ensure that a minimum number of idle
@@ -62,12 +64,15 @@
* configuring this optional feature. Eviction runs contend with client threads
* for access to objects in the pool, so if they run too frequently performance
* issues may result.
+ *
*
* Implementation note: To prevent possible deadlocks, care has been taken to
* ensure that no call to a factory method will occur within a synchronization
* block. See POOL-125 and DBCP-44 for more information.
+ *
*
* This class is intended to be thread-safe.
+ *
*
* @see GenericObjectPool
*
@@ -447,6 +452,11 @@
final ObjectDeque objectDeque = poolMap.get(key);
+ if (objectDeque == null) {
+ throw new IllegalStateException(
+ "No keyed pool found under the given key.");
+ }
+
final PooledObject p = objectDeque.getAllObjects().get(new IdentityWrapper<>(obj));
if (p == null) {
@@ -1077,8 +1087,16 @@
final ObjectDeque objectDeque = register(key);
try {
- final boolean isIdle = objectDeque.getIdleObjects().remove(toDestroy);
-
+ boolean isIdle;
+ synchronized(toDestroy) {
+ // Check idle state directly
+ isIdle = toDestroy.getState().equals(PooledObjectState.IDLE);
+ // If idle, not under eviction test, or always is true, remove instance,
+ // updating isIdle if instance is found in idle objects
+ if (isIdle || always) {
+ isIdle = objectDeque.getIdleObjects().remove(toDestroy);
+ }
+ }
if (isIdle || always) {
objectDeque.getAllObjects().remove(new IdentityWrapper<>(toDestroy.getObject()));
toDestroy.invalidate();
@@ -1151,10 +1169,9 @@
*/
private void deregister(final K k) {
Lock lock = keyLock.readLock();
- ObjectDeque objectDeque;
try {
lock.lock();
- objectDeque = poolMap.get(k);
+ final ObjectDeque objectDeque = poolMap.get(k);
final long numInterested = objectDeque.getNumInterested().decrementAndGet();
if (numInterested == 0 && objectDeque.getCreateCount().get() == 0) {
// Potential to remove key
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java 2020-02-05 19:26:48.000000000 +0000
@@ -38,7 +38,8 @@
*
* When coupled with the appropriate {@link PooledObjectFactory},
* GenericObjectPool provides robust pooling functionality for
- * arbitrary objects.
+ * arbitrary objects.
+ *
*
* Optionally, one may configure the pool to examine and possibly evict objects
* as they sit idle in the pool and to ensure that a minimum number of idle
@@ -46,7 +47,8 @@
* which runs asynchronously. Caution should be used when configuring this
* optional feature. Eviction runs contend with client threads for access to
* objects in the pool, so if they run too frequently performance issues may
- * result.
+ * result.
+ *
*
* The pool can also be configured to detect and remove "abandoned" objects,
* i.e. objects that have been checked out of the pool but neither used nor
@@ -59,13 +61,16 @@
* their last use will be queried
* using the getLastUsed method on that interface; otherwise
* abandonment is determined by how long an object has been checked out from
- * the pool.
+ * the pool.
+ *
*
* Implementation note: To prevent possible deadlocks, care has been taken to
* ensure that no call to a factory method will occur within a synchronization
- * block. See POOL-125 and DBCP-44 for more information.
+ * block. See POOL-125 and DBCP-44 for more information.
+ *
*
- * This class is intended to be thread-safe.
+ * This class is intended to be thread-safe.
+ *
*
* @see GenericKeyedObjectPool
*
@@ -576,6 +581,11 @@
} catch (final Exception e) {
swallowException(e);
}
+ try {
+ ensureIdle(1, false);
+ } catch (final Exception e) {
+ swallowException(e);
+ }
} else {
if (getLifo()) {
idleObjects.addFirst(p);
@@ -931,15 +941,6 @@
destroyedCount.incrementAndGet();
createCount.decrementAndGet();
}
-
- if (idleObjects.isEmpty() && idleObjects.hasTakeWaiters()) {
- // POOL-356.
- // In case there are already threads waiting on something in the pool
- // (e.g. idleObjects.takeFirst(); then we need to provide them a fresh instance.
- // Otherwise they will be stuck forever (or until timeout)
- final PooledObject freshPooled = create();
- idleObjects.put(freshPooled);
- }
}
@Override
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java 2020-02-05 19:26:48.000000000 +0000
@@ -26,6 +26,7 @@
* class is intended for internal use only.
*
* This class is intended to be thread-safe.
+ *
*
* @since 2.0
*/
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java 2020-02-05 19:26:48.000000000 +0000
@@ -34,6 +34,7 @@
* is equal to {@link Integer#MAX_VALUE}. Linked nodes are
* dynamically created upon each insertion unless this would bring the
* deque above capacity.
+ *
*
*
Most operations run in constant time (ignoring time spent
* blocking). Exceptions include {@link #remove(Object) remove},
@@ -41,14 +42,17 @@
* #removeLastOccurrence removeLastOccurrence}, {@link #contains
* contains}, {@link #iterator iterator.remove()}, and the bulk
* operations, all of which run in linear time.
+ *
*
*
This class and its iterator implement all of the
* optional methods of the {@link Collection} and {@link
* Iterator} interfaces.
+ *
*
* @param the type of elements held in this collection
*
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/SecurityManagerCallStack.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/SecurityManagerCallStack.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/SecurityManagerCallStack.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/SecurityManagerCallStack.java 2020-02-05 19:26:48.000000000 +0000
@@ -52,12 +52,7 @@
public SecurityManagerCallStack(final String messageFormat, final boolean useTimestamp) {
this.messageFormat = messageFormat;
this.dateFormat = useTimestamp ? new SimpleDateFormat(messageFormat) : null;
- this.securityManager = AccessController.doPrivileged(new PrivilegedAction() {
- @Override
- public PrivateSecurityManager run() {
- return new PrivateSecurityManager();
- }
- });
+ this.securityManager = AccessController.doPrivileged((PrivilegedAction) () -> new PrivateSecurityManager());
}
@Override
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java 2020-02-05 19:26:48.000000000 +0000
@@ -32,6 +32,7 @@
* {@link org.apache.tomcat.dbcp.pool2.ObjectPool}.
*
* This class is intended to be thread-safe.
+ *
*
* @param
* Type of element pooled in this pool.
@@ -185,6 +186,8 @@
*
* @param obj
* instance to return to the pool
+ * @throws IllegalArgumentException
+ * if obj is not currently part of this pool
*/
@Override
public synchronized void returnObject(final T obj) throws Exception {
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java 2020-02-05 19:26:48.000000000 +0000
@@ -17,6 +17,8 @@
package org.apache.tomcat.dbcp.pool2;
import java.io.Closeable;
+import java.util.Collection;
+import java.util.Iterator;
import java.util.NoSuchElementException;
/**
@@ -66,6 +68,75 @@
* @since 2.0
*/
public interface KeyedObjectPool extends Closeable {
+
+ /**
+ * Create an object using the {@link KeyedPooledObjectFactory factory} or
+ * other implementation dependent mechanism, passivate it, and then place it
+ * in the idle object pool. addObject is useful for
+ * "pre-loading" a pool with idle objects (Optional operation).
+ *
+ * @param key the key a new instance should be added to
+ *
+ * @throws Exception
+ * when {@link KeyedPooledObjectFactory#makeObject} fails.
+ * @throws IllegalStateException
+ * after {@link #close} has been called on this pool.
+ * @throws UnsupportedOperationException
+ * when this pool cannot add new idle objects.
+ */
+ void addObject(K key) throws Exception, IllegalStateException,
+ UnsupportedOperationException;
+
+ /**
+ * Calls {@link KeyedObjectPool#addObject(Object)} with each
+ * key in keys for count number of times. This has
+ * the same effect as calling {@link #addObjects(Object, int)}
+ * for each key in the keys collection.
+ *
+ * @param keys
+ * {@link Collection} of keys to add objects for.
+ * @param count
+ * the number of idle objects to add for each key.
+ * @throws Exception
+ * when {@link KeyedObjectPool#addObject(Object)} fails.
+ * @throws IllegalArgumentException
+ * when keyedPool, keys, or any value
+ * in keys is null.
+ * @see #addObjects(Object, int)
+ */
+ default void addObjects(final Collection keys, final int count) throws Exception, IllegalArgumentException {
+ if (keys == null) {
+ throw new IllegalArgumentException(PoolUtils.MSG_NULL_KEYS);
+ }
+ final Iterator iter = keys.iterator();
+ while (iter.hasNext()) {
+ addObjects(iter.next(), count);
+ }
+ }
+
+ /**
+ * Calls {@link KeyedObjectPool#addObject(Object)}
+ * keycount number of times.
+ *
+ * @param key
+ * the key to add objects for.
+ * @param count
+ * the number of idle objects to add for key.
+ * @throws Exception
+ * when {@link KeyedObjectPool#addObject(Object)} fails.
+ * @throws IllegalArgumentException
+ * when key is null.
+ * @since 2.8.0
+ */
+ default void addObjects(final K key, final int count) throws Exception, IllegalArgumentException {
+ if (key == null) {
+ throw new IllegalArgumentException(PoolUtils.MSG_NULL_KEY);
+ }
+ for (int i = 0; i < count; i++) {
+ addObject(key);
+ }
+ }
+
/**
* Obtains an instance from this pool for the specified key.
*
@@ -105,75 +176,50 @@
V borrowObject(K key) throws Exception, NoSuchElementException, IllegalStateException;
/**
- * Return an instance to the pool. By contract, obj
- * must have been obtained using
- * {@link #borrowObject borrowObject} or a related method as defined in an
- * implementation or sub-interface using a key that is
- * equivalent to the one used to borrow the instance in the first place.
- *
- * @param key the key used to obtain the object
- * @param obj a {@link #borrowObject borrowed} instance to be returned.
+ * Clears the pool, removing all pooled instances (optional operation).
*
- * @throws IllegalStateException
- * if an attempt is made to return an object to the pool that
- * is in any state other than allocated (i.e. borrowed).
- * Attempting to return an object more than once or attempting
- * to return an object that was never borrowed from the pool
- * will trigger this exception.
+ * @throws UnsupportedOperationException when this implementation doesn't
+ * support the operation
*
- * @throws Exception if an instance cannot be returned to the pool
+ * @throws Exception if the pool cannot be cleared
*/
- void returnObject(K key, V obj) throws Exception;
+ void clear() throws Exception, UnsupportedOperationException;
/**
- * Invalidates an object from the pool.
- *
- * By contract, objmust have been obtained
- * using {@link #borrowObject borrowObject} or a related method as defined
- * in an implementation or sub-interface using a key that is
- * equivalent to the one used to borrow the Object in the first
- * place.
- *
- *
- * This method should be used when an object that has been borrowed is
- * determined (due to an exception or other problem) to be invalid.
- *
+ * Clears the specified pool, removing all pooled instances corresponding to
+ * the given key (optional operation).
*
- * @param key the key used to obtain the object
- * @param obj a {@link #borrowObject borrowed} instance to be returned.
+ * @param key the key to clear
*
- * @throws Exception if the instance cannot be invalidated
+ * @throws UnsupportedOperationException when this implementation doesn't
+ * support the operation
+ *
+ * @throws Exception if the key cannot be cleared
*/
- void invalidateObject(K key, V obj) throws Exception;
+ void clear(K key) throws Exception, UnsupportedOperationException;
/**
- * Create an object using the {@link KeyedPooledObjectFactory factory} or
- * other implementation dependent mechanism, passivate it, and then place it
- * in the idle object pool. addObject is useful for
- * "pre-loading" a pool with idle objects (Optional operation).
- *
- * @param key the key a new instance should be added to
- *
- * @throws Exception
- * when {@link KeyedPooledObjectFactory#makeObject} fails.
- * @throws IllegalStateException
- * after {@link #close} has been called on this pool.
- * @throws UnsupportedOperationException
- * when this pool cannot add new idle objects.
+ * Close this pool, and free any resources associated with it.
+ *
+ * Calling {@link #addObject addObject} or
+ * {@link #borrowObject borrowObject} after invoking this method on a pool
+ * will cause them to throw an {@link IllegalStateException}.
+ *
+ *
+ * Implementations should silently fail if not all resources can be freed.
+ *
*/
- void addObject(K key) throws Exception, IllegalStateException,
- UnsupportedOperationException;
+ @Override
+ void close();
/**
- * Returns the number of instances corresponding to the given
- * key currently idle in this pool. Returns a negative value if
- * this information is not available.
- *
- * @param key the key to query
- * @return the number of instances corresponding to the given
- * key currently idle in this pool.
+ * Returns the total number of instances currently borrowed from this pool but
+ * not yet returned. Returns a negative value if this information is not
+ * available.
+ * @return the total number of instances currently borrowed from this pool but
+ * not yet returned.
*/
- int getNumIdle(K key);
+ int getNumActive();
/**
* Returns the number of instances currently borrowed from but not yet
@@ -194,48 +240,55 @@
int getNumIdle();
/**
- * Returns the total number of instances currently borrowed from this pool but
- * not yet returned. Returns a negative value if this information is not
- * available.
- * @return the total number of instances currently borrowed from this pool but
- * not yet returned.
+ * Returns the number of instances corresponding to the given
+ * key currently idle in this pool. Returns a negative value if
+ * this information is not available.
+ *
+ * @param key the key to query
+ * @return the number of instances corresponding to the given
+ * key currently idle in this pool.
*/
- int getNumActive();
+ int getNumIdle(K key);
/**
- * Clears the pool, removing all pooled instances (optional operation).
+ * Invalidates an object from the pool.
+ *
+ * By contract, objmust have been obtained
+ * using {@link #borrowObject borrowObject} or a related method as defined
+ * in an implementation or sub-interface using a key that is
+ * equivalent to the one used to borrow the Object in the first
+ * place.
+ *
+ *
+ * This method should be used when an object that has been borrowed is
+ * determined (due to an exception or other problem) to be invalid.
+ *
*
- * @throws UnsupportedOperationException when this implementation doesn't
- * support the operation
+ * @param key the key used to obtain the object
+ * @param obj a {@link #borrowObject borrowed} instance to be returned.
*
- * @throws Exception if the pool cannot be cleared
+ * @throws Exception if the instance cannot be invalidated
*/
- void clear() throws Exception, UnsupportedOperationException;
+ void invalidateObject(K key, V obj) throws Exception;
/**
- * Clears the specified pool, removing all pooled instances corresponding to
- * the given key (optional operation).
+ * Return an instance to the pool. By contract, obj
+ * must have been obtained using
+ * {@link #borrowObject borrowObject} or a related method as defined in an
+ * implementation or sub-interface using a key that is
+ * equivalent to the one used to borrow the instance in the first place.
*
- * @param key the key to clear
+ * @param key the key used to obtain the object
+ * @param obj a {@link #borrowObject borrowed} instance to be returned.
*
- * @throws UnsupportedOperationException when this implementation doesn't
- * support the operation
+ * @throws IllegalStateException
+ * if an attempt is made to return an object to the pool that
+ * is in any state other than allocated (i.e. borrowed).
+ * Attempting to return an object more than once or attempting
+ * to return an object that was never borrowed from the pool
+ * will trigger this exception.
*
- * @throws Exception if the key cannot be cleared
- */
- void clear(K key) throws Exception, UnsupportedOperationException;
-
- /**
- * Close this pool, and free any resources associated with it.
- *
- * Calling {@link #addObject addObject} or
- * {@link #borrowObject borrowObject} after invoking this method on a pool
- * will cause them to throw an {@link IllegalStateException}.
- *
- *
- * Implementations should silently fail if not all resources can be freed.
- *
+ * @throws Exception if an instance cannot be returned to the pool
*/
- @Override
- void close();
+ void returnObject(K key, V obj) throws Exception;
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java 2020-02-05 19:26:48.000000000 +0000
@@ -60,6 +60,38 @@
public interface ObjectPool extends Closeable {
/**
+ * Creates an object using the {@link PooledObjectFactory factory} or other
+ * implementation dependent mechanism, passivate it, and then place it in
+ * the idle object pool. addObject is useful for "pre-loading"
+ * a pool with idle objects. (Optional operation).
+ *
+ * @throws Exception
+ * when {@link PooledObjectFactory#makeObject} fails.
+ * @throws IllegalStateException
+ * after {@link #close} has been called on this pool.
+ * @throws UnsupportedOperationException
+ * when this pool cannot add new idle objects.
+ */
+ void addObject() throws Exception, IllegalStateException,
+ UnsupportedOperationException;
+
+ /**
+ * Calls {@link ObjectPool#addObject()} count
+ * number of times.
+ *
+ * @param count
+ * the number of idle objects to add.
+ * @throws Exception
+ * when {@link ObjectPool#addObject()} fails.
+ * @since 2.8.0
+ */
+ default void addObjects(final int count) throws Exception {
+ for (int i = 0; i < count; i++) {
+ addObject();
+ }
+ }
+
+ /**
* Obtains an instance from this pool.
*
* Instances returned from this method will have been either newly created
@@ -94,56 +126,36 @@
IllegalStateException;
/**
- * Returns an instance to the pool. By contract, obj
- * must have been obtained using {@link #borrowObject()} or
- * a related method as defined in an implementation or sub-interface.
- *
- * @param obj a {@link #borrowObject borrowed} instance to be returned.
+ * Clears any objects sitting idle in the pool, releasing any associated
+ * resources (optional operation). Idle objects cleared must be
+ * {@link PooledObjectFactory#destroyObject(PooledObject)}.
*
- * @throws IllegalStateException
- * if an attempt is made to return an object to the pool that
- * is in any state other than allocated (i.e. borrowed).
- * Attempting to return an object more than once or attempting
- * to return an object that was never borrowed from the pool
- * will trigger this exception.
+ * @throws UnsupportedOperationException
+ * if this implementation does not support the operation
*
- * @throws Exception if an instance cannot be returned to the pool
+ * @throws Exception if the pool cannot be cleared
*/
- void returnObject(T obj) throws Exception;
+ void clear() throws Exception, UnsupportedOperationException;
/**
- * Invalidates an object from the pool.
+ * Closes this pool, and free any resources associated with it.
*
- * By contract, objmust have been obtained
- * using {@link #borrowObject} or a related method as defined in an
- * implementation or sub-interface.
+ * Calling {@link #addObject} or {@link #borrowObject} after invoking this
+ * method on a pool will cause them to throw an {@link IllegalStateException}.
*
*
- * This method should be used when an object that has been borrowed is
- * determined (due to an exception or other problem) to be invalid.
+ * Implementations should silently fail if not all resources can be freed.
*
- *
- * @param obj a {@link #borrowObject borrowed} instance to be disposed.
- *
- * @throws Exception if the instance cannot be invalidated
*/
- void invalidateObject(T obj) throws Exception;
+ @Override
+ void close();
/**
- * Creates an object using the {@link PooledObjectFactory factory} or other
- * implementation dependent mechanism, passivate it, and then place it in
- * the idle object pool. addObject is useful for "pre-loading"
- * a pool with idle objects. (Optional operation).
- *
- * @throws Exception
- * when {@link PooledObjectFactory#makeObject} fails.
- * @throws IllegalStateException
- * after {@link #close} has been called on this pool.
- * @throws UnsupportedOperationException
- * when this pool cannot add new idle objects.
+ * Returns the number of instances currently borrowed from this pool. Returns
+ * a negative value if this information is not available.
+ * @return the number of instances currently borrowed from this pool.
*/
- void addObject() throws Exception, IllegalStateException,
- UnsupportedOperationException;
+ int getNumActive();
/**
* Returns the number of instances currently idle in this pool. This may be
@@ -155,34 +167,38 @@
int getNumIdle();
/**
- * Returns the number of instances currently borrowed from this pool. Returns
- * a negative value if this information is not available.
- * @return the number of instances currently borrowed from this pool.
- */
- int getNumActive();
-
- /**
- * Clears any objects sitting idle in the pool, releasing any associated
- * resources (optional operation). Idle objects cleared must be
- * {@link PooledObjectFactory#destroyObject(PooledObject)}.
+ * Invalidates an object from the pool.
+ *
+ * By contract, objmust have been obtained
+ * using {@link #borrowObject} or a related method as defined in an
+ * implementation or sub-interface.
+ *
+ *
+ * This method should be used when an object that has been borrowed is
+ * determined (due to an exception or other problem) to be invalid.
+ *
*
- * @throws UnsupportedOperationException
- * if this implementation does not support the operation
+ * @param obj a {@link #borrowObject borrowed} instance to be disposed.
*
- * @throws Exception if the pool cannot be cleared
+ * @throws Exception if the instance cannot be invalidated
*/
- void clear() throws Exception, UnsupportedOperationException;
+ void invalidateObject(T obj) throws Exception;
/**
- * Closes this pool, and free any resources associated with it.
- *
- * Calling {@link #addObject} or {@link #borrowObject} after invoking this
- * method on a pool will cause them to throw an {@link IllegalStateException}.
- *
- *
- * Implementations should silently fail if not all resources can be freed.
- *
+ * Returns an instance to the pool. By contract, obj
+ * must have been obtained using {@link #borrowObject()} or
+ * a related method as defined in an implementation or sub-interface.
+ *
+ * @param obj a {@link #borrowObject borrowed} instance to be returned.
+ *
+ * @throws IllegalStateException
+ * if an attempt is made to return an object to the pool that
+ * is in any state other than allocated (i.e. borrowed).
+ * Attempting to return an object more than once or attempting
+ * to return an object that was never borrowed from the pool
+ * will trigger this exception.
+ *
+ * @throws Exception if an instance cannot be returned to the pool
*/
- @Override
- void close();
+ void returnObject(T obj) throws Exception;
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/PooledObject.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/PooledObject.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/PooledObject.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/PooledObject.java 2020-02-05 19:26:48.000000000 +0000
@@ -24,6 +24,7 @@
* state, for the pooled objects.
*
* Implementations of this class are required to be thread-safe.
+ *
*
* @param the type of object in the pool
*
@@ -187,7 +188,7 @@
* @param requireFullStackTrace the new configuration setting for abandoned object logging
* @since 2.7.0
*/
- default void setRequireFullStackTrace(boolean requireFullStackTrace) {
+ default void setRequireFullStackTrace(final boolean requireFullStackTrace) {
// noop
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java
--- tomcat9-9.0.27/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java 2020-02-05 19:26:48.000000000 +0000
@@ -38,9 +38,9 @@
private static final String MSG_FACTOR_NEGATIVE = "factor must be positive.";
private static final String MSG_MIN_IDLE = "minIdle must be non-negative.";
- private static final String MSG_NULL_KEY = "key must not be null.";
+ static final String MSG_NULL_KEY = "key must not be null.";
private static final String MSG_NULL_KEYED_POOL = "keyedPool must not be null.";
- private static final String MSG_NULL_KEYS = "keys must not be null.";
+ static final String MSG_NULL_KEYS = "keys must not be null.";
private static final String MSG_NULL_POOL = "pool must not be null.";
/**
@@ -220,15 +220,15 @@
* when {@link ObjectPool#addObject()} fails.
* @throws IllegalArgumentException
* when pool is null.
+ * @deprecated Use {@link ObjectPool#addObjects(int)}.
*/
+ @Deprecated
public static void prefill(final ObjectPool pool, final int count)
throws Exception, IllegalArgumentException {
if (pool == null) {
throw new IllegalArgumentException(MSG_NULL_POOL);
}
- for (int i = 0; i < count; i++) {
- pool.addObject();
- }
+ pool.addObjects(count);
}
/**
@@ -248,19 +248,16 @@
* @throws IllegalArgumentException
* when keyedPool or key is
* null.
+ * @deprecated Use {@link KeyedObjectPool#addObjects(Object, int)}.
*/
+ @Deprecated
public static void prefill(final KeyedObjectPool keyedPool,
final K key, final int count) throws Exception,
IllegalArgumentException {
if (keyedPool == null) {
throw new IllegalArgumentException(MSG_NULL_KEYED_POOL);
}
- if (key == null) {
- throw new IllegalArgumentException(MSG_NULL_KEY);
- }
- for (int i = 0; i < count; i++) {
- keyedPool.addObject(key);
- }
+ keyedPool.addObjects(key, count);
}
/**
@@ -283,17 +280,16 @@
* when keyedPool, keys, or any value
* in keys is null.
* @see #prefill(KeyedObjectPool, Object, int)
+ * @deprecated Use {@link KeyedObjectPool#addObjects(Collection, int)}.
*/
+ @Deprecated
public static void prefill(final KeyedObjectPool keyedPool,
final Collection keys, final int count) throws Exception,
IllegalArgumentException {
if (keys == null) {
throw new IllegalArgumentException(MSG_NULL_KEYS);
}
- final Iterator iter = keys.iterator();
- while (iter.hasNext()) {
- prefill(keyedPool, iter.next(), count);
- }
+ keyedPool.addObjects(keys, count);
}
/**
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/JarScanFilter.java tomcat9-9.0.31/java/org/apache/tomcat/JarScanFilter.java
--- tomcat9-9.0.27/java/org/apache/tomcat/JarScanFilter.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/JarScanFilter.java 2020-02-05 19:26:48.000000000 +0000
@@ -28,4 +28,13 @@
* false if it should be excluded
*/
boolean check(JarScanType jarScanType, String jarName);
+
+ /**
+ *
+ * @return true if all of the scans should be skipped which
+ * can improve startup performance. The default is false.
+ */
+ default boolean isSkipAll() {
+ return false;
+ }
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantClass.java tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantClass.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantClass.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantClass.java 2020-02-05 19:26:48.000000000 +0000
@@ -48,7 +48,7 @@
/**
* @return Name index in constant pool of class name.
*/
- public final int getNameIndex() {
+ public int getNameIndex() {
return name_index;
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantDouble.java tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantDouble.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantDouble.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantDouble.java 2020-02-05 19:26:48.000000000 +0000
@@ -48,7 +48,7 @@
/**
* @return data, i.e., 8 bytes.
*/
- public final double getBytes() {
+ public double getBytes() {
return bytes;
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantFloat.java tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantFloat.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantFloat.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantFloat.java 2020-02-05 19:26:48.000000000 +0000
@@ -48,7 +48,7 @@
/**
* @return data, i.e., 4 bytes.
*/
- public final float getBytes() {
+ public float getBytes() {
return bytes;
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantInteger.java tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantInteger.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantInteger.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantInteger.java 2020-02-05 19:26:48.000000000 +0000
@@ -48,7 +48,7 @@
/**
* @return data, i.e., 4 bytes.
*/
- public final int getBytes() {
+ public int getBytes() {
return bytes;
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/classfile/ConstantLong.java 2020-02-05 19:26:48.000000000 +0000
@@ -48,7 +48,7 @@
/**
* @return data, i.e., 8 bytes.
*/
- public final long getBytes() {
+ public long getBytes() {
return bytes;
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/Const.java tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/Const.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/bcel/Const.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/bcel/Const.java 2020-02-05 19:26:48.000000000 +0000
@@ -23,12 +23,14 @@
public final class Const {
/** One of the access flags for fields, methods, or classes.
- * @see
- * Flag definitions for Fields in the Java Virtual Machine Specification (Java SE 8 Edition).
- * @see
- * Flag definitions for Methods in the Java Virtual Machine Specification (Java SE 8 Edition).
- * @see
- * Flag definitions for Classes in the Java Virtual Machine Specification (Java SE 8 Edition).
+ * @see
+ * Flag definitions for Classes in the Java Virtual Machine Specification (Java SE 9 Edition).
+ * @see
+ * Flag definitions for Fields in the Java Virtual Machine Specification (Java SE 9 Edition).
+ * @see
+ * Flag definitions for Methods in the Java Virtual Machine Specification (Java SE 9 Edition).
+ * @see
+ * Flag definitions for Inner Classes in the Java Virtual Machine Specification (Java SE 9 Edition).
*/
public static final short ACC_FINAL = 0x0010;
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/buf/Asn1Parser.java tomcat9-9.0.31/java/org/apache/tomcat/util/buf/Asn1Parser.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/buf/Asn1Parser.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/buf/Asn1Parser.java 2020-02-05 19:26:48.000000000 +0000
@@ -83,6 +83,12 @@
}
+ public void parseBytes(byte[] dest) {
+ System.arraycopy(source, pos, dest, 0, dest.length);
+ pos += dest.length;
+ }
+
+
private int next() {
return source[pos++] & 0xFF;
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/buf/Asn1Writer.java tomcat9-9.0.31/java/org/apache/tomcat/util/buf/Asn1Writer.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/buf/Asn1Writer.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/buf/Asn1Writer.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,95 @@
+/*
+ * 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 Asn1Writer {
+
+ public static byte[] writeSequence(byte[]... components) {
+ int len = 0;
+ for (byte[] component : components) {
+ len += component.length;
+ }
+
+ byte[] combined = new byte[len];
+ int pos = 0;
+ for (byte[] component : components) {
+ System.arraycopy(component, 0, combined, pos, component.length);
+ pos += component.length;
+ }
+
+ return writeTag((byte) 0x30, combined);
+ }
+
+
+ public static byte[] writeInteger(int value) {
+ // How many bytes required to write the value? No more than 4 for int.
+ int valueSize = 1;
+ while ((value >> (valueSize * 8)) > 0) {
+ valueSize++;
+ }
+
+ byte[] valueBytes = new byte[valueSize];
+ int i = 0;
+ while (valueSize > 0) {
+ valueBytes[i] = (byte) (value >> (8 * (valueSize - 1)));
+ value = value >> 8;
+ valueSize--;
+ i++;
+ }
+
+ return writeTag((byte) 0x02, valueBytes);
+ }
+
+ public static byte[] writeOctetString(byte[] data) {
+ return writeTag((byte) 0x04, data);
+ }
+
+ public static byte[] writeTag(byte tagId, byte[] data) {
+ int dataSize = data.length;
+ // How many bytes to write the length?
+ int lengthSize = 1;
+ if (dataSize >127) {
+ // 1 byte we have is now used to record how many bytes we need to
+ // record a length > 127
+ // Result is lengthSize = 1 + number of bytes to record length
+ do {
+ lengthSize++;
+ }
+ while ((dataSize >> (lengthSize * 8)) > 0);
+ }
+
+ // 1 for tag + lengthSize + dataSize
+ byte[] result = new byte[1 + lengthSize + dataSize];
+ result[0] = tagId;
+ if (dataSize < 128) {
+ result[1] = (byte) dataSize;
+ } else {
+ // lengthSize is 1 + number of bytes for length
+ result[1] = (byte) (127 + lengthSize);
+ int i = lengthSize;
+ while (dataSize > 0) {
+ result[i] = (byte) (dataSize & 0xFF);
+ dataSize = dataSize >> 8;
+ i--;
+ }
+ }
+
+ System.arraycopy(data, 0, result, 1 + lengthSize, data.length);
+
+ return result;
+ }
+}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/buf/CharChunk.java tomcat9-9.0.31/java/org/apache/tomcat/util/buf/CharChunk.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/buf/CharChunk.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/buf/CharChunk.java 2020-02-05 19:26:48.000000000 +0000
@@ -165,7 +165,7 @@
// -------------------- Adding data to the buffer --------------------
- public void append(char b) throws IOException {
+ public void append(char c) throws IOException {
makeSpace(1);
int limit = getLimitInternal();
@@ -173,7 +173,7 @@
if (end >= limit) {
flushBuffer();
}
- buff[end++] = b;
+ buff[end++] = c;
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/buf/CharsetCache.java tomcat9-9.0.31/java/org/apache/tomcat/util/buf/CharsetCache.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/buf/CharsetCache.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/buf/CharsetCache.java 2020-02-05 19:26:48.000000000 +0000
@@ -149,7 +149,9 @@
"29626c", "833", "cp29626c", "ibm-1140", "ibm-1141", "ibm-1142", "ibm-1143", "ibm-1144", "ibm-1145",
"ibm-1146", "ibm-1147", "ibm-1148", "ibm-1149", "ibm-29626c", "ibm-858", "ibm-eucjp", "ibm1140", "ibm1141",
"ibm1142", "ibm1143", "ibm1144", "ibm1145", "ibm1146", "ibm1147", "ibm1148", "ibm1149", "ibm29626c",
- "ibm858", "x-ibm29626c"
+ "ibm858", "x-ibm29626c",
+ // Added from HPE JVM 1.8.0.17-hp-ux
+ "cp1051", "cp1386", "cshproman8", "hp-roman8", "ibm-1051", "r8", "roman8", "roman9"
};
private static final Charset DUMMY_CHARSET = new DummyCharset("Dummy", null);
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/buf/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/tomcat/util/buf/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/buf/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/buf/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,4 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+asn1Parser.lengthInvalid=无效长度 [{0}]字节报告,但是输入数据的长度是 [{1}]字节
+asn1Parser.tagMismatch=期望找到值 [{0}]但是却找到值 [{1}]
+
+hexUtils.fromHex.nonHex=输入只能由十六进制数字组成
+
uDecoder.urlDecode.conversionError=使用编码[{1}]解码[{0}]失败
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/buf/package.html tomcat9-9.0.31/java/org/apache/tomcat/util/buf/package.html
--- tomcat9-9.0.27/java/org/apache/tomcat/util/buf/package.html 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/buf/package.html 2020-02-05 19:26:48.000000000 +0000
@@ -22,7 +22,7 @@
Encoding is a critical operation for performance. There are few tricks in this package - the C2B and
-B2C converters are caching a ISReader/OSWriter and keep everything allocated to do the conversions
+B2C converters are caching an ISReader/OSWriter and keep everything allocated to do the conversions
in any VM without any garbage.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/codec/binary/Base64.java tomcat9-9.0.31/java/org/apache/tomcat/util/codec/binary/Base64.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/codec/binary/Base64.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/codec/binary/Base64.java 2020-02-05 19:26:48.000000000 +0000
@@ -139,6 +139,10 @@
*/
/** Mask used to extract 6 bits, used when encoding */
private static final int MASK_6BITS = 0x3f;
+ /** Mask used to extract 4 bits, used when decoding final trailing character. */
+ private static final int MASK_4BITS = 0xf;
+ /** Mask used to extract 2 bits, used when decoding final trailing character. */
+ private static final int MASK_2BITS = 0x3;
// The static final fields above are used for the original static byte[] methods on Base64.
// The private member fields below are used with the new streaming approach, which requires
@@ -483,12 +487,12 @@
// TODO not currently tested; perhaps it is impossible?
break;
case 2 : // 12 bits = 8 + 4
- validateCharacter(4, context);
+ validateCharacter(MASK_4BITS, context);
context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits
buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
break;
case 3 : // 18 bits = 8 + 8 + 2
- validateCharacter(2, context);
+ validateCharacter(MASK_2BITS, context);
context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits
buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS);
buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS);
@@ -792,20 +796,22 @@
/**
- *
- * Validates whether the character is possible in the context of the set of possible base 64 values.
- *
+ * Validates whether decoding the final trailing character is possible in the context
+ * of the set of possible base 64 values.
+ *
+ *
The character is valid if the lower bits within the provided mask are zero. This
+ * is used to test the final trailing base-64 digit is zero in the bits that will be discarded.
*
- * @param numBitsToDrop number of least significant bits to check
+ * @param emptyBitsMask The mask of the lower bits that should be empty
* @param context the context to be used
*
* @throws IllegalArgumentException if the bits being checked contain any non-zero value
*/
- private long validateCharacter(final int numBitsToDrop, final Context context) {
- if ((context.ibitWorkArea & numBitsToDrop) != 0) {
- throw new IllegalArgumentException(
- "Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible value");
+ private static void validateCharacter(final int emptyBitsMask, final Context context) {
+ if ((context.ibitWorkArea & emptyBitsMask) != 0) {
+ throw new IllegalArgumentException(
+ "Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible value. " +
+ "Expected the discarded bits to be zero.");
}
- return context.ibitWorkArea >> numBitsToDrop;
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java tomcat9-9.0.31/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java 2020-02-05 19:26:48.000000000 +0000
@@ -136,6 +136,18 @@
*/
private static final int DEFAULT_BUFFER_SIZE = 128;
+ /**
+ * The maximum size buffer to allocate.
+ *
+ *
This is set to the same size used in the JDK {@code java.util.ArrayList}:
+ *
+ * Some VMs reserve some header words in an array.
+ * Attempts to allocate larger arrays may result in
+ * OutOfMemoryError: Requested array size exceeds VM limit.
+ *
+ */
+ private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
+
/** Mask used to extract 8 bits, used in decoding bytes */
protected static final int MASK_8BITS = 0xff;
@@ -165,7 +177,7 @@
private final int chunkSeparatorLength;
/**
- * Note lineLength is rounded down to the nearest multiple of {@link #encodedBlockSize}
+ * Note lineLength is rounded down to the nearest multiple of the encoded block size.
* If chunkSeparatorLength is zero, then chunking is disabled.
* @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3)
* @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
@@ -178,7 +190,7 @@
}
/**
- * Note lineLength is rounded down to the nearest multiple of {@link #encodedBlockSize}
+ * Note lineLength is rounded down to the nearest multiple of the encoded block size.
* If chunkSeparatorLength is zero, then chunking is disabled.
* @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3)
* @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4)
@@ -220,7 +232,7 @@
/**
* Get the default buffer size. Can be overridden.
*
- * @return {@link #DEFAULT_BUFFER_SIZE}
+ * @return the default buffer size.
*/
protected int getDefaultBufferSize() {
return DEFAULT_BUFFER_SIZE;
@@ -229,18 +241,69 @@
/**
* Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}.
* @param context the context to be used
+ * @param minCapacity the minimum required capacity
+ * @return the resized byte[] buffer
+ * @throws OutOfMemoryError if the {@code minCapacity} is negative
+ */
+ private static byte[] resizeBuffer(final Context context, final int minCapacity) {
+ // Overflow-conscious code treats the min and new capacity as unsigned.
+ final int oldCapacity = context.buffer.length;
+ int newCapacity = oldCapacity * DEFAULT_BUFFER_RESIZE_FACTOR;
+ if (compareUnsigned(newCapacity, minCapacity) < 0) {
+ newCapacity = minCapacity;
+ }
+ if (compareUnsigned(newCapacity, MAX_BUFFER_SIZE) > 0) {
+ newCapacity = createPositiveCapacity(minCapacity);
+ }
+
+ final byte[] b = new byte[newCapacity];
+ System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
+ context.buffer = b;
+ return b;
+ }
+
+ /**
+ * Compares two {@code int} values numerically treating the values
+ * as unsigned. Taken from JDK 1.8.
+ *
+ *
TODO: Replace with JDK 1.8 Integer::compareUnsigned(int, int).
+ *
+ * @param x the first {@code int} to compare
+ * @param y the second {@code int} to compare
+ * @return the value {@code 0} if {@code x == y}; a value less
+ * than {@code 0} if {@code x < y} as unsigned values; and
+ * a value greater than {@code 0} if {@code x > y} as
+ * unsigned values
*/
- private byte[] resizeBuffer(final Context context) {
- if (context.buffer == null) {
- context.buffer = new byte[getDefaultBufferSize()];
- context.pos = 0;
- context.readPos = 0;
- } else {
- final byte[] b = new byte[context.buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR];
- System.arraycopy(context.buffer, 0, b, 0, context.buffer.length);
- context.buffer = b;
+ private static int compareUnsigned(int x, int y) {
+ return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);
+ }
+
+ /**
+ * Create a positive capacity at least as large the minimum required capacity.
+ * If the minimum capacity is negative then this throws an OutOfMemoryError as no array
+ * can be allocated.
+ *
+ * @param minCapacity the minimum capacity
+ * @return the capacity
+ * @throws OutOfMemoryError if the {@code minCapacity} is negative
+ */
+ private static int createPositiveCapacity(int minCapacity) {
+ if (minCapacity < 0) {
+ // overflow
+ throw new OutOfMemoryError("Unable to allocate array size: " + (minCapacity & 0xffffffffL));
}
- return context.buffer;
+ // This is called when we require buffer expansion to a very big array.
+ // Use the conservative maximum buffer size if possible, otherwise the biggest required.
+ //
+ // Note: In this situation JDK 1.8 java.util.ArrayList returns Integer.MAX_VALUE.
+ // This excludes some VMs that can exceed MAX_BUFFER_SIZE but not allocate a full
+ // Integer.MAX_VALUE length array.
+ // The result is that we may have to allocate an array of this size more than once if
+ // the capacity must be expanded again.
+ return (minCapacity > MAX_BUFFER_SIZE) ?
+ minCapacity :
+ MAX_BUFFER_SIZE;
}
/**
@@ -251,8 +314,15 @@
* @return the buffer
*/
protected byte[] ensureBufferSize(final int size, final Context context){
- if ((context.buffer == null) || (context.buffer.length < context.pos + size)){
- return resizeBuffer(context);
+ if (context.buffer == null) {
+ context.buffer = new byte[getDefaultBufferSize()];
+ context.pos = 0;
+ context.readPos = 0;
+
+ // Overflow-conscious:
+ // x + y > z == x + y - z > 0
+ } else if (context.pos + size - context.buffer.length > 0) {
+ return resizeBuffer(context, context.pos + size);
}
return context.buffer;
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/compat/GraalCompat.java tomcat9-9.0.31/java/org/apache/tomcat/util/compat/GraalCompat.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/compat/GraalCompat.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/compat/GraalCompat.java 2020-02-05 19:26:48.000000000 +0000
@@ -18,7 +18,7 @@
import java.io.IOException;
-class GraalCompat extends JreCompat {
+class GraalCompat extends Jre9Compat {
private static final boolean GRAAL;
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/compat/JreCompat.java tomcat9-9.0.31/java/org/apache/tomcat/util/compat/JreCompat.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/compat/JreCompat.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/compat/JreCompat.java 2020-02-05 19:26:48.000000000 +0000
@@ -49,7 +49,7 @@
if (GraalCompat.isSupported()) {
instance = new GraalCompat();
graalAvailable = true;
- jre9Available = false;
+ jre9Available = Jre9Compat.isSupported();
} else if (Jre9Compat.isSupported()) {
instance = new Jre9Compat();
graalAvailable = false;
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/compat/LocalStrings_fr.properties tomcat9-9.0.31/java/org/apache/tomcat/util/compat/LocalStrings_fr.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/compat/LocalStrings_fr.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/compat/LocalStrings_fr.properties 2020-02-05 19:26:48.000000000 +0000
@@ -14,6 +14,8 @@
# limitations under the License.
jre9Compat.invalidModuleUri=L''URI du module fournie [{0}] n''a pas pu être convertie en URL pour être traitée par le JarScanner
+jre9Compat.javaPre9=Le code est considéré être exécuté sur une JVM antérieure à Java 9 car la classe n'a pas été trouvée
+jre9Compat.unexpected=Impossible de créer les références vers les classes et méthodes de Java 9
jreCompat.noApplicationProtocol=Le Java Runtime utilisé ne supporte pas SSLEngine.getApplicationProtocol(). Il faut Java 9 pour utiliser cette option.
jreCompat.noApplicationProtocols=L'environnement Java ne supporte pas SSLParameters.setApplicationProtocols(), cette fonctionnalité demande Java 9
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/compat/LocalStrings_ko.properties tomcat9-9.0.31/java/org/apache/tomcat/util/compat/LocalStrings_ko.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/compat/LocalStrings_ko.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/compat/LocalStrings_ko.properties 2020-02-05 19:26:48.000000000 +0000
@@ -14,6 +14,8 @@
# limitations under the License.
jre9Compat.invalidModuleUri=[{0}](으)로 제공된 모듈 URI는 JarScanner가 처리할 수 있는 URL로 변환될 수 없습니다.
+jre9Compat.javaPre9=Java 9 클래스가 발견되지 않습니다. Java 9 이전 버전에서 동작하고 있는 것으로 보입니다.
+jre9Compat.unexpected=Java 9 클래스들과 메소드들을 참조할 수 없습니다.
jreCompat.noApplicationProtocol=자바 런타임이 SSLEngine.getApplicationProtocol()을 지원하지 않습니다. 이 기능을 사용하려면 Java 9를 사용해야 합니다.
jreCompat.noApplicationProtocols=자바 런타임이 SSLParameters.setApplicationProtocols()을 지원하지 않습니다. 이 기능을 사용하려면 Java 9를 사용해야 합니다.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/compat/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/tomcat/util/compat/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/compat/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/compat/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -14,5 +14,7 @@
# limitations under the License.
jre9Compat.invalidModuleUri=提供的模块URI [{0}]无法转换为JarScanner要处理的URL
+jre9Compat.javaPre9=类未找到,所以假设代码运行在pre-Java 8虚拟机上
+jre9Compat.unexpected=创建对Java 9类的依赖和方法失败
jreCompat.noApplicationProtocol=Java 运行时不支持 SSLEngine.getApplicationProtocol()。要使用该功能你必须使用 Java 9。
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/descriptor/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/tomcat/util/descriptor/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/descriptor/LocalStrings_zh_CN.properties 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/descriptor/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,16 @@
+# 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.
+
+digesterFactory.missingSchema=XML模型[{0}]未找到,如果XML校验功能开启了的话,这很可能终止XML校验
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/descriptor/web/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/tomcat/util/descriptor/web/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/descriptor/web/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/descriptor/web/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -16,15 +16,25 @@
filterDef.invalidFilterName=过滤器定义中的 [{0}] 无效。
securityConstraint.uncoveredHttpOmittedMethod=对于URL模式[{0}]的安全性约束,将发现HTTP方法[{1}]。
+securityConstraint.uncoveredHttpOmittedMethodFix=添加url模式为[{0}]的安全约束以拒绝使用未覆盖的http方法[{1}]的访问
+webRuleSet.absoluteOrdering=<绝对值排序>元素在web片段xml中无效,将被忽略。
webRuleSet.nameCount=元素只能出现1次
webRuleSet.postconstruct.duplicate=class [{0}] 有重复的 post 构造方法声明
+webXml.duplicateEnvEntry=重复的env-entry 名 [{0}]
webXml.duplicateFilter=重复的过滤器名称 [{0}]
webXml.duplicateServletMapping=名为 [{0}]和 [{1}] 的servlet不能映射为一个url模式(url-pattern) [{2}]
webXml.mergeConflictDisplayName=显示名称在多个片段中被定义,这些片段包含不同的值,包括位于[{1}]的[{0}]的片段。
+webXml.mergeConflictFilter=筛选器[{0}]在多个片段中定义不一致,包括位于[{2}]的名为[{1}]的片段
+webXml.mergeConflictOrder=片段相对顺序包含循环引用。这可以通过在web.xml中使用绝对排序来解决。
+webXml.mergeConflictServlet=Servlet[{0}]在多个片段中的定义不一致,包括位于[{2}]的名为[{1}]的片段
webXml.mergeConflictSessionCookieName=会话cookie名称在多个具有不同值的片段中定义不一致,包括位于 [{1}] 的片段 [{0}]
webXml.mergeConflictSessionTimeout=会话超时以不同值的多个片段不一致地定义,这些片段包括位于[{1}]的具有名称[{0}]的片段。
webXml.mergeConflictSessionTrackingMode=会话跟踪模式在多个片段中定义不一致,包括位于[{1}]的名称为[{0}]的片段
webXml.reservedName=使用保留名称[{0}]检测到web.xml文件。 此片段将忽略name元素。
+webXml.unrecognisedPublicId=对于web.xml文件,公共ID[{0}]不匹配任何已知的公共ID‘,因此无法识别版本。
webXml.version.unknown=未知版本字符串 [{0}]。将使用默认版本。
+webXml.wrongFragmentName=在web.xml绝对排序标签上使用了错误的片段名[{0}]!
+
+webXmlParser.applicationPosition=出现在第 [{0}] 行 第 [{1}] 列
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/digester/Digester.java tomcat9-9.0.31/java/org/apache/tomcat/util/digester/Digester.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/digester/Digester.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/digester/Digester.java 2020-02-05 19:26:48.000000000 +0000
@@ -41,6 +41,7 @@
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.IntrospectionUtils.PropertySource;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.security.PermissionCheck;
@@ -55,6 +56,7 @@
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.DefaultHandler2;
+import org.xml.sax.ext.EntityResolver2;
import org.xml.sax.ext.Locator2;
import org.xml.sax.helpers.AttributesImpl;
@@ -815,12 +817,20 @@
reader.setDTDHandler(this);
reader.setContentHandler(this);
+ EntityResolver entityResolver = getEntityResolver();
if (entityResolver == null) {
- reader.setEntityResolver(this);
+ entityResolver = this;
+ }
+
+ // Wrap the resolver so we can perform ${...} property replacement
+ if (entityResolver instanceof EntityResolver2) {
+ entityResolver = new EntityResolver2Wrapper((EntityResolver2) entityResolver, source, classLoader);
} else {
- reader.setEntityResolver(entityResolver);
+ entityResolver = new EntityResolverWrapper(entityResolver, source, classLoader);
}
+ reader.setEntityResolver(entityResolver);
+
reader.setProperty("http://xml.org/sax/properties/lexical-handler", this);
reader.setErrorHandler(this);
@@ -1976,4 +1986,64 @@
return new StringBuilder(out);
}
}
+
+
+ private static class EntityResolverWrapper implements EntityResolver {
+
+ private final EntityResolver entityResolver;
+ private final PropertySource[] source;
+ private final ClassLoader classLoader;
+
+ public EntityResolverWrapper(EntityResolver entityResolver, PropertySource[] source, ClassLoader classLoader) {
+ this.entityResolver = entityResolver;
+ this.source = source;
+ this.classLoader = classLoader;
+ }
+
+ @Override
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException, IOException {
+ publicId = replace(publicId);
+ systemId = replace(systemId);
+ return entityResolver.resolveEntity(publicId, systemId);
+ }
+
+ protected String replace(String input) {
+ try {
+ return IntrospectionUtils.replaceProperties(input, null, source, classLoader);
+ } catch (Exception e) {
+ return input;
+ }
+ }
+ }
+
+
+ private static class EntityResolver2Wrapper extends EntityResolverWrapper implements EntityResolver2 {
+
+ private final EntityResolver2 entityResolver2;
+
+ public EntityResolver2Wrapper(EntityResolver2 entityResolver, PropertySource[] source,
+ ClassLoader classLoader) {
+ super(entityResolver, source, classLoader);
+ this.entityResolver2 = entityResolver;
+ }
+
+ @Override
+ public InputSource getExternalSubset(String name, String baseURI)
+ throws SAXException, IOException {
+ name = replace(name);
+ baseURI = replace(baseURI);
+ return entityResolver2.getExternalSubset(name, baseURI);
+ }
+
+ @Override
+ public InputSource resolveEntity(String name, String publicId, String baseURI,
+ String systemId) throws SAXException, IOException {
+ name = replace(name);
+ publicId = replace(publicId);
+ baseURI = replace(baseURI);
+ systemId = replace(systemId);
+ return entityResolver2.resolveEntity(name, publicId, baseURI, systemId);
+ }
+ }
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/digester/LocalStrings_ja.properties tomcat9-9.0.31/java/org/apache/tomcat/util/digester/LocalStrings_ja.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/digester/LocalStrings_ja.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/digester/LocalStrings_ja.properties 2020-02-05 19:26:48.000000000 +0000
@@ -28,9 +28,9 @@
digester.noRulesFound=[{0}]と一致するルールは見つかりませんでした。
digester.parseError=行[{0}]の列[{1}]のエラー解析
digester.parseErrorFatal=行[{0}]の列[{1}]で致命的なエラーを解析します。
-digester.parseWarning=行番号 [{0}] 列番号 [{1]] の解釈中に警告が発生しました。
+digester.parseWarning=行番号 [{0}] 列番号 [{1}] の解釈中に警告が発生しました。
digester.propertySourceLoadError=プロパティソースクラス [{0}] の読み込み中に異常が発生しました。
rule.createError=オブジェクトの作成中にエラーが発生しました: [{0}]
rule.noClassName=[{0}] [{1}]に指定されたクラス名がありません。
-rule.noProperty=マッチしたパターン [{0}] でプロパティ [[1}] に [{2}] を設定できません。
+rule.noProperty=マッチしたパターン [{0}] でプロパティ [{1}] に [{2}] を設定できません。
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/digester/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/tomcat/util/digester/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/digester/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/digester/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,5 +13,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-digester.encodingInvalid=Java运行时环境无法识别 [{0}]编码,将被忽略
+digester.encodingInvalid=JRE无法识别 [{0}]编码,将被忽略
digester.failedToUpdateAttributes=属性[{0}]更新失败,旧数据为[{1}]
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/file/ConfigFileLoader.java tomcat9-9.0.31/java/org/apache/tomcat/util/file/ConfigFileLoader.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/file/ConfigFileLoader.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/file/ConfigFileLoader.java 2020-02-05 19:26:48.000000000 +0000
@@ -21,8 +21,6 @@
import java.io.InputStream;
import java.net.URI;
-import org.apache.tomcat.util.res.StringManager;
-
/**
* This class is used to obtain {@link InputStream}s for configuration files
* from a given location String. This allows greater flexibility than these
@@ -30,14 +28,11 @@
*/
public class ConfigFileLoader {
- private static final StringManager sm = StringManager.getManager(ConfigFileLoader.class
- .getPackage().getName());
-
private static ConfigurationSource source;
public static final ConfigurationSource getSource() {
if (ConfigFileLoader.source == null) {
- throw new IllegalStateException(sm.getString("configFileLoader.noConfigurationSource"));
+ return ConfigurationSource.DEFAULT;
}
return source;
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/file/ConfigurationSource.java tomcat9-9.0.31/java/org/apache/tomcat/util/file/ConfigurationSource.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/file/ConfigurationSource.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/file/ConfigurationSource.java 2020-02-05 19:26:48.000000000 +0000
@@ -16,10 +16,14 @@
*/
package org.apache.tomcat.util.file;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
+import java.net.URL;
/**
* Abstracts configuration file storage. Allows Tomcat embedding using the regular
@@ -30,6 +34,44 @@
*/
public interface ConfigurationSource {
+ public static final ConfigurationSource DEFAULT = new ConfigurationSource() {
+ protected final File userDir = new File(System.getProperty("user.dir"));
+ protected final URI userDirUri = userDir.toURI();
+ @Override
+ public Resource getResource(String name) throws IOException {
+ File f = new File(name);
+ if (!f.isAbsolute()) {
+ f = new File(userDir, name);
+ }
+ if (f.isFile()) {
+ return new Resource(new FileInputStream(f), f.toURI());
+ }
+ URI uri = null;
+ try {
+ uri = getURI(name);
+ } catch (IllegalArgumentException e) {
+ throw new FileNotFoundException(name);
+ }
+ try {
+ URL url = uri.toURL();
+ return new Resource(url.openConnection().getInputStream(), uri);
+ } catch (MalformedURLException e) {
+ throw new FileNotFoundException(name);
+ }
+ }
+ @Override
+ public URI getURI(String name) {
+ File f = new File(name);
+ if (!f.isAbsolute()) {
+ f = new File(userDir, name);
+ }
+ if (f.isFile()) {
+ return f.toURI();
+ }
+ return userDirUri.resolve(name);
+ }
+ };
+
/**
* Represents a resource: a stream to the resource associated with
* its URI.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_de.properties tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_de.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_de.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_de.properties 1970-01-01 00:00:00.000000000 +0000
@@ -1,16 +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.
-
-configFileLoader.noConfigurationSource=Es wurde keine Konfigurationsquelle per ConfigFileLoader.setSource gesetzt.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_fr.properties tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_fr.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_fr.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_fr.properties 1970-01-01 00:00:00.000000000 +0000
@@ -1,16 +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.
-
-configFileLoader.noConfigurationSource=Aucune source de configuration n'a été définie par ConfigFileLoader.setSource.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_ja.properties tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_ja.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_ja.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_ja.properties 1970-01-01 00:00:00.000000000 +0000
@@ -1,16 +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.
-
-configFileLoader.noConfigurationSource=ConfigFileLoader.setSourceを使用して設定ソースが設定されていません。
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_ko.properties tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_ko.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_ko.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_ko.properties 1970-01-01 00:00:00.000000000 +0000
@@ -1,16 +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.
-
-configFileLoader.noConfigurationSource=ConfigFileLoader.setSource를 사용하여, 설정 원본이 설정되지 않았습니다.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings.properties tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings.properties 1970-01-01 00:00:00.000000000 +0000
@@ -1,16 +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.
-
-configFileLoader.noConfigurationSource=No configuration source has been set using ConfigFileLoader.setSource.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/file/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/file/LocalStrings_zh_CN.properties 1970-01-01 00:00:00.000000000 +0000
@@ -1,16 +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.
-
-configFileLoader.noConfigurationSource=没有配置源时,采用ConfigFileLoader.setSource 配置
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/CookieProcessorBase.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/CookieProcessorBase.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/CookieProcessorBase.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/CookieProcessorBase.java 2020-02-05 19:26:48.000000000 +0000
@@ -43,7 +43,7 @@
ANCIENT_DATE = COOKIE_DATE_FORMAT.get().format(new Date(10000));
}
- private SameSiteCookies sameSiteCookies = SameSiteCookies.NONE;
+ private SameSiteCookies sameSiteCookies = SameSiteCookies.UNSET;
public SameSiteCookies getSameSiteCookies() {
return sameSiteCookies;
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java 2020-02-05 19:26:48.000000000 +0000
@@ -546,7 +546,7 @@
* Removes the file contents from the temporary storage.
*/
@Override
- protected void finalize() {
+ protected void finalize() throws Throwable {
if (dfos == null || dfos.isInMemory()) {
return;
}
@@ -555,6 +555,7 @@
if (outputFile != null && outputFile.exists()) {
outputFile.delete();
}
+ super.finalize();
}
/**
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/FileItemIterator.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/FileItemIterator.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/FileItemIterator.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/FileItemIterator.java 2020-02-05 19:26:48.000000000 +0000
@@ -17,12 +17,56 @@
package org.apache.tomcat.util.http.fileupload;
import java.io.IOException;
+import java.util.List;
+
+import org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException;
+import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException;
/**
* An iterator, as returned by
* {@link FileUploadBase#getItemIterator(RequestContext)}.
*/
public interface FileItemIterator {
+ /** Returns the maximum size of a single file. An {@link FileSizeLimitExceededException}
+ * will be thrown, if there is an uploaded file, which is exceeding this value.
+ * By default, this value will be copied from the {@link FileUploadBase#getFileSizeMax()
+ * FileUploadBase} object, however, the user may replace the default value with a
+ * request specific value by invoking {@link #setFileSizeMax(long)} on this object.
+ * @return The maximum size of a single, uploaded file. The value -1 indicates "unlimited".
+ */
+ public long getFileSizeMax();
+
+ /** Sets the maximum size of a single file. An {@link FileSizeLimitExceededException}
+ * will be thrown, if there is an uploaded file, which is exceeding this value.
+ * By default, this value will be copied from the {@link FileUploadBase#getFileSizeMax()
+ * FileUploadBase} object, however, the user may replace the default value with a
+ * request specific value by invoking {@link #setFileSizeMax(long)} on this object, so
+ * there is no need to configure it here.
+ * Note:Changing this value doesn't affect files, that have already been uploaded.
+ * @param pFileSizeMax The maximum size of a single, uploaded file. The value -1 indicates "unlimited".
+ */
+ public void setFileSizeMax(long pFileSizeMax);
+
+ /** Returns the maximum size of the complete HTTP request. A {@link SizeLimitExceededException}
+ * will be thrown, if the HTTP request will exceed this value.
+ * By default, this value will be copied from the {@link FileUploadBase#getSizeMax()
+ * FileUploadBase} object, however, the user may replace the default value with a
+ * request specific value by invoking {@link #setSizeMax(long)} on this object.
+ * @return The maximum size of the complete HTTP requqest. The value -1 indicates "unlimited".
+ */
+ public long getSizeMax();
+
+ /** Returns the maximum size of the complete HTTP request. A {@link SizeLimitExceededException}
+ * will be thrown, if the HTTP request will exceed this value.
+ * By default, this value will be copied from the {@link FileUploadBase#getSizeMax()
+ * FileUploadBase} object, however, the user may replace the default value with a
+ * request specific value by invoking {@link #setSizeMax(long)} on this object.
+ * Note: Setting the maximum size on this object will work only, if the iterator is not
+ * yet initialized. In other words: If the methods {@link #hasNext()}, {@link #next()} have not
+ * yet been invoked.
+ * @param pSizeMax The maximum size of the complete HTTP request. The value -1 indicates "unlimited".
+ */
+ public void setSizeMax(long pSizeMax);
/**
* Returns, whether another instance of {@link FileItemStream}
@@ -34,7 +78,7 @@
* @return True, if one or more additional file items
* are available, otherwise false.
*/
- boolean hasNext() throws FileUploadException, IOException;
+ public boolean hasNext() throws FileUploadException, IOException;
/**
* Returns the next available {@link FileItemStream}.
@@ -47,6 +91,7 @@
* @return FileItemStream instance, which provides
* access to the next file item.
*/
- FileItemStream next() throws FileUploadException, IOException;
+ public FileItemStream next() throws FileUploadException, IOException;
+ public List getFileItems() throws FileUploadException, IOException;
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java 2020-02-05 19:26:48.000000000 +0000
@@ -17,19 +17,18 @@
package org.apache.tomcat.util.http.fileupload;
import java.io.IOException;
-import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.NoSuchElementException;
-import org.apache.tomcat.util.http.fileupload.MultipartStream.ItemInputStream;
-import org.apache.tomcat.util.http.fileupload.util.Closeable;
+import org.apache.tomcat.util.http.fileupload.impl.FileItemIteratorImpl;
+import org.apache.tomcat.util.http.fileupload.impl.FileItemStreamImpl;
+import org.apache.tomcat.util.http.fileupload.impl.FileUploadIOException;
+import org.apache.tomcat.util.http.fileupload.impl.IOFileUploadException;
import org.apache.tomcat.util.http.fileupload.util.FileItemHeadersImpl;
-import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
import org.apache.tomcat.util.http.fileupload.util.Streams;
@@ -253,7 +252,7 @@
public FileItemIterator getItemIterator(RequestContext ctx)
throws FileUploadException, IOException {
try {
- return new FileItemIteratorImpl(ctx);
+ return new FileItemIteratorImpl(this, ctx);
} catch (FileUploadIOException e) {
// unwrap encapsulated SizeException
throw (FileUploadException) e.getCause();
@@ -286,7 +285,7 @@
while (iter.hasNext()) {
final FileItemStream item = iter.next();
// Don't use getName() here to prevent an InvalidFileNameException.
- final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
+ final String fileName = ((FileItemStreamImpl) item).getName();
FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
item.isFormField(), fileName);
items.add(fileItem);
@@ -363,7 +362,7 @@
*
* @return The boundary, as a byte array.
*/
- protected byte[] getBoundary(String contentType) {
+ public byte[] getBoundary(String contentType) {
ParameterParser parser = new ParameterParser();
parser.setLowerCaseNames(true);
// Parameter parser can handle null input
@@ -387,7 +386,7 @@
*
* @return The file name for the current encapsulation.
*/
- protected String getFileName(FileItemHeaders headers) {
+ public String getFileName(FileItemHeaders headers) {
return getFileName(headers.getHeader(CONTENT_DISPOSITION));
}
@@ -429,7 +428,7 @@
*
* @return The field name for the current encapsulation.
*/
- protected String getFieldName(FileItemHeaders headers) {
+ public String getFieldName(FileItemHeaders headers) {
return getFieldName(headers.getHeader(CONTENT_DISPOSITION));
}
@@ -467,7 +466,7 @@
*
* @return A Map containing the parsed HTTP request headers.
*/
- protected FileItemHeaders getParsedHeaders(String headerPart) {
+ public FileItemHeaders getParsedHeaders(String headerPart) {
final int len = headerPart.length();
FileItemHeadersImpl headers = newFileItemHeaders();
int start = 0;
@@ -549,687 +548,6 @@
}
/**
- * The iterator, which is returned by
- * {@link FileUploadBase#getItemIterator(RequestContext)}.
- */
- private class FileItemIteratorImpl implements FileItemIterator {
-
- /**
- * Default implementation of {@link FileItemStream}.
- */
- class FileItemStreamImpl implements FileItemStream {
-
- /**
- * The file items content type.
- */
- private final String contentType;
-
- /**
- * The file items field name.
- */
- private final String fieldName;
-
- /**
- * The file items file name.
- */
- private final String name;
-
- /**
- * Whether the file item is a form field.
- */
- private final boolean formField;
-
- /**
- * The file items input stream.
- */
- private final InputStream stream;
-
- /**
- * The headers, if any.
- */
- private FileItemHeaders headers;
-
- /**
- * Creates a new instance.
- *
- * @param pName The items file name, or null.
- * @param pFieldName The items field name.
- * @param pContentType The items content type, or null.
- * @param pFormField Whether the item is a form field.
- * @param pContentLength The items content length, if known, or -1
- * @throws IOException Creating the file item failed.
- */
- FileItemStreamImpl(String pName, String pFieldName,
- String pContentType, boolean pFormField,
- long pContentLength) throws IOException {
- name = pName;
- fieldName = pFieldName;
- contentType = pContentType;
- formField = pFormField;
- if (fileSizeMax != -1) { // Check if limit is already exceeded
- if (pContentLength != -1
- && pContentLength > fileSizeMax) {
- FileSizeLimitExceededException e =
- new FileSizeLimitExceededException(
- String.format("The field %s exceeds its maximum permitted size of %s bytes.",
- fieldName, Long.valueOf(fileSizeMax)),
- pContentLength, fileSizeMax);
- e.setFileName(pName);
- e.setFieldName(pFieldName);
- throw new FileUploadIOException(e);
- }
- }
- // OK to construct stream now
- final ItemInputStream itemStream = multi.newInputStream();
- InputStream istream = itemStream;
- if (fileSizeMax != -1) {
- istream = new LimitedInputStream(istream, fileSizeMax) {
- @Override
- protected void raiseError(long pSizeMax, long pCount)
- throws IOException {
- itemStream.close(true);
- FileSizeLimitExceededException e =
- new FileSizeLimitExceededException(
- String.format("The field %s exceeds its maximum permitted size of %s bytes.",
- fieldName, Long.valueOf(pSizeMax)),
- pCount, pSizeMax);
- e.setFieldName(fieldName);
- e.setFileName(name);
- throw new FileUploadIOException(e);
- }
- };
- }
- stream = istream;
- }
-
- /**
- * Returns the items content type, or null.
- *
- * @return Content type, if known, or null.
- */
- @Override
- public String getContentType() {
- return contentType;
- }
-
- /**
- * Returns the items field name.
- *
- * @return Field name.
- */
- @Override
- public String getFieldName() {
- return fieldName;
- }
-
- /**
- * Returns the items file name.
- *
- * @return File name, if known, or null.
- * @throws InvalidFileNameException The file name contains a NUL character,
- * which might be an indicator of a security attack. If you intend to
- * use the file name anyways, catch the exception and use
- * InvalidFileNameException#getName().
- */
- @Override
- public String getName() {
- return Streams.checkFileName(name);
- }
-
- /**
- * Returns, whether this is a form field.
- *
- * @return True, if the item is a form field,
- * otherwise false.
- */
- @Override
- public boolean isFormField() {
- return formField;
- }
-
- /**
- * Returns an input stream, which may be used to
- * read the items contents.
- *
- * @return Opened input stream.
- * @throws IOException An I/O error occurred.
- */
- @Override
- public InputStream openStream() throws IOException {
- if (((Closeable) stream).isClosed()) {
- throw new FileItemStream.ItemSkippedException();
- }
- return stream;
- }
-
- /**
- * Closes the file item.
- *
- * @throws IOException An I/O error occurred.
- */
- void close() throws IOException {
- stream.close();
- }
-
- /**
- * Returns the file item headers.
- *
- * @return The items header object
- */
- @Override
- public FileItemHeaders getHeaders() {
- return headers;
- }
-
- /**
- * Sets the file item headers.
- *
- * @param pHeaders The items header object
- */
- @Override
- public void setHeaders(FileItemHeaders pHeaders) {
- headers = pHeaders;
- }
-
- }
-
- /**
- * The multi part stream to process.
- */
- private final MultipartStream multi;
-
- /**
- * The notifier, which used for triggering the
- * {@link ProgressListener}.
- */
- private final MultipartStream.ProgressNotifier notifier;
-
- /**
- * The boundary, which separates the various parts.
- */
- private final byte[] boundary;
-
- /**
- * The item, which we currently process.
- */
- private FileItemStreamImpl currentItem;
-
- /**
- * The current items field name.
- */
- private String currentFieldName;
-
- /**
- * Whether we are currently skipping the preamble.
- */
- private boolean skipPreamble;
-
- /**
- * Whether the current item may still be read.
- */
- private boolean itemValid;
-
- /**
- * Whether we have seen the end of the file.
- */
- private boolean eof;
-
- /**
- * Creates a new instance.
- *
- * @param ctx The request context.
- * @throws FileUploadException An error occurred while
- * parsing the request.
- * @throws IOException An I/O error occurred.
- */
- FileItemIteratorImpl(RequestContext ctx)
- throws FileUploadException, IOException {
- if (ctx == null) {
- throw new NullPointerException("ctx parameter");
- }
-
- String contentType = ctx.getContentType();
- if ((null == contentType)
- || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(MULTIPART))) {
- throw new InvalidContentTypeException(String.format(
- "the request doesn't contain a %s or %s stream, content type header is %s",
- MULTIPART_FORM_DATA, MULTIPART_MIXED, contentType));
- }
-
-
- final long requestSize = ((UploadContext) ctx).contentLength();
-
- InputStream input; // N.B. this is eventually closed in MultipartStream processing
- if (sizeMax >= 0) {
- if (requestSize != -1 && requestSize > sizeMax) {
- throw new SizeLimitExceededException(String.format(
- "the request was rejected because its size (%s) exceeds the configured maximum (%s)",
- Long.valueOf(requestSize), Long.valueOf(sizeMax)),
- requestSize, sizeMax);
- }
- // N.B. this is eventually closed in MultipartStream processing
- input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
- @Override
- protected void raiseError(long pSizeMax, long pCount)
- throws IOException {
- FileUploadException ex = new SizeLimitExceededException(
- String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
- Long.valueOf(pCount), Long.valueOf(pSizeMax)),
- pCount, pSizeMax);
- throw new FileUploadIOException(ex);
- }
- };
- } else {
- input = ctx.getInputStream();
- }
-
- String charEncoding = headerEncoding;
- if (charEncoding == null) {
- charEncoding = ctx.getCharacterEncoding();
- }
-
- boundary = getBoundary(contentType);
- if (boundary == null) {
- IOUtils.closeQuietly(input); // avoid possible resource leak
- throw new FileUploadException("the request was rejected because no multipart boundary was found");
- }
-
- notifier = new MultipartStream.ProgressNotifier(listener, requestSize);
- try {
- multi = new MultipartStream(input, boundary, notifier);
- } catch (IllegalArgumentException iae) {
- IOUtils.closeQuietly(input); // avoid possible resource leak
- throw new InvalidContentTypeException(
- String.format("The boundary specified in the %s header is too long", CONTENT_TYPE), iae);
- }
- multi.setHeaderEncoding(charEncoding);
-
- skipPreamble = true;
- findNextItem();
- }
-
- /**
- * Called for finding the next item, if any.
- *
- * @return True, if an next item was found, otherwise false.
- * @throws IOException An I/O error occurred.
- */
- private boolean findNextItem() throws IOException {
- if (eof) {
- return false;
- }
- if (currentItem != null) {
- currentItem.close();
- currentItem = null;
- }
- for (;;) {
- boolean nextPart;
- if (skipPreamble) {
- nextPart = multi.skipPreamble();
- } else {
- nextPart = multi.readBoundary();
- }
- if (!nextPart) {
- if (currentFieldName == null) {
- // Outer multipart terminated -> No more data
- eof = true;
- return false;
- }
- // Inner multipart terminated -> Return to parsing the outer
- multi.setBoundary(boundary);
- currentFieldName = null;
- continue;
- }
- FileItemHeaders headers = getParsedHeaders(multi.readHeaders());
- if (currentFieldName == null) {
- // We're parsing the outer multipart
- String fieldName = getFieldName(headers);
- if (fieldName != null) {
- String subContentType = headers.getHeader(CONTENT_TYPE);
- if (subContentType != null
- && subContentType.toLowerCase(Locale.ENGLISH)
- .startsWith(MULTIPART_MIXED)) {
- currentFieldName = fieldName;
- // Multiple files associated with this field name
- byte[] subBoundary = getBoundary(subContentType);
- multi.setBoundary(subBoundary);
- skipPreamble = true;
- continue;
- }
- String fileName = getFileName(headers);
- currentItem = new FileItemStreamImpl(fileName,
- fieldName, headers.getHeader(CONTENT_TYPE),
- fileName == null, getContentLength(headers));
- currentItem.setHeaders(headers);
- notifier.noteItem();
- itemValid = true;
- return true;
- }
- } else {
- String fileName = getFileName(headers);
- if (fileName != null) {
- currentItem = new FileItemStreamImpl(fileName,
- currentFieldName,
- headers.getHeader(CONTENT_TYPE),
- false, getContentLength(headers));
- currentItem.setHeaders(headers);
- notifier.noteItem();
- itemValid = true;
- return true;
- }
- }
- multi.discardBodyData();
- }
- }
-
- private long getContentLength(FileItemHeaders pHeaders) {
- try {
- return Long.parseLong(pHeaders.getHeader(CONTENT_LENGTH));
- } catch (Exception e) {
- return -1;
- }
- }
-
- /**
- * Returns, whether another instance of {@link FileItemStream}
- * is available.
- *
- * @throws FileUploadException Parsing or processing the
- * file item failed.
- * @throws IOException Reading the file item failed.
- * @return True, if one or more additional file items
- * are available, otherwise false.
- */
- @Override
- public boolean hasNext() throws FileUploadException, IOException {
- if (eof) {
- return false;
- }
- if (itemValid) {
- return true;
- }
- try {
- return findNextItem();
- } catch (FileUploadIOException e) {
- // unwrap encapsulated SizeException
- throw (FileUploadException) e.getCause();
- }
- }
-
- /**
- * Returns the next available {@link FileItemStream}.
- *
- * @throws java.util.NoSuchElementException No more items are
- * available. Use {@link #hasNext()} to prevent this exception.
- * @throws FileUploadException Parsing or processing the
- * file item failed.
- * @throws IOException Reading the file item failed.
- * @return FileItemStream instance, which provides
- * access to the next file item.
- */
- @Override
- public FileItemStream next() throws FileUploadException, IOException {
- if (eof || (!itemValid && !hasNext())) {
- throw new NoSuchElementException();
- }
- itemValid = false;
- return currentItem;
- }
-
- }
-
- /**
- * This exception is thrown for hiding an inner
- * {@link FileUploadException} in an {@link IOException}.
- */
- public static class FileUploadIOException extends IOException {
-
- private static final long serialVersionUID = -3082868232248803474L;
-
- public FileUploadIOException() {
- super();
- }
-
- public FileUploadIOException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public FileUploadIOException(String message) {
- super(message);
- }
-
- public FileUploadIOException(Throwable cause) {
- super(cause);
- }
- }
-
- /**
- * Thrown to indicate that the request is not a multipart request.
- */
- public static class InvalidContentTypeException
- extends FileUploadException {
-
- /**
- * The exceptions UID, for serializing an instance.
- */
- private static final long serialVersionUID = -9073026332015646668L;
-
- /**
- * Constructs a InvalidContentTypeException with no
- * detail message.
- */
- public InvalidContentTypeException() {
- super();
- }
-
- /**
- * Constructs an InvalidContentTypeException with
- * the specified detail message.
- *
- * @param message The detail message.
- */
- public InvalidContentTypeException(String message) {
- super(message);
- }
-
- /**
- * Constructs an InvalidContentTypeException with
- * the specified detail message and cause.
- *
- * @param msg The detail message.
- * @param cause the original cause
- *
- * @since 1.3.1
- */
- public InvalidContentTypeException(String msg, Throwable cause) {
- super(msg, cause);
- }
- }
-
- /**
- * Thrown to indicate an IOException.
- */
- public static class IOFileUploadException extends FileUploadException {
-
- private static final long serialVersionUID = -5858565745868986701L;
-
- public IOFileUploadException() {
- super();
- }
-
- public IOFileUploadException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public IOFileUploadException(String message) {
- super(message);
- }
-
- public IOFileUploadException(Throwable cause) {
- super(cause);
- }
- }
-
- /**
- * This exception is thrown, if a requests permitted size
- * is exceeded.
- */
- public abstract static class SizeException extends FileUploadException {
-
- /**
- * Serial version UID, being used, if serialized.
- */
- private static final long serialVersionUID = -8776225574705254126L;
-
- /**
- * The actual size of the request.
- */
- private final long actual;
-
- /**
- * The maximum permitted size of the request.
- */
- private final long permitted;
-
- /**
- * Creates a new instance.
- *
- * @param message The detail message.
- * @param actual The actual number of bytes in the request.
- * @param permitted The requests size limit, in bytes.
- */
- protected SizeException(String message, long actual, long permitted) {
- super(message);
- this.actual = actual;
- this.permitted = permitted;
- }
-
- /**
- * Retrieves the actual size of the request.
- *
- * @return The actual size of the request.
- * @since 1.3
- */
- public long getActualSize() {
- return actual;
- }
-
- /**
- * Retrieves the permitted size of the request.
- *
- * @return The permitted size of the request.
- * @since 1.3
- */
- public long getPermittedSize() {
- return permitted;
- }
-
- }
-
- /**
- * Thrown to indicate that the request size exceeds the configured maximum.
- */
- public static class SizeLimitExceededException
- extends SizeException {
-
- /**
- * The exceptions UID, for serializing an instance.
- */
- private static final long serialVersionUID = -2474893167098052828L;
-
- /**
- * Constructs a SizeExceededException with
- * the specified detail message, and actual and permitted sizes.
- *
- * @param message The detail message.
- * @param actual The actual request size.
- * @param permitted The maximum permitted request size.
- */
- public SizeLimitExceededException(String message, long actual,
- long permitted) {
- super(message, actual, permitted);
- }
-
- }
-
- /**
- * Thrown to indicate that A files size exceeds the configured maximum.
- */
- public static class FileSizeLimitExceededException
- extends SizeException {
-
- /**
- * The exceptions UID, for serializing an instance.
- */
- private static final long serialVersionUID = 8150776562029630058L;
-
- /**
- * File name of the item, which caused the exception.
- */
- private String fileName;
-
- /**
- * Field name of the item, which caused the exception.
- */
- private String fieldName;
-
- /**
- * Constructs a SizeExceededException with
- * the specified detail message, and actual and permitted sizes.
- *
- * @param message The detail message.
- * @param actual The actual request size.
- * @param permitted The maximum permitted request size.
- */
- public FileSizeLimitExceededException(String message, long actual,
- long permitted) {
- super(message, actual, permitted);
- }
-
- /**
- * Returns the file name of the item, which caused the
- * exception.
- *
- * @return File name, if known, or null.
- */
- public String getFileName() {
- return fileName;
- }
-
- /**
- * Sets the file name of the item, which caused the
- * exception.
- *
- * @param pFileName the file name of the item, which caused the exception.
- */
- public void setFileName(String pFileName) {
- fileName = pFileName;
- }
-
- /**
- * Returns the field name of the item, which caused the
- * exception.
- *
- * @return Field name, if known, or null.
- */
- public String getFieldName() {
- return fieldName;
- }
-
- /**
- * Sets the field name of the item, which caused the
- * exception.
- *
- * @param pFieldName the field name of the item,
- * which caused the exception.
- */
- public void setFieldName(String pFieldName) {
- fieldName = pFieldName;
- }
-
- }
-
- /**
* Returns the progress listener.
*
* @return The progress listener, if any, or null.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,339 @@
+/*
+ * 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.fileupload.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+
+import org.apache.tomcat.util.http.fileupload.FileItem;
+import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
+import org.apache.tomcat.util.http.fileupload.FileItemIterator;
+import org.apache.tomcat.util.http.fileupload.FileItemStream;
+import org.apache.tomcat.util.http.fileupload.FileUploadBase;
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+import org.apache.tomcat.util.http.fileupload.IOUtils;
+import org.apache.tomcat.util.http.fileupload.MultipartStream;
+import org.apache.tomcat.util.http.fileupload.ProgressListener;
+import org.apache.tomcat.util.http.fileupload.RequestContext;
+import org.apache.tomcat.util.http.fileupload.UploadContext;
+import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
+
+/**
+ * The iterator, which is returned by
+ * {@link FileUploadBase#getItemIterator(RequestContext)}.
+ */
+public class FileItemIteratorImpl implements FileItemIterator {
+ private final FileUploadBase fileUploadBase;
+ private final RequestContext ctx;
+ private long sizeMax, fileSizeMax;
+
+
+ @Override
+ public long getSizeMax() {
+ return sizeMax;
+ }
+
+ @Override
+ public void setSizeMax(long sizeMax) {
+ this.sizeMax = sizeMax;
+ }
+
+ @Override
+ public long getFileSizeMax() {
+ return fileSizeMax;
+ }
+
+ @Override
+ public void setFileSizeMax(long fileSizeMax) {
+ this.fileSizeMax = fileSizeMax;
+ }
+
+ /**
+ * The multi part stream to process.
+ */
+ private MultipartStream multiPartStream;
+
+ /**
+ * The notifier, which used for triggering the
+ * {@link ProgressListener}.
+ */
+ private MultipartStream.ProgressNotifier progressNotifier;
+
+ /**
+ * The boundary, which separates the various parts.
+ */
+ private byte[] multiPartBoundary;
+
+ /**
+ * The item, which we currently process.
+ */
+ private FileItemStreamImpl currentItem;
+
+ /**
+ * The current items field name.
+ */
+ private String currentFieldName;
+
+ /**
+ * Whether we are currently skipping the preamble.
+ */
+ private boolean skipPreamble;
+
+ /**
+ * Whether the current item may still be read.
+ */
+ private boolean itemValid;
+
+ /**
+ * Whether we have seen the end of the file.
+ */
+ private boolean eof;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param pFileUploadBase Upload instance
+ * @param pRequestContext The request context.
+ * @throws FileUploadException An error occurred while
+ * parsing the request.
+ * @throws IOException An I/O error occurred.
+ */
+ public FileItemIteratorImpl(FileUploadBase pFileUploadBase, RequestContext pRequestContext)
+ throws FileUploadException, IOException {
+ fileUploadBase = pFileUploadBase;
+ sizeMax = fileUploadBase.getSizeMax();
+ fileSizeMax = fileUploadBase.getFileSizeMax();
+ ctx = pRequestContext;
+ if (ctx == null) {
+ throw new NullPointerException("ctx parameter");
+ }
+
+
+ skipPreamble = true;
+ findNextItem();
+ }
+
+ protected void init(FileUploadBase fileUploadBase, @SuppressWarnings("unused") RequestContext pRequestContext)
+ throws FileUploadException, IOException {
+ String contentType = ctx.getContentType();
+ if ((null == contentType)
+ || (!contentType.toLowerCase(Locale.ENGLISH).startsWith(FileUploadBase.MULTIPART))) {
+ throw new InvalidContentTypeException(
+ String.format("the request doesn't contain a %s or %s stream, content type header is %s",
+ FileUploadBase.MULTIPART_FORM_DATA, FileUploadBase.MULTIPART_MIXED, contentType));
+ }
+
+ final long requestSize = ((UploadContext) ctx).contentLength();
+
+ InputStream input; // N.B. this is eventually closed in MultipartStream processing
+ if (sizeMax >= 0) {
+ if (requestSize != -1 && requestSize > sizeMax) {
+ throw new SizeLimitExceededException(
+ String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
+ Long.valueOf(requestSize), Long.valueOf(sizeMax)),
+ requestSize, sizeMax);
+ }
+ // N.B. this is eventually closed in MultipartStream processing
+ input = new LimitedInputStream(ctx.getInputStream(), sizeMax) {
+ @Override
+ protected void raiseError(long pSizeMax, long pCount)
+ throws IOException {
+ FileUploadException ex = new SizeLimitExceededException(
+ String.format("the request was rejected because its size (%s) exceeds the configured maximum (%s)",
+ Long.valueOf(pCount), Long.valueOf(pSizeMax)),
+ pCount, pSizeMax);
+ throw new FileUploadIOException(ex);
+ }
+ };
+ } else {
+ input = ctx.getInputStream();
+ }
+
+ String charEncoding = fileUploadBase.getHeaderEncoding();
+ if (charEncoding == null) {
+ charEncoding = ctx.getCharacterEncoding();
+ }
+
+ multiPartBoundary = fileUploadBase.getBoundary(contentType);
+ if (multiPartBoundary == null) {
+ IOUtils.closeQuietly(input); // avoid possible resource leak
+ throw new FileUploadException("the request was rejected because no multipart boundary was found");
+ }
+
+ progressNotifier = new MultipartStream.ProgressNotifier(fileUploadBase.getProgressListener(), requestSize);
+ try {
+ multiPartStream = new MultipartStream(input, multiPartBoundary, progressNotifier);
+ } catch (IllegalArgumentException iae) {
+ IOUtils.closeQuietly(input); // avoid possible resource leak
+ throw new InvalidContentTypeException(
+ String.format("The boundary specified in the %s header is too long", FileUploadBase.CONTENT_TYPE), iae);
+ }
+ multiPartStream.setHeaderEncoding(charEncoding);
+ }
+
+ public MultipartStream getMultiPartStream() throws FileUploadException, IOException {
+ if (multiPartStream == null) {
+ init(fileUploadBase, ctx);
+ }
+ return multiPartStream;
+ }
+
+ /**
+ * Called for finding the next item, if any.
+ *
+ * @return True, if an next item was found, otherwise false.
+ * @throws IOException An I/O error occurred.
+ */
+ private boolean findNextItem() throws FileUploadException, IOException {
+ if (eof) {
+ return false;
+ }
+ if (currentItem != null) {
+ currentItem.close();
+ currentItem = null;
+ }
+ final MultipartStream multi = getMultiPartStream();
+ for (;;) {
+ boolean nextPart;
+ if (skipPreamble) {
+ nextPart = multi.skipPreamble();
+ } else {
+ nextPart = multi.readBoundary();
+ }
+ if (!nextPart) {
+ if (currentFieldName == null) {
+ // Outer multipart terminated -> No more data
+ eof = true;
+ return false;
+ }
+ // Inner multipart terminated -> Return to parsing the outer
+ multi.setBoundary(multiPartBoundary);
+ currentFieldName = null;
+ continue;
+ }
+ FileItemHeaders headers = fileUploadBase.getParsedHeaders(multi.readHeaders());
+ if (currentFieldName == null) {
+ // We're parsing the outer multipart
+ String fieldName = fileUploadBase.getFieldName(headers);
+ if (fieldName != null) {
+ String subContentType = headers.getHeader(FileUploadBase.CONTENT_TYPE);
+ if (subContentType != null
+ && subContentType.toLowerCase(Locale.ENGLISH)
+ .startsWith(FileUploadBase.MULTIPART_MIXED)) {
+ currentFieldName = fieldName;
+ // Multiple files associated with this field name
+ byte[] subBoundary = fileUploadBase.getBoundary(subContentType);
+ multi.setBoundary(subBoundary);
+ skipPreamble = true;
+ continue;
+ }
+ String fileName = fileUploadBase.getFileName(headers);
+ currentItem = new FileItemStreamImpl(this, fileName,
+ fieldName, headers.getHeader(FileUploadBase.CONTENT_TYPE),
+ fileName == null, getContentLength(headers));
+ currentItem.setHeaders(headers);
+ progressNotifier.noteItem();
+ itemValid = true;
+ return true;
+ }
+ } else {
+ String fileName = fileUploadBase.getFileName(headers);
+ if (fileName != null) {
+ currentItem = new FileItemStreamImpl(this, fileName,
+ currentFieldName,
+ headers.getHeader(FileUploadBase.CONTENT_TYPE),
+ false, getContentLength(headers));
+ currentItem.setHeaders(headers);
+ progressNotifier.noteItem();
+ itemValid = true;
+ return true;
+ }
+ }
+ multi.discardBodyData();
+ }
+ }
+
+ private long getContentLength(FileItemHeaders pHeaders) {
+ try {
+ return Long.parseLong(pHeaders.getHeader(FileUploadBase.CONTENT_LENGTH));
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Returns, whether another instance of {@link FileItemStream}
+ * is available.
+ *
+ * @throws FileUploadException Parsing or processing the
+ * file item failed.
+ * @throws IOException Reading the file item failed.
+ * @return True, if one or more additional file items
+ * are available, otherwise false.
+ */
+ @Override
+ public boolean hasNext() throws FileUploadException, IOException {
+ if (eof) {
+ return false;
+ }
+ if (itemValid) {
+ return true;
+ }
+ try {
+ return findNextItem();
+ } catch (FileUploadIOException e) {
+ // unwrap encapsulated SizeException
+ throw (FileUploadException) e.getCause();
+ }
+ }
+
+ /**
+ * Returns the next available {@link FileItemStream}.
+ *
+ * @throws java.util.NoSuchElementException No more items are
+ * available. Use {@link #hasNext()} to prevent this exception.
+ * @throws FileUploadException Parsing or processing the
+ * file item failed.
+ * @throws IOException Reading the file item failed.
+ * @return FileItemStream instance, which provides
+ * access to the next file item.
+ */
+ @Override
+ public FileItemStream next() throws FileUploadException, IOException {
+ if (eof || (!itemValid && !hasNext())) {
+ throw new NoSuchElementException();
+ }
+ itemValid = false;
+ return currentItem;
+ }
+
+ @Override
+ public List getFileItems() throws FileUploadException, IOException {
+ final List items = new ArrayList<>();
+ while (hasNext()) {
+ final FileItemStream fis = next();
+ final FileItem fi = fileUploadBase.getFileItemFactory().createItem(fis.getFieldName(), fis.getContentType(), fis.isFormField(), fis.getName());
+ items.add(fi);
+ }
+ return items;
+ }
+
+}
\ No newline at end of file
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/FileItemStreamImpl.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/FileItemStreamImpl.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/FileItemStreamImpl.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/FileItemStreamImpl.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,213 @@
+/*
+ * 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.fileupload.impl;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.tomcat.util.http.fileupload.FileItemHeaders;
+import org.apache.tomcat.util.http.fileupload.FileItemStream;
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+import org.apache.tomcat.util.http.fileupload.InvalidFileNameException;
+import org.apache.tomcat.util.http.fileupload.MultipartStream.ItemInputStream;
+import org.apache.tomcat.util.http.fileupload.util.Closeable;
+import org.apache.tomcat.util.http.fileupload.util.LimitedInputStream;
+import org.apache.tomcat.util.http.fileupload.util.Streams;
+
+
+/**
+ * Default implementation of {@link FileItemStream}.
+ */
+public class FileItemStreamImpl implements FileItemStream {
+ private final FileItemIteratorImpl fileItemIteratorImpl;
+
+ /**
+ * The file items content type.
+ */
+ private final String contentType;
+
+ /**
+ * The file items field name.
+ */
+ private final String fieldName;
+
+ /**
+ * The file items file name.
+ */
+ final String name;
+
+ /**
+ * Whether the file item is a form field.
+ */
+ private final boolean formField;
+
+ /**
+ * The file items input stream.
+ */
+ private final InputStream stream;
+
+ /**
+ * The headers, if any.
+ */
+ private FileItemHeaders headers;
+
+ /**
+ * Creates a new instance.
+ * @param pFileItemIterator Iterator for all files in this upload
+ * @param pName The items file name, or null.
+ * @param pFieldName The items field name.
+ * @param pContentType The items content type, or null.
+ * @param pFormField Whether the item is a form field.
+ * @param pContentLength The items content length, if known, or -1
+ * @throws FileUploadException If an error is encountered processing the request
+ * @throws IOException Creating the file item failed.
+ */
+ public FileItemStreamImpl(FileItemIteratorImpl pFileItemIterator, String pName, String pFieldName,
+ String pContentType, boolean pFormField,
+ long pContentLength) throws FileUploadException, IOException {
+ fileItemIteratorImpl = pFileItemIterator;
+ name = pName;
+ fieldName = pFieldName;
+ contentType = pContentType;
+ formField = pFormField;
+ final long fileSizeMax = fileItemIteratorImpl.getFileSizeMax();
+ if (fileSizeMax != -1) { // Check if limit is already exceeded
+ if (pContentLength != -1
+ && pContentLength > fileSizeMax) {
+ FileSizeLimitExceededException e =
+ new FileSizeLimitExceededException(
+ String.format("The field %s exceeds its maximum permitted size of %s bytes.",
+ fieldName, Long.valueOf(fileSizeMax)),
+ pContentLength, fileSizeMax);
+ e.setFileName(pName);
+ e.setFieldName(pFieldName);
+ throw new FileUploadIOException(e);
+ }
+ }
+ // OK to construct stream now
+ final ItemInputStream itemStream = fileItemIteratorImpl.getMultiPartStream().newInputStream();
+ InputStream istream = itemStream;
+ if (fileSizeMax != -1) {
+ istream = new LimitedInputStream(istream, fileSizeMax) {
+ @Override
+ protected void raiseError(long pSizeMax, long pCount)
+ throws IOException {
+ itemStream.close(true);
+ FileSizeLimitExceededException e =
+ new FileSizeLimitExceededException(
+ String.format("The field %s exceeds its maximum permitted size of %s bytes.",
+ fieldName, Long.valueOf(pSizeMax)),
+ pCount, pSizeMax);
+ e.setFieldName(fieldName);
+ e.setFileName(name);
+ throw new FileUploadIOException(e);
+ }
+ };
+ }
+ stream = istream;
+ }
+
+ /**
+ * Returns the items content type, or null.
+ *
+ * @return Content type, if known, or null.
+ */
+ @Override
+ public String getContentType() {
+ return contentType;
+ }
+
+ /**
+ * Returns the items field name.
+ *
+ * @return Field name.
+ */
+ @Override
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ /**
+ * Returns the items file name.
+ *
+ * @return File name, if known, or null.
+ * @throws InvalidFileNameException The file name contains a NUL character,
+ * which might be an indicator of a security attack. If you intend to
+ * use the file name anyways, catch the exception and use
+ * InvalidFileNameException#getName().
+ */
+ @Override
+ public String getName() {
+ return Streams.checkFileName(name);
+ }
+
+ /**
+ * Returns, whether this is a form field.
+ *
+ * @return True, if the item is a form field,
+ * otherwise false.
+ */
+ @Override
+ public boolean isFormField() {
+ return formField;
+ }
+
+ /**
+ * Returns an input stream, which may be used to
+ * read the items contents.
+ *
+ * @return Opened input stream.
+ * @throws IOException An I/O error occurred.
+ */
+ @Override
+ public InputStream openStream() throws IOException {
+ if (((Closeable) stream).isClosed()) {
+ throw new FileItemStream.ItemSkippedException();
+ }
+ return stream;
+ }
+
+ /**
+ * Closes the file item.
+ *
+ * @throws IOException An I/O error occurred.
+ */
+ public void close() throws IOException {
+ stream.close();
+ }
+
+ /**
+ * Returns the file item headers.
+ *
+ * @return The items header object
+ */
+ @Override
+ public FileItemHeaders getHeaders() {
+ return headers;
+ }
+
+ /**
+ * Sets the file item headers.
+ *
+ * @param pHeaders The items header object
+ */
+ @Override
+ public void setHeaders(FileItemHeaders pHeaders) {
+ headers = pHeaders;
+ }
+
+}
\ No newline at end of file
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/FileSizeLimitExceededException.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/FileSizeLimitExceededException.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/FileSizeLimitExceededException.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/FileSizeLimitExceededException.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,94 @@
+/*
+ * 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.fileupload.impl;
+
+/**
+ * Thrown to indicate that A files size exceeds the configured maximum.
+ */
+public class FileSizeLimitExceededException
+ extends SizeException {
+
+ /**
+ * The exceptions UID, for serializing an instance.
+ */
+ private static final long serialVersionUID = 8150776562029630058L;
+
+ /**
+ * File name of the item, which caused the exception.
+ */
+ private String fileName;
+
+ /**
+ * Field name of the item, which caused the exception.
+ */
+ private String fieldName;
+
+ /**
+ * Constructs a SizeExceededException with
+ * the specified detail message, and actual and permitted sizes.
+ *
+ * @param message The detail message.
+ * @param actual The actual request size.
+ * @param permitted The maximum permitted request size.
+ */
+ public FileSizeLimitExceededException(String message, long actual,
+ long permitted) {
+ super(message, actual, permitted);
+ }
+
+ /**
+ * Returns the file name of the item, which caused the
+ * exception.
+ *
+ * @return File name, if known, or null.
+ */
+ public String getFileName() {
+ return fileName;
+ }
+
+ /**
+ * Sets the file name of the item, which caused the
+ * exception.
+ *
+ * @param pFileName the file name of the item, which caused the exception.
+ */
+ public void setFileName(String pFileName) {
+ fileName = pFileName;
+ }
+
+ /**
+ * Returns the field name of the item, which caused the
+ * exception.
+ *
+ * @return Field name, if known, or null.
+ */
+ public String getFieldName() {
+ return fieldName;
+ }
+
+ /**
+ * Sets the field name of the item, which caused the
+ * exception.
+ *
+ * @param pFieldName the field name of the item,
+ * which caused the exception.
+ */
+ public void setFieldName(String pFieldName) {
+ fieldName = pFieldName;
+ }
+
+}
\ No newline at end of file
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/FileUploadIOException.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/FileUploadIOException.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/FileUploadIOException.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/FileUploadIOException.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,63 @@
+/*
+ * 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.fileupload.impl;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+
+/**
+ * This exception is thrown for hiding an inner
+ * {@link FileUploadException} in an {@link IOException}.
+ */
+public class FileUploadIOException extends IOException {
+
+ /**
+ * The exceptions UID, for serializing an instance.
+ */
+ private static final long serialVersionUID = -7047616958165584154L;
+
+ /**
+ * The exceptions cause; we overwrite the parent
+ * classes field, which is available since Java
+ * 1.4 only.
+ */
+ private final FileUploadException cause;
+
+ /**
+ * Creates a FileUploadIOException with the
+ * given cause.
+ *
+ * @param pCause The exceptions cause, if any, or null.
+ */
+ public FileUploadIOException(FileUploadException pCause) {
+ // We're not doing super(pCause) cause of 1.3 compatibility.
+ cause = pCause;
+ }
+
+ /**
+ * Returns the exceptions cause.
+ *
+ * @return The exceptions cause, if any, or null.
+ */
+ @SuppressWarnings("sync-override") // Field is final
+ @Override
+ public Throwable getCause() {
+ return cause;
+ }
+
+}
\ No newline at end of file
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/InvalidContentTypeException.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/InvalidContentTypeException.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/InvalidContentTypeException.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/InvalidContentTypeException.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,62 @@
+/*
+ * 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.fileupload.impl;
+
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+
+/**
+ * Thrown to indicate that the request is not a multipart request.
+ */
+public class InvalidContentTypeException
+ extends FileUploadException {
+
+ /**
+ * The exceptions UID, for serializing an instance.
+ */
+ private static final long serialVersionUID = -9073026332015646668L;
+
+ /**
+ * Constructs a InvalidContentTypeException with no
+ * detail message.
+ */
+ public InvalidContentTypeException() {
+ super();
+ }
+
+ /**
+ * Constructs an InvalidContentTypeException with
+ * the specified detail message.
+ *
+ * @param message The detail message.
+ */
+ public InvalidContentTypeException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an InvalidContentTypeException with
+ * the specified detail message and cause.
+ *
+ * @param msg The detail message.
+ * @param cause the original cause
+ *
+ * @since 1.3.1
+ */
+ public InvalidContentTypeException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
\ No newline at end of file
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/IOFileUploadException.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/IOFileUploadException.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/IOFileUploadException.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/IOFileUploadException.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,62 @@
+/*
+ * 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.fileupload.impl;
+
+import java.io.IOException;
+
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+
+/**
+ * Thrown to indicate an IOException.
+ */
+public class IOFileUploadException extends FileUploadException {
+
+ /**
+ * The exceptions UID, for serializing an instance.
+ */
+ private static final long serialVersionUID = 1749796615868477269L;
+
+ /**
+ * The exceptions cause; we overwrite the parent
+ * classes field, which is available since Java
+ * 1.4 only.
+ */
+ private final IOException cause;
+
+ /**
+ * Creates a new instance with the given cause.
+ *
+ * @param pMsg The detail message.
+ * @param pException The exceptions cause.
+ */
+ public IOFileUploadException(String pMsg, IOException pException) {
+ super(pMsg);
+ cause = pException;
+ }
+
+ /**
+ * Returns the exceptions cause.
+ *
+ * @return The exceptions cause, if any, or null.
+ */
+ @SuppressWarnings("sync-override") // Field is final
+ @Override
+ public Throwable getCause() {
+ return cause;
+ }
+
+}
\ No newline at end of file
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/SizeException.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/SizeException.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/SizeException.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/SizeException.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,75 @@
+/*
+ * 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.fileupload.impl;
+
+import org.apache.tomcat.util.http.fileupload.FileUploadException;
+
+/**
+ * This exception is thrown, if a requests permitted size
+ * is exceeded.
+ */
+public abstract class SizeException extends FileUploadException {
+
+ /**
+ * Serial version UID, being used, if serialized.
+ */
+ private static final long serialVersionUID = -8776225574705254126L;
+
+ /**
+ * The actual size of the request.
+ */
+ private final long actual;
+
+ /**
+ * The maximum permitted size of the request.
+ */
+ private final long permitted;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param message The detail message.
+ * @param actual The actual number of bytes in the request.
+ * @param permitted The requests size limit, in bytes.
+ */
+ protected SizeException(String message, long actual, long permitted) {
+ super(message);
+ this.actual = actual;
+ this.permitted = permitted;
+ }
+
+ /**
+ * Retrieves the actual size of the request.
+ *
+ * @return The actual size of the request.
+ * @since 1.3
+ */
+ public long getActualSize() {
+ return actual;
+ }
+
+ /**
+ * Retrieves the permitted size of the request.
+ *
+ * @return The permitted size of the request.
+ * @since 1.3
+ */
+ public long getPermittedSize() {
+ return permitted;
+ }
+
+}
\ No newline at end of file
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/SizeLimitExceededException.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/SizeLimitExceededException.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/impl/SizeLimitExceededException.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/impl/SizeLimitExceededException.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,43 @@
+/*
+ * 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.fileupload.impl;
+
+/**
+ * Thrown to indicate that the request size exceeds the configured maximum.
+ */
+public class SizeLimitExceededException
+ extends SizeException {
+
+ /**
+ * The exceptions UID, for serializing an instance.
+ */
+ private static final long serialVersionUID = -2474893167098052828L;
+
+ /**
+ * Constructs a SizeExceededException with
+ * the specified detail message, and actual and permitted sizes.
+ *
+ * @param message The detail message.
+ * @param actual The actual request size.
+ * @param permitted The maximum permitted request size.
+ */
+ public SizeLimitExceededException(String message, long actual,
+ long permitted) {
+ super(message, actual, permitted);
+ }
+
+}
\ No newline at end of file
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java 2020-02-05 19:26:48.000000000 +0000
@@ -22,7 +22,7 @@
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
-import org.apache.tomcat.util.http.fileupload.FileUploadBase.FileUploadIOException;
+import org.apache.tomcat.util.http.fileupload.impl.FileUploadIOException;
import org.apache.tomcat.util.http.fileupload.util.Closeable;
import org.apache.tomcat.util.http.fileupload.util.Streams;
@@ -115,7 +115,7 @@
* @param pListener The listener to invoke.
* @param pContentLength The expected content length.
*/
- ProgressNotifier(ProgressListener pListener, long pContentLength) {
+ public ProgressNotifier(ProgressListener pListener, long pContentLength) {
listener = pListener;
contentLength = pContentLength;
}
@@ -136,7 +136,7 @@
/**
* Called to indicate, that a new file item has been detected.
*/
- void noteItem() {
+ public void noteItem() {
++items;
notifyListener();
}
@@ -332,7 +332,7 @@
*
* @see #MultipartStream(InputStream, byte[], int, ProgressNotifier)
*/
- MultipartStream(InputStream input,
+ public MultipartStream(InputStream input,
byte[] boundary,
ProgressNotifier pNotifier) {
this(input, boundary, DEFAULT_BUFSIZE, pNotifier);
@@ -576,7 +576,7 @@
* Creates a new {@link ItemInputStream}.
* @return A new instance of {@link ItemInputStream}.
*/
- ItemInputStream newInputStream() {
+ public ItemInputStream newInputStream() {
return new ItemInputStream();
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/HeaderUtil.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/HeaderUtil.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/HeaderUtil.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/HeaderUtil.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,53 @@
+/*
+ * 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;
+
+public class HeaderUtil {
+
+ /**
+ * Converts an HTTP header line in byte form to a printable String.
+ * Bytes corresponding to visible ASCII characters will converted to those
+ * characters. All other bytes (0x00 to 0x1F, 0x7F to OxFF) will be
+ * represented in 0xNN form.
+ *
+ * @param bytes Contains an HTTP header line
+ * @param offset The start position of the header line in the array
+ * @param len The length of the HTTP header line
+ *
+ * @return A String with non-printing characters replaced by the 0xNN
+ * equivalent
+ */
+ public static String toPrintableString(byte[] bytes, int offset, int len) {
+ StringBuilder result = new StringBuilder();
+ for (int i = offset; i < offset + len; i++) {
+ char c = (char) (bytes[i] & 0xFF);
+ if (c < 0x20 || c > 0x7E) {
+ result.append("0x");
+ result.append(Character.forDigit((c >> 4) & 0xF, 16));
+ result.append(Character.forDigit((c) & 0xF, 16));
+ } else {
+ result.append(c);
+ }
+ }
+ return result.toString();
+ }
+
+
+ private HeaderUtil() {
+ // Utility class. Hide default constructor.
+ }
+}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/LegacyCookieProcessor.java 2020-02-05 19:26:48.000000000 +0000
@@ -326,7 +326,7 @@
SameSiteCookies sameSiteCookiesValue = getSameSiteCookies();
- if (!sameSiteCookiesValue.equals(SameSiteCookies.NONE)) {
+ if (!sameSiteCookiesValue.equals(SameSiteCookies.UNSET)) {
buf.append("; SameSite=");
buf.append(sameSiteCookiesValue.getValue());
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/LocalStrings.properties tomcat9-9.0.31/java/org/apache/tomcat/util/http/LocalStrings.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/LocalStrings.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/LocalStrings.properties 2020-02-05 19:26:48.000000000 +0000
@@ -16,7 +16,7 @@
cookies.fallToDebug=\n\
\ Note: further occurrences of Cookie errors will be logged at DEBUG level.
cookies.invalidCookieToken=Cookies: Invalid cookie. Value not a token or quoted value
-cookies.invalidSameSiteCookies=Unknown setting [{0}], must be one of: none, lax, strict. Default value is none.
+cookies.invalidSameSiteCookies=Unknown setting [{0}], must be one of: unset, none, lax, strict. Default value is unset.
cookies.invalidSpecial=Cookies: Unknown Special Cookie
cookies.maxCountFail=More than the maximum allowed number of cookies, [{0}], were detected.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/tomcat/util/http/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -15,12 +15,17 @@
cookies.invalidCookieToken=Cookie:cookie无效。值不是令牌或引用值
cookies.invalidSpecial=Cookies:未知特殊的Cookie
+cookies.maxCountFail=检测到超过Cookie最大允许的数量[{0}]
+
+headers.maxCountFail=检测到超过了允许设置的最大header 数[{0}]
parameters.bytes=开始处理输入[{0}]
parameters.copyFail=无法创建以调试日志记录为目的的原始参数值的副本
parameters.decodeFail.debug=字符解码失败.参数 [{0}]和值 [{1}]被忽略
+parameters.emptyChunk=忽略空参数块
parameters.fallToDebug=注:更多的参数错误将以DEBUG级别日志进行记录。
parameters.maxCountFail=检测到单个请求([{0}])的最大请求参数数(GET加POST)。 超出此限制的任何参数都被忽略。 要更改此限制,请在Connector上设置maxParameterCount属性。
parameters.maxCountFail.fallToDebug=注意:更多的错误信息只在debug级别日志中记录
+parameters.noequal=):参数从位置[{0}]开始,到位置[{1}]结束,值为[{2}],后面没有“=”字符
rfc6265CookieProcessor.invalidPath=这个cookie被指定了一个无效的路径 [{0}]
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/MimeHeaders.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/MimeHeaders.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/MimeHeaders.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/MimeHeaders.java 2020-02-05 19:26:48.000000000 +0000
@@ -395,7 +395,7 @@
* reset and swap with last header
* @param idx the index of the header to remove.
*/
- private void removeHeader(int idx) {
+ public void removeHeader(int idx) {
MimeHeaderField mh = headers[idx];
mh.recycle();
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/parser/HttpParser.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/parser/HttpParser.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/parser/HttpParser.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/parser/HttpParser.java 2020-02-05 19:26:48.000000000 +0000
@@ -317,6 +317,17 @@
}
+ public static boolean isControl(int c) {
+ // Fast for valid control characters, slower for some incorrect
+ // ones
+ try {
+ return IS_CONTROL[c];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ return false;
+ }
+ }
+
+
// Skip any LWS and position to read the next character. The next character
// is returned as being able to 'peek()' it allows a small optimisation in
// some cases.
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/parser/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/tomcat/util/http/parser/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/parser/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/parser/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -15,6 +15,7 @@
cookie.valueNotPresent=<不存在>
+http.closingBracket=在非IPv6主机名中找到了右括号']'。
http.illegalCharacterIpv4=字符[{0}]为非法的IPv4地址。
http.illegalCharacterIpv6=字符[{0}]为非法的IPv6地址。
http.invalidCharacterDomain.afterColon=字符 [{0}] 在域名中的冒号后无效。
@@ -25,9 +26,12 @@
http.invalidCharacterDomain.atEnd=字符 [{0}] 在域名末尾无效。
http.invalidCharacterDomain.atStart=字符 [{0}] 在域名开头无效。
http.invalidHextet=hextet无效。 hextet必须包含4个或更少的十六进制字符。
+http.invalidIpv4Location=IPv6地址在无效位置包含嵌入的IPv4地址。
http.invalidLeadingZero=非零的IPv4字符可能不包含前导零。
http.invalidOctet=无效字符[{0}].IPv4字符的有效范围为0~255。
+http.invalidSegmentEndState=状态[{0}]对于段的结尾无效。
http.noClosingBracket=ipv6 地址缺失一个闭合的圆括号
+http.noOpeningBracket=IPv6地址缺少开括号(
http.singleColonEnd=IPv6地址不能以单个“.”结尾。
http.singleColonStart=一个IPv6地址也许不是以单个冒号":"开头的。
http.tooManyColons=IPv6地址不能包含超过2个连续冒号字符。
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/parser/TokenList.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/parser/TokenList.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/parser/TokenList.java 1970-01-01 00:00:00.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/parser/TokenList.java 2020-02-05 19:26:48.000000000 +0000
@@ -0,0 +1,113 @@
+/*
+ * 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.parser;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Locale;
+
+public class TokenList {
+
+ private TokenList() {
+ // Utility class. Hide default constructor.
+ }
+
+
+ /**
+ * Parses an enumeration of header values of the form 1#token, forcing all
+ * parsed values to lower case.
+ *
+ * @param inputs The headers to parse
+ * @param collection The Collection (usually a list of a set) to which the
+ * parsed tokens should be added
+ *
+ * @return {@code} true if the header values were parsed cleanly, otherwise
+ * {@code false} (e.g. if a non-token value was encountered)
+ *
+ * @throws IOException If an I/O error occurs reading the header
+ */
+ public static boolean parseTokenList(Enumeration inputs, Collection collection) throws IOException {
+ boolean result = true;
+ while (inputs.hasMoreElements()) {
+ String nextHeaderValue = inputs.nextElement();
+ if (nextHeaderValue != null) {
+ if (!TokenList.parseTokenList(new StringReader(nextHeaderValue), collection)) {
+ result = false;
+ }
+ }
+ }
+ return result;
+ }
+
+
+ /**
+ * Parses a header of the form 1#token, forcing all parsed values to lower
+ * case. This is typically used when header values are case-insensitive.
+ *
+ * @param input The header to parse
+ * @param collection The Collection (usually a list of a set) to which the
+ * parsed tokens should be added
+ *
+ * @return {@code} true if the header was parsed cleanly, otherwise
+ * {@code false} (e.g. if a non-token value was encountered)
+ *
+ * @throws IOException If an I/O error occurs reading the header
+ */
+ public static boolean parseTokenList(Reader input, Collection collection) throws IOException {
+ boolean invalid = false;
+ boolean valid = false;
+
+ do {
+ String fieldName = HttpParser.readToken(input);
+ if (fieldName == null) {
+ // Invalid field-name, skip to the next one
+ invalid = true;
+ HttpParser.skipUntil(input, 0, ',');
+ continue;
+ }
+
+ if (fieldName.length() == 0) {
+ // No more data to read
+ break;
+ }
+
+ SkipResult skipResult = HttpParser.skipConstant(input, ",");
+ if (skipResult == SkipResult.EOF) {
+ // EOF
+ valid = true;
+ collection.add(fieldName.toLowerCase(Locale.ENGLISH));
+ break;
+ } else if (skipResult == SkipResult.FOUND) {
+ valid = true;
+ collection.add(fieldName.toLowerCase(Locale.ENGLISH));
+ continue;
+ } else {
+ // Not a token - ignore it
+ invalid = true;
+ HttpParser.skipUntil(input, 0, ',');
+ continue;
+ }
+ } while (true);
+
+ // Only return true if at least one valid token was read and no invalid
+ // entries were found
+ return valid && !invalid;
+ }
+}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/parser/Vary.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/parser/Vary.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/parser/Vary.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/parser/Vary.java 2020-02-05 19:26:48.000000000 +0000
@@ -18,9 +18,12 @@
import java.io.IOException;
import java.io.StringReader;
-import java.util.Locale;
import java.util.Set;
+/**
+ * @deprecated Use {@link TokenList}.
+ */
+@Deprecated
public class Vary {
private Vary() {
@@ -29,33 +32,6 @@
public static void parseVary(StringReader input, Set result) throws IOException {
-
- do {
- String fieldName = HttpParser.readToken(input);
- if (fieldName == null) {
- // Invalid field-name, skip to the next one
- HttpParser.skipUntil(input, 0, ',');
- continue;
- }
-
- if (fieldName.length() == 0) {
- // No more data to read
- break;
- }
-
- SkipResult skipResult = HttpParser.skipConstant(input, ",");
- if (skipResult == SkipResult.EOF) {
- // EOF
- result.add(fieldName.toLowerCase(Locale.ENGLISH));
- break;
- } else if (skipResult == SkipResult.FOUND) {
- result.add(fieldName.toLowerCase(Locale.ENGLISH));
- continue;
- } else {
- // Not a token - ignore it
- HttpParser.skipUntil(input, 0, ',');
- continue;
- }
- } while (true);
+ TokenList.parseTokenList(input, result);
}
}
diff -Nru tomcat9-9.0.27/java/org/apache/tomcat/util/http/RequestUtil.java tomcat9-9.0.31/java/org/apache/tomcat/util/http/RequestUtil.java
--- tomcat9-9.0.27/java/org/apache/tomcat/util/http/RequestUtil.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/tomcat/util/http/RequestUtil.java 2020-02-05 19:26:48.000000000 +0000
@@ -16,6 +16,12 @@
*/
package org.apache.tomcat.util.http;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Locale;
+
+import javax.servlet.http.HttpServletRequest;
+
public class RequestUtil {
private RequestUtil() {
@@ -113,4 +119,92 @@
// Return the normalized path that we have completed
return normalized;
}
+
+
+ public static boolean isSameOrigin(HttpServletRequest request, String origin) {
+ // Build scheme://host:port from request
+ StringBuilder target = new StringBuilder();
+ String scheme = request.getScheme();
+ if (scheme == null) {
+ return false;
+ } else {
+ scheme = scheme.toLowerCase(Locale.ENGLISH);
+ }
+ target.append(scheme);
+ target.append("://");
+
+ String host = request.getServerName();
+ if (host == null) {
+ return false;
+ }
+ target.append(host);
+
+ int port = request.getServerPort();
+ // Origin may or may not include the (default) port.
+ // At this point target doesn't include a port.
+ if (target.length() == origin.length()) {
+ // origin and target can only be equal if both are using default
+ // ports. Therefore only append the port to the target if a
+ // non-default port is used.
+ if (("http".equals(scheme) || "ws".equals(scheme)) && port != 80 ||
+ ("https".equals(scheme) || "wss".equals(scheme)) && port != 443) {
+ target.append(':');
+ target.append(port);
+ }
+ } else {
+ // origin and target can only be equal if:
+ // a) origin includes an explicit default port
+ // b) origin is using a non-default port
+ // Either way, add the port to the target so it can be compared
+ target.append(':');
+ target.append(port);
+ }
+
+
+ // Both scheme and host are case-insensitive but the CORS spec states
+ // this check should be case-sensitive
+ return origin.equals(target.toString());
+ }
+
+
+ /**
+ * Checks if a given origin is valid or not. Criteria:
+ *
+ *
If an encoded character is present in origin, it's not valid.
\n" +
"\n" +
"\n" +
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/manager/HTMLManagerServlet.java tomcat9-9.0.31/java/org/apache/catalina/manager/HTMLManagerServlet.java
--- tomcat9-9.0.27/java/org/apache/catalina/manager/HTMLManagerServlet.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/manager/HTMLManagerServlet.java 2020-02-05 19:26:48.000000000 +0000
@@ -794,7 +794,7 @@
*/
@Override
public String getServletInfo() {
- return "HTMLManagerServlet, Copyright (c) 1999-2019, The Apache Software Foundation";
+ return "HTMLManagerServlet, Copyright (c) 1999-2020, The Apache Software Foundation";
}
/**
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/manager/JMXProxyServlet.java tomcat9-9.0.31/java/org/apache/catalina/manager/JMXProxyServlet.java
--- tomcat9-9.0.27/java/org/apache/catalina/manager/JMXProxyServlet.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/manager/JMXProxyServlet.java 2020-02-05 19:26:48.000000000 +0000
@@ -21,7 +21,9 @@
import java.util.Set;
import javax.management.Attribute;
+import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
+import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServer;
@@ -35,6 +37,7 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.mbeans.MBeanDumper;
+import org.apache.catalina.tribes.util.StringManager;
import org.apache.tomcat.util.modeler.Registry;
/**
@@ -51,6 +54,8 @@
// without any parameters.
private static final String[] NO_PARAMETERS = new String[0];
+ private static final StringManager sm = StringManager.getManager(JMXProxyServlet.class);
+
// ----------------------------------------------------- Instance Variables
/**
* MBean server.
@@ -257,10 +262,27 @@
* call the requested operation.
* @return The value returned by the requested operation.
*/
+ @SuppressWarnings("null") // parameters can't be null if signature.length > 0
private Object invokeOperationInternal(String onameStr, String operation, String[] parameters)
throws OperationsException, MBeanException, ReflectionException {
ObjectName oname = new ObjectName(onameStr);
- MBeanOperationInfo methodInfo = registry.getMethodInfo(oname, operation);
+ int paramCount = null == parameters ? 0 : parameters.length;
+ MBeanOperationInfo methodInfo = registry.getMethodInfo(oname, operation, paramCount);
+ if(null == methodInfo) {
+ // getMethodInfo returns null for both "object not found" and "operation not found"
+ MBeanInfo info = null;
+ try {
+ info = registry.getMBeanServer().getMBeanInfo(oname);
+ } catch (InstanceNotFoundException infe) {
+ throw infe;
+ } catch (Exception e) {
+ throw new IllegalArgumentException(sm.getString("jmxProxyServlet.noBeanFound", onameStr), e);
+ }
+ throw new IllegalArgumentException(
+ sm.getString("jmxProxyServlet.noOperationOnBean",
+ operation, Integer.valueOf(paramCount), onameStr, info.getClassName()));
+ }
+
MBeanParameterInfo[] signature = methodInfo.getSignature();
String[] signatureTypes = new String[signature.length];
Object[] values = new Object[signature.length];
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/manager/LocalStrings.properties tomcat9-9.0.31/java/org/apache/catalina/manager/LocalStrings.properties
--- tomcat9-9.0.27/java/org/apache/catalina/manager/LocalStrings.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/manager/LocalStrings.properties 2020-02-05 19:26:48.000000000 +0000
@@ -192,3 +192,6 @@
statusServlet.complete=Complete Server Status
statusServlet.title=Server Status
+
+jmxProxyServlet.noOperationOnBean=Cannot find operation [{0}] with [{1}] arguments on object name [{2}], which is a [{3}]
+jmxProxyServlet.noBeanFound=Cannot find MBean with object name [{0}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/Manager.java tomcat9-9.0.31/java/org/apache/catalina/Manager.java
--- tomcat9-9.0.27/java/org/apache/catalina/Manager.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/Manager.java 2020-02-05 19:26:48.000000000 +0000
@@ -215,11 +215,44 @@
* session ID.
*
* @param session The session to change the session ID for
+ *
+ * @deprecated Use {@link #rotateSessionId(Session)}.
+ * Will be removed in Tomcat 10
*/
+ @Deprecated
public void changeSessionId(Session session);
/**
+ * Change the session ID of the current session to a new randomly generated
+ * session ID.
+ *
+ * @param session The session to change the session ID for
+ *
+ * @return The new session ID
+ */
+ public default String rotateSessionId(Session session) {
+ String newSessionId = null;
+ // Assume there new Id is a duplicate until we prove it isn't. The
+ // chances of a duplicate are extremely low but the current ManagerBase
+ // code protects against duplicates so this default method does too.
+ boolean duplicate = true;
+ do {
+ newSessionId = getSessionIdGenerator().generateSessionId();
+ try {
+ if (findSession(newSessionId) == null) {
+ duplicate = false;
+ }
+ } catch (IOException ioe) {
+ // Swallow. An IOE means the ID was known so continue looping
+ }
+ } while (duplicate);
+ changeSessionId(session, newSessionId);
+ return newSessionId;
+ }
+
+
+ /**
* Change the session ID of the current session to a specified session ID.
*
* @param session The session to change the session ID for
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/mapper/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/mapper/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/mapper/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/mapper/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,9 +13,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+mapper.addContext.noHost=找不到主机 [{0}]
mapper.addHostAlias.success=为虚拟主机 [{1}] 注册了别名 [{0}]
mapperListener.pauseContext=根据服务需要,注册内容[{0}]已经重新加载
mapperListener.registerHost=这域名[{1}]注册主机[{0}],服务:[{2}]
mapperListener.registerWrapper=为服务Service[{2}]在上下文Context[{1}]注册Wrapper[{0}]
+mapperListener.unregisterContext=注销服务[{1}]的上下文[{0}]
mapperListener.unregisterHost=在域[{1}]中.,不能注册主机[{0}]为服务[{2}]
+mapperListener.unregisterWrapper=在上下文[{1}]中注销服务[{2}]的包装程序[{0}]
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/mapper/Mapper.java tomcat9-9.0.31/java/org/apache/catalina/mapper/Mapper.java
--- tomcat9-9.0.27/java/org/apache/catalina/mapper/Mapper.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/mapper/Mapper.java 2020-02-05 19:26:48.000000000 +0000
@@ -732,6 +732,7 @@
* Map the specified URI.
* @throws IOException
*/
+ @SuppressWarnings("deprecation") // contextPath
private final void internalMap(CharChunk host, CharChunk uri,
String version, MappingData mappingData) throws IOException {
@@ -1064,6 +1065,7 @@
/**
* Exact mapping.
*/
+ @SuppressWarnings("deprecation") // contextPath
private final void internalMapExactWrapper
(MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) {
MappedWrapper wrapper = exactFind(wrappers, path);
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/mapper/MappingData.java tomcat9-9.0.31/java/org/apache/catalina/mapper/MappingData.java
--- tomcat9-9.0.27/java/org/apache/catalina/mapper/MappingData.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/mapper/MappingData.java 2020-02-05 19:26:48.000000000 +0000
@@ -38,6 +38,10 @@
public Wrapper wrapper = null;
public boolean jspWildCard = false;
+ /**
+ * @deprecated Unused. This will be removed in Tomcat 10.
+ */
+ @Deprecated
public final MessageBytes contextPath = MessageBytes.newInstance();
public final MessageBytes requestPath = MessageBytes.newInstance();
public final MessageBytes wrapperPath = MessageBytes.newInstance();
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java tomcat9-9.0.31/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
--- tomcat9-9.0.27/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java 2020-02-05 19:26:48.000000000 +0000
@@ -25,10 +25,11 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
+import java.rmi.AccessException;
import java.rmi.AlreadyBoundException;
+import java.rmi.NotBoundException;
+import java.rmi.Remote;
import java.rmi.RemoteException;
-import java.rmi.registry.LocateRegistry;
-import java.rmi.registry.Registry;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.util.HashMap;
@@ -61,7 +62,13 @@
* instance that is running behind a firewall. Only the ports are configured via
* the listener. The remainder of the configuration is via the standard system
* properties for configuring JMX.
+ *
+ * @deprecated The features provided by this listener are now available in the
+ * remote JMX capability included with the JRE.
+ * This listener will be removed in Tomcat 10 and may be removed
+ * from Tomcat 9.0.x some time after 2020-12-31.
*/
+@Deprecated
public class JmxRemoteLifecycleListener extends SSLHostConfig implements LifecycleListener {
private static final long serialVersionUID = 1L;
@@ -292,8 +299,10 @@
@Override
public void lifecycleEvent(LifecycleEvent event) {
- // When the server starts, configure JMX/RMI
- if (Lifecycle.START_EVENT.equals(event.getType())) {
+ if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType())) {
+ log.warn(sm.getString("jmxRemoteLifecycleListener.deprecated"));
+ } else if (Lifecycle.START_EVENT.equals(event.getType())) {
+ // When the server starts, configure JMX/RMI
// Configure using standard JMX system properties
init();
@@ -417,18 +426,6 @@
RMIClientSocketFactory registryCsf, RMIServerSocketFactory registrySsf,
RMIClientSocketFactory serverCsf, RMIServerSocketFactory serverSsf) {
- // Create the RMI registry
- Registry registry;
- try {
- registry = LocateRegistry.createRegistry(
- theRmiRegistryPort, registryCsf, registrySsf);
- } catch (RemoteException e) {
- log.error(sm.getString(
- "jmxRemoteLifecycleListener.createRegistryFailed",
- serverName, Integer.toString(theRmiRegistryPort)), e);
- return null;
- }
-
if (bindAddress == null) {
bindAddress = "localhost";
}
@@ -449,11 +446,26 @@
cs = new RMIConnectorServer(serviceUrl, theEnv, server,
ManagementFactory.getPlatformMBeanServer());
cs.start();
- registry.bind("jmxrmi", server.toStub());
+ Remote jmxServer = server.toStub();
+ // Create the RMI registry
+ try {
+ /*
+ * JmxRegistry is registered as a side-effect of creation.
+ * This object is here so we can tell the IDE it is OK for it
+ * not to be used.
+ */
+ @SuppressWarnings("unused")
+ JmxRegistry unused = new JmxRegistry(theRmiRegistryPort, registryCsf, registrySsf, "jmxrmi", jmxServer);
+ } catch (RemoteException e) {
+ log.error(sm.getString(
+ "jmxRemoteLifecycleListener.createRegistryFailed",
+ serverName, Integer.toString(theRmiRegistryPort)), e);
+ return null;
+ }
log.info(sm.getString("jmxRemoteLifecycleListener.start",
Integer.toString(theRmiRegistryPort),
Integer.toString(theRmiServerPort), serverName));
- } catch (IOException | AlreadyBoundException e) {
+ } catch (IOException e) {
log.error(sm.getString(
"jmxRemoteLifecycleListener.createServerFailed",
serverName), e);
@@ -589,4 +601,43 @@
return true;
}
}
+
+
+ /*
+ * Better to use the internal API than re-invent the wheel.
+ */
+ @SuppressWarnings("restriction")
+ private static class JmxRegistry extends sun.rmi.registry.RegistryImpl {
+ private static final long serialVersionUID = -3772054804656428217L;
+ private final String jmxName;
+ private final Remote jmxServer;
+ public JmxRegistry(int port, RMIClientSocketFactory csf,
+ RMIServerSocketFactory ssf, String jmxName, Remote jmxServer) throws RemoteException {
+ super(port, csf, ssf);
+ this.jmxName = jmxName;
+ this.jmxServer = jmxServer;
+ }
+ @Override
+ public Remote lookup(String name)
+ throws RemoteException, NotBoundException {
+ return (jmxName.equals(name)) ? jmxServer : null;
+ }
+ @Override
+ public void bind(String name, Remote obj)
+ throws RemoteException, AlreadyBoundException, AccessException {
+ }
+ @Override
+ public void unbind(String name)
+ throws RemoteException, NotBoundException, AccessException {
+ }
+ @Override
+ public void rebind(String name, Remote obj)
+ throws RemoteException, AccessException {
+ }
+ @Override
+ public String[] list() throws RemoteException {
+ return new String[] { jmxName };
+ }
+ }
+
}
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/mbeans/LocalStrings.properties tomcat9-9.0.31/java/org/apache/catalina/mbeans/LocalStrings.properties
--- tomcat9-9.0.27/java/org/apache/catalina/mbeans/LocalStrings.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/mbeans/LocalStrings.properties 2020-02-05 19:26:48.000000000 +0000
@@ -25,6 +25,7 @@
jmxRemoteLifecycleListener.createRegistryFailed=Unable to create the RMI registry for the [{0}] server using port [{1}]
jmxRemoteLifecycleListener.createServerFailed=The JMX connector server could not be created or failed to start for the [{0}] server
+jmxRemoteLifecycleListener.deprecated=The JmxRemoteLifecycleListener is deprecated as as the features it provides are now available in the remote JMX capability included with the JRE. This listener will be removed in Tomcat 10 and may be removed from Tomcat 9 some time after 2020-12-31.
jmxRemoteLifecycleListener.destroyServerFailed=The JMX connector server could not be stopped for the [{0}] server
jmxRemoteLifecycleListener.invalidRmiBindAddress=Invalid RMI bind address [{0}]
jmxRemoteLifecycleListener.invalidSSLConfiguration=SSL configuration error
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/mbeans/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/mbeans/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/mbeans/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/mbeans/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -13,5 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+jmxRemoteLifecycleListener.createRegistryFailed=无法使用端口[{1}]为[{0}]服务器创建RMI注册表
+jmxRemoteLifecycleListener.createServerFailed=无法为[{0}]服务器创建JMX连接器服务器或启动失败
jmxRemoteLifecycleListener.invalidSSLConfiguration=SSL配置错误
jmxRemoteLifecycleListener.invalidURL=为[{0}]服务器[{1}]请求的JMX服务URL无效
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/realm/CombinedRealm.java tomcat9-9.0.31/java/org/apache/catalina/realm/CombinedRealm.java
--- tomcat9-9.0.27/java/org/apache/catalina/realm/CombinedRealm.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/realm/CombinedRealm.java 2020-02-05 19:26:48.000000000 +0000
@@ -29,9 +29,11 @@
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Realm;
+import org.apache.catalina.Wrapper;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
@@ -346,22 +348,18 @@
public Principal authenticate(GSSContext gssContext, boolean storeCred) {
if (gssContext.isEstablished()) {
Principal authenticatedUser = null;
- String username = null;
-
- GSSName name = null;
+ GSSName gssName = null;
try {
- name = gssContext.getSrcName();
+ gssName = gssContext.getSrcName();
} catch (GSSException e) {
log.warn(sm.getString("realmBase.gssNameFail"), e);
return null;
}
- username = name.toString();
-
for (Realm realm : realms) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authStart",
- username, realm.getClass().getName()));
+ gssName, realm.getClass().getName()));
}
authenticatedUser = realm.authenticate(gssContext, storeCred);
@@ -369,12 +367,12 @@
if (authenticatedUser == null) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authFail",
- username, realm.getClass().getName()));
+ gssName, realm.getClass().getName()));
}
} else {
if (log.isDebugEnabled()) {
log.debug(sm.getString("combinedRealm.authSuccess",
- username, realm.getClass().getName()));
+ gssName, realm.getClass().getName()));
}
break;
}
@@ -386,6 +384,50 @@
return null;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Principal authenticate(GSSName gssName, GSSCredential gssCredential) {
+ Principal authenticatedUser = null;
+
+ for (Realm realm : realms) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("combinedRealm.authStart",
+ gssName, realm.getClass().getName()));
+ }
+
+ authenticatedUser = realm.authenticate(gssName, gssCredential);
+
+ if (authenticatedUser == null) {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("combinedRealm.authFail",
+ gssName, realm.getClass().getName()));
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("combinedRealm.authSuccess",
+ gssName, realm.getClass().getName()));
+ }
+ break;
+ }
+ }
+ return authenticatedUser;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean hasRole(Wrapper wrapper, Principal principal, String role) {
+ for (Realm realm : realms) {
+ if (realm.hasRole(wrapper, principal, role)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
protected String getPassword(String username) {
// This method should never be called
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/realm/JNDIRealm.java tomcat9-9.0.31/java/org/apache/catalina/realm/JNDIRealm.java
--- tomcat9-9.0.27/java/org/apache/catalina/realm/JNDIRealm.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/realm/JNDIRealm.java 2020-02-05 19:26:48.000000000 +0000
@@ -62,6 +62,7 @@
import org.apache.catalina.LifecycleException;
import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSName;
/**
*
Implementation of Realm that works with a directory
@@ -1527,7 +1528,6 @@
containerLog.debug("Found user by search [" + user + "]");
}
}
-
if (userPassword == null && credentials != null && user != null) {
// The password is available. Insert it since it may be required for
// role searches.
@@ -2222,7 +2222,7 @@
try {
User user = getUser(open(), username, null);
- if (user == null) {
+ if (user == null) {
// User should be found...
return null;
} else {
@@ -2246,6 +2246,22 @@
}
@Override
+ protected Principal getPrincipal(GSSName gssName,
+ GSSCredential gssCredential) {
+ String name = gssName.toString();
+
+ if (isStripRealmForGss()) {
+ int i = name.indexOf('@');
+ if (i > 0) {
+ // Zero so we don't leave a zero length name
+ name = name.substring(0, i);
+ }
+ }
+
+ return getPrincipal(name, gssCredential);
+ }
+
+ @Override
protected Principal getPrincipal(String username,
GSSCredential gssCredential) {
@@ -2341,12 +2357,14 @@
roles = getRoles(context, user);
}
} finally {
- restoreEnvironmentParameter(context,
- Context.SECURITY_AUTHENTICATION, preservedEnvironment);
- restoreEnvironmentParameter(context,
- "javax.security.sasl.server.authentication", preservedEnvironment);
- restoreEnvironmentParameter(context, "javax.security.sasl.qop",
- preservedEnvironment);
+ if (gssCredential != null && isUseDelegatedCredential()) {
+ restoreEnvironmentParameter(context,
+ Context.SECURITY_AUTHENTICATION, preservedEnvironment);
+ restoreEnvironmentParameter(context,
+ "javax.security.sasl.server.authentication", preservedEnvironment);
+ restoreEnvironmentParameter(context, "javax.security.sasl.qop",
+ preservedEnvironment);
+ }
}
if (user != null) {
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/realm/LocalStrings_zh_CN.properties tomcat9-9.0.31/java/org/apache/catalina/realm/LocalStrings_zh_CN.properties
--- tomcat9-9.0.27/java/org/apache/catalina/realm/LocalStrings_zh_CN.properties 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/realm/LocalStrings_zh_CN.properties 2020-02-05 19:26:48.000000000 +0000
@@ -21,15 +21,24 @@
jaasCallback.username=返回用户名 [{0}]
+jaasMemoryLoginModule.invalidCredentials=用户名或密码不正确
+jaasMemoryLoginModule.noConfig=无法加载配置文件 [{0}]
+
+jaasRealm.authenticateFailure=用户 [{0}] 认证失败
jaasRealm.authenticateSuccess=用户名 [{0}] 已被成功认证为身份 [{1}] -- 主体也已创建
+jaasRealm.classNotFound=找不到类 [{0}]
jaasRealm.failedLogin=由于登录失败,用户名 [{0}] 无法授权
jaasRealm.loginContextCreated=为用户名创建的JAAS 登陆上下文[{0}]
jaasRealm.loginException=登录异常,认证用户名 [{0}]
+jdbcRealm.authenticateFailure=用户名称[{0}]未校验成功
+jdbcRealm.open=打开数据库连接时发生异常
+
jndiRealm.authenticateFailure=用户名[{0}]没有成功认证
jndiRealm.authenticateSuccess=用户名[{0}]成功认证
jndiRealm.cipherSuites=启用 [{0}] 作为 TLS 连接的加密套件。
jndiRealm.exception=执行认证异常
+jndiRealm.negotiatedTls=使用协议[{0}]协商的TLS连接
jndiRealm.open=打开目录服务器链接异常
lockOutRealm.authLockedUser=尝试对锁定的用户[{0}]进行身份验证
@@ -37,6 +46,7 @@
memoryRealm.loadExist=内存数据库文件[{0}]无法读取
memoryRealm.loadPath=从内存数据库文件 [{0}] 加载用户
memoryRealm.readXml=读取内存数据库文件时出现异常
+memoryRealm.xmlFeatureEncoding=配置Digester以允许XML文件中的java编码名称的异常。只支持IANA编码名称。
pbeCredentialHandler.invalidKeySpec=无法生成基于密码的密钥
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/realm/LockOutRealm.java tomcat9-9.0.31/java/org/apache/catalina/realm/LockOutRealm.java
--- tomcat9-9.0.27/java/org/apache/catalina/realm/LockOutRealm.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/realm/LockOutRealm.java 2020-02-05 19:26:48.000000000 +0000
@@ -27,6 +27,7 @@
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSName;
@@ -200,6 +201,18 @@
return null;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Principal authenticate(GSSName gssName, GSSCredential gssCredential) {
+ String username = gssName.toString();
+
+ Principal authenticatedUser = super.authenticate(gssName, gssCredential);
+
+ return filterLockedAccounts(username, authenticatedUser);
+ }
+
/*
* Filters authenticated principals to ensure that null is
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/realm/RealmBase.java tomcat9-9.0.31/java/org/apache/catalina/realm/RealmBase.java
--- tomcat9-9.0.27/java/org/apache/catalina/realm/RealmBase.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/realm/RealmBase.java 2020-02-05 19:26:48.000000000 +0000
@@ -16,7 +16,6 @@
*/
package org.apache.catalina.realm;
-
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
@@ -149,7 +148,6 @@
// ------------------------------------------------------------- Properties
-
/**
* @return The HTTP status code used when the container needs to issue an
* HTTP redirect to meet the requirements of a configured transport
@@ -368,6 +366,7 @@
}
}
+
/**
* Try to authenticate with the specified username, which
* matches the digest calculated using the given parameters using the
@@ -497,16 +496,7 @@
}
}
- String name = gssName.toString();
-
- if (isStripRealmForGss()) {
- int i = name.indexOf('@');
- if (i > 0) {
- // Zero so we don't leave a zero length name
- name = name.substring(0, i);
- }
- }
- return getPrincipal(name, gssCredential);
+ return getPrincipal(gssName, gssCredential);
}
} else {
log.error(sm.getString("realmBase.gssContextNotEstablished"));
@@ -518,6 +508,19 @@
/**
+ * {@inheritDoc}
+ */
+ @Override
+ public Principal authenticate(GSSName gssName, GSSCredential gssCredential) {
+ if (gssName == null) {
+ return null;
+ }
+
+ return getPrincipal(gssName, gssCredential);
+ }
+
+
+ /**
* Execute a periodic task, such as reloading, etc. This method will be
* invoked inside the classloading context of this container. Unexpected
* throwables will be caught and logged.
@@ -1220,6 +1223,16 @@
protected abstract Principal getPrincipal(String username);
+ /**
+ * Get the principal associated with the specified user name.
+ *
+ * @param username The user name
+ * @param gssCredential the GSS credential of the principal
+ * @return the principal associated with the given user name.
+ * @deprecated This will be removed in Tomcat 10 onwards. Use
+ * {@link #getPrincipal(GSSName, GSSCredential)} instead.
+ */
+ @Deprecated
protected Principal getPrincipal(String username,
GSSCredential gssCredential) {
Principal p = getPrincipal(username);
@@ -1231,6 +1244,36 @@
return p;
}
+
+ /**
+ * Get the principal associated with the specified {@link GSSName}.
+ *
+ * @param gssName The GSS name
+ * @param gssCredential the GSS credential of the principal
+ * @return the principal associated with the given user name.
+ */
+ protected Principal getPrincipal(GSSName gssName,
+ GSSCredential gssCredential) {
+ String name = gssName.toString();
+
+ if (isStripRealmForGss()) {
+ int i = name.indexOf('@');
+ if (i > 0) {
+ // Zero so we don't leave a zero length name
+ name = name.substring(0, i);
+ }
+ }
+
+ Principal p = getPrincipal(name);
+
+ if (p instanceof GenericPrincipal) {
+ ((GenericPrincipal) p).setGssCredential(gssCredential);
+ }
+
+ return p;
+ }
+
+
/**
* Return the Server object that is the ultimate parent for the container
* with which this Realm is associated. If the server cannot be found (eg
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/Realm.java tomcat9-9.0.31/java/org/apache/catalina/Realm.java
--- tomcat9-9.0.27/java/org/apache/catalina/Realm.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/Realm.java 2020-02-05 19:26:48.000000000 +0000
@@ -25,6 +25,8 @@
import org.apache.catalina.connector.Response;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSName;
/**
* A Realm is a read-only facade for an underlying security realm
@@ -117,6 +119,22 @@
/**
+ * Try to authenticate using a {@link GSSName}
+ *
+ * Note that this default method will be turned into an abstract one in
+ * Tomcat 10.
+ *
+ * @param gssName The {@link GSSName} of the principal to look up
+ * @param gssCredential The {@link GSSCredential} of the principal, may be
+ * {@code null}
+ * @return the associated principal, or {@code null} if there is none
+ */
+ public default Principal authenticate(GSSName gssName, GSSCredential gssCredential) {
+ return null;
+ }
+
+
+ /**
* Try to authenticate using {@link X509Certificate}s
*
* @param certs Array of client certificates, with the first one in
@@ -211,7 +229,9 @@
* Return roles associated with given principal
* @param principal the {@link Principal} to get the roles for.
* @return principal roles
+ * @deprecated This will be removed in Tomcat 10.
*/
+ @Deprecated
public String[] getRoles(Principal principal);
diff -Nru tomcat9-9.0.27/java/org/apache/catalina/servlets/DefaultServlet.java tomcat9-9.0.31/java/org/apache/catalina/servlets/DefaultServlet.java
--- tomcat9-9.0.27/java/org/apache/catalina/servlets/DefaultServlet.java 2019-10-07 09:50:40.000000000 +0000
+++ tomcat9-9.0.31/java/org/apache/catalina/servlets/DefaultServlet.java 2020-02-05 19:26:48.000000000 +0000
@@ -1815,14 +1815,18 @@
String rewrittenContextPath = rewriteUrl(contextPath);
// Render the page header
- sb.append("\r\n");
+ sb.append("\r\n");
+ /* TODO Activate this as soon as we use smClient with the request locales
+ sb.append("\r\n");
+ */
sb.append("