diff -Nru tomcat7-7.0.68/bin/catalina.bat tomcat7-7.0.69/bin/catalina.bat --- tomcat7-7.0.68/bin/catalina.bat 2015-11-25 14:04:06.000000000 +0000 +++ tomcat7-7.0.69/bin/catalina.bat 2016-03-31 15:27:31.000000000 +0000 @@ -77,6 +77,10 @@ rem -agentlib:jdwp=transport=%JPDA_TRANSPORT%, rem address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND% rem +rem JSSE_OPTS (Optional) Java runtime options used to control the TLS +rem implementation when JSSE is used. Default is: +rem "-Djdk.tls.ephemeralDHKeySize=2048" +rem rem LOGGING_CONFIG (Optional) Override Tomcat's logging config file rem Example (all one line) rem set LOGGING_CONFIG="-Djava.util.logging.config.file=%CATALINA_BASE%\conf\logging.properties" @@ -171,6 +175,11 @@ set "CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\bin\tomcat-juli.jar" :juliClasspathDone +if not "%JSSE_OPTS%" == "" goto gotJsseOpts +set JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048" +:gotJsseOpts +set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%" + if not "%LOGGING_CONFIG%" == "" goto noJuliConfig set LOGGING_CONFIG=-Dnop if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig diff -Nru tomcat7-7.0.68/bin/catalina.sh tomcat7-7.0.69/bin/catalina.sh --- tomcat7-7.0.68/bin/catalina.sh 2015-11-25 14:04:06.000000000 +0000 +++ tomcat7-7.0.69/bin/catalina.sh 2016-03-31 15:27:31.000000000 +0000 @@ -82,6 +82,10 @@ # -agentlib:jdwp=transport=$JPDA_TRANSPORT, # address=$JPDA_ADDRESS,server=y,suspend=$JPDA_SUSPEND # +# JSSE_OPTS (Optional) Java runtime options used to control the TLS +# implementation when JSSE is used. Default is: +# "-Djdk.tls.ephemeralDHKeySize=2048" +# # CATALINA_PID (Optional) Path of the file which should contains the pid # of the catalina startup java process, when start (fork) is # used @@ -215,6 +219,11 @@ JAVA_ENDORSED_DIRS=`cygpath --path --windows "$JAVA_ENDORSED_DIRS"` fi +if [ -z "$JSSE_OPTS" ] ; then + JSSE_OPTS="-Djdk.tls.ephemeralDHKeySize=2048" +fi +JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS" + # Set juli LogManager config file if it is present and an override has not been issued if [ -z "$LOGGING_CONFIG" ]; then if [ -r "$CATALINA_BASE"/conf/logging.properties ]; then diff -Nru tomcat7-7.0.68/BUILDING.txt tomcat7-7.0.69/BUILDING.txt --- tomcat7-7.0.68/BUILDING.txt 2016-01-27 13:23:31.000000000 +0000 +++ tomcat7-7.0.69/BUILDING.txt 2016-02-23 22:23:52.000000000 +0000 @@ -147,14 +147,9 @@ directory outside of the source tree, so that you do not waste your time re-downloading the libraries. -* WARNING: The default value of base.path property makes the build script - to download libraries required to build Tomcat to the /usr/share/java - directory. On a typical Linux or MacOX system an ordinary user will not - have access to write to this directory. Even if you do have access to - that directory, it is likely not appropriate for you to write there. - - On Windows this usually corresponds to the "C:\usr\share\java" - directory, unless Cygwin is used. +* NOTE: The default value of the base.path property configures the build script + to download the libraries required to build Tomcat to the + ${user.home}/tomcat-build-libs directory. * NOTE: Users accessing the Internet through a proxy must use the properties file to indicate to Ant the proxy configuration. diff -Nru tomcat7-7.0.68/build.properties.default tomcat7-7.0.69/build.properties.default --- tomcat7-7.0.68/build.properties.default 2016-02-08 20:01:46.000000000 +0000 +++ tomcat7-7.0.69/build.properties.default 2016-04-11 07:49:30.000000000 +0000 @@ -25,7 +25,7 @@ # ----- Version Control Flags ----- version.major=7 version.minor=0 -version.build=68 +version.build=69 version.patch=0 version.suffix= @@ -61,9 +61,7 @@ # Please note this path must be absolute, not relative, # as it is referenced with different working directory # contexts by the various build scripts. -base.path=/usr/share/java -#base.path=C:/path/to/the/repository -#base.path=/usr/local +base.path=${user.home}/tomcat-build-libs compile.source=1.6 compile.target=1.6 @@ -163,7 +161,7 @@ commons-pool-src.loc.2=${base-commons.loc.2}/pool/source/commons-pool-${commons-pool.version}-src.tar.gz # ----- NSIS, version 2.0 or later ----- -nsis.version=2.50 +nsis.version=2.51 nsis.home=${base.path}/nsis-${nsis.version} nsis.exe=${nsis.home}/makensis.exe nsis.installoptions.dll=${nsis.home}/Plugins/InstallOptions.dll diff -Nru tomcat7-7.0.68/conf/tomcat-users.xml tomcat7-7.0.69/conf/tomcat-users.xml --- tomcat7-7.0.68/conf/tomcat-users.xml 2014-01-25 20:13:05.000000000 +0000 +++ tomcat7-7.0.69/conf/tomcat-users.xml 2016-03-08 18:08:16.000000000 +0000 @@ -19,18 +19,23 @@ diff -Nru tomcat7-7.0.68/debian/changelog tomcat7-7.0.69/debian/changelog --- tomcat7-7.0.68/debian/changelog 2016-02-18 21:28:39.000000000 +0000 +++ tomcat7-7.0.69/debian/changelog 2016-04-23 09:30:11.000000000 +0000 @@ -1,3 +1,11 @@ +tomcat7 (7.0.69-1) unstable; urgency=medium + + * New upstream release + - Refreshed the patches + * Standards-Version updated to 3.9.8 (no changes) + + -- Emmanuel Bourg Sat, 23 Apr 2016 11:30:01 +0200 + tomcat7 (7.0.68-1) unstable; urgency=medium * Team upload. diff -Nru tomcat7-7.0.68/debian/control tomcat7-7.0.69/debian/control --- tomcat7-7.0.68/debian/control 2016-02-18 09:23:48.000000000 +0000 +++ tomcat7-7.0.69/debian/control 2016-04-23 09:26:42.000000000 +0000 @@ -22,7 +22,7 @@ libjstl1.1-java, libjakarta-taglibs-standard-java, lsb-release -Standards-Version: 3.9.7 +Standards-Version: 3.9.8 Vcs-Git: https://anonscm.debian.org/git/pkg-java/tomcat7.git Vcs-Browser: https://anonscm.debian.org/cgit/pkg-java/tomcat7.git Homepage: http://tomcat.apache.org diff -Nru tomcat7-7.0.68/debian/patches/0009-Use-java.security.policy-file-in-catalina.sh.patch tomcat7-7.0.69/debian/patches/0009-Use-java.security.policy-file-in-catalina.sh.patch --- tomcat7-7.0.68/debian/patches/0009-Use-java.security.policy-file-in-catalina.sh.patch 2016-02-18 09:23:48.000000000 +0000 +++ tomcat7-7.0.69/debian/patches/0009-Use-java.security.policy-file-in-catalina.sh.patch 2016-04-23 09:27:14.000000000 +0000 @@ -14,7 +14,7 @@ --- a/bin/catalina.sh +++ b/bin/catalina.sh -@@ -283,7 +283,7 @@ +@@ -292,7 +292,7 @@ -Djava.endorsed.dirs="$JAVA_ENDORSED_DIRS" -classpath "$CLASSPATH" \ -sourcepath "$CATALINA_HOME"/../../java \ -Djava.security.manager \ @@ -23,7 +23,7 @@ -Dcatalina.base="$CATALINA_BASE" \ -Dcatalina.home="$CATALINA_HOME" \ -Djava.io.tmpdir="$CATALINA_TMPDIR" \ -@@ -310,7 +310,7 @@ +@@ -319,7 +319,7 @@ eval exec "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \ -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \ -Djava.security.manager \ @@ -32,7 +32,7 @@ -Dcatalina.base="\"$CATALINA_BASE\"" \ -Dcatalina.home="\"$CATALINA_HOME\"" \ -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ -@@ -376,7 +376,7 @@ +@@ -385,7 +385,7 @@ eval "\"$_RUNJAVA\"" "\"$LOGGING_CONFIG\"" $LOGGING_MANAGER $JAVA_OPTS $CATALINA_OPTS \ -Djava.endorsed.dirs="\"$JAVA_ENDORSED_DIRS\"" -classpath "\"$CLASSPATH\"" \ -Djava.security.manager \ diff -Nru tomcat7-7.0.68/debian/rules tomcat7-7.0.69/debian/rules --- tomcat7-7.0.68/debian/rules 2016-02-18 09:23:48.000000000 +0000 +++ tomcat7-7.0.69/debian/rules 2016-04-23 09:29:48.000000000 +0000 @@ -24,7 +24,7 @@ javadoc -locale en -subpackages "javax.servlet" -d "output/api" \ -sourcepath "java" -author -version -breakiterator -notimestamp \ -windowtitle "Tomcat API Documentation" -doctitle "Tomcat API" \ - -bottom "Copyright © 2000-2015 Apache Software Foundation. All Rights Reserved." + -bottom "Copyright © 2000-2016 Apache Software Foundation. All Rights Reserved." override_dh_auto_test: ifeq (,$(findstring nocheck, $(DEB_BUILD_OPTIONS))) diff -Nru tomcat7-7.0.68/java/org/apache/catalina/authenticator/AuthenticatorBase.java tomcat7-7.0.69/java/org/apache/catalina/authenticator/AuthenticatorBase.java --- tomcat7-7.0.68/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2015-03-18 21:38:31.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2016-02-26 14:36:48.000000000 +0000 @@ -849,7 +849,9 @@ Session session = request.getSessionInternal(false); if (session != null) { - if (changeSessionIdOnAuthentication) { + // If the principal is null then this is a logout. No need to change + // the session ID. See BZ 59043. + if (changeSessionIdOnAuthentication && principal != null) { Manager manager = request.getContext().getManager(); manager.changeSessionId(session); request.changeSessionId(session.getId()); diff -Nru tomcat7-7.0.68/java/org/apache/catalina/authenticator/SingleSignOn.java tomcat7-7.0.69/java/org/apache/catalina/authenticator/SingleSignOn.java --- tomcat7-7.0.68/java/org/apache/catalina/authenticator/SingleSignOn.java 2015-11-06 02:45:03.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/authenticator/SingleSignOn.java 2016-03-10 15:12:09.000000000 +0000 @@ -360,7 +360,16 @@ containerLog.debug(sm.getString("singleSignOn.debug.sessionLogout", ssoId, session)); } - deregister(ssoId); + // First remove the session that we know has expired / been logged + // out since it has already been removed from its Manager and, if + // we don't remove it first, deregister() will log a warning that it + // can't be found + removeSession(ssoId, session); + // If the SSO session was only associated with one web app the call + // above will have removed the SSO session from the cache + if (cache.containsKey(ssoId)) { + deregister(ssoId); + } } } diff -Nru tomcat7-7.0.68/java/org/apache/catalina/connector/CoyoteAdapter.java tomcat7-7.0.69/java/org/apache/catalina/connector/CoyoteAdapter.java --- tomcat7-7.0.68/java/org/apache/catalina/connector/CoyoteAdapter.java 2015-12-18 14:13:18.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/connector/CoyoteAdapter.java 2016-04-07 18:31:21.000000000 +0000 @@ -457,8 +457,7 @@ } } - AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext(); - if (asyncConImpl != null) { + if (request.isAsync()) { async = true; } else if (!comet) { try { diff -Nru tomcat7-7.0.68/java/org/apache/catalina/connector/Response.java tomcat7-7.0.69/java/org/apache/catalina/connector/Response.java --- tomcat7-7.0.68/java/org/apache/catalina/connector/Response.java 2016-01-30 00:54:35.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/connector/Response.java 2016-03-09 15:15:57.000000000 +0000 @@ -20,7 +20,6 @@ import java.io.OutputStream; import java.io.PrintWriter; import java.net.MalformedURLException; -import java.net.URI; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; @@ -1381,7 +1380,7 @@ // Relative redirects require HTTP/1.1 if (getRequest().getCoyoteRequest().getSupportsRelativeRedirects() && getContext().getUseRelativeRedirects()) { - locationUri = URI.create(location).toASCIIString(); + locationUri = location; } else { locationUri = toAbsolute(location); } diff -Nru tomcat7-7.0.68/java/org/apache/catalina/core/ApplicationPart.java tomcat7-7.0.69/java/org/apache/catalina/core/ApplicationPart.java --- tomcat7-7.0.68/java/org/apache/catalina/core/ApplicationPart.java 2014-01-27 14:53:14.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/core/ApplicationPart.java 2016-03-05 18:29:06.000000000 +0000 @@ -32,6 +32,7 @@ import org.apache.tomcat.util.http.fileupload.FileItem; import org.apache.tomcat.util.http.fileupload.ParameterParser; import org.apache.tomcat.util.http.fileupload.disk.DiskFileItem; +import org.apache.tomcat.util.http.parser.HttpParser; /** * Adaptor to allow {@link FileItem} objects generated by the package renamed @@ -150,12 +151,20 @@ ParameterParser paramParser = new ParameterParser(); paramParser.setLowerCaseNames(true); // Parameter parser can handle null input - Map params = - paramParser.parse(cd, ';'); + Map params = paramParser.parse(cd, ';'); if (params.containsKey("filename")) { fileName = params.get("filename"); + // The parser will remove surrounding '"' but will not + // unquote any \x sequences. if (fileName != null) { - fileName = fileName.trim(); + // RFC 6266. This is either a token or a quoted-string + if (fileName.indexOf('\\') > -1) { + // This is a quoted-string + fileName = HttpParser.unquote(fileName.trim()); + } else { + // This is a token + fileName = fileName.trim(); + } } else { // Even if there is no value, the parameter is present, // so we return an empty file name rather than no file diff -Nru tomcat7-7.0.68/java/org/apache/catalina/core/AsyncContextImpl.java tomcat7-7.0.69/java/org/apache/catalina/core/AsyncContextImpl.java --- tomcat7-7.0.68/java/org/apache/catalina/core/AsyncContextImpl.java 2016-01-04 18:23:42.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/core/AsyncContextImpl.java 2016-03-25 20:35:52.000000000 +0000 @@ -223,7 +223,7 @@ @Override public void dispatch(String path) { check(); - dispatch(request.getServletContext(),path); + dispatch(getRequest().getServletContext(), path); } @Override @@ -316,6 +316,8 @@ check(); AsyncListenerWrapper wrapper = new AsyncListenerWrapper(); wrapper.setListener(listener); + wrapper.setServletRequest(servletRequest); + wrapper.setServletResponse(servletResponse); listeners.add(wrapper); } diff -Nru tomcat7-7.0.68/java/org/apache/catalina/core/AsyncListenerWrapper.java tomcat7-7.0.69/java/org/apache/catalina/core/AsyncListenerWrapper.java --- tomcat7-7.0.68/java/org/apache/catalina/core/AsyncListenerWrapper.java 2010-09-22 20:04:32.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/core/AsyncListenerWrapper.java 2016-03-25 20:35:52.000000000 +0000 @@ -20,6 +20,8 @@ import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; /** * TODO SERVLET3 - async @@ -29,25 +31,27 @@ public class AsyncListenerWrapper { private AsyncListener listener = null; - - + private ServletRequest servletRequest = null; + private ServletResponse servletResponse = null; + + public void fireOnStartAsync(AsyncEvent event) throws IOException { - listener.onStartAsync(event); + listener.onStartAsync(customizeEvent(event)); } public void fireOnComplete(AsyncEvent event) throws IOException { - listener.onComplete(event); + listener.onComplete(customizeEvent(event)); } public void fireOnTimeout(AsyncEvent event) throws IOException { - listener.onTimeout(event); + listener.onTimeout(customizeEvent(event)); } public void fireOnError(AsyncEvent event) throws IOException { - listener.onError(event); + listener.onError(customizeEvent(event)); } @@ -61,4 +65,22 @@ } + public void setServletRequest(ServletRequest servletRequest) { + this.servletRequest = servletRequest; + } + + + public void setServletResponse(ServletResponse servletResponse) { + this.servletResponse = servletResponse; + } + + + private AsyncEvent customizeEvent(AsyncEvent event) { + if (servletRequest != null && servletResponse != null) { + return new AsyncEvent(event.getAsyncContext(), servletRequest, servletResponse, + event.getThrowable()); + } else { + return event; + } + } } diff -Nru tomcat7-7.0.68/java/org/apache/catalina/core/mbeans-descriptors.xml tomcat7-7.0.69/java/org/apache/catalina/core/mbeans-descriptors.xml --- tomcat7-7.0.68/java/org/apache/catalina/core/mbeans-descriptors.xml 2015-12-01 20:33:57.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/core/mbeans-descriptors.xml 2016-02-24 09:38:05.000000000 +0000 @@ -191,7 +191,7 @@ + type="org.apache.tomcat.InstanceManager" /> + + + + ) obj).get(); if (this.equals(key) || loadedByThisOrChild(key)) { - potentialLeak = true; + keyLoadedByWebapp = true; } // Check the value Field valueField = @@ -2711,9 +2712,9 @@ valueField.setAccessible(true); Object value = valueField.get(obj); if (this.equals(value) || loadedByThisOrChild(value)) { - potentialLeak = true; + valueLoadedByWebapp = true; } - if (potentialLeak) { + if (keyLoadedByWebapp || valueLoadedByWebapp) { Object[] args = new Object[5]; args[0] = contextName; if (key != null) { @@ -2740,16 +2741,22 @@ "webappClassLoader.checkThreadLocalsForLeaks.unknown"); } } - if (value == null) { + if (valueLoadedByWebapp) { + log.error(sm.getString( + "webappClassLoader.checkThreadLocalsForLeaks", + args)); + } else if (value == null) { if (log.isDebugEnabled()) { log.debug(sm.getString( - "webappClassLoader.checkThreadLocalsForLeaksDebug", + "webappClassLoader.checkThreadLocalsForLeaksNull", args)); } } else { - log.error(sm.getString( - "webappClassLoader.checkThreadLocalsForLeaks", - args)); + if (log.isDebugEnabled()) { + log.debug(sm.getString( + "webappClassLoader.checkThreadLocalsForLeaksNone", + args)); + } } } } @@ -3314,9 +3321,7 @@ entry = new ResourceEntry(); try { entry.codeBase = getURI(jarRealFiles[i]); - String jarFakeUrl = entry.codeBase.toString(); - jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path; - entry.source = new URL(jarFakeUrl); + entry.source = UriUtil.buildJarUrl(entry.codeBase.toString(), path); entry.lastModified = jarRealFiles[i].lastModified(); } catch (MalformedURLException e) { return null; diff -Nru tomcat7-7.0.68/java/org/apache/catalina/realm/JNDIRealm.java tomcat7-7.0.69/java/org/apache/catalina/realm/JNDIRealm.java --- tomcat7-7.0.68/java/org/apache/catalina/realm/JNDIRealm.java 2015-11-12 09:41:45.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/realm/JNDIRealm.java 2016-03-07 16:29:52.000000000 +0000 @@ -531,7 +531,7 @@ */ public String getConnectionName() { - return (this.connectionName); + return this.connectionName; } @@ -553,7 +553,7 @@ */ public String getConnectionPassword() { - return (this.connectionPassword); + return this.connectionPassword; } @@ -575,7 +575,7 @@ */ public String getConnectionURL() { - return (this.connectionURL); + return this.connectionURL; } @@ -597,7 +597,7 @@ */ public String getContextFactory() { - return (this.contextFactory); + return this.contextFactory; } @@ -689,7 +689,7 @@ */ public String getUserBase() { - return (this.userBase); + return this.userBase; } @@ -711,7 +711,7 @@ */ public String getUserSearch() { - return (this.userSearch); + return this.userSearch; } @@ -747,7 +747,7 @@ */ public boolean getUserSubtree() { - return (this.userSubtree); + return this.userSubtree; } @@ -790,7 +790,7 @@ */ public String getRoleBase() { - return (this.roleBase); + return this.roleBase; } @@ -816,7 +816,7 @@ */ public String getRoleName() { - return (this.roleName); + return this.roleName; } @@ -838,7 +838,7 @@ */ public String getRoleSearch() { - return (this.roleSearch); + return this.roleSearch; } @@ -874,7 +874,7 @@ */ public boolean getRoleSubtree() { - return (this.roleSubtree); + return this.roleSubtree; } @@ -895,7 +895,7 @@ */ public boolean getRoleNested() { - return (this.roleNested); + return this.roleNested; } @@ -917,7 +917,7 @@ */ public String getUserPassword() { - return (this.userPassword); + return this.userPassword; } @@ -947,7 +947,7 @@ */ public String getUserPattern() { - return (this.userPattern); + return this.userPattern; } @@ -1345,7 +1345,7 @@ release(context); // Return the authenticated Principal (if any) - return (principal); + return principal; } catch (NamingException e) { @@ -1359,7 +1359,7 @@ // Return "not authenticated" for this request if (containerLog.isDebugEnabled()) containerLog.debug("Returning null principal."); - return (null); + return null; } @@ -1392,7 +1392,7 @@ || credentials == null || credentials.equals("")) { if (containerLog.isDebugEnabled()) containerLog.debug("username null or empty: returning null principal."); - return (null); + return null; } if (userPatternArray != null) { @@ -1433,11 +1433,11 @@ // Retrieve user information User user = getUser(context, username, credentials); if (user == null) - return (null); + return null; // Check the user's credentials if (!checkCredentials(context, user, credentials)) - return (null); + return null; // Search for additional roles List roles = getRoles(context, user); @@ -1450,7 +1450,7 @@ } // Create and return a suitable Principal for this user - return (new GenericPrincipal(username, credentials, roles)); + return new GenericPrincipal(username, credentials, roles); } } @@ -1593,10 +1593,10 @@ try { attrs = context.getAttributes(dn, attrIds); } catch (NameNotFoundException e) { - return (null); + return null; } if (attrs == null) - return (null); + return null; // Retrieve value of userPassword String password = null; @@ -1642,7 +1642,7 @@ User user = null; if (username == null || userPatternFormatArray[curUserPattern] == null) - return (null); + return null; // Form the dn from the user pattern String dn = userPatternFormatArray[curUserPattern].format(new String[] { username }); @@ -1650,7 +1650,7 @@ try { user = getUserByPattern(context, username, attrIds, dn); } catch (NameNotFoundException e) { - return (null); + return null; } catch (NamingException e) { // If the getUserByPattern() call fails, try it again with the // credentials of the user that we're searching for @@ -1683,7 +1683,7 @@ throws NamingException { if (username == null || userSearchFormat == null) - return (null); + return null; // Form the search filter String filter = userSearchFormat.format(new String[] { username }); @@ -1709,60 +1709,65 @@ NamingEnumeration results = context.search(userBase, filter, constraints); - - // Fail if no entries found try { - if (results == null || !results.hasMore()) { - return (null); + // Fail if no entries found + try { + if (results == null || !results.hasMore()) { + return null; + } + } catch (PartialResultException ex) { + if (!adCompat) + throw ex; + else + return null; } - } catch (PartialResultException ex) { - if (!adCompat) - throw ex; - else - return (null); - } - - // Get result for the first entry found - SearchResult result = results.next(); - - // Check no further entries were found - try { - if (results.hasMore()) { - if(containerLog.isInfoEnabled()) - containerLog.info("username " + username + " has multiple entries"); - return (null); + + // Get result for the first entry found + SearchResult result = results.next(); + + // Check no further entries were found + try { + if (results.hasMore()) { + if(containerLog.isInfoEnabled()) + containerLog.info("username " + username + " has multiple entries"); + return null; + } + } catch (PartialResultException ex) { + if (!adCompat) + throw ex; + } + + String dn = getDistinguishedName(context, userBase, result); + + if (containerLog.isTraceEnabled()) + containerLog.trace(" entry found for " + username + " with dn " + dn); + + // Get the entry's attributes + Attributes attrs = result.getAttributes(); + if (attrs == null) + return null; + + // Retrieve value of userPassword + String password = null; + if (userPassword != null) + password = getAttributeValue(userPassword, attrs); + + String userRoleAttrValue = null; + if (userRoleAttribute != null) { + userRoleAttrValue = getAttributeValue(userRoleAttribute, attrs); + } + + // Retrieve values of userRoleName attribute + ArrayList roles = null; + if (userRoleName != null) + roles = addAttributeValues(userRoleName, attrs, roles); + + return new User(username, dn, password, roles, userRoleAttrValue); + } finally { + if (results != null) { + results.close(); } - } catch (PartialResultException ex) { - if (!adCompat) - throw ex; - } - - String dn = getDistinguishedName(context, userBase, result); - - if (containerLog.isTraceEnabled()) - containerLog.trace(" entry found for " + username + " with dn " + dn); - - // Get the entry's attributes - Attributes attrs = result.getAttributes(); - if (attrs == null) - return null; - - // Retrieve value of userPassword - String password = null; - if (userPassword != null) - password = getAttributeValue(userPassword, attrs); - - String userRoleAttrValue = null; - if (userRoleAttribute != null) { - userRoleAttrValue = getAttributeValue(userRoleAttribute, attrs); } - - // Retrieve values of userRoleName attribute - ArrayList roles = null; - if (userRoleName != null) - roles = addAttributeValues(userRoleName, attrs, roles); - - return new User(username, dn, password, roles, userRoleAttrValue); } @@ -1803,7 +1808,7 @@ user.getUserName())); } } - return (validated); + return validated; } @@ -1880,7 +1885,7 @@ userCredentialsRemove(context); - return (validated); + return validated; } /** @@ -1939,14 +1944,14 @@ throws NamingException { if (user == null) - return (null); + return null; String dn = user.getDN(); String username = user.getUserName(); String userRoleId = user.getUserRoleId(); if (dn == null || username == null) - return (null); + return null; if (containerLog.isTraceEnabled()) containerLog.trace(" getRoles(" + dn + ")"); @@ -1968,7 +1973,7 @@ // Are we configured to do role searches? if ((roleFormat == null) || (roleName == null)) - return (list); + return list; // Set up parameters for an appropriate search String filter = roleFormat.format(new String[] { doRFC2254Encoding(dn), username, userRoleId }); @@ -2007,7 +2012,7 @@ } if (results == null) - return (list); // Should never happen, but just in case ... + return list; // Should never happen, but just in case ... HashMap groupMap = new HashMap(); try { @@ -2025,6 +2030,8 @@ } catch (PartialResultException ex) { if (!adCompat) throw ex; + } finally { + results.close(); } if (containerLog.isTraceEnabled()) { @@ -2076,6 +2083,8 @@ } catch (PartialResultException ex) { if (!adCompat) throw ex; + } finally { + results.close(); } } @@ -2107,10 +2116,10 @@ Attribute attr = attrs.get(attrId); if (attr == null) - return (null); + return null; Object value = attr.get(); if (value == null) - return (null); + return null; String valueString = null; if (value instanceof byte[]) valueString = new String((byte[]) value); @@ -2143,7 +2152,7 @@ values = new ArrayList(); Attribute attr = attrs.get(attrId); if (attr == null) - return (values); + return values; NamingEnumeration e = attr.getAll(); try { while(e.hasMore()) { @@ -2153,6 +2162,8 @@ } catch (PartialResultException ex) { if (!adCompat) throw ex; + } finally { + e.close(); } return values; } @@ -2196,7 +2207,7 @@ @Override protected String getName() { - return (name); + return name; } @@ -2290,7 +2301,7 @@ release(context); // Return the authenticated Principal (if any) - return (principal); + return principal; } catch (NamingException e) { @@ -2302,7 +2313,7 @@ close(context); // Return "not authenticated" for this request - return (null); + return null; } @@ -2379,7 +2390,7 @@ // Do nothing if there is a directory server connection already open if (context != null) - return (context); + return context; try { @@ -2404,7 +2415,7 @@ } - return (context); + return context; } diff -Nru tomcat7-7.0.68/java/org/apache/catalina/security/SecurityClassLoad.java tomcat7-7.0.69/java/org/apache/catalina/security/SecurityClassLoad.java --- tomcat7-7.0.68/java/org/apache/catalina/security/SecurityClassLoad.java 2015-09-08 12:49:24.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/security/SecurityClassLoad.java 2016-04-07 20:15:09.000000000 +0000 @@ -105,6 +105,9 @@ final String basePackage = "org.apache.catalina.loader."; loader.loadClass (basePackage + + "ResourceEntry"); + loader.loadClass + (basePackage + "WebappClassLoaderBase$PrivilegedFindResourceByName"); } @@ -146,6 +149,7 @@ final String basePackage = "org.apache.catalina.util."; loader.loadClass(basePackage + "Enumerator"); loader.loadClass(basePackage + "ParameterMap"); + loader.loadClass(basePackage + "RequestUtil"); } @@ -264,6 +268,7 @@ loader.loadClass(basePackage + "util.buf.StringCache"); loader.loadClass(basePackage + "util.buf.StringCache$ByteEntry"); loader.loadClass(basePackage + "util.buf.StringCache$CharEntry"); + loader.loadClass(basePackage + "util.buf.UriUtil"); // http loader.loadClass(basePackage + "util.http.HttpMessages"); // Make sure system property is read at this point diff -Nru tomcat7-7.0.68/java/org/apache/catalina/session/Constants.java tomcat7-7.0.69/java/org/apache/catalina/session/Constants.java --- tomcat7-7.0.68/java/org/apache/catalina/session/Constants.java 2016-01-26 15:14:56.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/session/Constants.java 2016-02-23 15:13:01.000000000 +0000 @@ -21,6 +21,7 @@ import java.util.Set; import org.apache.catalina.Globals; +import org.apache.catalina.valves.CrawlerSessionManagerValve; /** * Manifest constants for the org.apache.catalina.session @@ -42,6 +43,7 @@ Set names = new HashSet(); names.add(Globals.SUBJECT_ATTR); names.add(Globals.GSS_CREDENTIAL_ATTR); + names.add(CrawlerSessionManagerValve.class.getName()); excludedAttributeNames = Collections.unmodifiableSet(names); } } diff -Nru tomcat7-7.0.68/java/org/apache/catalina/session/LocalStrings.properties tomcat7-7.0.69/java/org/apache/catalina/session/LocalStrings.properties --- tomcat7-7.0.68/java/org/apache/catalina/session/LocalStrings.properties 2016-01-26 23:30:59.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/session/LocalStrings.properties 2016-02-27 22:17:05.000000000 +0000 @@ -39,6 +39,7 @@ managerBase.sessionAttributeValueClassNameFilter=Skipped session attribute named [{0}] because the value type [{1}] did not match the filter [{2}] managerBase.sessionTimeout=Invalid session timeout setting {0} managerBase.setContextNotNew=It is illegal to call setContext() to change the Context associated with a Manager if the Manager is not in the NEW state +managerBase.setMaxInactiveIntervalUnused=Manager.setMaxInactiveInterval() is deprecated and calls to this method are ignored. Session timeouts should be configured in web.xml or via Context.setSessionTimeout(int timeoutInMinutes) serverSession.value.iae=null value standardManager.expireException=processsExpire: Exception during session expiration standardManager.loading=Loading persisted sessions from {0} diff -Nru tomcat7-7.0.68/java/org/apache/catalina/session/ManagerBase.java tomcat7-7.0.69/java/org/apache/catalina/session/ManagerBase.java --- tomcat7-7.0.68/java/org/apache/catalina/session/ManagerBase.java 2016-01-27 19:22:47.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/session/ManagerBase.java 2016-02-27 22:19:48.000000000 +0000 @@ -423,7 +423,8 @@ public int getMaxInactiveInterval() { Container container = getContainer(); if (container instanceof Context) { - return ((Context) container).getSessionTimeout(); + // This method returns seconds, the Context uses minutes + return ((Context) container).getSessionTimeout() * 60; } return -1; } @@ -432,7 +433,7 @@ @Deprecated @Override public void setMaxInactiveInterval(int interval) { - // NO-OP + log.warn(sm.getString("managerBase.setMaxInactiveIntervalUnused")); } diff -Nru tomcat7-7.0.68/java/org/apache/catalina/session/PersistentManagerBase.java tomcat7-7.0.69/java/org/apache/catalina/session/PersistentManagerBase.java --- tomcat7-7.0.68/java/org/apache/catalina/session/PersistentManagerBase.java 2016-01-26 15:22:12.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/session/PersistentManagerBase.java 2016-04-06 20:27:42.000000000 +0000 @@ -172,16 +172,18 @@ /** - * Minimum time a session must be idle before it is swapped to disk. - * This overrides maxActiveSessions, to prevent thrashing if there are lots - * of active sessions. Setting to {@code -1} means it's ignored. + * The minimum time in seconds a session must be idle before it is eligible + * to be swapped to disk to keep the active session count below + * maxActiveSessions. Setting to {@code -1} means sessions will not be + * swapped out to keep the active session count down. */ protected int minIdleSwap = -1; + /** - * The maximum time a session may be idle before it should be swapped - * to file just on general principle. Setting this to {@code -1} means sessions - * should not be forced out. + * The maximum time in seconds a session may be idle before it is eligible + * to be swapped to disk due to inactivity. Setting this to {@code -1} means + * sessions should not be swapped out just because of inactivity. */ protected int maxIdleSwap = -1; @@ -244,19 +246,20 @@ /** - * @return The time in seconds after which a session should be swapped out of - * memory to disk. + * @return The maximum time in seconds a session may be idle before it is + * eligible to be swapped to disk due to inactivity. A value of {@code -1} + * means sessions should not be swapped out just because of inactivity. */ public int getMaxIdleSwap() { - return maxIdleSwap; - } /** - * Sets the time in seconds after which a session should be swapped out of - * memory to disk. + * Sets the maximum time in seconds a session may be idle before it is + * eligible to be swapped to disk due to inactivity. Setting this to + * {@code -1} means sessions should not be swapped out just because of + * inactivity. * * @param max time in seconds to wait for possible swap out */ @@ -269,26 +272,25 @@ support.firePropertyChange("maxIdleSwap", Integer.valueOf(oldMaxIdleSwap), Integer.valueOf(this.maxIdleSwap)); - } /** - * @return The minimum time in seconds that a session must be idle before - * it can be swapped out of memory, or {@code -1} if it can be swapped out - * at any time. + * @return The minimum time in seconds a session must be idle before it is + * eligible to be swapped to disk to keep the active session count below + * maxActiveSessions. A value of {@code -1} means sessions will not be + * swapped out to keep the active session count down. */ public int getMinIdleSwap() { - return minIdleSwap; - } /** - * Sets the minimum time in seconds that a session must be idle before - * it can be swapped out of memory due to maxActiveSession. Set it to {@code -1} - * if it can be swapped out at any time. + * Sets the minimum time in seconds a session must be idle before it is + * eligible to be swapped to disk to keep the active session count below + * maxActiveSessions. Setting to {@code -1} means sessions will not be + * swapped out to keep the active session count down. * * @param min time in seconds before a possible swap out */ @@ -963,7 +965,9 @@ Session sessions[] = findSessions(); // FIXME: Smarter algorithm (LRU) - if (getMaxActiveSessions() >= sessions.length) + int limit = (int) (getMaxActiveSessions() * 0.9); + + if (limit >= sessions.length) return; if(log.isDebugEnabled()) @@ -971,7 +975,7 @@ ("persistentManager.tooManyActive", Integer.valueOf(sessions.length))); - int toswap = sessions.length - getMaxActiveSessions(); + int toswap = sessions.length - limit; long timeNow = System.currentTimeMillis(); for (int i = 0; i < sessions.length && toswap > 0; i++) { diff -Nru tomcat7-7.0.68/java/org/apache/catalina/startup/ClassLoaderFactory.java tomcat7-7.0.69/java/org/apache/catalina/startup/ClassLoaderFactory.java --- tomcat7-7.0.68/java/org/apache/catalina/startup/ClassLoaderFactory.java 2015-11-13 10:46:05.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/startup/ClassLoaderFactory.java 2016-03-01 23:04:09.000000000 +0000 @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.security.AccessController; @@ -164,7 +165,7 @@ if (repositories != null) { for (Repository repository : repositories) { if (repository.getType() == RepositoryType.URL) { - URL url = new URL(repository.getLocation()); + URL url = buildClassLoaderUrl(repository.getLocation()); if (log.isDebugEnabled()) log.debug(" Including URL " + url); set.add(url); @@ -174,7 +175,7 @@ if (!validateFile(directory, RepositoryType.DIR)) { continue; } - URL url = directory.toURI().toURL(); + URL url = buildClassLoaderUrl(directory); if (log.isDebugEnabled()) log.debug(" Including directory " + url); set.add(url); @@ -184,7 +185,7 @@ if (!validateFile(file, RepositoryType.JAR)) { continue; } - URL url = file.toURI().toURL(); + URL url = buildClassLoaderUrl(file); if (log.isDebugEnabled()) log.debug(" Including jar file " + url); set.add(url); @@ -213,7 +214,7 @@ if (log.isDebugEnabled()) log.debug(" Including glob jar file " + file.getAbsolutePath()); - URL url = file.toURI().toURL(); + URL url = buildClassLoaderUrl(file); set.add(url); } } @@ -277,6 +278,30 @@ return true; } + + /* + * These two methods would ideally be in the utility class + * org.apache.tomcat.util.buf.UriUtil but that class is not visible until + * after the class loaders have been constructed. + */ + private static URL buildClassLoaderUrl(String urlString) throws MalformedURLException { + // URLs passed to class loaders may point to directories that contain + // JARs. If these URLs are used to construct URLs for resources in a JAR + // the URL will be used as is. It is therefore necessary to ensure that + // the sequence "!/" is not present in a class loader URL. + String result = urlString.replaceAll("!/", "%21/"); + return new URL(result); + } + + + private static URL buildClassLoaderUrl(File file) throws MalformedURLException { + // Could be a directory or a file + String fileUrlString = file.toURI().toString(); + fileUrlString = fileUrlString.replaceAll("!/", "%21/"); + return new URL(fileUrlString); + } + + public static enum RepositoryType { DIR, GLOB, diff -Nru tomcat7-7.0.68/java/org/apache/catalina/startup/ContextConfig.java tomcat7-7.0.69/java/org/apache/catalina/startup/ContextConfig.java --- tomcat7-7.0.68/java/org/apache/catalina/startup/ContextConfig.java 2016-01-18 03:30:42.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/startup/ContextConfig.java 2016-04-04 08:24:24.000000000 +0000 @@ -92,6 +92,7 @@ import org.apache.tomcat.util.bcel.classfile.ElementValue; import org.apache.tomcat.util.bcel.classfile.ElementValuePair; import org.apache.tomcat.util.bcel.classfile.JavaClass; +import org.apache.tomcat.util.buf.UriUtil; import org.apache.tomcat.util.descriptor.DigesterFactory; import org.apache.tomcat.util.descriptor.XmlErrorHandler; import org.apache.tomcat.util.digester.Digester; @@ -730,8 +731,8 @@ } if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) { + URL war = UriUtil.buildJarUrl(new File(docBase)); if (unpackWARs) { - URL war = new URL("jar:" + (new File(docBase)).toURI().toURL() + "!/"); docBase = ExpandWar.expand(host, war, pathName); file = new File(docBase); docBase = file.getCanonicalPath(); @@ -739,8 +740,6 @@ ((StandardContext) context).setOriginalDocBase(origDocBase); } } else { - URL war = - new URL("jar:" + (new File (docBase)).toURI().toURL() + "!/"); ExpandWar.validate(host, war, pathName); } } else { @@ -748,8 +747,7 @@ if (!docDir.exists()) { File warFile = new File(docBase + ".war"); if (warFile.exists()) { - URL war = - new URL("jar:" + warFile.toURI().toURL() + "!/"); + URL war = UriUtil.buildJarUrl(warFile); if (unpackWARs) { docBase = ExpandWar.expand(host, war, pathName); file = new File(docBase); @@ -2401,7 +2399,7 @@ if ("value".equals(name) || "urlPatterns".equals(name)) { if (urlPatternsSet) { throw new IllegalArgumentException(sm.getString( - "contextConfig.urlPatternValue", className)); + "contextConfig.urlPatternValue", "WebServlet", className)); } urlPatternsSet = true; urlPatterns = processAnnotationsStringArray(evp.getValue()); @@ -2513,7 +2511,7 @@ if ("value".equals(name) || "urlPatterns".equals(name)) { if (urlPatternsSet) { throw new IllegalArgumentException(sm.getString( - "contextConfig.urlPatternValue", className)); + "contextConfig.urlPatternValue", "WebFilter", className)); } urlPatterns = processAnnotationsStringArray(evp.getValue()); urlPatternsSet = urlPatterns.length > 0; diff -Nru tomcat7-7.0.68/java/org/apache/catalina/startup/HostConfig.java tomcat7-7.0.69/java/org/apache/catalina/startup/HostConfig.java --- tomcat7-7.0.68/java/org/apache/catalina/startup/HostConfig.java 2016-01-21 11:48:31.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/startup/HostConfig.java 2016-03-04 15:07:16.000000000 +0000 @@ -23,7 +23,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -62,6 +61,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.ExceptionUtils; +import org.apache.tomcat.util.buf.UriUtil; import org.apache.tomcat.util.digester.Digester; import org.apache.tomcat.util.modeler.Registry; import org.apache.tomcat.util.res.StringManager; @@ -331,6 +331,8 @@ // Process the event that has occurred if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) { check(); + } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) { + beforeStart(); } else if (event.getType().equals(Lifecycle.START_EVENT)) { start(); } else if (event.getType().equals(Lifecycle.STOP_EVENT)) { @@ -958,9 +960,8 @@ if (context == null) { context = new FailedContext(); } - context.setConfigFile(new URL("jar:" + - war.toURI().toString() + "!/" + - Constants.ApplicationContextXml)); + context.setConfigFile( + UriUtil.buildJarUrl(war, Constants.ApplicationContextXml)); } } } else if (!deployXML && xmlInWar) { @@ -1671,6 +1672,18 @@ } + public void beforeStart() { + if (host.getCreateDirs()) { + File[] dirs = new File[] {appBase(),configBase()}; + for (int i=0; i + + diff -Nru tomcat7-7.0.68/java/org/apache/catalina/tribes/membership/McastService.java tomcat7-7.0.69/java/org/apache/catalina/tribes/membership/McastService.java --- tomcat7-7.0.68/java/org/apache/catalina/tribes/membership/McastService.java 2015-11-02 13:09:03.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/tribes/membership/McastService.java 2016-04-05 07:40:25.000000000 +0000 @@ -171,6 +171,7 @@ localMember.setUniqueId(UUIDGenerator.randomUUID(true)); localMember.setPayload(getPayload()); localMember.setDomain(getDomain()); + localMember.setLocal(true); } localMember.setSecurePort(securePort); localMember.setUdpPort(udpPort); @@ -391,6 +392,7 @@ if ( localMember == null ) { localMember = new MemberImpl(host, port, 100); localMember.setUniqueId(UUIDGenerator.randomUUID(true)); + localMember.setLocal(true); } else { localMember.setHostname(host); localMember.setPort(port); diff -Nru tomcat7-7.0.68/java/org/apache/catalina/tribes/membership/MemberImpl.java tomcat7-7.0.69/java/org/apache/catalina/tribes/membership/MemberImpl.java --- tomcat7-7.0.68/java/org/apache/catalina/tribes/membership/MemberImpl.java 2014-01-27 14:09:29.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/tribes/membership/MemberImpl.java 2016-04-05 07:40:25.000000000 +0000 @@ -123,6 +123,11 @@ protected byte[] domain = new byte[0]; /** + * The flag indicating that this member is a local member. + */ + protected volatile boolean local = false; + + /** * Empty constructor for serialization */ public MemberImpl() { @@ -646,6 +651,14 @@ this.dataPkg = null; } + public boolean isLocal() { + return local; + } + + public void setLocal(boolean local) { + this.local = local; + } + @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int length = in.readInt(); diff -Nru tomcat7-7.0.68/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java tomcat7-7.0.69/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java --- tomcat7-7.0.68/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java 2015-12-07 04:38:41.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java 2016-03-22 05:59:02.000000000 +0000 @@ -519,7 +519,6 @@ } /** - * TODO implement state transfer * @param msg Serializable * @return Serializable - null if no reply should be sent */ @@ -593,6 +592,10 @@ mapMemberAdded(mapmsg.getPrimary()); } else if (mapmsg.getMsgType() == MapMessage.MSG_INIT) { memberAlive(mapmsg.getPrimary()); + } else { + // other messages are ignored. + if (log.isInfoEnabled()) + log.info("Message[" + mapmsg.getTypeDesc() + "] is ignored."); } } catch (IOException x ) { log.error("Unable to deserialize MapMessage.",x); @@ -940,7 +943,7 @@ msg = new MapMessage(getMapContextName(), MapMessage.MSG_RETRIEVE_BACKUP, false, (Serializable) key, null, null, null,null); Response[] resp = getRpcChannel().send(entry.getBackupNodes(),msg, RpcChannel.FIRST_REPLY, Channel.SEND_OPTIONS_DEFAULT, getRpcTimeout()); - if (resp == null || resp.length == 0) { + if (resp == null || resp.length == 0 || resp[0].getMessage() == null) { //no responses log.warn("Unable to retrieve remote object for key:" + key); return null; @@ -949,6 +952,13 @@ msg.deserialize(getExternalLoaders()); backup = entry.getBackupNodes(); if ( msg.getValue()!=null ) entry.setValue((V) msg.getValue()); + + // notify member + msg = new MapMessage(getMapContextName(), MapMessage.MSG_NOTIFY_MAPMEMBER,false, + (Serializable)entry.getKey(), null, null, channel.getLocalMember(false), backup); + if ( backup != null && backup.length > 0) { + getChannel().send(backup, msg, getChannelSendOptions()); + } //invalidate the previous primary msg = new MapMessage(getMapContextName(),MapMessage.MSG_PROXY,false,(Serializable)key,null,null,channel.getLocalMember(false),backup); diff -Nru tomcat7-7.0.68/java/org/apache/coyote/AsyncStateMachine.java tomcat7-7.0.69/java/org/apache/coyote/AsyncStateMachine.java --- tomcat7-7.0.68/java/org/apache/coyote/AsyncStateMachine.java 2015-08-17 07:33:34.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/coyote/AsyncStateMachine.java 2016-04-07 19:35:07.000000000 +0000 @@ -56,13 +56,13 @@ * |----------------->--------------| * | \|/ * | |----------<---------------ERROR - * | | complete() /|\ | \ - * | | | | \---------------| - * | | | | |dispatch() - * | | | |postProcess() \|/ - * | | error()| | | - * | | | | |--|timeout() | - * | | postProcess() | \|/ | \|/ | auto + * | | complete() | \ + * | | | \---------------| + * | | | |dispatch() + * | | |postProcess() \|/ + * | | | | + * | | | |--|timeout() | + * | | postProcess() \|/ | \|/ | auto * | | |--------------->DISPATCHED<---------- | --------------COMPLETING<-----| * | | | /|\ | | | /|\ | * | | | |--->-------| | | |--| | @@ -108,10 +108,10 @@ STARTED (true, true, false, false, false), MUST_COMPLETE(true, true, true, false, false), COMPLETING (true, false, true, false, false), - TIMING_OUT (true, false, false, false, false), + TIMING_OUT (true, true, false, false, false), MUST_DISPATCH(true, true, false, true, true), DISPATCHING (true, false, false, true, false), - ERROR (true, false, false, false, false); + ERROR (true, true, false, false, false); private final boolean isAsync; private final boolean isStarted; diff -Nru tomcat7-7.0.68/java/org/apache/coyote/http11/Http11NioProcessor.java tomcat7-7.0.69/java/org/apache/coyote/http11/Http11NioProcessor.java --- tomcat7-7.0.68/java/org/apache/coyote/http11/Http11NioProcessor.java 2015-03-13 11:11:26.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/coyote/http11/Http11NioProcessor.java 2016-02-19 22:31:42.000000000 +0000 @@ -279,17 +279,23 @@ SelectionKey key = socketWrapper.getSocket().getIOChannel().keyFor( socketWrapper.getSocket().getPoller().getSelector()); //do the first write on this thread, might as well - if (socketWrapper.getSocket().getPoller().processSendfile(key, - (KeyAttachment) socketWrapper, true)) { + switch (socketWrapper.getSocket().getPoller().processSendfile( + key, (KeyAttachment) socketWrapper, true)) { + case DONE: + // If sendfile is complete, no need to break keep-alive loop + sendfileData = null; + return false; + case PENDING: sendfileInProgress = true; - } else { + return true; + case ERROR: // Write failed if (log.isDebugEnabled()) { log.debug(sm.getString("http11processor.sendfile.error")); } setErrorState(ErrorState.CLOSE_NOW, null); + return true; } - return true; } return false; } diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/jni/SSL.java tomcat7-7.0.69/java/org/apache/tomcat/jni/SSL.java --- tomcat7-7.0.68/java/org/apache/tomcat/jni/SSL.java 2015-05-22 18:40:44.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/jni/SSL.java 2016-02-18 07:31:26.000000000 +0000 @@ -228,10 +228,10 @@ * Add certificate chain number to that flag (0 ... verify depth) */ public static final int SSL_INFO_CLIENT_CERT_CHAIN = 0x0400; - /* Return OpenSSL version number */ + /* Return OpenSSL version number (compile time version, if version < 1.1.0) */ public static native int version(); - /* Return OpenSSL version string */ + /* Return OpenSSL version string (run time version) */ public static native String versionString(); /** diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/buf/UriUtil.java tomcat7-7.0.69/java/org/apache/tomcat/util/buf/UriUtil.java --- tomcat7-7.0.68/java/org/apache/tomcat/util/buf/UriUtil.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/buf/UriUtil.java 2016-03-01 23:04:09.000000000 +0000 @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.buf; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.regex.Pattern; + +/** + * Utility class for working with URIs and URLs. + */ +public final class UriUtil { + + private static Pattern PATTERN_EXCLAMATION_MARK = Pattern.compile("!/"); + private static Pattern PATTERN_CARET = Pattern.compile("\\^/"); + private static Pattern PATTERN_ASTERISK = Pattern.compile("\\*/"); + + private UriUtil() { + // Utility class. Hide default constructor + } + + + /** + * Determine if the character is allowed in the scheme of a URI. + * See RFC 2396, Section 3.1 + * + * @param c The character to test + * + * @return {@code true} if a the character is allowed, otherwise {code + * @false} + */ + private static boolean isSchemeChar(char c) { + return Character.isLetterOrDigit(c) || c == '+' || c == '-' || c == '.'; + } + + + /** + * Determine if a URI string has a scheme component. + * + * @param uri The URI to test + * + * @return {@code true} if a scheme is present, otherwise {code @false} + */ + public static boolean hasScheme(CharSequence uri) { + int len = uri.length(); + for(int i=0; i < len ; i++) { + char c = uri.charAt(i); + if(c == ':') { + return i > 0; + } else if(!UriUtil.isSchemeChar(c)) { + return false; + } + } + return false; + } + + + public static URL buildJarUrl(File jarFile) throws MalformedURLException { + return buildJarUrl(jarFile, null); + } + + + public static URL buildJarUrl(File jarFile, String entryPath) throws MalformedURLException { + return buildJarUrl(jarFile.toURI().toString(), entryPath); + } + + + public static URL buildJarUrl(String fileUrlString) throws MalformedURLException { + return buildJarUrl(fileUrlString, null); + } + + + public static URL buildJarUrl(String fileUrlString, String entryPath) throws MalformedURLException { + String safeString = makeSafeForJarUrl(fileUrlString); + StringBuilder sb = new StringBuilder(); + sb.append("jar:"); + sb.append(safeString); + sb.append("!/"); + if (entryPath != null) { + sb.append(makeSafeForJarUrl(entryPath)); + } + return new URL(sb.toString()); + } + + + public static URL buildJarSafeUrl(File file) throws MalformedURLException { + String safe = makeSafeForJarUrl(file.toURI().toString()); + return new URL(safe); + } + + + /* + * When testing on markt's desktop each iteration was taking ~1420ns when + * using String.replaceAll(). + * + * Switching the implementation to use pre-compiled patterns and + * Pattern.matcher(input).replaceAll(replacement) reduced this by ~10%. + * + * Note: Given the very small absolute time of a single iteration, even for + * a web application with 1000 JARs this is only going to add ~3ms. + * It is therefore unlikely that further optimisation will be + * necessary. + */ + /* + * Pulled out into a separate method in case we need to handle other unusual + * sequences in the future. + */ + private static String makeSafeForJarUrl(String input) { + // Since "!/" has a special meaning in a JAR URL, make sure that the + // sequence is properly escaped if present. + String tmp = PATTERN_EXCLAMATION_MARK.matcher(input).replaceAll("%21/"); + // Tomcat's custom jar:war: URL handling treats */ and ^/ as special + tmp = PATTERN_CARET.matcher(tmp).replaceAll("%5e/"); + return PATTERN_ASTERISK.matcher(tmp).replaceAll("%2a/"); + } +} diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/file/ConfigFileLoader.java tomcat7-7.0.69/java/org/apache/tomcat/util/file/ConfigFileLoader.java --- tomcat7-7.0.68/java/org/apache/tomcat/util/file/ConfigFileLoader.java 2015-11-11 09:55:51.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/file/ConfigFileLoader.java 2016-03-22 20:38:47.000000000 +0000 @@ -23,6 +23,8 @@ 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,12 +32,21 @@ */ public class ConfigFileLoader { + private static final StringManager sm = StringManager.getManager(ConfigFileLoader.class + .getPackage().getName()); + private static final File CATALINA_BASE_FILE; private static final URI CATALINA_BASE_URI; static { - CATALINA_BASE_FILE = new File(System.getProperty("catalina.base")); - CATALINA_BASE_URI = CATALINA_BASE_FILE.toURI(); + String catalinaBase = System.getProperty("catalina.base"); + if (catalinaBase != null) { + CATALINA_BASE_FILE = new File(catalinaBase); + CATALINA_BASE_URI = CATALINA_BASE_FILE.toURI(); + } else { + CATALINA_BASE_FILE = null; + CATALINA_BASE_URI = null; + } } private ConfigFileLoader() { @@ -77,9 +88,18 @@ } // Third and final guess, a URI - URI uri = CATALINA_BASE_URI.resolve(location); - return uri.toURL().openStream(); - } - + URI uri; + if (CATALINA_BASE_URI != null) { + uri = CATALINA_BASE_URI.resolve(location); + } else { + uri = URI.create(location); + } -} \ No newline at end of file + // Obtain the input stream we need + try { + return uri.toURL().openStream(); + } catch (IllegalArgumentException e) { + throw new IOException(sm.getString("configFileLoader.cannotObtainURL", location), e); + } + } +} diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/file/LocalStrings.properties tomcat7-7.0.69/java/org/apache/tomcat/util/file/LocalStrings.properties --- tomcat7-7.0.68/java/org/apache/tomcat/util/file/LocalStrings.properties 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/file/LocalStrings.properties 2016-03-22 20:38:47.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. + +configFileLoader.cannotObtainURL=Cannot obtain URL for the relative path [{0}]. Check that catalina.base is set. \ No newline at end of file diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/http/parser/HttpParser.java tomcat7-7.0.69/java/org/apache/tomcat/util/http/parser/HttpParser.java --- tomcat7-7.0.68/java/org/apache/tomcat/util/http/parser/HttpParser.java 2015-12-28 01:12:43.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/http/parser/HttpParser.java 2016-03-05 18:29:06.000000000 +0000 @@ -229,12 +229,24 @@ } public static String unquote(String input) { - if (input == null || input.length() < 2 || input.charAt(0) != '"') { + if (input == null || input.length() < 2) { return input; } + int start; + int end; + + // Skip surrounding quotes if there are any + if (input.charAt(0) == '"') { + start = 1; + end = input.length() - 1; + } else { + start = 0; + end = input.length(); + } + StringBuilder result = new StringBuilder(); - for (int i = 1 ; i < (input.length() - 1); i++) { + for (int i = start ; i < end; i++) { char c = input.charAt(i); if (input.charAt(i) == '\\') { i++; diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/http/ServerCookie.java tomcat7-7.0.69/java/org/apache/tomcat/util/http/ServerCookie.java --- tomcat7-7.0.68/java/org/apache/tomcat/util/http/ServerCookie.java 2014-01-27 12:48:36.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/http/ServerCookie.java 2016-03-23 08:01:49.000000000 +0000 @@ -86,7 +86,6 @@ } public void recycle() { - path.recycle(); name.recycle(); value.recycle(); comment.recycle(); diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/net/AprEndpoint.java tomcat7-7.0.69/java/org/apache/tomcat/util/net/AprEndpoint.java --- tomcat7-7.0.68/java/org/apache/tomcat/util/net/AprEndpoint.java 2015-10-27 13:36:03.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/net/AprEndpoint.java 2016-03-31 15:52:41.000000000 +0000 @@ -243,7 +243,7 @@ /** * SSL cipher suite. */ - protected String SSLCipherSuite = "ALL"; + protected String SSLCipherSuite = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA"; public String getSSLCipherSuite() { return SSLCipherSuite; } public void setSSLCipherSuite(String SSLCipherSuite) { this.SSLCipherSuite = SSLCipherSuite; } @@ -2032,7 +2032,7 @@ } } - if (reset) { + if (reset && pollerRunning) { // Reallocate the current poller int count = Poll.pollset(pollers[i], desc); long newPoller = allocatePoller(actualPollerSize, pool, -1); diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java tomcat7-7.0.69/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java --- tomcat7-7.0.68/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java 2015-11-11 09:55:51.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/net/jsse/JSSESocketFactory.java 2016-03-31 15:27:31.000000000 +0000 @@ -172,11 +172,48 @@ } try { - defaultServerCipherSuites = socket.getEnabledCipherSuites(); + // Many of the default ciphers supported by older JRE versions are + // now considered insecure. This code attempts to filter them out + List filteredCiphers = new ArrayList(); + for (String cipher : socket.getEnabledCipherSuites()) { + // Remove export ciphers - FREAK + if (cipher.toUpperCase(Locale.ENGLISH).contains("EXP")) { + log.debug(sm.getString("jsse.excludeDefaultCipher", cipher)); + continue; + } + // Remove DES ciphers + if (cipher.toUpperCase(Locale.ENGLISH).contains("_DES_")) { + log.debug(sm.getString("jsse.excludeDefaultCipher", cipher)); + continue; + } + // Remove RC4 ciphers + if (cipher.toUpperCase(Locale.ENGLISH).contains("_RC4_")) { + log.debug(sm.getString("jsse.excludeDefaultCipher", cipher)); + continue; + } + // Remove DHE ciphers unless running on Java 8 or above + if (!JreCompat.isJre8Available() && + cipher.toUpperCase(Locale.ENGLISH).contains("_DHE_")) { + log.debug(sm.getString("jsse.excludeDefaultCipher", cipher)); + continue; + } + // Remove kRSA ciphers when running on Java 7 or above. Can't + // remove them for Java 6 since they are likely to be the only + // ones left + if (JreCompat.isJre7Available() && + (cipher.toUpperCase(Locale.ENGLISH).startsWith("TLS_RSA_") || + cipher.toUpperCase(Locale.ENGLISH).startsWith("SSL_RSA_"))) { + log.debug(sm.getString("jsse.excludeDefaultCipher", cipher)); + continue; + } + filteredCiphers.add(cipher); + } + + defaultServerCipherSuites = filteredCiphers.toArray(new String[filteredCiphers.size()]); if (defaultServerCipherSuites.length == 0) { log.warn(sm.getString("jsse.noDefaultCiphers", endpoint.getName())); } - + // Filter out all the SSL protocols (SSLv2 and SSLv3) from the defaults // since they are no longer considered secure List filteredProtocols = new ArrayList(); diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties tomcat7-7.0.69/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties --- tomcat7-7.0.68/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties 2015-02-28 19:38:30.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/net/jsse/res/LocalStrings.properties 2016-03-31 15:27:31.000000000 +0000 @@ -24,6 +24,7 @@ jsse.requested_protocols_not_supported=None of the SSL protocols specified are supported by the SSL engine : {0} jsse.enableable_protocols=Specified SSL protocols that are supported and enableable are : {0} jsse.unsupported_protocols=Some specified SSL protocols are not supported by the SSL engine : {0} +jsse.excludeDefaultCipher=The SSL cipher [{0}] which is enabled by default in this JRE was excluded from the defaults used by Tomcat jsse.excludeDefaultProtocol=The SSL protocol [{0}] which is enabled by default in this JRE was excluded from the defaults used by Tomcat jsse.noDefaultCiphers=Unable to determine a default for ciphers for [{0}]. Set an explicit value to ensure the connector can start. jsse.noDefaultProtocols=Unable to determine a default for sslEnabledProtocols for [{0}]. Set an explicit value to ensure the connector can start. diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/net/NioEndpoint.java tomcat7-7.0.69/java/org/apache/tomcat/util/net/NioEndpoint.java --- tomcat7-7.0.68/java/org/apache/tomcat/util/net/NioEndpoint.java 2015-10-25 17:50:03.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/net/NioEndpoint.java 2016-02-19 22:31:42.000000000 +0000 @@ -1319,17 +1319,8 @@ return result; } - /** - * @deprecated Replaced by processSendfile(sk, attachment, event) - */ - @Deprecated - public boolean processSendfile(SelectionKey sk, - KeyAttachment attachment, - @SuppressWarnings("unused") boolean reg, boolean event) { - return processSendfile(sk, attachment, event); - } - - public boolean processSendfile(SelectionKey sk, KeyAttachment attachment, boolean event) { + public SendfileState processSendfile(SelectionKey sk, KeyAttachment attachment, + boolean calledByProcessor) { NioChannel sc = null; try { unreg(sk, attachment, sk.readyOps()); @@ -1344,7 +1335,7 @@ File f = new File(sd.fileName); if ( !f.exists() ) { cancelledKey(sk,SocketStatus.ERROR,false); - return false; + return SendfileState.ERROR; } @SuppressWarnings("resource") // Closed when channel is closed FileInputStream fis = new FileInputStream(f); @@ -1385,42 +1376,43 @@ sd.fchannel.close(); } catch (Exception ignore) { } - if ( sd.keepAlive ) { - if (log.isDebugEnabled()) { - log.debug("Connection is keep alive, registering back for OP_READ"); - } - if (event) { - this.add(attachment.getChannel(),SelectionKey.OP_READ); - } else { + // For calls from outside the Poller, the caller is + // responsible for registering the socket for the + // appropriate event(s) if sendfile completes. + if (!calledByProcessor) { + if ( sd.keepAlive ) { + if (log.isDebugEnabled()) { + log.debug("Connection is keep alive, registering back for OP_READ"); + } reg(sk,attachment,SelectionKey.OP_READ); + } else { + if (log.isDebugEnabled()) { + log.debug("Send file connection is being closed"); } - } else { - if (log.isDebugEnabled()) { - log.debug("Send file connection is being closed"); + cancelledKey(sk,SocketStatus.STOP,false); } - cancelledKey(sk,SocketStatus.STOP,false); - return false; } + return SendfileState.DONE; } else { if (log.isDebugEnabled()) { log.debug("OP_WRITE for sendfile: " + sd.fileName); } - if (event) { + if (calledByProcessor) { add(attachment.getChannel(),SelectionKey.OP_WRITE); } else { reg(sk,attachment,SelectionKey.OP_WRITE); } + return SendfileState.PENDING; } }catch ( IOException x ) { if ( log.isDebugEnabled() ) log.debug("Unable to complete sendfile request:", x); cancelledKey(sk,SocketStatus.ERROR,false); - return false; + return SendfileState.ERROR; }catch ( Throwable t ) { log.error("",t); cancelledKey(sk, SocketStatus.ERROR, false); - return false; + return SendfileState.ERROR; } - return true; } protected void unreg(SelectionKey sk, KeyAttachment attachment, int readyOps) { diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/net/SendfileState.java tomcat7-7.0.69/java/org/apache/tomcat/util/net/SendfileState.java --- tomcat7-7.0.68/java/org/apache/tomcat/util/net/SendfileState.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/net/SendfileState.java 2016-02-19 22:31:42.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * 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.net; + +public enum SendfileState { + + /** + * The sending of the file has started but has not completed. Sendfile is + * still using the socket. + */ + PENDING, + + /** + * The file has been fully sent. Sendfile is no longer using the socket. + */ + DONE, + + /** + * Something went wrong. The file may or may not have been sent. The socket + * is in an unknown state. + */ + ERROR +} diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/util/scan/StandardJarScanner.java tomcat7-7.0.69/java/org/apache/tomcat/util/scan/StandardJarScanner.java --- tomcat7-7.0.68/java/org/apache/tomcat/util/scan/StandardJarScanner.java 2015-12-16 21:02:17.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/util/scan/StandardJarScanner.java 2016-03-01 23:04:09.000000000 +0000 @@ -35,6 +35,7 @@ import org.apache.tomcat.JarScanner; import org.apache.tomcat.JarScannerCallback; import org.apache.tomcat.util.ExceptionUtils; +import org.apache.tomcat.util.buf.UriUtil; import org.apache.tomcat.util.file.Matcher; import org.apache.tomcat.util.res.StringManager; @@ -254,7 +255,7 @@ if (urlStr.startsWith("file:") || urlStr.startsWith("jndi:") || urlStr.startsWith("http:") || urlStr.startsWith("https:")) { if (urlStr.endsWith(Constants.JAR_EXT)) { - URL jarURL = new URL("jar:" + urlStr + "!/"); + URL jarURL = UriUtil.buildJarUrl(urlStr); callback.scan((JarURLConnection) jarURL.openConnection()); } else { File f; @@ -262,7 +263,7 @@ f = new File(url.toURI()); if (f.isFile() && scanAllFiles) { // Treat this file as a JAR - URL jarURL = new URL("jar:" + urlStr + "!/"); + URL jarURL = UriUtil.buildJarUrl(f); callback.scan((JarURLConnection) jarURL.openConnection()); } else if (f.isDirectory() && scanAllDirectories) { File metainf = new File(f.getAbsoluteFile() + diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java tomcat7-7.0.69/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java --- tomcat7-7.0.68/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java 2015-10-27 10:17:55.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java 2016-03-07 12:33:06.000000000 +0000 @@ -263,8 +263,8 @@ socketReadBuffer.compact(); if (forceRead) { - Future f = - socketChannel.read(socketReadBuffer); + forceRead = false; + Future f = socketChannel.read(socketReadBuffer); Integer socketRead = f.get(); if (socketRead.intValue() == -1) { throw new EOFException(sm.getString( diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/websocket/FutureToSendHandler.java tomcat7-7.0.69/java/org/apache/tomcat/websocket/FutureToSendHandler.java --- tomcat7-7.0.68/java/org/apache/tomcat/websocket/FutureToSendHandler.java 2013-09-19 11:10:06.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/websocket/FutureToSendHandler.java 2016-02-23 15:00:53.000000000 +0000 @@ -32,10 +32,22 @@ private final CountDownLatch latch = new CountDownLatch(1); private final WsSession wsSession; + private final boolean closeMessage; private volatile SendResult result = null; public FutureToSendHandler(WsSession wsSession) { + this(wsSession, false); + } + + + public FutureToSendHandler(WsSession wsSession, boolean closeMessage) { this.wsSession = wsSession; + this.closeMessage = closeMessage; + } + + + public boolean isCloseMessage() { + return closeMessage; } diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/websocket/PerMessageDeflate.java tomcat7-7.0.69/java/org/apache/tomcat/websocket/PerMessageDeflate.java --- tomcat7-7.0.68/java/org/apache/tomcat/websocket/PerMessageDeflate.java 2015-09-19 07:41:49.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/websocket/PerMessageDeflate.java 2016-03-18 11:32:53.000000000 +0000 @@ -321,7 +321,8 @@ // Control messages can appear in the middle of other messages // and must not be compressed. Pass it straight through allCompressedParts.add(uncompressedPart); - } else if (uncompressedPart.getPayload().limit() == 0) { + } else if (uncompressedPart.getPayload().limit() == 0 && uncompressedPart.isFin() && + deflater.getBytesRead() == 0) { // Zero length messages can't be compressed so pass them // straight through. allCompressedParts.add(uncompressedPart); @@ -456,4 +457,13 @@ } return result; } + + + @Override + public void close() { + // There will always be a next transformation + next.close(); + inflater.end(); + deflater.end(); + } } diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/websocket/server/WsServerContainer.java tomcat7-7.0.69/java/org/apache/tomcat/websocket/server/WsServerContainer.java --- tomcat7-7.0.68/java/org/apache/tomcat/websocket/server/WsServerContainer.java 2015-11-05 10:35:27.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/websocket/server/WsServerContainer.java 2016-03-28 07:39:29.000000000 +0000 @@ -330,6 +330,24 @@ } + /** + * Until the WebSocket specification provides such a mechanism, this Tomcat + * proprietary method is provided to enable applications to programmatically + * determine whether or not to upgrade an individual request to WebSocket. + *

+ * Note: This method is not used by Tomcat but is used directly by + * third-party code and must not be removed. + * + * @param request The request object to be upgraded + * @param response The response object to be populated with the result of + * the upgrade + * @param sec The server endpoint to use to process the upgrade request + * @param pathParams The path parameters associated with the upgrade request + * + * @throws ServletException If a configuration error prevents the upgrade + * from taking place + * @throws IOException If an I/O error occurs during the upgrade process + */ public void doUpgrade(HttpServletRequest request, HttpServletResponse response, ServerEndpointConfig sec, Map pathParams) diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/websocket/Transformation.java tomcat7-7.0.69/java/org/apache/tomcat/websocket/Transformation.java --- tomcat7-7.0.68/java/org/apache/tomcat/websocket/Transformation.java 2014-09-17 06:31:22.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/websocket/Transformation.java 2016-03-18 11:32:53.000000000 +0000 @@ -94,4 +94,9 @@ * may be bigger or smaller than the size of the input list */ List sendMessagePart(List messageParts); + + /** + * Clean-up any resources that were used by the transformation. + */ + void close(); } diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/websocket/WsFrameBase.java tomcat7-7.0.69/java/org/apache/tomcat/websocket/WsFrameBase.java --- tomcat7-7.0.68/java/org/apache/tomcat/websocket/WsFrameBase.java 2015-12-11 12:51:51.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/websocket/WsFrameBase.java 2016-03-18 11:32:53.000000000 +0000 @@ -737,6 +737,11 @@ public boolean validateRsv(int rsv, byte opCode) { return rsv == 0; } + + @Override + public void close() { + // NO-OP for the terminal transformations + } } diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java tomcat7-7.0.69/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java --- tomcat7-7.0.68/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java 2015-12-11 13:55:37.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java 2016-03-18 11:32:53.000000000 +0000 @@ -271,7 +271,8 @@ // trigger a session close and depending on timing the client // session may close before we can read the timeout. long timeout = getBlockingSendTimeout(); - FutureToSendHandler f2sh = new FutureToSendHandler(wsSession); + FutureToSendHandler f2sh = + new FutureToSendHandler(wsSession, opCode == Constants.OPCODE_CLOSE); startMessage(opCode, payload, last, f2sh); try { if (timeout == -1) { @@ -689,6 +690,9 @@ for (EncoderEntry entry : encoderEntries) { entry.getEncoder().destroy(); } + // The transformation handles both input and output. It only needs to be + // closed once so it is closed here on the output side. + transformation.close(); doClose(); } diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/websocket/WsSession.java tomcat7-7.0.69/java/org/apache/tomcat/websocket/WsSession.java --- tomcat7-7.0.68/java/org/apache/tomcat/websocket/WsSession.java 2016-02-02 12:00:49.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/websocket/WsSession.java 2016-02-23 15:00:53.000000000 +0000 @@ -621,7 +621,8 @@ // If the session has already been closed the any registered futures // will have been processed so the failure result for this future // needs to be set here. - if (state == State.OPEN) { + if (state == State.OPEN || f2sh.isCloseMessage()) { + // WebSocket session is open or this is the close message futures.put(f2sh, f2sh); } else if (f2sh.isDone()) { // NO-OP. The future completed before the session closed so no diff -Nru tomcat7-7.0.68/java/org/apache/tomcat/websocket/WsWebSocketContainer.java tomcat7-7.0.69/java/org/apache/tomcat/websocket/WsWebSocketContainer.java --- tomcat7-7.0.68/java/org/apache/tomcat/websocket/WsWebSocketContainer.java 2015-11-27 20:29:40.000000000 +0000 +++ tomcat7-7.0.69/java/org/apache/tomcat/websocket/WsWebSocketContainer.java 2016-03-07 19:42:49.000000000 +0000 @@ -243,6 +243,7 @@ proxyPath = URI.create("http" + path.toString().substring(2)); } else if ("wss".equalsIgnoreCase(scheme)) { proxyPath = URI.create("https" + path.toString().substring(3)); + secure = true; } else { throw new DeploymentException(sm.getString( "wsWebSocketContainer.pathWrongScheme", scheme)); @@ -284,12 +285,8 @@ } else { // Must be wss due to scheme validation above sa = new InetSocketAddress(host, 443); - secure = true; } } else { - if ("wss".equalsIgnoreCase(scheme)) { - secure = true; - } sa = new InetSocketAddress(host, port); } } else { diff -Nru tomcat7-7.0.68/res/confinstall/tomcat-users_2.xml tomcat7-7.0.69/res/confinstall/tomcat-users_2.xml --- tomcat7-7.0.68/res/confinstall/tomcat-users_2.xml 2014-01-26 21:56:50.000000000 +0000 +++ tomcat7-7.0.69/res/confinstall/tomcat-users_2.xml 2016-03-08 18:08:16.000000000 +0000 @@ -1,18 +1,23 @@ diff -Nru tomcat7-7.0.68/res/ide-support/eclipse/eclipse.classpath tomcat7-7.0.69/res/ide-support/eclipse/eclipse.classpath --- tomcat7-7.0.68/res/ide-support/eclipse/eclipse.classpath 2015-11-12 14:26:44.000000000 +0000 +++ tomcat7-7.0.69/res/ide-support/eclipse/eclipse.classpath 2016-03-22 09:25:42.000000000 +0000 @@ -27,5 +27,6 @@ + diff -Nru tomcat7-7.0.68/res/maven/mvn.properties.default tomcat7-7.0.69/res/maven/mvn.properties.default --- tomcat7-7.0.68/res/maven/mvn.properties.default 2015-12-07 15:18:38.000000000 +0000 +++ tomcat7-7.0.69/res/maven/mvn.properties.default 2016-02-09 07:25:45.000000000 +0000 @@ -35,7 +35,7 @@ maven.asf.release.repo.repositoryId=apache.releases # Release version info -maven.asf.release.deploy.version=7.0.68 +maven.asf.release.deploy.version=7.0.69 #Where do we load the libraries from tomcat.lib.path=../../output/build/lib diff -Nru tomcat7-7.0.68/res/rat/rat-excludes.txt tomcat7-7.0.69/res/rat/rat-excludes.txt --- tomcat7-7.0.68/res/rat/rat-excludes.txt 2012-06-01 18:38:58.000000000 +0000 +++ tomcat7-7.0.69/res/rat/rat-excludes.txt 2016-02-15 21:14:55.000000000 +0000 @@ -24,9 +24,11 @@ - *.manifest JAR manifest files cannot contain license - package-list files in API documentation (javadoc) are generated - bug52121-part1, bug52121-part2 files in tests are test data for a hard - to reproduce testcase and should be used as is. + to reproduce testcase and should be used as is. - other trivial test files, such as textual files containing only "OK' string, - are also excluded. + are also excluded. + - *.bmp files image files cannot contain license + - *.dia files image files cannot contain license output/build/webapps/docs/*.html output/build/webapps/docs/appdev/*.html @@ -61,3 +63,5 @@ test/org/apache/coyote/http11/filters/bug52121-part2 test/webapp-3.0/bug53257/*.txt test/webapp-3.0-fragments/WEB-INF/classes/*.txt +**/*.bmp +**/*.dia diff -Nru tomcat7-7.0.68/res/tomcat.nsi tomcat7-7.0.69/res/tomcat.nsi --- tomcat7-7.0.68/res/tomcat.nsi 2014-08-04 18:43:42.000000000 +0000 +++ tomcat7-7.0.69/res/tomcat.nsi 2016-02-25 20:38:14.000000000 +0000 @@ -1122,6 +1122,12 @@ SetShellVarContext current RMDir /r "$SMPROGRAMS\Apache Tomcat @VERSION_MAJOR_MINOR@ $TomcatServiceName" + ; Before files are removed using recursive deletes, remove any symbolic + ; links in the installation directory and the directory structure below it + ; to ensure the recursive deletes don't result in any nasty surprises. + Push "$INSTDIR" + Call un.RemoveSymlinks + Delete "$INSTDIR\tomcat.ico" Delete "$INSTDIR\LICENSE" Delete "$INSTDIR\NOTICE" @@ -1160,7 +1166,7 @@ ; ================= ; uninstall init function ; -; Read the command line paramater and set up the service name variables so the +; Read the command line parameter and set up the service name variables so the ; uninstaller knows which service it is working with ; ================= Function un.onInit @@ -1170,4 +1176,46 @@ StrCpy $TomcatServiceFileName $R1.exe StrCpy $TomcatServiceManagerFileName $R1w.exe FunctionEnd + +; ================= +; Removes symbolic links from the path found on top of the stack. +; The path is removed from the stack as a result of calling this function. +; ================= +Function un.RemoveSymlinks + Pop $0 + ${GetFileAttributes} "$0" "REPARSE_POINT" $3 + ; DetailPrint "Processing directory [$0] [$3]" + FindFirst $1 $2 $0\*.* + ; DetailPrint "Search [$1] found [$2]" + StrCmp $3 "1" RemoveSymlinks-delete +RemoveSymlinks-loop: + ; DetailPrint "Search [$1] processing [$0\$2]" + StrCmp $2 "" RemoveSymlinks-exit + StrCmp $2 "." RemoveSymlinks-skip + StrCmp $2 ".." RemoveSymlinks-skip + IfFileExists $0\$2\*.* RemoveSymlinks-directory +RemoveSymlinks-skip: + ; DetailPrint "Search [$1] ignoring file [$0\$2]" + FindNext $1 $2 + StrCmp $2 "" RemoveSymlinks-exit + goto RemoveSymlinks-loop +RemoveSymlinks-directory: + ; DetailPrint "Search [$1] found directory [$0\$2]" + Push $0 + Push $1 + Push $0\$2 + Call un.RemoveSymlinks + Pop $1 + Pop $0 + ; DetailPrint "Search [$1] restored for [$0]" + FindNext $1 $2 + goto RemoveSymlinks-loop +RemoveSymlinks-delete: + ; DetailPrint "Deleting symlink [$0]" + SetFileAttributes "$0" "NORMAL" + System::Call "kernel32::RemoveDirectoryW(w `$0`) i.n" +RemoveSymlinks-exit: + ; DetailPrint "Search [$1] closed" + FindClose $1 +FunctionEnd ;eof diff -Nru tomcat7-7.0.68/test/org/apache/catalina/connector/TestResponse.java tomcat7-7.0.69/test/org/apache/catalina/connector/TestResponse.java --- tomcat7-7.0.68/test/org/apache/catalina/connector/TestResponse.java 2015-11-30 15:35:05.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/catalina/connector/TestResponse.java 2016-03-16 18:03:52.000000000 +0000 @@ -38,6 +38,7 @@ import org.apache.catalina.Context; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.unittest.TesterContext; import org.apache.tomcat.unittest.TesterRequest; import org.apache.tomcat.util.buf.ByteChunk; @@ -574,6 +575,45 @@ public void testEncodeRedirectURL16() throws Exception { doTestEncodeURL("./..#/../..", "./..;jsessionid=1234#/../.."); } @Test + public void testSendRedirect01() throws Exception { + doTestSendRedirect("../foo", "../foo"); + } + + + @Test + public void testSendRedirect02() throws Exception { + doTestSendRedirect("../foo bar", "../foo bar"); + } + + + @Test + public void testSendRedirect03() throws Exception { + doTestSendRedirect("../foo%20bar", "../foo%20bar"); + } + + + private void doTestSendRedirect(String input, String expectedLocation) throws Exception { + // Set-up. + // Note: Not sufficient for testing relative -> absolute + Connector connector = new Connector(); + org.apache.coyote.Response cResponse = new org.apache.coyote.Response(); + Response response = new Response(); + response.setConnector(connector); + response.setCoyoteResponse(cResponse); + Request request = new Request(); + org.apache.coyote.Request cRequest = new org.apache.coyote.Request(); + request.setCoyoteRequest(cRequest); + Context context = new TesterContext(); + request.setContext(context); + response.setRequest(request); + // Do test + response.sendRedirect(input); + String location = response.getHeader("Location"); + Assert.assertEquals(expectedLocation, location); + } + + + @Test public void testBug53469a() throws Exception { Request req = new TesterRequest(); Response resp = new Response(); diff -Nru tomcat7-7.0.68/test/org/apache/catalina/connector/TestSendFile.java tomcat7-7.0.69/test/org/apache/catalina/connector/TestSendFile.java --- tomcat7-7.0.68/test/org/apache/catalina/connector/TestSendFile.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/catalina/connector/TestSendFile.java 2016-02-19 22:53:50.000000000 +0000 @@ -0,0 +1,166 @@ +/* + * 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.connector; + +import java.io.BufferedInputStream; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.Globals; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; + +public class TestSendFile extends TomcatBaseTest{ + + public static final int ITERATIONS = 10; + public static final int EXPECTED_CONTENT_LENGTH = 100000; + + @Test + public void testSendFile() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + Context root = tomcat.addContext("", TEMP_DIR); + + File[] files = new File[ITERATIONS]; + for (int i=0; i> respHeaders = new HashMap>(); + for (int i=0; i 0) { + int bytes = Math.min(size, defSize); + char[] b = new char[bytes]; + Arrays.fill(b, 'X'); + w.write(b); + size = size - bytes; + } + w.flush(); + } finally { + if (w != null) { + w.close(); + } + if (fw != null) { + fw.close(); + } + } + System.out.println("Created file:" + f.getAbsolutePath() + " with " + f.length() + + " bytes."); + return f; + + } + + + private static class WritingServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + private final File f; + + public WritingServlet(File f) { + this.f = f; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + resp.setContentType("'application/octet-stream"); + resp.setCharacterEncoding("ISO-8859-1"); + resp.setContentLength((int) f.length()); + if (Boolean.TRUE.equals(req.getAttribute(Globals.SENDFILE_SUPPORTED_ATTR))) { + req.setAttribute(Globals.SENDFILE_FILENAME_ATTR, f.getAbsolutePath()); + req.setAttribute(Globals.SENDFILE_FILE_START_ATTR, new Long(0)); + req.setAttribute(Globals.SENDFILE_FILE_END_ATTR, new Long(f.length())); + } else { + byte[] c = new byte[8192]; + BufferedInputStream in = null; + try { + in = new BufferedInputStream(new FileInputStream(f)); + int len = 0; + int written = 0; + long start = System.currentTimeMillis(); + do { + len = in.read(c); + if (len>0) { + resp.getOutputStream().write(c,0,len); + written += len; + } + } while (len > 0); + System.out.println("Server Wrote "+written + " bytes in "+(System.currentTimeMillis()-start)+" ms."); + } finally { + if (in != null) { + in.close(); + } + } + } + } + } + +} diff -Nru tomcat7-7.0.68/test/org/apache/catalina/core/TestAsyncContextImpl.java tomcat7-7.0.69/test/org/apache/catalina/core/TestAsyncContextImpl.java --- tomcat7-7.0.68/test/org/apache/catalina/core/TestAsyncContextImpl.java 2016-01-04 18:23:42.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/catalina/core/TestAsyncContextImpl.java 2016-04-07 18:25:19.000000000 +0000 @@ -51,12 +51,16 @@ import org.junit.Test; import org.apache.catalina.Context; +import org.apache.catalina.Loader; import org.apache.catalina.Wrapper; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; import org.apache.catalina.deploy.ErrorPage; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.catalina.valves.TesterAccessLogValve; import org.apache.tomcat.util.buf.ByteChunk; +import org.easymock.EasyMock; public class TestAsyncContextImpl extends TomcatBaseTest { @@ -1165,6 +1169,7 @@ @Test public void testErrorHandling() throws Exception { + resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -2212,4 +2217,58 @@ } } + + + @Test + public void testAsyncListenerSupplyRequestResponse() { + final ServletRequest servletRequest = EasyMock.createMock(ServletRequest.class); + final ServletResponse servletResponse = EasyMock.createMock(ServletResponse.class); + final AsyncListener listener = new AsyncListener() { + + @Override + public void onTimeout(AsyncEvent event) throws IOException { + checkRequestResponse(event); + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException { + checkRequestResponse(event); + } + + @Override + public void onError(AsyncEvent event) throws IOException { + checkRequestResponse(event); + } + + @Override + public void onComplete(AsyncEvent event) throws IOException { + checkRequestResponse(event); + } + + private void checkRequestResponse(AsyncEvent event) { + assertEquals(servletRequest, event.getSuppliedRequest()); + assertEquals(servletResponse, event.getSuppliedResponse()); + } + }; + final Context context = EasyMock.createMock(Context.class); + final Loader loader = EasyMock.createMock(Loader.class); + final Response response = new Response(); + final Request request = new Request(); + request.setCoyoteRequest(new org.apache.coyote.Request()); + request.setContext(context); + final AsyncContextImpl ac = new AsyncContextImpl(request); + EasyMock.expect(context.getApplicationEventListeners()).andReturn(null); + EasyMock.expect(context.getLoader()).andReturn(loader); + EasyMock.expect(loader.getClassLoader()).andReturn(null); + + EasyMock.replay(context, loader); + + ac.addListener(listener, servletRequest, servletResponse); + ac.setStarted(context, request, response, true); + ac.addListener(listener, servletRequest, servletResponse); + ac.setErrorState(new Exception(), true); + ac.fireOnComplete(); + + EasyMock.verify(context, loader); + } } diff -Nru tomcat7-7.0.68/test/org/apache/catalina/session/TesterStore.java tomcat7-7.0.69/test/org/apache/catalina/session/TesterStore.java --- tomcat7-7.0.68/test/org/apache/catalina/session/TesterStore.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/catalina/session/TesterStore.java 2016-04-06 20:24:19.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.catalina.session; + +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.catalina.Manager; +import org.apache.catalina.Session; +import org.apache.catalina.Store; + +class TesterStore implements Store { + + private Manager manager; + private Map sessions = new HashMap(); + private List savedIds = new ArrayList(); + + List getSavedIds() { + return savedIds; + } + + @Override + public Manager getManager() { + return this.manager; + } + + @Override + public void setManager(Manager manager) { + this.manager = manager; + } + + @Override + public int getSize() throws IOException { + return savedIds.size(); + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + } + + @Override + public String[] keys() throws IOException { + return new ArrayList(sessions.keySet()).toArray(new String[] {}); + } + + @Override + public Session load(String id) throws ClassNotFoundException, + IOException { + return sessions.get(id); + } + + @Override + public void remove(String id) throws IOException { + sessions.remove(id); + } + + @Override + public void clear() throws IOException { + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + } + + @Override + public void save(Session session) throws IOException { + sessions.put(session.getId(), session); + savedIds.add(session.getId()); + } + + @Override + public String getInfo() { + return null; + } +} + diff -Nru tomcat7-7.0.68/test/org/apache/catalina/session/TestPersistentManagerIntegration.java tomcat7-7.0.69/test/org/apache/catalina/session/TestPersistentManagerIntegration.java --- tomcat7-7.0.68/test/org/apache/catalina/session/TestPersistentManagerIntegration.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/catalina/session/TestPersistentManagerIntegration.java 2016-04-06 20:24:19.000000000 +0000 @@ -0,0 +1,241 @@ +/* + * 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.session; + +import java.io.IOException; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.Session; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.catalina.valves.PersistentValve; + +public class TestPersistentManagerIntegration extends TomcatBaseTest { + + private final String ACTIVITY_CHECK = "org.apache.catalina.session.StandardSession.ACTIVITY_CHECK"; + + private String oldActivityCheck; + + /** + * As documented in config/manager.html, the "ACTIVITY_CHECK" property must + * be set to "true" for PersistentManager to function correctly. + */ + @Before + public void setActivityCheck() { + oldActivityCheck = System.setProperty(ACTIVITY_CHECK, "true"); + } + + @After + public void resetActivityCheck() { + if (oldActivityCheck != null) { + System.setProperty(ACTIVITY_CHECK, oldActivityCheck); + } else { + System.clearProperty(ACTIVITY_CHECK); + } + } + + /** + * Wait enough for the system clock to update its value. On some systems + * (e.g. old Windows) the clock granularity is tens of milliseconds. + */ + private void waitForClockUpdate() throws InterruptedException { + long startTime = System.currentTimeMillis(); + int waitTime = 1; + do { + Thread.sleep(waitTime); + waitTime *= 10; + } while (System.currentTimeMillis() == startTime); + } + + /** + * Wait while session access counter has a positive value. + */ + private void waitWhileSessionIsActive(StandardSession session) + throws InterruptedException { + long maxWaitTime = System.currentTimeMillis() + 60000; + AtomicInteger accessCount = session.accessCount; + while (accessCount.get() > 0) { + // Wait until o.a.c.connector.Request.recycle() completes, + // as it updates lastAccessedTime. + Assert.assertTrue(System.currentTimeMillis() < maxWaitTime); + Thread.sleep(200); + } + } + + @Test + public void noSessionCreate_57637() throws IOException, LifecycleException { + + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + StandardContext ctx = (StandardContext) tomcat.addContext("", null); + ctx.setDistributable(true); + + Tomcat.addServlet(ctx, "DummyServlet", new DummyServlet()); + ctx.addServletMapping("/dummy", "DummyServlet"); + + PersistentManager manager = new PersistentManager(); + TesterStore store = new TesterStore(); + + manager.setStore(store); + manager.setMaxIdleBackup(0); + ctx.setManager(manager); + ctx.addValve(new PersistentValve()); + tomcat.start(); + Assert.assertEquals(manager.getActiveSessions(), 0); + Assert.assertTrue("No sessions managed", manager.getSessionIdsFull().isEmpty()); + Assert.assertEquals( + "NO_SESSION", + getUrl( + "http://localhost:" + getPort() + + "/dummy?no_create_session=true").toString()); + Assert.assertEquals(manager.getActiveSessions(), 0); + Assert.assertTrue("No sessions where created", manager.getSessionIdsFull().isEmpty()); + } + + @Test + public void testCreateSessionAndPassivate() throws IOException, LifecycleException, ClassNotFoundException { + + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + StandardContext ctx = (StandardContext) tomcat.addContext("", null); + ctx.setDistributable(true); + + Tomcat.addServlet(ctx, "DummyServlet", new DummyServlet()); + ctx.addServletMapping("/dummy", "DummyServlet"); + + PersistentManager manager = new PersistentManager(); + TesterStore store = new TesterStore(); + + manager.setStore(store); + manager.setMaxIdleBackup(0); + ctx.setManager(manager); + ctx.addValve(new PersistentValve()); + tomcat.start(); + Assert.assertEquals("No active sessions", manager.getActiveSessions(), 0); + Assert.assertTrue("No sessions managed", manager.getSessionIdsFull().isEmpty()); + String sessionId = getUrl( + "http://localhost:" + getPort() + + "/dummy?no_create_session=false").toString(); + Assert.assertNotNull("Session is stored", store.load(sessionId)); + Assert.assertEquals("All sessions are passivated", manager.getActiveSessions(), 0); + Assert.assertTrue("One session was created", !manager.getSessionIdsFull().isEmpty()); + } + + @Test + public void backsUpOnce_56698() throws IOException, LifecycleException, + InterruptedException { + + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = tomcat.addContext("", null); + ctx.setDistributable(true); + + Tomcat.addServlet(ctx, "DummyServlet", new DummyServlet()); + ctx.addServletMapping("/dummy", "DummyServlet"); + + PersistentManager manager = new PersistentManager(); + TesterStore store = new TesterStore(); + + manager.setStore(store); + manager.setMaxIdleBackup(0); + ctx.setManager(manager); + tomcat.start(); + String sessionId = getUrl("http://localhost:" + getPort() + "/dummy") + .toString(); + + // Note: PersistenceManager.findSession() silently updates + // session.lastAccessedTime, so call it only once before other work. + Session session = manager.findSession(sessionId); + + // Wait until request processing ends, as Request.recycle() updates + // session.lastAccessedTime via session.endAccess(). + waitWhileSessionIsActive((StandardSession) session); + + long lastAccessedTime = session.getLastAccessedTimeInternal(); + + // Session should be idle at least for 0 second (maxIdleBackup) + // to be eligible for persistence, thus no need to wait. + + // Waiting a bit, to catch changes in last accessed time of a session + waitForClockUpdate(); + + manager.processPersistenceChecks(); + Assert.assertEquals(Arrays.asList(sessionId), store.getSavedIds()); + Assert.assertEquals(lastAccessedTime, session.getLastAccessedTimeInternal()); + + // session was not accessed, so no save will be performed + waitForClockUpdate(); + manager.processPersistenceChecks(); + Assert.assertEquals(Arrays.asList(sessionId), store.getSavedIds()); + Assert.assertEquals(lastAccessedTime, session.getLastAccessedTimeInternal()); + + // access session + session.access(); + session.endAccess(); + + // session was accessed, so it will be saved once again + manager.processPersistenceChecks(); + Assert.assertEquals(Arrays.asList(sessionId, sessionId), + store.getSavedIds()); + + // session was not accessed, so once again no save will happen + manager.processPersistenceChecks(); + Assert.assertEquals(Arrays.asList(sessionId, sessionId), + store.getSavedIds()); + } + + private static class DummyServlet extends HttpServlet { + + private static final long serialVersionUID = -3696433049266123995L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + boolean createSession = !Boolean.parseBoolean(req + .getParameter("no_create_session")); + HttpSession session = req.getSession(createSession); + if (session == null) { + resp.getWriter().print("NO_SESSION"); + } else { + String id = session.getId(); + resp.getWriter().print(id); + } + } + + } +} diff -Nru tomcat7-7.0.68/test/org/apache/catalina/session/TestPersistentManager.java tomcat7-7.0.69/test/org/apache/catalina/session/TestPersistentManager.java --- tomcat7-7.0.68/test/org/apache/catalina/session/TestPersistentManager.java 2016-01-26 23:40:33.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/catalina/session/TestPersistentManager.java 2016-04-06 20:27:42.000000000 +0000 @@ -16,298 +16,44 @@ */ package org.apache.catalina.session; -import java.beans.PropertyChangeListener; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import org.junit.After; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.apache.catalina.Context; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.Manager; -import org.apache.catalina.Session; -import org.apache.catalina.Store; -import org.apache.catalina.core.StandardContext; -import org.apache.catalina.startup.Tomcat; -import org.apache.catalina.startup.TomcatBaseTest; -import org.apache.catalina.valves.PersistentValve; - -public class TestPersistentManager extends TomcatBaseTest { - - private final String ACTIVITY_CHECK = "org.apache.catalina.session.StandardSession.ACTIVITY_CHECK"; - - private String oldActivityCheck; - - /** - * As documented in config/manager.html, the "ACTIVITY_CHECK" property must - * be set to "true" for PersistentManager to function correctly. - */ - @Before - public void setActivityCheck() { - oldActivityCheck = System.setProperty(ACTIVITY_CHECK, "true"); - } - - @After - public void resetActivityCheck() { - if (oldActivityCheck != null) { - System.setProperty(ACTIVITY_CHECK, oldActivityCheck); - } else { - System.clearProperty(ACTIVITY_CHECK); - } - } +import org.apache.catalina.Host; +import org.apache.tomcat.unittest.TesterContext; +import org.apache.tomcat.unittest.TesterHost; - /** - * Wait enough for the system clock to update its value. On some systems - * (e.g. old Windows) the clock granularity is tens of milliseconds. - */ - private void waitForClockUpdate() throws InterruptedException { - long startTime = System.currentTimeMillis(); - int waitTime = 1; - do { - Thread.sleep(waitTime); - waitTime *= 10; - } while (System.currentTimeMillis() == startTime); - } - - /** - * Wait while session access counter has a positive value. - */ - private void waitWhileSessionIsActive(StandardSession session) - throws InterruptedException { - long maxWaitTime = System.currentTimeMillis() + 60000; - AtomicInteger accessCount = session.accessCount; - while (accessCount.get() > 0) { - // Wait until o.a.c.connector.Request.recycle() completes, - // as it updates lastAccessedTime. - Assert.assertTrue(System.currentTimeMillis() < maxWaitTime); - Thread.sleep(200); - } - } +public class TestPersistentManager { @Test - public void noSessionCreate_57637() throws IOException, LifecycleException { - - // Setup Tomcat instance - Tomcat tomcat = getTomcatInstance(); - - File appDir = new File("test/webapp-3.0-fragments-empty-absolute-ordering"); - StandardContext ctx = (StandardContext) tomcat.addContext("", appDir.getAbsolutePath()); - ctx.setDistributable(true); - - Tomcat.addServlet(ctx, "DummyServlet", new DummyServlet()); - ctx.addServletMapping("/dummy", "DummyServlet"); - + public void testMinIdleSwap() throws Exception { PersistentManager manager = new PersistentManager(); - DummyStore store = new DummyStore(); + manager.setStore(new TesterStore()); - manager.setStore(store); - manager.setMaxIdleBackup(0); - ctx.setManager(manager); - ctx.addValve(new PersistentValve()); - tomcat.start(); - Assert.assertEquals(manager.getActiveSessions(), 0); - Assert.assertTrue("No sessions managed", manager.getSessionIdsFull().isEmpty()); - Assert.assertEquals( - "NO_SESSION", - getUrl( - "http://localhost:" + getPort() - + "/dummy?no_create_session=true").toString()); - Assert.assertEquals(manager.getActiveSessions(), 0); - Assert.assertTrue("No sessions where created", manager.getSessionIdsFull().isEmpty()); - } - - @Test - public void testCreateSessionAndPassivate() throws IOException, LifecycleException, ClassNotFoundException { - - // Setup Tomcat instance - Tomcat tomcat = getTomcatInstance(); - - File appDir = new File("test/webapp-3.0-fragments-empty-absolute-ordering"); - StandardContext ctx = (StandardContext) tomcat.addContext("", appDir.getAbsolutePath()); - ctx.setDistributable(true); + Host host = new TesterHost(); + Context context = new TesterContext(); + context.setParent(host); - Tomcat.addServlet(ctx, "DummyServlet", new DummyServlet()); - ctx.addServletMapping("/dummy", "DummyServlet"); + manager.setContainer(context); - PersistentManager manager = new PersistentManager(); - DummyStore store = new DummyStore(); - - manager.setStore(store); - manager.setMaxIdleBackup(0); - ctx.setManager(manager); - ctx.addValve(new PersistentValve()); - tomcat.start(); - Assert.assertEquals("No active sessions", manager.getActiveSessions(), 0); - Assert.assertTrue("No sessions managed", manager.getSessionIdsFull().isEmpty()); - String sessionId = getUrl( - "http://localhost:" + getPort() - + "/dummy?no_create_session=false").toString(); - Assert.assertNotNull("Session is stored", store.load(sessionId)); - Assert.assertEquals("All sessions are passivated", manager.getActiveSessions(), 0); - Assert.assertTrue("One session was created", !manager.getSessionIdsFull().isEmpty()); - } - - @Test - public void backsUpOnce_56698() throws IOException, LifecycleException, - InterruptedException { + manager.setMaxActiveSessions(2); + manager.setMinIdleSwap(0); - // Setup Tomcat instance - Tomcat tomcat = getTomcatInstance(); - // No file system docBase required - Context ctx = tomcat.addContext("", null); - ctx.setDistributable(true); + manager.start(); - Tomcat.addServlet(ctx, "DummyServlet", new DummyServlet()); - ctx.addServletMapping("/dummy", "DummyServlet"); + // Create the maximum number of sessions + manager.createSession(null); + manager.createSession(null); - PersistentManager manager = new PersistentManager(); - DummyStore store = new DummyStore(); - - manager.setStore(store); - manager.setMaxIdleBackup(0); - ctx.setManager(manager); - tomcat.start(); - String sessionId = getUrl("http://localhost:" + getPort() + "/dummy") - .toString(); - - // Note: PersistenceManager.findSession() silently updates - // session.lastAccessedTime, so call it only once before other work. - Session session = manager.findSession(sessionId); - - // Wait until request processing ends, as Request.recycle() updates - // session.lastAccessedTime via session.endAccess(). - waitWhileSessionIsActive((StandardSession) session); - - long lastAccessedTime = session.getLastAccessedTimeInternal(); - - // Session should be idle at least for 0 second (maxIdleBackup) - // to be eligible for persistence, thus no need to wait. - - // Waiting a bit, to catch changes in last accessed time of a session - waitForClockUpdate(); - - manager.processPersistenceChecks(); - Assert.assertEquals(Arrays.asList(sessionId), store.getSavedIds()); - Assert.assertEquals(lastAccessedTime, session.getLastAccessedTimeInternal()); - - // session was not accessed, so no save will be performed - waitForClockUpdate(); - manager.processPersistenceChecks(); - Assert.assertEquals(Arrays.asList(sessionId), store.getSavedIds()); - Assert.assertEquals(lastAccessedTime, session.getLastAccessedTimeInternal()); - - // access session - session.access(); - session.endAccess(); - - // session was accessed, so it will be saved once again - manager.processPersistenceChecks(); - Assert.assertEquals(Arrays.asList(sessionId, sessionId), - store.getSavedIds()); - - // session was not accessed, so once again no save will happen + // Given the minIdleSwap settings, this should swap one out to get below + // the limit manager.processPersistenceChecks(); - Assert.assertEquals(Arrays.asList(sessionId, sessionId), - store.getSavedIds()); - } - - private static class DummyServlet extends HttpServlet { - - private static final long serialVersionUID = -3696433049266123995L; - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { - boolean createSession = !Boolean.parseBoolean(req - .getParameter("no_create_session")); - HttpSession session = req.getSession(createSession); - if (session == null) { - resp.getWriter().print("NO_SESSION"); - } else { - String id = session.getId(); - resp.getWriter().print(id); - } - } - - } - - private static class DummyStore implements Store { + Assert.assertEquals(1, manager.getActiveSessions()); + Assert.assertEquals(2, manager.getActiveSessionsFull()); - private Manager manager; - private Map sessions = new HashMap(); - private List savedIds = new ArrayList(); - - List getSavedIds() { - return savedIds; - } - - @Override - public Manager getManager() { - return this.manager; - } - - @Override - public void setManager(Manager manager) { - this.manager = manager; - } - - @Override - public int getSize() throws IOException { - return 0; - } - - @Override - public void addPropertyChangeListener(PropertyChangeListener listener) { - } - - @Override - public String[] keys() throws IOException { - return new ArrayList(sessions.keySet()).toArray(new String[] {}); - } - - @Override - public Session load(String id) throws ClassNotFoundException, - IOException { - return sessions.get(id); - } - - @Override - public void remove(String id) throws IOException { - sessions.remove(id); - } - - @Override - public void clear() throws IOException { - } - - @Override - public void removePropertyChangeListener(PropertyChangeListener listener) { - } - - @Override - public void save(Session session) throws IOException { - sessions.put(session.getId(), session); - savedIds.add(session.getId()); - } - - @Override - public String getInfo() { - return null; - } + manager.createSession(null); + Assert.assertEquals(2, manager.getActiveSessions()); + Assert.assertEquals(3, manager.getActiveSessionsFull()); } } diff -Nru tomcat7-7.0.68/test/org/apache/tomcat/unittest/TesterContext.java tomcat7-7.0.69/test/org/apache/tomcat/unittest/TesterContext.java --- tomcat7-7.0.68/test/org/apache/tomcat/unittest/TesterContext.java 2015-12-01 13:07:02.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/tomcat/unittest/TesterContext.java 2016-04-06 20:24:19.000000000 +0000 @@ -148,24 +148,26 @@ // NO-OP } + private String name = "/test"; @Override public String getName() { - return "/test"; + return name; } @Override public void setName(String name) { - // NO-OP + this.name = name; } + private Container parent = null; @Override public Container getParent() { - return null; + return parent; } @Override public void setParent(Container container) { - // NO-OP + this.parent = container; } @Override diff -Nru tomcat7-7.0.68/test/org/apache/tomcat/unittest/TesterHost.java tomcat7-7.0.69/test/org/apache/tomcat/unittest/TesterHost.java --- tomcat7-7.0.68/test/org/apache/tomcat/unittest/TesterHost.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/tomcat/unittest/TesterHost.java 2016-04-06 20:24:19.000000000 +0000 @@ -0,0 +1,391 @@ +/* + * 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.unittest; + +import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.regex.Pattern; + +import javax.management.ObjectName; +import javax.naming.directory.DirContext; +import javax.servlet.ServletException; + +import org.apache.catalina.AccessLog; +import org.apache.catalina.Cluster; +import org.apache.catalina.Container; +import org.apache.catalina.ContainerListener; +import org.apache.catalina.Host; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.Loader; +import org.apache.catalina.Manager; +import org.apache.catalina.Pipeline; +import org.apache.catalina.Realm; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.juli.logging.Log; + +public class TesterHost implements Host { + + @Override + public Log getLogger() { + return null; + } + + @Override + public ObjectName getObjectName() { + return null; + } + + @Override + public Pipeline getPipeline() { + return null; + } + + @Override + public Cluster getCluster() { + return null; + } + + @Override + public void setCluster(Cluster cluster) { + // NO-OP + } + + @Override + public int getBackgroundProcessorDelay() { + return 0; + } + + @Override + public void setBackgroundProcessorDelay(int delay) { + // NO-OP + } + + private String name = "TestHost"; + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public Container getParent() { + return null; + } + + @Override + public void setParent(Container container) { + // NO-OP + } + + @Override + public ClassLoader getParentClassLoader() { + return null; + } + + @Override + public void setParentClassLoader(ClassLoader parent) { + // NO-OP + } + + @Override + public Realm getRealm() { + return null; + } + + @Override + public void setRealm(Realm realm) { + // NO-OP + } + + @Override + public void backgroundProcess() { + // NO-OP + } + + @Override + public void addChild(Container child) { + // NO-OP + } + + @Override + public void addContainerListener(ContainerListener listener) { + // NO-OP + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + // NO-OP + } + + @Override + public Container findChild(String name) { + return null; + } + + @Override + public Container[] findChildren() { + return null; + } + + @Override + public ContainerListener[] findContainerListeners() { + return null; + } + + @Override + public void removeChild(Container child) { + // NO-OP + } + + @Override + public void removeContainerListener(ContainerListener listener) { + // NO-OP + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + // NO-OP + } + + @Override + public void fireContainerEvent(String type, Object data) { + // NO-OP + } + + @Override + public void logAccess(Request request, Response response, long time, boolean useDefault) { + // NO-OP + } + + @Override + public AccessLog getAccessLog() { + return null; + } + + @Override + public int getStartStopThreads() { + return 0; + } + + @Override + public void setStartStopThreads(int startStopThreads) { + // NO-OP + } + + @Override + public void addLifecycleListener(LifecycleListener listener) { + // NO-OP + } + + @Override + public LifecycleListener[] findLifecycleListeners() { + return null; + } + + @Override + public void removeLifecycleListener(LifecycleListener listener) { + // NO-OP + } + + @Override + public void init() throws LifecycleException { + // NO-OP + } + + @Override + public void start() throws LifecycleException { + // NO-OP + } + + @Override + public void stop() throws LifecycleException { + // NO-OP + } + + @Override + public void destroy() throws LifecycleException { + // NO-OP + } + + @Override + public LifecycleState getState() { + return null; + } + + @Override + public String getStateName() { + return null; + } + + @Override + public String getXmlBase() { + return null; + } + + @Override + public void setXmlBase(String xmlBase) { + // NO-OP + } + + @Override + public String getAppBase() { + return null; + } + + @Override + public void setAppBase(String appBase) { + // NO-OP + } + + @Override + public boolean getAutoDeploy() { + return false; + } + + @Override + public void setAutoDeploy(boolean autoDeploy) { + // NO-OP + } + + @Override + public String getConfigClass() { + return null; + } + + @Override + public void setConfigClass(String configClass) { + // NO-OP + } + + @Override + public boolean getDeployOnStartup() { + return false; + } + + @Override + public void setDeployOnStartup(boolean deployOnStartup) { + // NO-OP + } + + @Override + public String getDeployIgnore() { + return null; + } + + @Override + public Pattern getDeployIgnorePattern() { + return null; + } + + @Override + public void setDeployIgnore(String deployIgnore) { + // NO-OP + } + + @Override + public ExecutorService getStartStopExecutor() { + return null; + } + + @Override + public boolean getCreateDirs() { + return false; + } + + @Override + public void setCreateDirs(boolean createDirs) { + // NO-OP + } + + @Override + public boolean getUndeployOldVersions() { + return false; + } + + @Override + public void setUndeployOldVersions(boolean undeployOldVersions) { + // NO-OP + } + + @Override + public void addAlias(String alias) { + // NO-OP + } + + @Override + public String[] findAliases() { + return null; + } + + @Override + public void removeAlias(String alias) { + // NO-OP + } + + @Override + public String getInfo() { + return null; + } + + @Override + public Loader getLoader() { + return null; + } + + @Override + public void setLoader(Loader loader) { + // NO-OP + } + + @Override + public Manager getManager() { + return null; + } + + @Override + public void setManager(Manager manager) { + // NO-OP + } + + @Override + public Object getMappingObject() { + return null; + } + + @Override + public DirContext getResources() { + return null; + } + + @Override + public void setResources(DirContext resources) { + // NO-OP + } + + @Override + public void invoke(Request request, Response response) throws IOException, ServletException { + // NO-OP + } +} diff -Nru tomcat7-7.0.68/test/org/apache/tomcat/util/buf/TestUriUtil.java tomcat7-7.0.69/test/org/apache/tomcat/util/buf/TestUriUtil.java --- tomcat7-7.0.68/test/org/apache/tomcat/util/buf/TestUriUtil.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/tomcat/util/buf/TestUriUtil.java 2016-03-01 23:04:09.000000000 +0000 @@ -0,0 +1,87 @@ +/* + * 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; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +import org.junit.Assert; +import org.junit.Test; + +public class TestUriUtil { + + @Test + public void testBuildJarUrl01() throws MalformedURLException { + File jarFile = new File("/patha/pathb!/pathc"); + String result = UriUtil.buildJarUrl(jarFile).toString(); + + int index = result.indexOf("!/"); + Assert.assertEquals(result, result.length() - 2, index); + } + + + @Test + public void testBuildJarUrl02() throws MalformedURLException { + File jarFile = new File("/patha/pathb*/pathc"); + String result = UriUtil.buildJarUrl(jarFile).toString(); + + int index = result.indexOf("!/"); + Assert.assertEquals(result, result.length() - 2, index); + + index = result.indexOf("*/"); + Assert.assertEquals(result, -1, index); + } + + + @Test + public void testBuildJarUrl03() throws MalformedURLException { + File jarFile = new File("/patha/pathb^/pathc"); + String result = UriUtil.buildJarUrl(jarFile).toString(); + + int index = result.indexOf("!/"); + Assert.assertEquals(result, result.length() - 2, index); + + index = result.indexOf("^/"); + Assert.assertEquals(result, -1, index); + } + + + // @Test /* Uncomment to test performance for different implementations. */ + public void performanceTestBuildJarUrl() throws MalformedURLException { + File jarFile = new File("/patha/pathb^/pathc"); + + URL url = null; + + int count = 1000000; + + // Warm up + for (int i = 0; i < count / 10; i++) { + url = UriUtil.buildJarUrl(jarFile); + } + + // Test + long start = System.nanoTime(); + for (int i = 0; i < count / 10; i++) { + url = UriUtil.buildJarUrl(jarFile); + } + long duration = System.nanoTime() - start; + + System.out.println("[" + count + "] iterations took [" + + duration + "] ns for [" + url + "]"); + } +} diff -Nru tomcat7-7.0.68/test/org/apache/tomcat/websocket/server/TestCloseBug58264.java tomcat7-7.0.69/test/org/apache/tomcat/websocket/server/TestCloseBug58264.java --- tomcat7-7.0.68/test/org/apache/tomcat/websocket/server/TestCloseBug58264.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat7-7.0.69/test/org/apache/tomcat/websocket/server/TestCloseBug58264.java 2016-02-23 15:00:53.000000000 +0000 @@ -0,0 +1,134 @@ +/* + * 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.websocket.server; + +import java.net.URI; + +import javax.servlet.ServletContextEvent; +import javax.websocket.ClientEndpoint; +import javax.websocket.CloseReason; +import javax.websocket.ContainerProvider; +import javax.websocket.DeploymentException; +import javax.websocket.OnClose; +import javax.websocket.OnError; +import javax.websocket.OnMessage; +import javax.websocket.OnOpen; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; +import javax.websocket.server.ServerContainer; +import javax.websocket.server.ServerEndpointConfig; + +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.servlets.DefaultServlet; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; + +public class TestCloseBug58264 extends TomcatBaseTest { + + @Test + public void testOnErrorNotCalledWhenClosingConnection() throws Throwable { + Tomcat tomcat = getTomcatInstance(); + // No file system docBase required + Context ctx = tomcat.addContext("", null); + ctx.addApplicationListener(Bug58624ServerConfig.class.getName()); + Tomcat.addServlet(ctx, "default", new DefaultServlet()); + ctx.addServletMapping("/", "default"); + + WebSocketContainer wsContainer = ContainerProvider.getWebSocketContainer(); + + tomcat.start(); + + Bug58624ClientEndpoint client = new Bug58624ClientEndpoint(); + URI uri = new URI("ws://localhost:" + getPort() + Bug58624ServerConfig.PATH); + + Session session = wsContainer.connectToServer(client, uri); + session.close(); + + if (client.getError() != null) { + throw client.getError(); + } + } + + @ClientEndpoint + public class Bug58624ClientEndpoint { + + private volatile Throwable t; + + + @OnError + public void onError(Throwable t) { + this.t = t; + } + + + public Throwable getError() { + return this.t; + } + } + + public static class Bug58624ServerConfig extends WsContextListener { + + public static String PATH = "/bug58624"; + + + @Override + public void contextInitialized(ServletContextEvent sce) { + super.contextInitialized(sce); + + ServerContainer sc = (ServerContainer) sce.getServletContext().getAttribute( + Constants.SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE); + + ServerEndpointConfig sec = ServerEndpointConfig.Builder.create( + Bug58624ServerEndpoint.class, PATH).build(); + + try { + sc.addEndpoint(sec); + } catch (DeploymentException e) { + throw new RuntimeException(e); + } + } + } + + public static class Bug58624ServerEndpoint { + + @OnOpen + public void onOpen() { + System.out.println("Session opened"); + } + + + @OnMessage + public void onMessage(@SuppressWarnings("unused") Session session, String message) { + System.out.println("Received message " + message); + } + + + @OnError + public void onError(Throwable t) { + System.out.println("HERE"); + t.printStackTrace(); + } + + + @OnClose + public void onClose(CloseReason cr) { + System.out.println("Session closed: " + cr); + } + } +} diff -Nru tomcat7-7.0.68/webapps/docs/building.xml tomcat7-7.0.69/webapps/docs/building.xml --- tomcat7-7.0.68/webapps/docs/building.xml 2015-08-18 17:49:57.000000000 +0000 +++ tomcat7-7.0.69/webapps/docs/building.xml 2016-02-23 22:23:52.000000000 +0000 @@ -119,12 +119,9 @@

- By default the build is configured to download dependencies into directory - /usr/share/java. On a typical Linux or MacOX system, an ordinary - user will not have access to write to this directory, and, even if you do, - it is likely not appropriate for you to write there. On Windows this usually - corresponds to the C:\usr\share\java directory, unless Cygwin is - used. + By default the build is configured to download the dependencies into the + ${user.home}/tomcat-build-libs directory. You can change this + (see below) but it must be an absolute path.

diff -Nru tomcat7-7.0.68/webapps/docs/changelog.xml tomcat7-7.0.69/webapps/docs/changelog.xml --- tomcat7-7.0.68/webapps/docs/changelog.xml 2016-02-04 11:59:55.000000000 +0000 +++ tomcat7-7.0.69/webapps/docs/changelog.xml 2016-04-11 07:27:16.000000000 +0000 @@ -57,7 +57,253 @@ They eventually become mixed with the numbered issues. (I.e., numbered issues do not "pop up" wrt. others). --> -

+
+ + + + Fix the type of InstanceManager attribute of mbean + definition of StandardContext. (kfujino) + + + 58351: Make the server build date and server version number + accessible via JMX. Patch provided by Huxing Zhang. (markt) + + + 59001: Correctly handle the case when Tomcat is installed on + a path where one of the segments ends in an exclamation mark. (markt) + + + Expand the fix for 59001 to cover the special sequences used + in Tomcat's custom jar:war: URLs. (markt) + + + 59043: Avoid warning while expiring sessions associated with + a single sign on if HttpServletRequest.logout() is used. + (markt) + + + 59054: Ensure that using the + CrawlerSessionManagerValve in a distributed environment + does not trigger an error when the Valve registers itself in the + session. (markt) + + + Log a warning message if a user tries to configure the default session + timeout via the deprecated (and ignored) + Manager.setMaxInactiveInterval() method. (markt) + + + Correct a regression introduced in 7.0.68 where the deprecated + Manager.getMaxInactiveInterval() method returned the + current default session timeout in minutes rather than seconds. (markt) + + + When a Host is configured with an appBase that does not exist, create + the appBase before trying to expand an external WAR file into it. + (markt) + + + 59115: When using the Servlet 3.0 file upload, the submitted + file name may be provided as a token or a quoted-string. If a + quoted-string, unquote the string before returning it to the user. + (markt) + + + 59123: Close NamingEnumeration objects used by + the JNDIRealm once they are no longer required. + (fschumacher/markt) + + + 59138: Correct a false positive warning for ThreadLocal + related memory leaks when the key class but not the value class has been + loaded by the web application class loader. (markt) + + + 59145: Don't log an invalid warning when a user logs out of + a session associated with SSO. (markt) + + + 59151: Fix a regression in the fix for 56917 that + added additional (and arguably unnecessary) validation to the provided + redirect location. (markt) + + + 59206: Ensure NPE will not be thrown by + o.a.tomcat.util.file.ConfigFileLoader when + catalina.base is not specified. (violetagg) + + + 59213: Async dispatches should be based off a wrapped request. + (remm) + + + 59217: Remove duplication in the recycling of the path in + o.a.tomcat.util.http.ServerCookie. Patch is provided by + Kyohei Nakamura. (violetagg) + + + Ensure that javax.servlet.ServletRequest and + javax.servlet.ServletResponse provided during + javax.servlet.AsyncListener registration are made + available via javax.servlet.AsyncEvent.getSuppliedRequest + and javax.servlet.AsyncEvent.getSuppliedResponse + (violetagg) + + + Clarify the log message that specifying both urlPatterns and value + attributes in WebServlet and WebFilter annotations is not allowed. + (violetagg) + + + Ensure the exceptions caused by Valves will be available in the log + files so that they can be evaluated when + o.a.catalina.valves.ErrorReportValve.showReport is + disabled. Patch is provided by Svetlin Zarev. (violetagg) + + + 59247: Preload ResourceEntry as a workaround for security + manager issues on some JVMs. (kkolinko/remm) + + + 59269: Correct the implementation of + PersistentManagerBase so that minIdleSwap + functions as designed and sessions are swapped out to keep the active + session count below maxActiveSessions. (markt) + + + + + + + 58646: Correct a problem with sendfile that resulted in a + Processor being added to the cache twice leading to broken responses. + (markt) + + + 59015: Fix potential cause of endless APR Poller loop during + shutdown if the Poller experiences an error during the shutdown process. + (markt) + + + Limit the default TLS ciphers for JSSE (BIO, NIO) and OpenSSL (APR) to + those currently considered secure. (markt) + + + Add a new environment variable JSSE_OPTS that is intended + to be used to pass JVM wide configuration to the JSSE implementation. + The default value is -Djdk.tls.ephemeralDHKeySize=2048 + which protects against weak Diffie-Hellman keys. (markt) + + + + + + + 59014: Ensure that a WebSocket close message can be sent + after a close message has been received. (markt) + + + Correctly handle compression of partial messages when the final message + fragment has a zero length payload. (markt) + + + Extend the WebSocket programmatic echo endpoint provided in the examples + to handle binary messages and also partial messages. This aligns the + code with Tomcat 8 and makes it easier to run the Autobahn testsuite + against the WebSocket implementation. (markt) + + + 59119: Correct read logic for WebSocket client when using + secure connections. (markt) + + + 59134: Correct client connect logic for secure connections + made through a proxy. (markt) + + + 59189: Explicitly release the native memory held by the + Inflater and Deflater when using + PerMessageDeflate and the WebSocket session ends. Based on a patch by + Henrik Olsson. (markt) + + + + + + + Correct the description of the + ServletRequest.getServerPort() in Proxy How-To. + Issue reported via comments.apache.org. (violetagg) + + + Fix a potential indefinite wait in the Comet Chat servlet in the + examples web application. (markt) + + + 59229: Fix error in HTTP docs and make clear the the HTTP NIO + connector uses non-blocking I/O to read the HTTP request headers. + (markt) + + + Update in the documentation the link to the maven repository where + Tomcat snapshot artifacts are deployed. (markt/violetagg) + + + Clarify in the documentation that calls to + ServletContext.log(String, Throwable) or + GenericServlet.log(String, Throwable) are logged at the + SEVERE level. (violetagg) + + + + + + + If promoting a proxy node to a primary node when getting a session, + notify the change of the new primary node to the original backup node. + (kfujino) + + + Avoid NPE when a proxy node failed to retrieve a backup entry. (kfujino) + + + Add log of when received an unexpected messages. (kfujino) + + + Add the flag indicating that member is a localMember. (kfujino) + + + + + + + 58283: Change the default download location for libraries + during the build process from /usr/share/java to + ${user.home}/tomcat-build-libs. Patch provided by + Ahmed Hosni. (markt) + + + 59031: When using the Windows uninstaller, do not remove the + contents of any directories that have been symlinked into the Tomcat + directory structure. (markt) + + + Modify the default tomcat-users.xml file to make it harder + for users to configure the entries intended for use with the examples + web application for the Manager application. (markt) + + + 59211: Add hamcrest to Eclipse classpath. Patch is provided + by Huxing Zhang. (violetagg) + + + 59280: Update the NSIS Installer used to build the + Windows Installers to version 2.51. (kkolinko) + + + +
+
@@ -169,7 +415,7 @@ Extend the feature available in the cluster session manager implementations that enables session attribute replication to be - filtered bases on attribute name to all session manager implementations. + filtered based on attribute name to all session manager implementations. Note that configuration attribute name has changed from sessionAttributeFilter to sessionAttributeNameFilter. Apply the filter on load as diff -Nru tomcat7-7.0.68/webapps/docs/config/ajp.xml tomcat7-7.0.69/webapps/docs/config/ajp.xml --- tomcat7-7.0.68/webapps/docs/config/ajp.xml 2016-01-27 11:42:26.000000000 +0000 +++ tomcat7-7.0.69/webapps/docs/config/ajp.xml 2016-03-30 10:58:45.000000000 +0000 @@ -180,7 +180,7 @@ PATH (Windows) or LD_LIBRARY_PATH (on most unix systems) environment variables contain the Tomcat native library, the native/APR connector will be used. If the native library cannot be - found, the Java based connector will be used.
+ found, the Java BIO based connector will be used.
To use an explicit protocol rather than rely on the auto-switching mechanism described above, the following values may be used:
org.apache.coyote.ajp.AjpProtocol diff -Nru tomcat7-7.0.68/webapps/docs/config/cluster-manager.xml tomcat7-7.0.69/webapps/docs/config/cluster-manager.xml --- tomcat7-7.0.68/webapps/docs/config/cluster-manager.xml 2016-01-27 13:13:20.000000000 +0000 +++ tomcat7-7.0.69/webapps/docs/config/cluster-manager.xml 2016-03-10 07:00:08.000000000 +0000 @@ -84,6 +84,8 @@ this value to true. Default value is false. +

Deprecated: This should be configured via the + Context.

The initial maximum time interval, in seconds, between client requests before a session is invalidated. A negative value will result in sessions never timing out. If the attribute is not provided, diff -Nru tomcat7-7.0.68/webapps/docs/config/http.xml tomcat7-7.0.69/webapps/docs/config/http.xml --- tomcat7-7.0.68/webapps/docs/config/http.xml 2015-11-11 09:55:51.000000000 +0000 +++ tomcat7-7.0.69/webapps/docs/config/http.xml 2016-03-31 15:52:41.000000000 +0000 @@ -987,12 +987,12 @@

The comma separated list of encryption ciphers to support for HTTPS connections. If specified, only the ciphers that are listed and supported by the SSL implementation will be used. By default, the default ciphers - for the JVM will be used. Note that this usually means that the weak - export grade ciphers will be included in the list of available ciphers. - The ciphers are specified using the JSSE cipher naming convention. The - special value of ALL will enable all supported ciphers. This - will include many that are not secure. ALL is intended for - testing purposes only.

+ for the JVM will be used less those considered to be insecure. Note that + with older JVMs this will result in a very limited set of ciphers being + available by default. The ciphers are specified using the JSSE cipher + naming convention. The special value of ALL will enable all + supported ciphers. This will include many that are not secure. + ALL is intended for testing purposes only.

@@ -1259,9 +1259,8 @@

Ciphers which may be used for communicating with clients. The default - is "ALL", with other acceptable values being a list of ciphers, with ":" - used as the delimiter (see OpenSSL documentation for the list of ciphers - supported).

+ is "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA". See the OpenSSL + documentation for details of the cipher configuration options.

@@ -1329,9 +1328,9 @@ Tomcat Version 3.x onwards 6.x onwards 5.5.x onwards Support Polling NO YES YES Polling Size N/A maxConnections maxConnections - Read HTTP Request Blocking Blocking Blocking - Read HTTP Body Blocking Blocking Blocking - Write HTTP Response Blocking Blocking Blocking + Read Request Headers Blocking Non Blocking Blocking + Read Request Body Blocking Blocking Blocking + Write Response Blocking Blocking Blocking Wait for next Request Blocking Non Blocking Non Blocking SSL Support Java SSL Java SSL OpenSSL SSL Handshake Blocking Non blocking Blocking diff -Nru tomcat7-7.0.68/webapps/docs/config/manager.xml tomcat7-7.0.69/webapps/docs/config/manager.xml --- tomcat7-7.0.68/webapps/docs/config/manager.xml 2016-01-27 13:13:20.000000000 +0000 +++ tomcat7-7.0.69/webapps/docs/config/manager.xml 2016-04-06 20:27:42.000000000 +0000 @@ -254,21 +254,21 @@ -

The time interval (in seconds) since the last access to a session - before it should be persisted to the session store, and - passivated out of the server's memory, or -1 to disable - this feature. If this feature is enabled, the time interval specified - here should be equal to or longer than the value specified for - maxIdleBackup. By default, this feature is disabled.

+

The maximum time a session may be idle before it is eligible to be + swapped to disk due to inactivity. Setting this to -1 means + sessions should not be swapped out just because of inactivity. If this + feature is enabled, the time interval specified here should be equal to + or longer than the value specified for maxIdleBackup. By + default, this feature is disabled.

-

The time interval (in seconds) since the last access to a session - before it will be eligible to be persisted to the session store, and - passivated out of the server's memory, or -1 for this - swapping to be available at any time. If specified, this value should - be less than that specified by maxIdleSwap. By default, - this value is set to -1.

+

The minimum time in seconds a session must be idle before it is + eligible to be swapped to disk to keep the active session count below + maxActiveSessions. Setting to -1 means sessions will not be + swapped out to keep the active session count down. If specified, this + value should be less than that specified by maxIdleSwap. + By default, this value is set to -1.

diff -Nru tomcat7-7.0.68/webapps/docs/logging.xml tomcat7-7.0.69/webapps/docs/logging.xml --- tomcat7-7.0.68/webapps/docs/logging.xml 2015-08-11 19:37:04.000000000 +0000 +++ tomcat7-7.0.69/webapps/docs/logging.xml 2016-04-06 10:40:30.000000000 +0000 @@ -139,7 +139,7 @@ or GenericServlet.log(String) are logged at the INFO level. The calls to ServletContext.log(String, Throwable) or GenericServlet.log(String, Throwable) - are logged at the ERROR level. + are logged at the SEVERE level.

diff -Nru tomcat7-7.0.68/webapps/docs/maven-jars.xml tomcat7-7.0.69/webapps/docs/maven-jars.xml --- tomcat7-7.0.68/webapps/docs/maven-jars.xml 2011-11-10 04:15:34.000000000 +0000 +++ tomcat7-7.0.69/webapps/docs/maven-jars.xml 2016-04-04 18:35:34.000000000 +0000 @@ -36,8 +36,8 @@
Tomcat snapshots are located in the - Apache Snapshot Repository. - The official URL is http://people.apache.org/repo/m2-snapshot-repository/org/apache/tomcat/ + Apache Snapshot Repository. + The official URL is http://repository.apache.org/content/repositories/snapshots/org/apache/tomcat/ Snapshots are done periodically, not on a regular basis, but when changes happen and the Tomcat team deems a new snapshot might useful. diff -Nru tomcat7-7.0.68/webapps/docs/proxy-howto.xml tomcat7-7.0.69/webapps/docs/proxy-howto.xml --- tomcat7-7.0.68/webapps/docs/proxy-howto.xml 2014-01-26 22:13:11.000000000 +0000 +++ tomcat7-7.0.69/webapps/docs/proxy-howto.xml 2016-03-09 08:31:26.000000000 +0000 @@ -44,7 +44,7 @@ calls of interest, for this purpose, are:

  • ServletRequest.getServerName(): Returns the host name of the server to which the request was sent.
  • -
  • ServletRequest.getServerPort(): Returns the host name of the server to which the request was sent.
  • +
  • ServletRequest.getServerPort(): Returns the port number of the server to which the request was sent.
  • ServletRequest.getLocalName(): Returns the host name of the Internet Protocol (IP) interface on which the request was received.
  • ServletRequest.getLocalPort(): Returns the Internet Protocol (IP) port number of the interface on which the request was received.
diff -Nru tomcat7-7.0.68/webapps/examples/WEB-INF/classes/chat/ChatServlet.java tomcat7-7.0.69/webapps/examples/WEB-INF/classes/chat/ChatServlet.java --- tomcat7-7.0.68/webapps/examples/WEB-INF/classes/chat/ChatServlet.java 2014-01-26 22:13:11.000000000 +0000 +++ tomcat7-7.0.69/webapps/examples/WEB-INF/classes/chat/ChatServlet.java 2016-03-11 11:15:04.000000000 +0000 @@ -225,7 +225,7 @@ String[] pendingMessages; synchronized (messages) { try { - if (messages.size() == 0) { + if (running && messages.size() == 0) { messages.wait(); } } catch (InterruptedException e) { diff -Nru tomcat7-7.0.68/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java tomcat7-7.0.69/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java --- tomcat7-7.0.68/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java 2013-10-15 11:07:03.000000000 +0000 +++ tomcat7-7.0.69/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java 2016-03-01 00:24:52.000000000 +0000 @@ -17,6 +17,7 @@ package websocket.echo; import java.io.IOException; +import java.nio.ByteBuffer; import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; @@ -29,23 +30,46 @@ @Override public void onOpen(Session session, EndpointConfig endpointConfig) { RemoteEndpoint.Basic remoteEndpointBasic = session.getBasicRemote(); - session.addMessageHandler(new EchoMessageHandler(remoteEndpointBasic)); + session.addMessageHandler(new EchoMessageHandlerText(remoteEndpointBasic)); + session.addMessageHandler(new EchoMessageHandlerBinary(remoteEndpointBasic)); } - private static class EchoMessageHandler - implements MessageHandler.Whole { + private static class EchoMessageHandlerText + implements MessageHandler.Partial { private final RemoteEndpoint.Basic remoteEndpointBasic; - private EchoMessageHandler(RemoteEndpoint.Basic remoteEndpointBasic) { + private EchoMessageHandlerText(RemoteEndpoint.Basic remoteEndpointBasic) { this.remoteEndpointBasic = remoteEndpointBasic; } @Override - public void onMessage(String message) { + public void onMessage(String message, boolean last) { try { if (remoteEndpointBasic != null) { - remoteEndpointBasic.sendText(message); + remoteEndpointBasic.sendText(message, last); + } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + private static class EchoMessageHandlerBinary + implements MessageHandler.Partial { + + private final RemoteEndpoint.Basic remoteEndpointBasic; + + private EchoMessageHandlerBinary(RemoteEndpoint.Basic remoteEndpointBasic) { + this.remoteEndpointBasic = remoteEndpointBasic; + } + + @Override + public void onMessage(ByteBuffer message, boolean last) { + try { + if (remoteEndpointBasic != null) { + remoteEndpointBasic.sendBinary(message, last); } } catch (IOException e) { // TODO Auto-generated catch block