Version in base suite: 10.1.34-0+deb12u2 Base version: tomcat10_10.1.34-0+deb12u2 Target version: tomcat10_10.1.52-1~deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/t/tomcat10/tomcat10_10.1.34-0+deb12u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/t/tomcat10/tomcat10_10.1.52-1~deb12u1.dsc /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/res/install-win/Uninstall.exe.sig |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/res/install-win/tomcat-installer.exe.sig |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/ca.jks |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-crl-rsa.jks |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-ec.jks |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa-copy1.jks |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa.jks |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/user1.jks |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/user2-crl.jks |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/user3-crl-long.jks |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/webapp/WEB-INF/lib/bug69623-lib.jar |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/webresources/dir1.jar |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/11_nio.png |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/21_http11.png |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/31_synchronous.png |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/41_basic.png |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/authentication-process.png |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/request-process.png |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/host-manager/images/favicon.ico |binary /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/manager/images/favicon.ico |binary tomcat10-10.1.52/.github/workflows/ci.yml | 7 tomcat10-10.1.52/.gitignore | 1 tomcat10-10.1.52/BUILDING.txt | 112 tomcat10-10.1.52/CONTRIBUTING.md | 2 tomcat10-10.1.52/MERGE.txt | 6 tomcat10-10.1.52/NOTICE | 2 tomcat10-10.1.52/README.md | 4 tomcat10-10.1.52/RUNNING.txt | 4 tomcat10-10.1.52/bin/catalina.bat | 10 tomcat10-10.1.52/bin/catalina.sh | 8 tomcat10-10.1.52/bin/service.bat | 12 tomcat10-10.1.52/build.properties.default | 98 tomcat10-10.1.52/build.properties.release | 8 tomcat10-10.1.52/build.xml | 682 tomcat10-10.1.52/conf/web.xml | 40 tomcat10-10.1.52/debian/changelog | 62 tomcat10-10.1.52/debian/copyright | 6 tomcat10-10.1.52/debian/libexec/tomcat-locate-java.sh | 2 tomcat10-10.1.52/debian/patches/0004-split-deploy-webapps-target-from-deploy-target.patch | 6 tomcat10-10.1.52/debian/patches/0005-skip-test-failures.patch | 4 tomcat10-10.1.52/debian/patches/0009-Use-java.security.policy-file-in-catalina.sh.patch | 8 tomcat10-10.1.52/debian/patches/0010-debianize-build-xml.patch | 6 tomcat10-10.1.52/debian/patches/0013-dont-look-for-build-properties-in-user-home.patch | 4 tomcat10-10.1.52/debian/patches/0018-fix-manager-webapp.patch | 16 tomcat10-10.1.52/debian/patches/0019-add-distribution-to-error-page.patch | 4 tomcat10-10.1.52/debian/patches/0021-dont-test-unsupported-ciphers.patch | 12 tomcat10-10.1.52/debian/patches/0023-disable-shutdown-by-socket.patch | 2 tomcat10-10.1.52/debian/patches/0024-systemd-log-formatter.patch | 3 tomcat10-10.1.52/debian/patches/0025-invalid-configuration-exit-status.patch | 14 tomcat10-10.1.52/debian/patches/0026-easymock4-compatibility.patch | 51 tomcat10-10.1.52/debian/patches/0030-eclipse-jdt-classpath.patch | 21 tomcat10-10.1.52/debian/patches/disable-jacoco.patch | 14 tomcat10-10.1.52/debian/patches/exclude-TestJNDIRealmIntegration.patch | 14 tomcat10-10.1.52/debian/patches/series | 4 tomcat10-10.1.52/java/jakarta/el/ELContextListener.java | 3 tomcat10-10.1.52/java/jakarta/el/ELResolver.java | 3 tomcat10-10.1.52/java/jakarta/el/ExpressionFactory.java | 6 tomcat10-10.1.52/java/jakarta/el/ExpressionFactoryCache.java | 84 tomcat10-10.1.52/java/jakarta/el/ImportHandler.java | 17 tomcat10-10.1.52/java/jakarta/el/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/jakarta/el/Util.java | 107 tomcat10-10.1.52/java/jakarta/servlet/LocalStrings_ru.properties | 6 tomcat10-10.1.52/java/jakarta/servlet/http/HttpServlet.java | 13 tomcat10-10.1.52/java/jakarta/servlet/http/HttpServletRequest.java | 7 tomcat10-10.1.52/java/jakarta/servlet/http/LocalStrings_ja.properties | 2 tomcat10-10.1.52/java/jakarta/servlet/http/LocalStrings_ru.properties | 21 tomcat10-10.1.52/java/jakarta/servlet/jsp/LocalStrings_ru.properties | 19 tomcat10-10.1.52/java/jakarta/servlet/jsp/tagext/BodyContent.java | 2 tomcat10-10.1.52/java/jakarta/websocket/ContainerProvider.java | 2 tomcat10-10.1.52/java/jakarta/websocket/server/ServerEndpointConfig.java | 2 tomcat10-10.1.52/java/org/apache/catalina/Authenticator.java | 2 tomcat10-10.1.52/java/org/apache/catalina/Cluster.java | 3 tomcat10-10.1.52/java/org/apache/catalina/Contained.java | 5 tomcat10-10.1.52/java/org/apache/catalina/Container.java | 7 tomcat10-10.1.52/java/org/apache/catalina/ContainerEvent.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ContainerListener.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ContainerServlet.java | 2 tomcat10-10.1.52/java/org/apache/catalina/Context.java | 104 tomcat10-10.1.52/java/org/apache/catalina/Engine.java | 2 tomcat10-10.1.52/java/org/apache/catalina/Globals.java | 5 tomcat10-10.1.52/java/org/apache/catalina/Group.java | 2 tomcat10-10.1.52/java/org/apache/catalina/Host.java | 10 tomcat10-10.1.52/java/org/apache/catalina/Lifecycle.java | 2 tomcat10-10.1.52/java/org/apache/catalina/LifecycleEvent.java | 2 tomcat10-10.1.52/java/org/apache/catalina/LifecycleException.java | 2 tomcat10-10.1.52/java/org/apache/catalina/LifecycleListener.java | 6 tomcat10-10.1.52/java/org/apache/catalina/Loader.java | 2 tomcat10-10.1.52/java/org/apache/catalina/Manager.java | 2 tomcat10-10.1.52/java/org/apache/catalina/Pipeline.java | 10 tomcat10-10.1.52/java/org/apache/catalina/Realm.java | 4 tomcat10-10.1.52/java/org/apache/catalina/Role.java | 2 tomcat10-10.1.52/java/org/apache/catalina/Server.java | 4 tomcat10-10.1.52/java/org/apache/catalina/Service.java | 4 tomcat10-10.1.52/java/org/apache/catalina/Session.java | 6 tomcat10-10.1.52/java/org/apache/catalina/SessionEvent.java | 2 tomcat10-10.1.52/java/org/apache/catalina/SessionListener.java | 2 tomcat10-10.1.52/java/org/apache/catalina/Store.java | 22 tomcat10-10.1.52/java/org/apache/catalina/User.java | 2 tomcat10-10.1.52/java/org/apache/catalina/UserDatabase.java | 4 tomcat10-10.1.52/java/org/apache/catalina/Valve.java | 9 tomcat10-10.1.52/java/org/apache/catalina/WebResourceRoot.java | 49 tomcat10-10.1.52/java/org/apache/catalina/WebResourceSet.java | 15 tomcat10-10.1.52/java/org/apache/catalina/Wrapper.java | 8 tomcat10-10.1.52/java/org/apache/catalina/ant/AbstractCatalinaTask.java | 25 tomcat10-10.1.52/java/org/apache/catalina/ant/BaseRedirectorHelperTask.java | 18 tomcat10-10.1.52/java/org/apache/catalina/ant/DeployTask.java | 8 tomcat10-10.1.52/java/org/apache/catalina/ant/JKStatusUpdateTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/JMXGetTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/JMXQueryTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/JMXSetTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/ListTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/ReloadTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/ResourcesTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/ServerinfoTask.java | 8 tomcat10-10.1.52/java/org/apache/catalina/ant/SessionsTask.java | 5 tomcat10-10.1.52/java/org/apache/catalina/ant/StartTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/StopTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/UndeployTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/ValidatorTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorCondition.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java | 12 tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorEqualsCondition.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java | 28 tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java | 13 tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java | 8 tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java | 14 tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java | 5 tomcat10-10.1.52/java/org/apache/catalina/authenticator/AuthenticatorBase.java | 139 tomcat10-10.1.52/java/org/apache/catalina/authenticator/BasicAuthenticator.java | 8 tomcat10-10.1.52/java/org/apache/catalina/authenticator/Constants.java | 2 tomcat10-10.1.52/java/org/apache/catalina/authenticator/DigestAuthenticator.java | 41 tomcat10-10.1.52/java/org/apache/catalina/authenticator/FormAuthenticator.java | 97 tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings.properties | 9 tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_fr.properties | 5 tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_ja.properties | 5 tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_ru.properties | 3 tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_zh_CN.properties | 3 tomcat10-10.1.52/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java | 4 tomcat10-10.1.52/java/org/apache/catalina/authenticator/SSLAuthenticator.java | 84 tomcat10-10.1.52/java/org/apache/catalina/authenticator/SavedRequest.java | 21 tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOn.java | 125 tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOnEntry.java | 20 tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOnListener.java | 14 tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOnSessionKey.java | 26 tomcat10-10.1.52/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java | 24 tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java | 38 tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java | 2 tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/LocalStrings_ru.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/PersistentProviderRegistrations.java | 8 tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/SimpleAuthConfigProvider.java | 2 tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthConfig.java | 10 tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthContext.java | 2 tomcat10-10.1.52/java/org/apache/catalina/connector/ClientAbortException.java | 2 tomcat10-10.1.52/java/org/apache/catalina/connector/Connector.java | 104 tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteAdapter.java | 79 tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteInputStream.java | 2 tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteOutputStream.java | 42 tomcat10-10.1.52/java/org/apache/catalina/connector/CoyotePrincipal.java | 2 tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteReader.java | 11 tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteWriter.java | 28 tomcat10-10.1.52/java/org/apache/catalina/connector/InputBuffer.java | 34 tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_es.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_fr.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_ja.properties | 4 tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_ru.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/connector/OutputBuffer.java | 38 tomcat10-10.1.52/java/org/apache/catalina/connector/Request.java | 277 tomcat10-10.1.52/java/org/apache/catalina/connector/RequestFacade.java | 5 tomcat10-10.1.52/java/org/apache/catalina/connector/Response.java | 49 tomcat10-10.1.52/java/org/apache/catalina/connector/ResponseFacade.java | 4 tomcat10-10.1.52/java/org/apache/catalina/core/AccessLogAdapter.java | 2 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationContext.java | 97 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationContextFacade.java | 2 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationDispatcher.java | 106 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationFilterChain.java | 18 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationFilterConfig.java | 27 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationFilterFactory.java | 7 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationHttpRequest.java | 40 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationHttpResponse.java | 2 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationPushBuilder.java | 2 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationRequest.java | 5 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationResponse.java | 4 tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationSessionCookieConfig.java | 2 tomcat10-10.1.52/java/org/apache/catalina/core/AprLifecycleListener.java | 144 tomcat10-10.1.52/java/org/apache/catalina/core/AprStatus.java | 30 tomcat10-10.1.52/java/org/apache/catalina/core/AsyncContextImpl.java | 8 tomcat10-10.1.52/java/org/apache/catalina/core/ContainerBase.java | 24 tomcat10-10.1.52/java/org/apache/catalina/core/DefaultInstanceManager.java | 48 tomcat10-10.1.52/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java | 10 tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings_ja.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings_ru.properties | 5 tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings_zh_CN.properties | 4 tomcat10-10.1.52/java/org/apache/catalina/core/NamingContextListener.java | 353 tomcat10-10.1.52/java/org/apache/catalina/core/OpenSSLLifecycleListener.java | 52 tomcat10-10.1.52/java/org/apache/catalina/core/PropertiesRoleMappingListener.java | 4 tomcat10-10.1.52/java/org/apache/catalina/core/StandardContext.java | 269 tomcat10-10.1.52/java/org/apache/catalina/core/StandardContextValve.java | 2 tomcat10-10.1.52/java/org/apache/catalina/core/StandardEngine.java | 2 tomcat10-10.1.52/java/org/apache/catalina/core/StandardEngineValve.java | 2 tomcat10-10.1.52/java/org/apache/catalina/core/StandardHost.java | 41 tomcat10-10.1.52/java/org/apache/catalina/core/StandardHostValve.java | 18 tomcat10-10.1.52/java/org/apache/catalina/core/StandardPipeline.java | 4 tomcat10-10.1.52/java/org/apache/catalina/core/StandardServer.java | 32 tomcat10-10.1.52/java/org/apache/catalina/core/StandardService.java | 17 tomcat10-10.1.52/java/org/apache/catalina/core/StandardWrapper.java | 83 tomcat10-10.1.52/java/org/apache/catalina/core/StandardWrapperFacade.java | 2 tomcat10-10.1.52/java/org/apache/catalina/core/StandardWrapperValve.java | 52 tomcat10-10.1.52/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java | 6 tomcat10-10.1.52/java/org/apache/catalina/deploy/NamingResourcesImpl.java | 44 tomcat10-10.1.52/java/org/apache/catalina/filters/AddDefaultCharsetFilter.java | 2 tomcat10-10.1.52/java/org/apache/catalina/filters/Constants.java | 8 tomcat10-10.1.52/java/org/apache/catalina/filters/CorsFilter.java | 97 tomcat10-10.1.52/java/org/apache/catalina/filters/CsrfPreventionFilter.java | 117 tomcat10-10.1.52/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java | 8 tomcat10-10.1.52/java/org/apache/catalina/filters/ExpiresFilter.java | 16 tomcat10-10.1.52/java/org/apache/catalina/filters/FilterBase.java | 2 tomcat10-10.1.52/java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java | 6 tomcat10-10.1.52/java/org/apache/catalina/filters/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/filters/RateLimitFilter.java | 23 tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteAddrFilter.java | 3 tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteCIDRFilter.java | 67 tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteHostFilter.java | 2 tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteIpFilter.java | 64 tomcat10-10.1.52/java/org/apache/catalina/filters/RequestDumperFilter.java | 13 tomcat10-10.1.52/java/org/apache/catalina/filters/RequestFilter.java | 13 tomcat10-10.1.52/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java | 16 tomcat10-10.1.52/java/org/apache/catalina/filters/SetCharacterEncodingFilter.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterDeployer.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterListener.java | 7 tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterManager.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterRuleSet.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterValve.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ha/backend/CollectedInfo.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ha/backend/HeartbeatListener.java | 18 tomcat10-10.1.52/java/org/apache/catalina/ha/backend/MultiCastSender.java | 8 tomcat10-10.1.52/java/org/apache/catalina/ha/backend/TcpSender.java | 19 tomcat10-10.1.52/java/org/apache/catalina/ha/context/ReplicatedContext.java | 10 tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java | 44 tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/FileMessage.java | 7 tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/FileMessageFactory.java | 8 tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/UndeployMessage.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/WarWatcher.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ha/session/BackupManager.java | 9 tomcat10-10.1.52/java/org/apache/catalina/ha/session/ClusterManagerBase.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ha/session/ClusterSessionListener.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ha/session/DeltaManager.java | 255 tomcat10-10.1.52/java/org/apache/catalina/ha/session/DeltaRequest.java | 37 tomcat10-10.1.52/java/org/apache/catalina/ha/session/DeltaSession.java | 20 tomcat10-10.1.52/java/org/apache/catalina/ha/session/JvmRouteBinderValve.java | 14 tomcat10-10.1.52/java/org/apache/catalina/ha/session/LocalStrings_ja.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/ha/session/SessionMessage.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ha/session/SessionMessageImpl.java | 6 tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/Constants.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/LocalStrings_ja.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/ReplicationValve.java | 60 tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/SendMessageData.java | 3 tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java | 33 tomcat10-10.1.52/java/org/apache/catalina/loader/JdbcLeakPrevention.java | 6 tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_fr.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_ja.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_ko.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_zh_CN.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/loader/ResourceEntry.java | 2 tomcat10-10.1.52/java/org/apache/catalina/loader/WebappClassLoaderBase.java | 55 tomcat10-10.1.52/java/org/apache/catalina/loader/WebappLoader.java | 58 tomcat10-10.1.52/java/org/apache/catalina/manager/Constants.java | 3 tomcat10-10.1.52/java/org/apache/catalina/manager/HTMLManagerServlet.java | 85 tomcat10-10.1.52/java/org/apache/catalina/manager/JMXProxyServlet.java | 34 tomcat10-10.1.52/java/org/apache/catalina/manager/JspHelper.java | 8 tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings.properties | 3 tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_fr.properties | 3 tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_ja.properties | 5 tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_ko.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_pt_BR.properties | 3 tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_ru.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/manager/ManagerServlet.java | 115 tomcat10-10.1.52/java/org/apache/catalina/manager/StatusManagerServlet.java | 10 tomcat10-10.1.52/java/org/apache/catalina/manager/StatusTransformer.java | 16 tomcat10-10.1.52/java/org/apache/catalina/manager/host/Constants.java | 2 tomcat10-10.1.52/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java | 21 tomcat10-10.1.52/java/org/apache/catalina/manager/host/HostManagerServlet.java | 23 tomcat10-10.1.52/java/org/apache/catalina/manager/host/LocalStrings_ja.properties | 4 tomcat10-10.1.52/java/org/apache/catalina/manager/host/LocalStrings_ru.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/manager/host/LocalStrings_zh_CN.properties | 7 tomcat10-10.1.52/java/org/apache/catalina/manager/util/SessionUtils.java | 15 tomcat10-10.1.52/java/org/apache/catalina/mapper/LocalStrings_ru.properties | 19 tomcat10-10.1.52/java/org/apache/catalina/mapper/Mapper.java | 33 tomcat10-10.1.52/java/org/apache/catalina/mapper/MapperListener.java | 5 tomcat10-10.1.52/java/org/apache/catalina/mapper/MappingData.java | 2 tomcat10-10.1.52/java/org/apache/catalina/mbeans/ClassNameMBean.java | 2 tomcat10-10.1.52/java/org/apache/catalina/mbeans/ConnectorMBean.java | 4 tomcat10-10.1.52/java/org/apache/catalina/mbeans/ContextEnvironmentMBean.java | 6 tomcat10-10.1.52/java/org/apache/catalina/mbeans/ContextResourceLinkMBean.java | 6 tomcat10-10.1.52/java/org/apache/catalina/mbeans/ContextResourceMBean.java | 6 tomcat10-10.1.52/java/org/apache/catalina/mbeans/DataSourceUserDatabaseMBean.java | 22 tomcat10-10.1.52/java/org/apache/catalina/mbeans/GlobalResourcesLifecycleListener.java | 4 tomcat10-10.1.52/java/org/apache/catalina/mbeans/GroupMBean.java | 2 tomcat10-10.1.52/java/org/apache/catalina/mbeans/MBeanDumper.java | 8 tomcat10-10.1.52/java/org/apache/catalina/mbeans/MBeanFactory.java | 22 tomcat10-10.1.52/java/org/apache/catalina/mbeans/MBeanUtils.java | 33 tomcat10-10.1.52/java/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java | 2 tomcat10-10.1.52/java/org/apache/catalina/mbeans/NamingResourcesMBean.java | 4 tomcat10-10.1.52/java/org/apache/catalina/mbeans/RoleMBean.java | 2 tomcat10-10.1.52/java/org/apache/catalina/mbeans/ServiceMBean.java | 4 tomcat10-10.1.52/java/org/apache/catalina/mbeans/SparseUserDatabaseMBean.java | 16 tomcat10-10.1.52/java/org/apache/catalina/mbeans/UserMBean.java | 2 tomcat10-10.1.52/java/org/apache/catalina/realm/AuthenticatedUserRealm.java | 2 tomcat10-10.1.52/java/org/apache/catalina/realm/CombinedRealm.java | 10 tomcat10-10.1.52/java/org/apache/catalina/realm/DataSourceRealm.java | 36 tomcat10-10.1.52/java/org/apache/catalina/realm/DigestCredentialHandlerBase.java | 13 tomcat10-10.1.52/java/org/apache/catalina/realm/GenericPrincipal.java | 11 tomcat10-10.1.52/java/org/apache/catalina/realm/JAASCallbackHandler.java | 9 tomcat10-10.1.52/java/org/apache/catalina/realm/JAASMemoryLoginModule.java | 31 tomcat10-10.1.52/java/org/apache/catalina/realm/JAASRealm.java | 45 tomcat10-10.1.52/java/org/apache/catalina/realm/JNDIRealm.java | 140 tomcat10-10.1.52/java/org/apache/catalina/realm/LocalStrings_ru.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/realm/LockOutRealm.java | 19 tomcat10-10.1.52/java/org/apache/catalina/realm/MemoryRealm.java | 6 tomcat10-10.1.52/java/org/apache/catalina/realm/MemoryRuleSet.java | 2 tomcat10-10.1.52/java/org/apache/catalina/realm/MessageDigestCredentialHandler.java | 18 tomcat10-10.1.52/java/org/apache/catalina/realm/NullRealm.java | 4 tomcat10-10.1.52/java/org/apache/catalina/realm/RealmBase.java | 66 tomcat10-10.1.52/java/org/apache/catalina/realm/SecretKeyCredentialHandler.java | 3 tomcat10-10.1.52/java/org/apache/catalina/realm/UserDatabaseRealm.java | 17 tomcat10-10.1.52/java/org/apache/catalina/realm/X509UsernameRetriever.java | 8 tomcat10-10.1.52/java/org/apache/catalina/security/SecurityClassLoad.java | 2 tomcat10-10.1.52/java/org/apache/catalina/security/SecurityConfig.java | 2 tomcat10-10.1.52/java/org/apache/catalina/security/SecurityListener.java | 16 tomcat10-10.1.52/java/org/apache/catalina/security/SecurityUtil.java | 1 tomcat10-10.1.52/java/org/apache/catalina/security/TLSCertificateReloadListener.java | 4 tomcat10-10.1.52/java/org/apache/catalina/servlets/CGIServlet.java | 345 tomcat10-10.1.52/java/org/apache/catalina/servlets/DataSourcePropertyStore.java | 102 tomcat10-10.1.52/java/org/apache/catalina/servlets/DefaultServlet.java | 607 tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings.properties | 9 tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_fr.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_ja.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_ko.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_ru.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/servlets/WebdavServlet.java | 362 tomcat10-10.1.52/java/org/apache/catalina/session/Constants.java | 14 tomcat10-10.1.52/java/org/apache/catalina/session/DataSourceStore.java | 32 tomcat10-10.1.52/java/org/apache/catalina/session/FileStore.java | 74 tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings.properties | 8 tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_es.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_fr.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_ja.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_ko.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_zh_CN.properties | 6 tomcat10-10.1.52/java/org/apache/catalina/session/ManagerBase.java | 8 tomcat10-10.1.52/java/org/apache/catalina/session/PersistentManager.java | 2 tomcat10-10.1.52/java/org/apache/catalina/session/PersistentManagerBase.java | 60 tomcat10-10.1.52/java/org/apache/catalina/session/StandardManager.java | 14 tomcat10-10.1.52/java/org/apache/catalina/session/StandardSession.java | 46 tomcat10-10.1.52/java/org/apache/catalina/session/StandardSessionFacade.java | 2 tomcat10-10.1.52/java/org/apache/catalina/session/StoreBase.java | 18 tomcat10-10.1.52/java/org/apache/catalina/ssi/ByteArrayServletOutputStream.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ssi/ExpressionParseTree.java | 40 tomcat10-10.1.52/java/org/apache/catalina/ssi/ExpressionTokenizer.java | 10 tomcat10-10.1.52/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java | 5 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSICommand.java | 5 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIConditional.java | 7 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIConditionalState.java | 6 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIConfig.java | 8 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIEcho.java | 5 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIExec.java | 14 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIExternalResolver.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIFilter.java | 2 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIFlastmod.java | 13 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIFsize.java | 29 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIInclude.java | 11 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIMediator.java | 14 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIPrintenv.java | 5 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIProcessor.java | 14 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIServlet.java | 10 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIServletExternalResolver.java | 44 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIServletRequestUtil.java | 4 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSISet.java | 6 tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIStopProcessingException.java | 4 tomcat10-10.1.52/java/org/apache/catalina/startup/Bootstrap.java | 107 tomcat10-10.1.52/java/org/apache/catalina/startup/Catalina.java | 25 tomcat10-10.1.52/java/org/apache/catalina/startup/CatalinaBaseConfigurationSource.java | 12 tomcat10-10.1.52/java/org/apache/catalina/startup/CatalinaProperties.java | 4 tomcat10-10.1.52/java/org/apache/catalina/startup/CertificateCreateRule.java | 2 tomcat10-10.1.52/java/org/apache/catalina/startup/ClassLoaderFactory.java | 8 tomcat10-10.1.52/java/org/apache/catalina/startup/ConnectorCreateRule.java | 4 tomcat10-10.1.52/java/org/apache/catalina/startup/Constants.java | 2 tomcat10-10.1.52/java/org/apache/catalina/startup/ContextConfig.java | 95 tomcat10-10.1.52/java/org/apache/catalina/startup/ContextRuleSet.java | 2 tomcat10-10.1.52/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java | 4 tomcat10-10.1.52/java/org/apache/catalina/startup/EngineConfig.java | 2 tomcat10-10.1.52/java/org/apache/catalina/startup/EngineRuleSet.java | 4 tomcat10-10.1.52/java/org/apache/catalina/startup/ExpandWar.java | 34 tomcat10-10.1.52/java/org/apache/catalina/startup/FailedContext.java | 20 tomcat10-10.1.52/java/org/apache/catalina/startup/HomesUserDatabase.java | 4 tomcat10-10.1.52/java/org/apache/catalina/startup/HostConfig.java | 97 tomcat10-10.1.52/java/org/apache/catalina/startup/HostRuleSet.java | 2 tomcat10-10.1.52/java/org/apache/catalina/startup/LifecycleListenerRule.java | 2 tomcat10-10.1.52/java/org/apache/catalina/startup/ListenerCreateRule.java | 2 tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_fr.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_ja.properties | 3 tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_ru.properties | 4 tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_zh_CN.properties | 5 tomcat10-10.1.52/java/org/apache/catalina/startup/MimeTypeMappings.properties | 9 tomcat10-10.1.52/java/org/apache/catalina/startup/NamingRuleSet.java | 3 tomcat10-10.1.52/java/org/apache/catalina/startup/PasswdUserDatabase.java | 8 tomcat10-10.1.52/java/org/apache/catalina/startup/SafeForkJoinWorkerThreadFactory.java | 2 tomcat10-10.1.52/java/org/apache/catalina/startup/SetNextNamingRule.java | 10 tomcat10-10.1.52/java/org/apache/catalina/startup/Tomcat.java | 98 tomcat10-10.1.52/java/org/apache/catalina/startup/Tool.java | 46 tomcat10-10.1.52/java/org/apache/catalina/startup/UserConfig.java | 20 tomcat10-10.1.52/java/org/apache/catalina/startup/UserDatabase.java | 2 tomcat10-10.1.52/java/org/apache/catalina/startup/WebAnnotationSet.java | 9 tomcat10-10.1.52/java/org/apache/catalina/startup/WebappServiceLoader.java | 4 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/CatalinaClusterSF.java | 6 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/CertificateStoreAppender.java | 2 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/ConnectorSF.java | 4 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java | 9 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/CredentialHandlerSF.java | 2 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/GlobalNamingResourcesSF.java | 2 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/InterceptorSF.java | 2 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/LoaderSF.java | 13 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/ManagerSF.java | 7 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/NamingResourcesSF.java | 2 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/OpenSSLConfSF.java | 15 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/RealmSF.java | 2 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java | 6 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardContextSF.java | 14 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardEngineSF.java | 6 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardHostSF.java | 8 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardServerSF.java | 4 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardServiceSF.java | 4 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreAppender.java | 8 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreConfig.java | 15 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java | 8 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreContextAppender.java | 9 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreFactoryBase.java | 8 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreFactoryRule.java | 8 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreLoader.java | 4 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreRegistry.java | 10 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/WatchedResourceSF.java | 2 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/WrapperLifecycleSF.java | 2 tomcat10-10.1.52/java/org/apache/catalina/storeconfig/WrapperListenerSF.java | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/ByteMessage.java | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/Channel.java | 18 tomcat10-10.1.52/java/org/apache/catalina/tribes/ChannelException.java | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/ChannelInterceptor.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/Member.java | 10 tomcat10-10.1.52/java/org/apache/catalina/tribes/MembershipService.java | 8 tomcat10-10.1.52/java/org/apache/catalina/tribes/UniqueId.java | 8 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/AbsoluteOrder.java | 10 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/ChannelCoordinator.java | 23 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/GroupChannel.java | 31 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/LocalStrings.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/LocalStrings_fr.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/LocalStrings_ja.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/RpcChannel.java | 14 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/RpcMessage.java | 11 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java | 8 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_fr.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ja.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ru.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java | 16 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java | 50 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java | 7 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/SimpleCoordinator.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java | 14 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java | 10 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java | 7 tomcat10-10.1.52/java/org/apache/catalina/tribes/io/ChannelData.java | 14 tomcat10-10.1.52/java/org/apache/catalina/tribes/io/LocalStrings_ru.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/io/ObjectReader.java | 6 tomcat10-10.1.52/java/org/apache/catalina/tribes/io/ReplicationStream.java | 13 tomcat10-10.1.52/java/org/apache/catalina/tribes/io/XByteBuffer.java | 28 tomcat10-10.1.52/java/org/apache/catalina/tribes/jmx/JmxRegistry.java | 6 tomcat10-10.1.52/java/org/apache/catalina/tribes/jmx/LocalStrings_ru.properties | 19 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/Constants.java | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/LocalStrings.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/LocalStrings_fr.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/LocalStrings_ja.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/McastService.java | 30 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/McastServiceImpl.java | 69 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/MemberImpl.java | 6 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/Membership.java | 18 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/StaticMember.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/StaticMembershipProvider.java | 16 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/StaticMembershipService.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java | 9 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java | 63 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_fr.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_ja.properties | 1 tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java | 15 tomcat10-10.1.52/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java | 81 tomcat10-10.1.52/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/tipis/ReplicatedMap.java | 3 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/Constants.java | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/PooledSender.java | 23 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/ReceiverBase.java | 12 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/RxTaskPool.java | 14 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/SenderState.java | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/NioReceiver.java | 58 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java | 19 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/NioSender.java | 74 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java | 53 tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java | 4 tomcat10-10.1.52/java/org/apache/catalina/tribes/util/Arrays.java | 2 tomcat10-10.1.52/java/org/apache/catalina/tribes/util/LocalStrings_ru.properties | 19 tomcat10-10.1.52/java/org/apache/catalina/tribes/util/StringManager.java | 32 tomcat10-10.1.52/java/org/apache/catalina/tribes/util/UUIDGenerator.java | 5 tomcat10-10.1.52/java/org/apache/catalina/users/AbstractGroup.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/AbstractRole.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/AbstractUser.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/Constants.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/DataSourceUserDatabase.java | 7 tomcat10-10.1.52/java/org/apache/catalina/users/DataSourceUserDatabaseFactory.java | 10 tomcat10-10.1.52/java/org/apache/catalina/users/GenericGroup.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/GenericRole.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/GenericUser.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/MemoryGroup.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/MemoryRole.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/MemoryUser.java | 2 tomcat10-10.1.52/java/org/apache/catalina/users/MemoryUserDatabase.java | 59 tomcat10-10.1.52/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java | 10 tomcat10-10.1.52/java/org/apache/catalina/util/CharsetMapper.java | 6 tomcat10-10.1.52/java/org/apache/catalina/util/ContextName.java | 7 tomcat10-10.1.52/java/org/apache/catalina/util/CustomObjectInputStream.java | 3 tomcat10-10.1.52/java/org/apache/catalina/util/DOMWriter.java | 12 tomcat10-10.1.52/java/org/apache/catalina/util/ErrorPageSupport.java | 4 tomcat10-10.1.52/java/org/apache/catalina/util/ExactRateLimiter.java | 66 tomcat10-10.1.52/java/org/apache/catalina/util/FastRateLimiter.java | 75 tomcat10-10.1.52/java/org/apache/catalina/util/FilterUtil.java | 16 tomcat10-10.1.52/java/org/apache/catalina/util/IOTools.java | 4 tomcat10-10.1.52/java/org/apache/catalina/util/Introspection.java | 14 tomcat10-10.1.52/java/org/apache/catalina/util/LifecycleBase.java | 38 tomcat10-10.1.52/java/org/apache/catalina/util/LifecycleMBeanBase.java | 20 tomcat10-10.1.52/java/org/apache/catalina/util/NetMask.java | 7 tomcat10-10.1.52/java/org/apache/catalina/util/ParameterMap.java | 16 tomcat10-10.1.52/java/org/apache/catalina/util/RateLimiter.java | 9 tomcat10-10.1.52/java/org/apache/catalina/util/RateLimiterBase.java | 155 tomcat10-10.1.52/java/org/apache/catalina/util/RequestUtil.java | 52 tomcat10-10.1.52/java/org/apache/catalina/util/ResourceSet.java | 2 tomcat10-10.1.52/java/org/apache/catalina/util/ServerInfo.java | 4 tomcat10-10.1.52/java/org/apache/catalina/util/SessionConfig.java | 10 tomcat10-10.1.52/java/org/apache/catalina/util/SessionIdGeneratorBase.java | 6 tomcat10-10.1.52/java/org/apache/catalina/util/StandardSessionIdGenerator.java | 6 tomcat10-10.1.52/java/org/apache/catalina/util/Strftime.java | 3 tomcat10-10.1.52/java/org/apache/catalina/util/StringUtil.java | 2 tomcat10-10.1.52/java/org/apache/catalina/util/TimeBucketCounter.java | 188 tomcat10-10.1.52/java/org/apache/catalina/util/TimeBucketCounterBase.java | 222 tomcat10-10.1.52/java/org/apache/catalina/util/ToStringUtil.java | 6 tomcat10-10.1.52/java/org/apache/catalina/util/URLEncoder.java | 19 tomcat10-10.1.52/java/org/apache/catalina/util/XMLWriter.java | 32 tomcat10-10.1.52/java/org/apache/catalina/valves/AbstractAccessLogValve.java | 225 tomcat10-10.1.52/java/org/apache/catalina/valves/AccessLogValve.java | 41 tomcat10-10.1.52/java/org/apache/catalina/valves/Constants.java | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/CrawlerSessionManagerValve.java | 28 tomcat10-10.1.52/java/org/apache/catalina/valves/ErrorReportValve.java | 20 tomcat10-10.1.52/java/org/apache/catalina/valves/ExtendedAccessLogValve.java | 221 tomcat10-10.1.52/java/org/apache/catalina/valves/HealthCheckValve.java | 7 tomcat10-10.1.52/java/org/apache/catalina/valves/JDBCAccessLogValve.java | 3 tomcat10-10.1.52/java/org/apache/catalina/valves/JsonAccessLogValve.java | 23 tomcat10-10.1.52/java/org/apache/catalina/valves/JsonErrorReportValve.java | 69 tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings.properties | 7 tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_fr.properties | 9 tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_ja.properties | 9 tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_ru.properties | 5 tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_zh_CN.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/ParameterLimitValve.java | 282 tomcat10-10.1.52/java/org/apache/catalina/valves/PersistentValve.java | 16 tomcat10-10.1.52/java/org/apache/catalina/valves/ProxyErrorReportValve.java | 4 tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteAddrValve.java | 3 tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteCIDRValve.java | 8 tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteHostValve.java | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteIpValve.java | 44 tomcat10-10.1.52/java/org/apache/catalina/valves/RequestFilterValve.java | 19 tomcat10-10.1.52/java/org/apache/catalina/valves/SSLValve.java | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/SemaphoreValve.java | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/StuckThreadDetectionValve.java | 6 tomcat10-10.1.52/java/org/apache/catalina/valves/ValveBase.java | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/LocalStrings.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/LocalStrings_fr.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/LocalStrings_ja.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java | 4 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java | 11 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/ResolverImpl.java | 48 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteCond.java | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteMap.java | 2 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteRule.java | 4 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteValve.java | 226 tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/Substitution.java | 10 tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractArchiveResource.java | 2 tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java | 54 tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractFileResourceSet.java | 78 tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractResource.java | 12 tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractResourceSet.java | 24 tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractSingleArchiveResource.java | 4 tomcat10-10.1.52/java/org/apache/catalina/webresources/Cache.java | 17 tomcat10-10.1.52/java/org/apache/catalina/webresources/CachedResource.java | 42 tomcat10-10.1.52/java/org/apache/catalina/webresources/DirResourceSet.java | 115 tomcat10-10.1.52/java/org/apache/catalina/webresources/EmptyResource.java | 2 tomcat10-10.1.52/java/org/apache/catalina/webresources/EmptyResourceSet.java | 19 tomcat10-10.1.52/java/org/apache/catalina/webresources/ExtractingRoot.java | 3 tomcat10-10.1.52/java/org/apache/catalina/webresources/FileResource.java | 4 tomcat10-10.1.52/java/org/apache/catalina/webresources/FileResourceSet.java | 4 tomcat10-10.1.52/java/org/apache/catalina/webresources/JarContents.java | 86 tomcat10-10.1.52/java/org/apache/catalina/webresources/JarWarResource.java | 9 tomcat10-10.1.52/java/org/apache/catalina/webresources/JarWarResourceSet.java | 26 tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings.properties | 5 tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings_fr.properties | 3 tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings_ja.properties | 3 tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings_ru.properties | 2 tomcat10-10.1.52/java/org/apache/catalina/webresources/StandardRoot.java | 35 tomcat10-10.1.52/java/org/apache/catalina/webresources/TomcatJarInputStream.java | 4 tomcat10-10.1.52/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java | 4 tomcat10-10.1.52/java/org/apache/coyote/AbstractProcessor.java | 28 tomcat10-10.1.52/java/org/apache/coyote/AbstractProcessorLight.java | 6 tomcat10-10.1.52/java/org/apache/coyote/AbstractProtocol.java | 53 tomcat10-10.1.52/java/org/apache/coyote/ActionCode.java | 4 tomcat10-10.1.52/java/org/apache/coyote/ActionHook.java | 2 tomcat10-10.1.52/java/org/apache/coyote/Adapter.java | 2 tomcat10-10.1.52/java/org/apache/coyote/AsyncStateMachine.java | 2 tomcat10-10.1.52/java/org/apache/coyote/CompressionConfig.java | 93 tomcat10-10.1.52/java/org/apache/coyote/Constants.java | 2 tomcat10-10.1.52/java/org/apache/coyote/ErrorState.java | 2 tomcat10-10.1.52/java/org/apache/coyote/NonPipeliningProcessor.java | 26 tomcat10-10.1.52/java/org/apache/coyote/OutputBuffer.java | 2 tomcat10-10.1.52/java/org/apache/coyote/Processor.java | 2 tomcat10-10.1.52/java/org/apache/coyote/ProtocolHandler.java | 5 tomcat10-10.1.52/java/org/apache/coyote/Request.java | 102 tomcat10-10.1.52/java/org/apache/coyote/RequestInfo.java | 22 tomcat10-10.1.52/java/org/apache/coyote/Response.java | 43 tomcat10-10.1.52/java/org/apache/coyote/ajp/AbstractAjpProtocol.java | 9 tomcat10-10.1.52/java/org/apache/coyote/ajp/AjpMessage.java | 20 tomcat10-10.1.52/java/org/apache/coyote/ajp/AjpProcessor.java | 96 tomcat10-10.1.52/java/org/apache/coyote/ajp/Constants.java | 20 tomcat10-10.1.52/java/org/apache/coyote/http11/AbstractHttp11Protocol.java | 21 tomcat10-10.1.52/java/org/apache/coyote/http11/Constants.java | 2 tomcat10-10.1.52/java/org/apache/coyote/http11/Http11InputBuffer.java | 45 tomcat10-10.1.52/java/org/apache/coyote/http11/Http11OutputBuffer.java | 2 tomcat10-10.1.52/java/org/apache/coyote/http11/Http11Processor.java | 70 tomcat10-10.1.52/java/org/apache/coyote/http11/InputFilter.java | 2 tomcat10-10.1.52/java/org/apache/coyote/http11/LocalStrings.properties | 1 tomcat10-10.1.52/java/org/apache/coyote/http11/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/org/apache/coyote/http11/OutputFilter.java | 2 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/BufferedInputFilter.java | 2 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java | 150 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java | 2 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/GzipOutputFilter.java | 6 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/IdentityInputFilter.java | 6 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java | 4 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/LocalStrings_zh_CN.properties | 2 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java | 11 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/VoidInputFilter.java | 2 tomcat10-10.1.52/java/org/apache/coyote/http11/filters/VoidOutputFilter.java | 2 tomcat10-10.1.52/java/org/apache/coyote/http11/upgrade/UpgradeGroupInfo.java | 8 tomcat10-10.1.52/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java | 8 tomcat10-10.1.52/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java | 8 tomcat10-10.1.52/java/org/apache/coyote/http2/AbstractNonZeroStream.java | 4 tomcat10-10.1.52/java/org/apache/coyote/http2/ConnectionSettingsBase.java | 23 tomcat10-10.1.52/java/org/apache/coyote/http2/ConnectionSettingsLocal.java | 5 tomcat10-10.1.52/java/org/apache/coyote/http2/HPackHuffman.java | 18 tomcat10-10.1.52/java/org/apache/coyote/http2/Hpack.java | 33 tomcat10-10.1.52/java/org/apache/coyote/http2/HpackDecoder.java | 18 tomcat10-10.1.52/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java | 26 tomcat10-10.1.52/java/org/apache/coyote/http2/Http2Parser.java | 37 tomcat10-10.1.52/java/org/apache/coyote/http2/Http2Protocol.java | 18 tomcat10-10.1.52/java/org/apache/coyote/http2/Http2UpgradeHandler.java | 150 tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings.properties | 10 tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_fr.properties | 9 tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_ja.properties | 11 tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_ko.properties | 1 tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_zh_CN.properties | 2 tomcat10-10.1.52/java/org/apache/coyote/http2/RecycledStream.java | 6 tomcat10-10.1.52/java/org/apache/coyote/http2/Setting.java | 8 tomcat10-10.1.52/java/org/apache/coyote/http2/Stream.java | 83 tomcat10-10.1.52/java/org/apache/coyote/http2/StreamProcessor.java | 22 tomcat10-10.1.52/java/org/apache/coyote/http2/WindowAllocationManager.java | 2 tomcat10-10.1.52/java/org/apache/el/ExpressionFactoryImpl.java | 3 tomcat10-10.1.52/java/org/apache/el/MethodExpressionImpl.java | 4 tomcat10-10.1.52/java/org/apache/el/ValueExpressionImpl.java | 2 tomcat10-10.1.52/java/org/apache/el/lang/ELArithmetic.java | 18 tomcat10-10.1.52/java/org/apache/el/lang/ELSupport.java | 70 tomcat10-10.1.52/java/org/apache/el/lang/ExpressionBuilder.java | 10 tomcat10-10.1.52/java/org/apache/el/lang/FunctionMapperFactory.java | 3 tomcat10-10.1.52/java/org/apache/el/lang/FunctionMapperImpl.java | 4 tomcat10-10.1.52/java/org/apache/el/parser/ArithmeticNode.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstAbstractEmpty.java | 2 tomcat10-10.1.52/java/org/apache/el/parser/AstAnd.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstBracketSuffix.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstChoice.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstCompositeExpression.java | 5 tomcat10-10.1.52/java/org/apache/el/parser/AstDeferredExpression.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstDiv.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstDotSuffix.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstDynamicExpression.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstEqual.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstFalse.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstFloatingPoint.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstFunction.java | 13 tomcat10-10.1.52/java/org/apache/el/parser/AstGreaterThan.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstGreaterThanEqual.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstIdentifier.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstInteger.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstLessThan.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstLessThanEqual.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstLiteralExpression.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstMinus.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstMod.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstMult.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstNegative.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstNot.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstNotEqual.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstNull.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstOr.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstPlus.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstString.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstTrue.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/AstValue.java | 32 tomcat10-10.1.52/java/org/apache/el/parser/BooleanNode.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/ELParser.java | 78 tomcat10-10.1.52/java/org/apache/el/parser/ELParser.jjt | 640 tomcat10-10.1.52/java/org/apache/el/parser/ELParserConstants.java | 41 tomcat10-10.1.52/java/org/apache/el/parser/ELParserTokenManager.java | 1296 - tomcat10-10.1.52/java/org/apache/el/parser/Node.java | 4 tomcat10-10.1.52/java/org/apache/el/parser/NodeVisitor.java | 3 tomcat10-10.1.52/java/org/apache/el/parser/SimpleNode.java | 12 tomcat10-10.1.52/java/org/apache/el/stream/Stream.java | 5 tomcat10-10.1.52/java/org/apache/el/util/ExceptionUtils.java | 6 tomcat10-10.1.52/java/org/apache/el/util/MessageFactory.java | 3 tomcat10-10.1.52/java/org/apache/el/util/ReflectionUtil.java | 22 tomcat10-10.1.52/java/org/apache/el/util/Validation.java | 27 tomcat10-10.1.52/java/org/apache/jasper/Constants.java | 45 tomcat10-10.1.52/java/org/apache/jasper/EmbeddedServletOptions.java | 108 tomcat10-10.1.52/java/org/apache/jasper/JasperException.java | 12 tomcat10-10.1.52/java/org/apache/jasper/JspC.java | 483 tomcat10-10.1.52/java/org/apache/jasper/JspCompilationContext.java | 205 tomcat10-10.1.52/java/org/apache/jasper/Options.java | 154 tomcat10-10.1.52/java/org/apache/jasper/compiler/AntCompiler.java | 29 tomcat10-10.1.52/java/org/apache/jasper/compiler/BeanRepository.java | 5 tomcat10-10.1.52/java/org/apache/jasper/compiler/Collector.java | 4 tomcat10-10.1.52/java/org/apache/jasper/compiler/Compiler.java | 34 tomcat10-10.1.52/java/org/apache/jasper/compiler/DefaultErrorHandler.java | 4 tomcat10-10.1.52/java/org/apache/jasper/compiler/ELFunctionMapper.java | 26 tomcat10-10.1.52/java/org/apache/jasper/compiler/ELNode.java | 7 tomcat10-10.1.52/java/org/apache/jasper/compiler/ELParser.java | 19 tomcat10-10.1.52/java/org/apache/jasper/compiler/ErrorDispatcher.java | 11 tomcat10-10.1.52/java/org/apache/jasper/compiler/ErrorHandler.java | 15 tomcat10-10.1.52/java/org/apache/jasper/compiler/Generator.java | 385 tomcat10-10.1.52/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java | 4 tomcat10-10.1.52/java/org/apache/jasper/compiler/JDTCompiler.java | 53 tomcat10-10.1.52/java/org/apache/jasper/compiler/JavaCompiler.java | 107 tomcat10-10.1.52/java/org/apache/jasper/compiler/JavacErrorDetail.java | 3 tomcat10-10.1.52/java/org/apache/jasper/compiler/JspConfig.java | 13 tomcat10-10.1.52/java/org/apache/jasper/compiler/JspDocumentParser.java | 34 tomcat10-10.1.52/java/org/apache/jasper/compiler/JspReader.java | 341 tomcat10-10.1.52/java/org/apache/jasper/compiler/JspRuntimeContext.java | 153 tomcat10-10.1.52/java/org/apache/jasper/compiler/JspUtil.java | 406 tomcat10-10.1.52/java/org/apache/jasper/compiler/Localizer.java | 21 tomcat10-10.1.52/java/org/apache/jasper/compiler/Mark.java | 12 tomcat10-10.1.52/java/org/apache/jasper/compiler/NewlineReductionServletWriter.java | 11 tomcat10-10.1.52/java/org/apache/jasper/compiler/Node.java | 650 tomcat10-10.1.52/java/org/apache/jasper/compiler/PageDataImpl.java | 233 tomcat10-10.1.52/java/org/apache/jasper/compiler/PageInfo.java | 100 tomcat10-10.1.52/java/org/apache/jasper/compiler/Parser.java | 556 tomcat10-10.1.52/java/org/apache/jasper/compiler/ParserController.java | 246 tomcat10-10.1.52/java/org/apache/jasper/compiler/ScriptingVariabler.java | 44 tomcat10-10.1.52/java/org/apache/jasper/compiler/ServletWriter.java | 20 tomcat10-10.1.52/java/org/apache/jasper/compiler/SmapStratum.java | 161 tomcat10-10.1.52/java/org/apache/jasper/compiler/SmapUtil.java | 165 tomcat10-10.1.52/java/org/apache/jasper/compiler/StringInterpreter.java | 35 tomcat10-10.1.52/java/org/apache/jasper/compiler/StringInterpreterFactory.java | 53 tomcat10-10.1.52/java/org/apache/jasper/compiler/TagFileProcessor.java | 319 tomcat10-10.1.52/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java | 70 tomcat10-10.1.52/java/org/apache/jasper/compiler/TagPluginManager.java | 32 tomcat10-10.1.52/java/org/apache/jasper/compiler/TextOptimizer.java | 10 tomcat10-10.1.52/java/org/apache/jasper/compiler/TldCache.java | 44 tomcat10-10.1.52/java/org/apache/jasper/compiler/Validator.java | 860 tomcat10-10.1.52/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java | 8 tomcat10-10.1.52/java/org/apache/jasper/el/ELContextImpl.java | 12 tomcat10-10.1.52/java/org/apache/jasper/el/ELContextWrapper.java | 2 tomcat10-10.1.52/java/org/apache/jasper/el/ELResolverImpl.java | 15 tomcat10-10.1.52/java/org/apache/jasper/el/ExpressionEvaluatorImpl.java | 12 tomcat10-10.1.52/java/org/apache/jasper/el/ExpressionImpl.java | 3 tomcat10-10.1.52/java/org/apache/jasper/el/JasperELResolver.java | 71 tomcat10-10.1.52/java/org/apache/jasper/el/JspMethodExpression.java | 12 tomcat10-10.1.52/java/org/apache/jasper/el/JspPropertyNotFoundException.java | 3 tomcat10-10.1.52/java/org/apache/jasper/el/JspPropertyNotWritableException.java | 3 tomcat10-10.1.52/java/org/apache/jasper/el/JspValueExpression.java | 22 tomcat10-10.1.52/java/org/apache/jasper/optimizations/ELInterpreterTagSetters.java | 112 tomcat10-10.1.52/java/org/apache/jasper/optimizations/StringInterpreterEnum.java | 4 tomcat10-10.1.52/java/org/apache/jasper/resources/LocalStrings_zh_CN.properties | 2 tomcat10-10.1.52/java/org/apache/jasper/runtime/BodyContentImpl.java | 48 tomcat10-10.1.52/java/org/apache/jasper/runtime/ExceptionUtils.java | 10 tomcat10-10.1.52/java/org/apache/jasper/runtime/HttpJspBase.java | 14 tomcat10-10.1.52/java/org/apache/jasper/runtime/JspApplicationContextImpl.java | 15 tomcat10-10.1.52/java/org/apache/jasper/runtime/JspContextWrapper.java | 122 tomcat10-10.1.52/java/org/apache/jasper/runtime/JspFactoryImpl.java | 60 tomcat10-10.1.52/java/org/apache/jasper/runtime/JspFragmentHelper.java | 20 tomcat10-10.1.52/java/org/apache/jasper/runtime/JspRuntimeLibrary.java | 673 tomcat10-10.1.52/java/org/apache/jasper/runtime/JspSourceDependent.java | 27 tomcat10-10.1.52/java/org/apache/jasper/runtime/JspSourceDirectives.java | 5 tomcat10-10.1.52/java/org/apache/jasper/runtime/JspSourceImports.java | 8 tomcat10-10.1.52/java/org/apache/jasper/runtime/JspWriterImpl.java | 91 tomcat10-10.1.52/java/org/apache/jasper/runtime/PageContextImpl.java | 252 tomcat10-10.1.52/java/org/apache/jasper/runtime/ProtectedFunctionMapper.java | 83 tomcat10-10.1.52/java/org/apache/jasper/runtime/ServletResponseWrapperInclude.java | 19 tomcat10-10.1.52/java/org/apache/jasper/runtime/TagHandlerPool.java | 36 tomcat10-10.1.52/java/org/apache/jasper/security/SecurityClassLoad.java | 3 tomcat10-10.1.52/java/org/apache/jasper/security/SecurityUtil.java | 12 tomcat10-10.1.52/java/org/apache/jasper/servlet/JasperInitializer.java | 21 tomcat10-10.1.52/java/org/apache/jasper/servlet/JasperLoader.java | 69 tomcat10-10.1.52/java/org/apache/jasper/servlet/JspCServletContext.java | 98 tomcat10-10.1.52/java/org/apache/jasper/servlet/JspServlet.java | 163 tomcat10-10.1.52/java/org/apache/jasper/servlet/JspServletWrapper.java | 180 tomcat10-10.1.52/java/org/apache/jasper/servlet/TldPreScanned.java | 8 tomcat10-10.1.52/java/org/apache/jasper/servlet/TldScanner.java | 90 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/Util.java | 126 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Catch.java | 30 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/ForEach.java | 24 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/ForTokens.java | 64 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Import.java | 299 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Out.java | 47 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Param.java | 42 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Redirect.java | 43 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Remove.java | 12 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Set.java | 108 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Url.java | 50 tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/When.java | 2 tomcat10-10.1.52/java/org/apache/jasper/util/FastRemovalDequeue.java | 99 tomcat10-10.1.52/java/org/apache/jasper/util/UniqueAttributesImpl.java | 13 tomcat10-10.1.52/java/org/apache/juli/AsyncFileHandler.java | 24 tomcat10-10.1.52/java/org/apache/juli/ClassLoaderLogManager.java | 41 tomcat10-10.1.52/java/org/apache/juli/DateFormatCache.java | 12 tomcat10-10.1.52/java/org/apache/juli/FileHandler.java | 27 tomcat10-10.1.52/java/org/apache/juli/JdkLoggerFormatter.java | 14 tomcat10-10.1.52/java/org/apache/juli/JsonFormatter.java | 207 tomcat10-10.1.52/java/org/apache/juli/LogUtil.java | 64 tomcat10-10.1.52/java/org/apache/juli/OneLineFormatter.java | 56 tomcat10-10.1.52/java/org/apache/juli/VerbatimFormatter.java | 7 tomcat10-10.1.52/java/org/apache/juli/logging/DirectJDKLog.java | 34 tomcat10-10.1.52/java/org/apache/juli/logging/Log.java | 196 tomcat10-10.1.52/java/org/apache/juli/logging/LogConfigurationException.java | 14 tomcat10-10.1.52/java/org/apache/juli/logging/LogFactory.java | 185 tomcat10-10.1.52/java/org/apache/naming/ContextAccessController.java | 22 tomcat10-10.1.52/java/org/apache/naming/ContextBindings.java | 80 tomcat10-10.1.52/java/org/apache/naming/EjbRef.java | 29 tomcat10-10.1.52/java/org/apache/naming/HandlerRef.java | 26 tomcat10-10.1.52/java/org/apache/naming/LocalStrings.properties | 4 tomcat10-10.1.52/java/org/apache/naming/LocalStrings_cs.properties | 1 tomcat10-10.1.52/java/org/apache/naming/LocalStrings_de.properties | 2 tomcat10-10.1.52/java/org/apache/naming/LocalStrings_es.properties | 3 tomcat10-10.1.52/java/org/apache/naming/LocalStrings_fr.properties | 4 tomcat10-10.1.52/java/org/apache/naming/LocalStrings_ja.properties | 4 tomcat10-10.1.52/java/org/apache/naming/LocalStrings_ko.properties | 2 tomcat10-10.1.52/java/org/apache/naming/LocalStrings_ru.properties | 2 tomcat10-10.1.52/java/org/apache/naming/LocalStrings_zh_CN.properties | 3 tomcat10-10.1.52/java/org/apache/naming/LookupRef.java | 2 tomcat10-10.1.52/java/org/apache/naming/NameParserImpl.java | 8 tomcat10-10.1.52/java/org/apache/naming/NamingContext.java | 213 tomcat10-10.1.52/java/org/apache/naming/NamingContextBindingsEnumeration.java | 20 tomcat10-10.1.52/java/org/apache/naming/NamingContextEnumeration.java | 14 tomcat10-10.1.52/java/org/apache/naming/NamingEntry.java | 5 tomcat10-10.1.52/java/org/apache/naming/ResourceEnvRef.java | 5 tomcat10-10.1.52/java/org/apache/naming/ResourceLinkRef.java | 21 tomcat10-10.1.52/java/org/apache/naming/ResourceRef.java | 44 tomcat10-10.1.52/java/org/apache/naming/SelectorContext.java | 158 tomcat10-10.1.52/java/org/apache/naming/ServiceRef.java | 28 tomcat10-10.1.52/java/org/apache/naming/StringManager.java | 105 tomcat10-10.1.52/java/org/apache/naming/TransactionRef.java | 7 tomcat10-10.1.52/java/org/apache/naming/factory/BeanFactory.java | 39 tomcat10-10.1.52/java/org/apache/naming/factory/Constants.java | 3 tomcat10-10.1.52/java/org/apache/naming/factory/DataSourceLinkFactory.java | 56 tomcat10-10.1.52/java/org/apache/naming/factory/EjbFactory.java | 19 tomcat10-10.1.52/java/org/apache/naming/factory/FactoryBase.java | 44 tomcat10-10.1.52/java/org/apache/naming/factory/LookupFactory.java | 33 tomcat10-10.1.52/java/org/apache/naming/factory/MailSessionFactory.java | 44 tomcat10-10.1.52/java/org/apache/naming/factory/OpenEjbFactory.java | 35 tomcat10-10.1.52/java/org/apache/naming/factory/ResourceEnvFactory.java | 2 tomcat10-10.1.52/java/org/apache/naming/factory/ResourceFactory.java | 18 tomcat10-10.1.52/java/org/apache/naming/factory/ResourceLinkFactory.java | 51 tomcat10-10.1.52/java/org/apache/naming/factory/SendMailFactory.java | 90 tomcat10-10.1.52/java/org/apache/naming/factory/TransactionFactory.java | 2 tomcat10-10.1.52/java/org/apache/naming/factory/webservices/ServiceProxy.java | 21 tomcat10-10.1.52/java/org/apache/naming/factory/webservices/ServiceRefFactory.java | 30 tomcat10-10.1.52/java/org/apache/naming/java/javaURLContextFactory.java | 59 tomcat10-10.1.52/java/org/apache/tomcat/ContextBind.java | 46 tomcat10-10.1.52/java/org/apache/tomcat/InstanceManager.java | 30 tomcat10-10.1.52/java/org/apache/tomcat/InstanceManagerBindings.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/InstrumentableClassLoader.java | 50 tomcat10-10.1.52/java/org/apache/tomcat/Jar.java | 59 tomcat10-10.1.52/java/org/apache/tomcat/JarScanFilter.java | 16 tomcat10-10.1.52/java/org/apache/tomcat/JarScanner.java | 22 tomcat10-10.1.52/java/org/apache/tomcat/JarScannerCallback.java | 31 tomcat10-10.1.52/java/org/apache/tomcat/SimpleInstanceManager.java | 22 tomcat10-10.1.52/java/org/apache/tomcat/buildutil/CheckEol.java | 46 tomcat10-10.1.52/java/org/apache/tomcat/buildutil/MimeTypeMappings.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/buildutil/RepeatableArchive.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/buildutil/Txt2Html.java | 92 tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/BackportEnglish.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/BackportTranslations.java | 11 tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/Import.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/Utils.java | 38 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/AbandonedTrace.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java | 46 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java | 81 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceMXBean.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactory.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactoryFactory.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/Constants.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DataSourceConnectionFactory.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DataSourceMXBean.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingCallableStatement.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java | 33 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java | 13 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java | 21 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java | 47 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DriverFactory.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java | 16 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/Jdbc41Bridge.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/LifetimeExceededException.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ListException.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/LocalStrings.properties | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ObjectNameWrapper.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PStmtKey.java | 196 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableCallableStatement.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java | 44 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java | 56 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionMXBean.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolablePreparedStatement.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java | 40 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolingDriver.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/SQLExceptionList.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/SwallowedExceptionLogger.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/Utils.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/ConnectionImpl.java | 67 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java | 56 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PStmtKeyCPDS.java | 26 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java | 157 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/package-info.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/AbstractConnectionFactory.java | 147 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java | 221 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/CharArray.java | 26 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java | 208 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSourceFactory.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/KeyedCPDSConnectionFactory.java | 182 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSource.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSourceFactory.java | 11 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PoolKey.java | 7 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionAndInfo.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionManager.java | 31 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java | 7 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSourceFactory.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java | 20 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/package-info.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/BasicManagedDataSource.java | 11 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/DataSourceXAConnectionFactory.java | 38 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/LocalXAConnectionFactory.java | 20 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedConnection.java | 34 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedDataSource.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnection.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnectionFactory.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/SynchronizationAdapter.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContext.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContextListener.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionRegistry.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/XAConnectionFactory.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/package-info.java | 3 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/package-info.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/BaseObject.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/BaseObjectPool.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/DestroyMode.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/KeyedPooledObjectFactory.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java | 86 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PooledObject.java | 33 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PooledObjectFactory.java | 172 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PooledObjectState.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/SwallowedExceptionListener.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/TrackedUse.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/UsageTracking.java | 3 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/AbandonedConfig.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/BaseGenericObjectPool.java | 140 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/BaseObjectPoolConfig.java | 94 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/CallStack.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObject.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfo.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfoMBean.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/EvictionPolicy.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java | 45 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java | 307 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolConfig.java | 109 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolMXBean.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java | 272 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolConfig.java | 33 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolMXBean.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java | 114 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/NoOpCallStack.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/PoolImplUtils.java | 41 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/PooledSoftReference.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java | 19 tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/ThrowableCallStack.java | 29 tomcat10-10.1.52/java/org/apache/tomcat/jni/AprStatus.java | 89 tomcat10-10.1.52/java/org/apache/tomcat/jni/Buffer.java | 3 tomcat10-10.1.52/java/org/apache/tomcat/jni/CertificateVerifier.java | 13 tomcat10-10.1.52/java/org/apache/tomcat/jni/FileInfo.java | 7 tomcat10-10.1.52/java/org/apache/tomcat/jni/Library.java | 46 tomcat10-10.1.52/java/org/apache/tomcat/jni/LibraryNotFoundError.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/jni/Pool.java | 15 tomcat10-10.1.52/java/org/apache/tomcat/jni/SSL.java | 539 tomcat10-10.1.52/java/org/apache/tomcat/jni/SSLConf.java | 38 tomcat10-10.1.52/java/org/apache/tomcat/jni/SSLContext.java | 395 tomcat10-10.1.52/java/org/apache/tomcat/jni/Sockaddr.java | 7 tomcat10-10.1.52/java/org/apache/tomcat/util/Diagnostics.java | 333 tomcat10-10.1.52/java/org/apache/tomcat/util/ExceptionUtils.java | 17 tomcat10-10.1.52/java/org/apache/tomcat/util/IntrospectionUtils.java | 240 tomcat10-10.1.52/java/org/apache/tomcat/util/MultiThrowable.java | 19 tomcat10-10.1.52/java/org/apache/tomcat/util/XReflectionIntrospectionUtils.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/bcel/Const.java | 16 tomcat10-10.1.52/java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/bcel/classfile/Utility.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/AbstractChunk.java | 25 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/Ascii.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/Asn1Parser.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/B2CConverter.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ByteBufferUtils.java | 51 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ByteBufferUtilsUnsafe.java | 84 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ByteChunk.java | 74 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/C2BConverter.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/CharChunk.java | 43 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/CharsetCache.java | 17 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/HexUtils.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings.properties | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings_fr.properties | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings_ja.properties | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings_zh_CN.properties | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/MessageBytes.java | 37 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/StringCache.java | 30 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/StringUtils.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ToStringUtil.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/UDecoder.java | 141 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/UEncoder.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/UriUtil.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/Utf8Encoder.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/buf/package.html | 8 tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/Base64.java | 267 tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java | 167 tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/StringUtils.java | 70 tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/package-info.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/collections/CaseInsensitiveKeyMap.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/util/collections/ConcurrentCache.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/collections/ConcurrentLruCache.java | 11 tomcat10-10.1.52/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java | 30 tomcat10-10.1.52/java/org/apache/tomcat/util/collections/SynchronizedQueue.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/util/collections/SynchronizedStack.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre12Compat.java | 154 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre16Compat.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre19Compat.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre20Compat.java | 66 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre21Compat.java | 31 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre22Compat.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/JreCompat.java | 182 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/JrePlatform.java | 13 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/JreVendor.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/util/compat/LocalStrings.properties | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/KeyedReentrantReadWriteLock.java | 179 tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/LocalStrings.properties | 19 tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/LocalStrings_fr.properties | 19 tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/LocalStrings_ja.properties | 19 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/Constants.java | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/DigesterFactory.java | 35 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/LocalResolver.java | 43 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/XmlErrorHandler.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/XmlIdentifiers.java | 33 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tagplugin/TagPluginParser.java | 7 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/ImplicitTldRuleSet.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TagFileXml.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TagXml.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TaglibXml.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TldParser.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TldResourcePath.java | 30 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TldRuleSet.java | 48 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/ValidatorXml.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ApplicationParameter.java | 13 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/Constants.java | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java | 19 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java | 58 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextResource.java | 34 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java | 16 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java | 20 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextService.java | 84 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextTransaction.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ErrorPage.java | 23 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/FilterDef.java | 37 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/FilterMap.java | 25 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback.java | 11 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/Injectable.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/JspConfigDescriptorImpl.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroup.java | 83 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/LocalStrings_es.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/LoginConfig.java | 57 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java | 26 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java | 17 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/MultipartDef.java | 25 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/NamingResources.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java | 40 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SecurityCollection.java | 82 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SecurityConstraint.java | 213 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SecurityRoleRef.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ServletDef.java | 31 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SessionConfig.java | 22 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/TaglibDescriptorImpl.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java | 967 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/WebXml.java | 999 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/WebXmlParser.java | 54 tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/XmlEncodingBase.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java | 24 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ArrayStack.java | 74 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/CallMethodRule.java | 213 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/CallParamRule.java | 85 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/Digester.java | 578 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/DocumentProperties.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/EnvironmentPropertySource.java | 19 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/FactoryCreateRule.java | 56 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ObjectCreateRule.java | 51 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java | 25 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/Rule.java | 66 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/RuleSet.java | 31 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/Rules.java | 36 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/RulesBase.java | 84 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ServiceBindingPropertySource.java | 42 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/SetNextRule.java | 100 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/SetPropertiesRule.java | 25 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/SystemPropertySource.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/util/digester/package.html | 12 tomcat10-10.1.52/java/org/apache/tomcat/util/file/ConfigFileLoader.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/util/file/ConfigurationSource.java | 59 tomcat10-10.1.52/java/org/apache/tomcat/util/file/Matcher.java | 75 tomcat10-10.1.52/java/org/apache/tomcat/util/http/CookieProcessor.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/http/CookiesWithoutEquals.java | 44 tomcat10-10.1.52/java/org/apache/tomcat/util/http/FastHttpDateFormat.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/http/HeaderUtil.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/http/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/http/Method.java | 164 tomcat10-10.1.52/java/org/apache/tomcat/util/http/MimeHeaders.java | 54 tomcat10-10.1.52/java/org/apache/tomcat/util/http/Parameters.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/util/http/RequestUtil.java | 35 tomcat10-10.1.52/java/org/apache/tomcat/util/http/ResponseUtil.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java | 11 tomcat10-10.1.52/java/org/apache/tomcat/util/http/ServerCookies.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/http/WebdavIfHeader.java | 412 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileItem.java | 140 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileItemHeaders.java | 22 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileItemStream.java | 49 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileUpload.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java | 504 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileUploadException.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/InvalidFileNameException.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java | 1045 - tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/ParameterParser.java | 292 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/ProgressListener.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/RFC2231Utility.java | 158 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java | 652 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java | 115 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/package-info.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/servlet/ServletRequestContext.java | 40 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/FileItemHeadersImpl.java | 37 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java | 81 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/Streams.java | 74 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/MimeUtility.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/ParseException.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/QuotedPrintableDecoder.java | 53 tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/RFC2231Utility.java | 154 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/AcceptEncoding.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/AcceptLanguage.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/Authorization.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/ContentRange.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/Cookie.java | 26 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/EntityTag.java | 13 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/HttpHeaderParser.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/HttpParser.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/LocalStrings_es.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/MediaType.java | 16 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/MediaTypeCache.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/Ranges.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/StructuredField.java | 27 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/TE.java | 96 tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/TokenList.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONFilter.java | 22 tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONParser.java | 1170 - tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONParserConstants.java | 165 tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONParserTokenManager.java | 1660 - tomcat10-10.1.52/java/org/apache/tomcat/util/json/JavaCharStream.java | 1149 - tomcat10-10.1.52/java/org/apache/tomcat/util/json/ParseException.java | 330 tomcat10-10.1.52/java/org/apache/tomcat/util/json/Token.java | 217 tomcat10-10.1.52/java/org/apache/tomcat/util/json/TokenMgrError.java | 264 tomcat10-10.1.52/java/org/apache/tomcat/util/log/CaptureLog.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/log/SystemLogHandler.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/util/log/UserDataHelper.java | 40 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/AttributeInfo.java | 36 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java | 51 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/BaseModelMBean.java | 486 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/BaseNotificationBroadcaster.java | 68 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/FeatureInfo.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/LocalStrings.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/LocalStrings_fr.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/LocalStrings_ja.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/ManagedBean.java | 144 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/NoDescriptorRegistry.java | 85 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/NotificationInfo.java | 31 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/OperationInfo.java | 43 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/ParameterInfo.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/Registry.java | 251 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/RegistryMBean.java | 90 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsDigesterSource.java | 139 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java | 151 tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/modules/ModelerSource.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/util/net/AbstractEndpoint.java | 577 tomcat10-10.1.52/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java | 119 tomcat10-10.1.52/java/org/apache/tomcat/util/net/Acceptor.java | 45 tomcat10-10.1.52/java/org/apache/tomcat/util/net/ApplicationBufferHandler.java | 7 tomcat10-10.1.52/java/org/apache/tomcat/util/net/Constants.java | 21 tomcat10-10.1.52/java/org/apache/tomcat/util/net/DispatchType.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/util/net/IPv6Utils.java | 72 tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings.properties | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_fr.properties | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_ja.properties | 5 tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_ko.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/net/Nio2Channel.java | 80 tomcat10-10.1.52/java/org/apache/tomcat/util/net/Nio2Endpoint.java | 254 tomcat10-10.1.52/java/org/apache/tomcat/util/net/NioChannel.java | 64 tomcat10-10.1.52/java/org/apache/tomcat/util/net/NioEndpoint.java | 404 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLContext.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLContextWrapper.java | 87 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLHostConfig.java | 348 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java | 68 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLImplementation.java | 26 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLSessionManager.java | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLSupport.java | 89 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLUtil.java | 58 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLUtilBase.java | 185 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SecureNio2Channel.java | 482 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SecureNioChannel.java | 345 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SendfileDataBase.java | 20 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SendfileKeepAliveState.java | 12 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SendfileState.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketBufferHandler.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketEvent.java | 24 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketProperties.java | 127 tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketWrapperBase.java | 780 tomcat10-10.1.52/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java | 141 tomcat10-10.1.52/java/org/apache/tomcat/util/net/WriteBuffer.java | 22 tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java | 9 tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java | 38 tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSESupport.java | 53 tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java | 24 tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/LocalStrings_fr.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/LocalStrings_ja.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/PEMFile.java | 25 tomcat10-10.1.52/java/org/apache/tomcat/util/net/mbeans-descriptors.xml | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLCertificateVerifier.java | 60 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLConfCmd.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java | 204 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java | 227 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java | 24 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLStatus.java | 51 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java | 11 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java | 24 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java | 67 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Group.java | 82 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/KeyExchange.java | 20 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/LocalStrings_zh_CN.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java | 262 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Protocol.java | 5 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/SignatureScheme.java | 100 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_fr.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_ja.properties | 1 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_zh_CN.properties | 66 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java | 575 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java | 501 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLImplementation.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java | 89 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionContext.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionStats.java | 30 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java | 11 tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLX509Certificate.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_alpn_select_cb$cb.java | 23 tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_cert_verify_callback$cb.java | 15 tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_tmp_dh_callback$dh.java | 16 tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_verify$callback.java | 15 tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_set_info_callback$cb.java | 17 tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_set_verify$callback.java | 15 tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/openssl_h.java |10171 ++++++---- tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/openssl_h_Compatibility.java | 238 tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/openssl_h_Macros.java | 78 tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/pem_password_cb.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/util/res/StringManager.java | 35 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java | 22 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/Constants.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/JarFactory.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/JarFileUrlJar.java | 40 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/JarFileUrlNestedJar.java | 7 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/NonClosingJarInputStream.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/ReferenceCountedJar.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/StandardJarScanFilter.java | 53 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/StandardJarScanner.java | 126 tomcat10-10.1.52/java/org/apache/tomcat/util/scan/UrlJar.java | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/security/ConcurrentMessageDigest.java | 35 tomcat10-10.1.52/java/org/apache/tomcat/util/security/Escape.java | 59 tomcat10-10.1.52/java/org/apache/tomcat/util/security/KeyStoreUtil.java | 22 tomcat10-10.1.52/java/org/apache/tomcat/util/security/MD5Encoder.java | 17 tomcat10-10.1.52/java/org/apache/tomcat/util/security/PermissionCheck.java | 19 tomcat10-10.1.52/java/org/apache/tomcat/util/security/PrivilegedSetAccessControlContext.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/InlineExecutorService.java | 14 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/LimitLatch.java | 43 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/LocalStrings_zh_CN.properties | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/ResizableExecutor.java | 3 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/RetryableQueue.java | 32 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java | 32 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/StopPooledThreadException.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/TaskQueue.java | 39 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/TaskThread.java | 22 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/TaskThreadFactory.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java | 1659 - tomcat10-10.1.52/java/org/apache/tomcat/util/threads/VirtualThreadExecutor.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java | 191 tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/ReflectionLessCodeGenerator.java | 88 tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java | 108 tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelGroupUtil.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelWrapper.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelWrapperNonSecure.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java | 57 tomcat10-10.1.52/java/org/apache/tomcat/websocket/Authenticator.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/websocket/BackgroundProcessManager.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/websocket/BasicAuthenticator.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/websocket/Constants.java | 36 tomcat10-10.1.52/java/org/apache/tomcat/websocket/DigestAuthenticator.java | 27 tomcat10-10.1.52/java/org/apache/tomcat/websocket/FutureToSendHandler.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings.properties | 3 tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_es.properties | 3 tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_fr.properties | 3 tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_ja.properties | 3 tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_ko.properties | 2 tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_zh_CN.properties | 4 tomcat10-10.1.52/java/org/apache/tomcat/websocket/MessagePart.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/websocket/PerMessageDeflate.java | 19 tomcat10-10.1.52/java/org/apache/tomcat/websocket/Transformation.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/websocket/TransformationBuilder.java | 26 tomcat10-10.1.52/java/org/apache/tomcat/websocket/TransformationFactory.java | 37 tomcat10-10.1.52/java/org/apache/tomcat/websocket/Util.java | 68 tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsFrameBase.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsFrameClient.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsHandshakeResponse.java | 8 tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java | 69 tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsSession.java | 71 tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsWebSocketContainer.java | 88 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties | 3 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_fr.properties | 4 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_ja.properties | 4 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_ko.properties | 2 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_zh_CN.properties | 2 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoEndpointClient.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBase.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java | 38 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/Constants.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/UpgradeUtil.java | 31 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/UriTemplate.java | 10 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsFrameServer.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsHandshakeRequest.java | 20 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java | 18 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsMappingResult.java | 6 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsPerSessionServerEndpointConfig.java | 4 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java | 16 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsSci.java | 2 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsServerContainer.java | 23 tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsWriteTimeout.java | 4 tomcat10-10.1.52/modules/cxf/.gitignore | 16 tomcat10-10.1.52/modules/cxf/pom.xml | 51 tomcat10-10.1.52/modules/cxf/src/main/java/tomcat/cxf/JsonBean.java | 6 tomcat10-10.1.52/modules/jdbc-pool/NOTICE | 2 tomcat10-10.1.52/modules/jdbc-pool/doc/changelog.xml | 1 tomcat10-10.1.52/modules/jdbc-pool/doc/jdbc-pool.xml | 1 tomcat10-10.1.52/modules/jdbc-pool/pom.xml | 9 tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java | 6 tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java | 30 tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java | 8 tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java | 10 tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java | 6 tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/Validator.java | 2 tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java | 1 tomcat10-10.1.52/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/Async0IdleTestBug50477.java | 4 tomcat10-10.1.52/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidation.java | 25 tomcat10-10.1.52/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Connection.java | 4 tomcat10-10.1.52/modules/owb/.gitignore | 16 tomcat10-10.1.52/modules/owb/pom.xml | 6 tomcat10-10.1.52/modules/stuffed/.gitignore | 16 tomcat10-10.1.52/modules/stuffed/conf/.gitignore | 16 tomcat10-10.1.52/modules/stuffed/pom.xml | 10 tomcat10-10.1.52/modules/stuffed/webapps/.gitignore | 16 tomcat10-10.1.52/res/bnd/tomcat-embed-core.jar.tmp.bnd | 1 tomcat10-10.1.52/res/bnd/tomcat-util.jar.tmp.bnd | 1 tomcat10-10.1.52/res/checkstyle/header-al2.txt | 2 tomcat10-10.1.52/res/checkstyle/org-import-control.xml | 4 tomcat10-10.1.52/res/ide-support/eclipse/formatting-asf-tomcat.xml | 6 tomcat10-10.1.52/res/ide-support/idea/codeStyles/Project.xml | 97 tomcat10-10.1.52/res/ide-support/idea/compiler.xml | 8 tomcat10-10.1.52/res/ide-support/idea/inspectionProfiles/Project_Default.xml | 35 tomcat10-10.1.52/res/ide-support/idea/tomcat.iml | 7 tomcat10-10.1.52/res/ide-support/idea/workspace.xml | 8 tomcat10-10.1.52/res/install-win/tomcat.nsi | 26 tomcat10-10.1.52/res/maven/mvn.properties.default | 4 tomcat10-10.1.52/res/maven/mvn.properties.release | 2 tomcat10-10.1.52/res/openssl/README.md | 14 tomcat10-10.1.52/res/openssl/openssl-tomcat.conf | 34 tomcat10-10.1.52/res/openssl/openssl.h | 1 tomcat10-10.1.52/res/rat/rat-excludes.txt | 11 tomcat10-10.1.52/res/scripts/check-mime.pl | 47 tomcat10-10.1.52/res/spotbugs/filter-false-positives.xml | 44 tomcat10-10.1.52/test-profiles.properties.default | 100 tomcat10-10.1.52/test/conf/TesterRewriteMapB.txt | 4 tomcat10-10.1.52/test/jakarta/el/TestBeanELResolver.java | 20 tomcat10-10.1.52/test/jakarta/el/TestBeanELResolverVarargsInvocation.java | 2 tomcat10-10.1.52/test/jakarta/el/TestELResolver.java | 4 tomcat10-10.1.52/test/jakarta/el/TestExpressionFactoryCache.java | 62 tomcat10-10.1.52/test/jakarta/el/TestImportHandler.java | 17 tomcat10-10.1.52/test/jakarta/el/TestImportHandlerStandardPackages.java | 4 tomcat10-10.1.52/test/jakarta/el/TestMapELResolver.java | 10 tomcat10-10.1.52/test/jakarta/el/TestStaticFieldELResolver.java | 8 tomcat10-10.1.52/test/jakarta/el/TesterBeanNameResolver.java | 2 tomcat10-10.1.52/test/jakarta/servlet/ServletRequestParametersBaseTest.java | 90 tomcat10-10.1.52/test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java | 3 tomcat10-10.1.52/test/jakarta/servlet/http/TestCookie.java | 8 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServlet.java | 92 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite0.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1023.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1024.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1025.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite511.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite512.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite513.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite0.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1023.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1024.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1025.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite511.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite512.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite513.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite0.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1023.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1024.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1025.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite511.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite512.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite513.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite0.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1023.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1024.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1025.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite511.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite512.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite513.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite0.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1023.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1024.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1025.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite511.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite512.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite513.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite0.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1023.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1024.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1025.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite511.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite512.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite513.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite0.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1023.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1024.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1025.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite511.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite512.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite513.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite0.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1023.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1024.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1025.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite511.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite512.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite513.java | 54 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite0.java | 57 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1.java | 57 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1023.java | 57 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1024.java | 57 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1025.java | 57 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite511.java | 57 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite512.java | 57 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite513.java | 57 tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletResponseSendError.java | 55 tomcat10-10.1.52/test/jakarta/servlet/http/TesterHttpServletPerformance.java | 2 tomcat10-10.1.52/test/jakarta/servlet/jsp/TestPageContext.java | 3 tomcat10-10.1.52/test/jakarta/servlet/jsp/TesterPageContext.java | 30 tomcat10-10.1.52/test/jakarta/servlet/jsp/TesterPageContextWithAttributes.java | 126 tomcat10-10.1.52/test/jakarta/servlet/jsp/el/TestScopedAttributeELResolver.java | 3 tomcat10-10.1.52/test/jakarta/servlet/jsp/el/TesterScopedAttributeELResolverPerformance.java | 3 tomcat10-10.1.52/test/jakarta/servlet/resources/TestSchemaValidation.java | 60 tomcat10-10.1.52/test/jakarta/websocket/TesterContainerProviderPerformance.java | 3 tomcat10-10.1.52/test/org/apache/catalina/ant/TestDeployTask.java | 10 tomcat10-10.1.52/test/org/apache/catalina/authenticator/ResponseDescriptor.java | 6 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestAuthInfoResponseHeaders.java | 22 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestAuthenticatorBaseCorsPreflight.java | 41 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestBasicAuthParser.java | 247 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestDigestAuthenticator.java | 124 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestDigestAuthenticatorAlgorithms.java | 7 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestFormAuthenticatorA.java | 252 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestFormAuthenticatorB.java | 186 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestFormAuthenticatorC.java | 185 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestJaspicCallbackHandlerInAuthenticator.java | 19 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java | 302 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestSSOChangeSessionId.java | 268 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java | 273 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java | 203 tomcat10-10.1.52/test/org/apache/catalina/authenticator/TesterDigestAuthenticatorPerformance.java | 42 tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java | 64 tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TestPersistentProviderRegistrations.java | 40 tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TestSimpleServerAuthConfig.java | 15 tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TesterServerAuthModuleA.java | 12 tomcat10-10.1.52/test/org/apache/catalina/connector/TestConnector.java | 16 tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteAdapter.java | 35 tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteAdapterCanonicalization.java | 7 tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteAdapterRequestFuzzing.java | 5 tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteInputStream.java | 6 tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteOutputStream.java | 35 tomcat10-10.1.52/test/org/apache/catalina/connector/TestInputBuffer.java | 15 tomcat10-10.1.52/test/org/apache/catalina/connector/TestKeepAliveCount.java | 22 tomcat10-10.1.52/test/org/apache/catalina/connector/TestMaxConnections.java | 20 tomcat10-10.1.52/test/org/apache/catalina/connector/TestOutputBuffer.java | 36 tomcat10-10.1.52/test/org/apache/catalina/connector/TestRequest.java | 202 tomcat10-10.1.52/test/org/apache/catalina/connector/TestResponse.java | 63 tomcat10-10.1.52/test/org/apache/catalina/connector/TestResponsePerformance.java | 5 tomcat10-10.1.52/test/org/apache/catalina/connector/TestSendFile.java | 40 tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContext.java | 38 tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextFacadeSecurityManager.java | 48 tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java | 179 tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherB.java | 531 tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherC.java | 158 tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextStripPathParams.java | 44 tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationFilterConfig.java | 9 tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationMapping.java | 49 tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationSessionCookieConfig.java | 3 tomcat10-10.1.52/test/org/apache/catalina/core/TestAprLifecycleListener.java | 11 tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextImpl.java | 456 tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextImplDispatch.java | 22 tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextImplListenerOnComplete.java | 11 tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextIoError.java | 17 tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextStateChanges.java | 39 tomcat10-10.1.52/test/org/apache/catalina/core/TestContextNamingInfoListener.java | 34 tomcat10-10.1.52/test/org/apache/catalina/core/TestDefaultInstanceManager.java | 3 tomcat10-10.1.52/test/org/apache/catalina/core/TestNamingContextListener.java | 15 tomcat10-10.1.52/test/org/apache/catalina/core/TestPropertiesRoleMappingListener.java | 4 tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContext.java | 205 tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContextAliases.java | 6 tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContextResources.java | 106 tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContextValve.java | 23 tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardHostValve.java | 18 tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardWrapper.java | 79 tomcat10-10.1.52/test/org/apache/catalina/core/TestSwallowAbortedUploads.java | 118 tomcat10-10.1.52/test/org/apache/catalina/filters/TestAddCharSetFilter.java | 2 tomcat10-10.1.52/test/org/apache/catalina/filters/TestCorsFilter.java | 125 tomcat10-10.1.52/test/org/apache/catalina/filters/TestCsrfPreventionFilter.java | 127 tomcat10-10.1.52/test/org/apache/catalina/filters/TestRateLimitFilterWithExactRateLimiter.java | 221 tomcat10-10.1.52/test/org/apache/catalina/filters/TestRemoteCIDRFilter.java | 90 tomcat10-10.1.52/test/org/apache/catalina/filters/TestRemoteIpFilter.java | 33 tomcat10-10.1.52/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter.java | 51 tomcat10-10.1.52/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter2.java | 52 tomcat10-10.1.52/test/org/apache/catalina/filters/TesterHttpServletRequest.java | 13 tomcat10-10.1.52/test/org/apache/catalina/filters/TesterHttpServletResponse.java | 4 tomcat10-10.1.52/test/org/apache/catalina/ha/context/TestReplicatedContext.java | 3 tomcat10-10.1.52/test/org/apache/catalina/ha/session/TestDeltaRequest.java | 2 tomcat10-10.1.52/test/org/apache/catalina/loader/EchoTag.java | 4 tomcat10-10.1.52/test/org/apache/catalina/loader/MyAnnotatedServlet.java | 3 tomcat10-10.1.52/test/org/apache/catalina/loader/TestVirtualContext.java | 271 tomcat10-10.1.52/test/org/apache/catalina/loader/TestVirtualWebappLoader.java | 13 tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoader.java | 133 tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoaderExecutorMemoryLeak.java | 12 tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoaderMemoryLeak.java | 13 tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoaderWeaving.java | 95 tomcat10-10.1.52/test/org/apache/catalina/loader/TesterWebappClassLoaderThreadLocalMemoryLeak.java | 75 tomcat10-10.1.52/test/org/apache/catalina/manager/TestHostManagerWebapp.java | 94 tomcat10-10.1.52/test/org/apache/catalina/manager/TestManagerWebapp.java | 310 tomcat10-10.1.52/test/org/apache/catalina/manager/TestManagerWebappSsl.java | 53 tomcat10-10.1.52/test/org/apache/catalina/manager/TestStatusTransformer.java | 25 tomcat10-10.1.52/test/org/apache/catalina/mapper/TestMapper.java | 22 tomcat10-10.1.52/test/org/apache/catalina/mapper/TestMapperWebapps.java | 4 tomcat10-10.1.52/test/org/apache/catalina/mbeans/TestRegistration.java | 103 tomcat10-10.1.52/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java | 571 tomcat10-10.1.52/test/org/apache/catalina/nonblocking/TesterAjpNonBlockingClient.java | 21 tomcat10-10.1.52/test/org/apache/catalina/realm/TestDataSourceRealm.java | 2 tomcat10-10.1.52/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java | 2 tomcat10-10.1.52/test/org/apache/catalina/realm/TestRealmBase.java | 13 tomcat10-10.1.52/test/org/apache/catalina/realm/TesterLoginModule.java | 4 tomcat10-10.1.52/test/org/apache/catalina/realm/TesterServletSecurity01.java | 7 tomcat10-10.1.52/test/org/apache/catalina/servlets/ServletOptionsBaseTest.java | 10 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServlet.java | 150 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java | 4 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletOptions.java | 4 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletPut.java | 38 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletRangeRequests.java | 38 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section13.java | 648 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section14.java | 3 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java | 21 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServlet.java | 1182 - tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServletOptionCollection.java | 6 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServletOptionsFile.java | 6 tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServletOptionsUnknown.java | 6 tomcat10-10.1.52/test/org/apache/catalina/session/Benchmarks.java | 4 tomcat10-10.1.52/test/org/apache/catalina/session/FileStoreTest.java | 8 tomcat10-10.1.52/test/org/apache/catalina/session/TestFileStoreConcurrency.java | 188 tomcat10-10.1.52/test/org/apache/catalina/session/TestPersistentManager.java | 2 tomcat10-10.1.52/test/org/apache/catalina/session/TestPersistentManagerDataSourceStore.java | 4 tomcat10-10.1.52/test/org/apache/catalina/session/TesterManager.java | 205 tomcat10-10.1.52/test/org/apache/catalina/startup/DuplicateMappingParamFilter.java | 3 tomcat10-10.1.52/test/org/apache/catalina/startup/DuplicateMappingParamServlet.java | 2 tomcat10-10.1.52/test/org/apache/catalina/startup/HostConfigAutomaticDeploymentBaseTest.java | 860 tomcat10-10.1.52/test/org/apache/catalina/startup/ParamFilter.java | 3 tomcat10-10.1.52/test/org/apache/catalina/startup/SimpleHttpClient.java | 18 tomcat10-10.1.52/test/org/apache/catalina/startup/TestContextConfigAnnotation.java | 2 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentA.java | 482 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentAddition.java | 182 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentB.java | 687 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentBrokenApp.java | 36 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC.java | 1162 - tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC8.java | 504 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentContextClassName.java | 62 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentCopyXML.java | 71 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteA.java | 62 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteB.java | 113 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteC.java | 101 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDir.java | 89 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDirXml.java | 91 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentModification.java | 167 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUnpackWAR.java | 71 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUpdateWarOffline.java | 73 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWar.java | 90 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWarXml.java | 94 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXml.java | 91 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalDirXml.java | 102 tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalWarXml.java | 105 tomcat10-10.1.52/test/org/apache/catalina/startup/TestStartupIPv6Connectors.java | 149 tomcat10-10.1.52/test/org/apache/catalina/startup/TestStrictServletComplianceAttributes.java | 77 tomcat10-10.1.52/test/org/apache/catalina/startup/TestTomcatNoServer.java | 43 tomcat10-10.1.52/test/org/apache/catalina/startup/TestTomcatStandalone.java | 222 tomcat10-10.1.52/test/org/apache/catalina/startup/TestWebappServiceLoader.java | 8 tomcat10-10.1.52/test/org/apache/catalina/startup/TestXmlValidationUsingContext.java | 83 tomcat10-10.1.52/test/org/apache/catalina/startup/TomcatBaseTest.java | 246 tomcat10-10.1.52/test/org/apache/catalina/util/TestFilterUtil.java | 120 tomcat10-10.1.52/test/org/apache/catalina/util/TestParameterMapPerformance.java | 103 tomcat10-10.1.52/test/org/apache/catalina/util/TestURLEncoder.java | 28 tomcat10-10.1.52/test/org/apache/catalina/valves/TestAccessLogValve.java | 23 tomcat10-10.1.52/test/org/apache/catalina/valves/TestCrawlerSessionManagerValve.java | 4 tomcat10-10.1.52/test/org/apache/catalina/valves/TestExtendedAccessLogValve.java | 244 tomcat10-10.1.52/test/org/apache/catalina/valves/TestExtendedAccessLogValveWrap.java | 81 tomcat10-10.1.52/test/org/apache/catalina/valves/TestJDBCAccessLogValve.java | 4 tomcat10-10.1.52/test/org/apache/catalina/valves/TestParameterLimitValve.java | 694 tomcat10-10.1.52/test/org/apache/catalina/valves/TestParameterLimitValveConfig.java | 44 tomcat10-10.1.52/test/org/apache/catalina/valves/TestRemoteIpValve.java | 20 tomcat10-10.1.52/test/org/apache/catalina/valves/TestRequestFilterValve.java | 11 tomcat10-10.1.52/test/org/apache/catalina/valves/TestSSLValve.java | 68 tomcat10-10.1.52/test/org/apache/catalina/valves/TestStuckThreadDetectionValve.java | 8 tomcat10-10.1.52/test/org/apache/catalina/valves/rewrite/TestResolverSSL.java | 2 tomcat10-10.1.52/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java | 237 tomcat10-10.1.52/test/org/apache/catalina/webresources/AbstractTestResourceSet.java | 69 tomcat10-10.1.52/test/org/apache/catalina/webresources/AbstractTestResourceSetMount.java | 5 tomcat10-10.1.52/test/org/apache/catalina/webresources/AbstractTestResourceSetMountTrailing.java | 80 tomcat10-10.1.52/test/org/apache/catalina/webresources/TestCachedResource.java | 35 tomcat10-10.1.52/test/org/apache/catalina/webresources/TestDirResourceSetMountTrailing.java | 85 tomcat10-10.1.52/test/org/apache/catalina/webresources/TestJarContents.java | 6 tomcat10-10.1.52/test/org/apache/catalina/webresources/TestJarResourceSetMountTrailing.java | 60 tomcat10-10.1.52/test/org/apache/catalina/webresources/TestWebResourceContentType.java | 88 tomcat10-10.1.52/test/org/apache/coyote/TestCompressionConfig.java | 61 tomcat10-10.1.52/test/org/apache/coyote/TestIoTimeouts.java | 12 tomcat10-10.1.52/test/org/apache/coyote/ajp/SimpleAjpClient.java | 58 tomcat10-10.1.52/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java | 21 tomcat10-10.1.52/test/org/apache/coyote/http11/TestHttp11InputBuffer.java | 62 tomcat10-10.1.52/test/org/apache/coyote/http11/TestHttp11Processor.java | 345 tomcat10-10.1.52/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java | 402 tomcat10-10.1.52/test/org/apache/coyote/http2/Http2TestBase.java | 36 tomcat10-10.1.52/test/org/apache/coyote/http2/TestAsync.java | 4 tomcat10-10.1.52/test/org/apache/coyote/http2/TestAsyncError.java | 19 tomcat10-10.1.52/test/org/apache/coyote/http2/TestFlowControl.java | 3 tomcat10-10.1.52/test/org/apache/coyote/http2/TestHpack.java | 64 tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2AccessLogs.java | 4 tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Limits.java | 5 tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2RequestParameters.java | 52 tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Section_3_5.java | 2 tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Section_6_1.java | 53 tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Section_8_1.java | 25 tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2UpgradeHandler.java | 3 tomcat10-10.1.52/test/org/apache/coyote/http2/TestRfc9218.java | 41 tomcat10-10.1.52/test/org/apache/coyote/http2/TestStandardSessionIntegrationHttp2.java | 174 tomcat10-10.1.52/test/org/apache/coyote/http2/TestStreamProcessor.java | 17 tomcat10-10.1.52/test/org/apache/el/TestELInJsp.java | 10 tomcat10-10.1.52/test/org/apache/el/parser/TestAstIdentifier.java | 78 tomcat10-10.1.52/test/org/apache/el/parser/TesterGenerateIdentifierRanges.java | 97 tomcat10-10.1.52/test/org/apache/jasper/compiler/TestGenerator.java | 139 tomcat10-10.1.52/test/org/apache/jasper/compiler/TestNonstandardTagPerformance.java | 148 tomcat10-10.1.52/test/org/apache/jasper/compiler/TestValidator.java | 4 tomcat10-10.1.52/test/org/apache/jasper/runtime/TestJspRuntimeLibrary.java | 37 tomcat10-10.1.52/test/org/apache/jasper/servlet/TestJspServlet.java | 7 tomcat10-10.1.52/test/org/apache/juli/TestJsonFormatter.java | 59 tomcat10-10.1.52/test/org/apache/juli/TestLogUtil.java | 93 tomcat10-10.1.52/test/org/apache/juli/TestPerWebappJuliIntegration.java | 164 tomcat10-10.1.52/test/org/apache/naming/TestNamingContext.java | 56 tomcat10-10.1.52/test/org/apache/tomcat/buildutil/translate/TestUtils.java | 44 tomcat10-10.1.52/test/org/apache/tomcat/security/TestSecurity2017Ocsp.java | 114 tomcat10-10.1.52/test/org/apache/tomcat/security/TestSecurity2023.java | 251 tomcat10-10.1.52/test/org/apache/tomcat/security/TestSecurity2025Http2.java | 62 tomcat10-10.1.52/test/org/apache/tomcat/util/buf/TestCharsetUtil.java | 2 tomcat10-10.1.52/test/org/apache/tomcat/util/buf/TestUDecoder.java | 189 tomcat10-10.1.52/test/org/apache/tomcat/util/concurrent/TestKeyedReentrantReadWriteLock.java | 31 tomcat10-10.1.52/test/org/apache/tomcat/util/descriptor/web/TestSecurityConstraint.java | 53 tomcat10-10.1.52/test/org/apache/tomcat/util/descriptor/web/TestWebXml.java | 3 tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java | 15 tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestCookieProcessorGenerationHttp.java | 45 tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestMethod.java | 43 tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestMethodPerformance.java | 67 tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestRequestUtilSameOrigin.java | 10 tomcat10-10.1.52/test/org/apache/tomcat/util/http/parser/TestTE.java | 95 tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestClientCertTls13.java | 2 tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestKeyManagerWrappingFips.java | 227 tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSSLHostConfig.java | 70 tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java | 161 tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSSLHostConfigCompat.java | 22 tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSsl.java | 166 tomcat10-10.1.52/test/org/apache/tomcat/util/net/TesterSupport.java | 106 tomcat10-10.1.52/test/org/apache/tomcat/util/net/ca-cert.pem | 73 tomcat10-10.1.52/test/org/apache/tomcat/util/net/index.db | 7 tomcat10-10.1.52/test/org/apache/tomcat/util/net/index.db.attr | 1 tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-crl-rsa-cert.pem | 108 tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-crl-rsa-key.pem | 28 tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-ec-cert.pem | 130 tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-ec-key.pem | 6 tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa-cert.pem | 165 tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa-key.pem | 52 tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp-responder-rsa-cert.pem | 105 tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp-responder-rsa-key.pem | 28 tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/OcspBaseTest.java | 186 tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java | 121 tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TestOcspSoftFail.java | 59 tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TestOcspTimeout.java | 73 tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponder.java | 125 tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponderNoResponse.java | 113 tomcat10-10.1.52/test/org/apache/tomcat/util/net/openssl/TestOpenSSLConf.java | 2 tomcat10-10.1.52/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java | 28 tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java | 12 tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestWebSocketFrameClient.java | 8 tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java | 10 tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestWsSessionSuspendResume.java | 6 tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterEchoServer.java | 40 tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterFirehoseServer.java | 2 tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterMessageCountClient.java | 16 tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterWsClientAutobahn.java | 8 tomcat10-10.1.52/test/org/apache/tomcat/websocket/server/TestSlowClient.java | 28 tomcat10-10.1.52/test/org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServerDeadlock.java | 2 tomcat10-10.1.52/test/webapp/WEB-INF/classes/bug69623-a.mdd | 1 tomcat10-10.1.52/test/webapp/WEB-INF/web.xml | 28 tomcat10-10.1.52/test/webapp/bug6nnnn/bug69508.jsp | 64 tomcat10-10.1.52/test/webapp/bug6nnnn/bug69635.jsp | 18 tomcat10-10.1.52/test/webapp/jsp/encoding/README.txt | 2 tomcat10-10.1.52/test/webapp/jsp/generator/lambda.jsp | 23 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-01.jsp | 26 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-02.jsp | 26 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-03.jsp | 26 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-04.jsp | 26 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-05.jsp | 26 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-01.jsp | 22 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-02.jsp | 22 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-03.jsp | 22 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-04.jsp | 22 tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-05.jsp | 22 tomcat10-10.1.52/test/webapp/jsp/generator/setproperty-01.jsp | 2 tomcat10-10.1.52/test/webapp/jsp/pageContext1.jsp | 2 tomcat10-10.1.52/webapps/ROOT/asf-logo-wide.svg | 322 tomcat10-10.1.52/webapps/docs/META-INF/context.xml | 4 tomcat10-10.1.52/webapps/docs/aio.xml | 1 tomcat10-10.1.52/webapps/docs/appdev/deployment.xml | 1 tomcat10-10.1.52/webapps/docs/appdev/index.xml | 14 tomcat10-10.1.52/webapps/docs/appdev/installation.xml | 2 tomcat10-10.1.52/webapps/docs/appdev/introduction.xml | 1 tomcat10-10.1.52/webapps/docs/appdev/processes.xml | 3 tomcat10-10.1.52/webapps/docs/appdev/sample/index.html | 1 tomcat10-10.1.52/webapps/docs/appdev/sample/src/mypackage/Hello.java | 2 tomcat10-10.1.52/webapps/docs/appdev/source.xml | 1 tomcat10-10.1.52/webapps/docs/apr.xml | 1 tomcat10-10.1.52/webapps/docs/architecture/index.xml | 5 tomcat10-10.1.52/webapps/docs/architecture/overview.xml | 1 tomcat10-10.1.52/webapps/docs/architecture/requestProcess.xml | 81 tomcat10-10.1.52/webapps/docs/architecture/requestProcess/11_nio.plantuml | 97 tomcat10-10.1.52/webapps/docs/architecture/requestProcess/21_http11.plantuml | 38 tomcat10-10.1.52/webapps/docs/architecture/requestProcess/31_synchronous.plantuml | 109 tomcat10-10.1.52/webapps/docs/architecture/requestProcess/41_basic.plantuml | 78 tomcat10-10.1.52/webapps/docs/architecture/startup.xml | 3 tomcat10-10.1.52/webapps/docs/balancer-howto.xml | 3 tomcat10-10.1.52/webapps/docs/cdi.xml | 85 tomcat10-10.1.52/webapps/docs/changelog.xml | 1558 + tomcat10-10.1.52/webapps/docs/class-loader-howto.xml | 4 tomcat10-10.1.52/webapps/docs/cluster-howto.xml | 14 tomcat10-10.1.52/webapps/docs/config/ajp.xml | 70 tomcat10-10.1.52/webapps/docs/config/cluster-channel.xml | 1 tomcat10-10.1.52/webapps/docs/config/cluster-deployer.xml | 1 tomcat10-10.1.52/webapps/docs/config/cluster-interceptor.xml | 1 tomcat10-10.1.52/webapps/docs/config/cluster-listener.xml | 1 tomcat10-10.1.52/webapps/docs/config/cluster-manager.xml | 52 tomcat10-10.1.52/webapps/docs/config/cluster-membership.xml | 1 tomcat10-10.1.52/webapps/docs/config/cluster-receiver.xml | 1 tomcat10-10.1.52/webapps/docs/config/cluster-sender.xml | 1 tomcat10-10.1.52/webapps/docs/config/cluster-valve.xml | 47 tomcat10-10.1.52/webapps/docs/config/cluster.xml | 98 tomcat10-10.1.52/webapps/docs/config/context.xml | 94 tomcat10-10.1.52/webapps/docs/config/credentialhandler.xml | 13 tomcat10-10.1.52/webapps/docs/config/engine.xml | 5 tomcat10-10.1.52/webapps/docs/config/executor.xml | 1 tomcat10-10.1.52/webapps/docs/config/filter.xml | 37 tomcat10-10.1.52/webapps/docs/config/globalresources.xml | 2 tomcat10-10.1.52/webapps/docs/config/host.xml | 15 tomcat10-10.1.52/webapps/docs/config/http.xml | 194 tomcat10-10.1.52/webapps/docs/config/http2.xml | 7 tomcat10-10.1.52/webapps/docs/config/index.xml | 1 tomcat10-10.1.52/webapps/docs/config/listeners.xml | 2 tomcat10-10.1.52/webapps/docs/config/loader.xml | 1 tomcat10-10.1.52/webapps/docs/config/manager.xml | 2 tomcat10-10.1.52/webapps/docs/config/project.xml | 1 tomcat10-10.1.52/webapps/docs/config/realm.xml | 22 tomcat10-10.1.52/webapps/docs/config/resources.xml | 47 tomcat10-10.1.52/webapps/docs/config/runtime-attributes.xml | 190 tomcat10-10.1.52/webapps/docs/config/server.xml | 3 tomcat10-10.1.52/webapps/docs/config/service.xml | 1 tomcat10-10.1.52/webapps/docs/config/valve.xml | 204 tomcat10-10.1.52/webapps/docs/connectors.xml | 1 tomcat10-10.1.52/webapps/docs/default-servlet.xml | 41 tomcat10-10.1.52/webapps/docs/deployer-howto.xml | 1 tomcat10-10.1.52/webapps/docs/developers.xml | 2 tomcat10-10.1.52/webapps/docs/graal.xml | 2 tomcat10-10.1.52/webapps/docs/html-manager-howto.xml | 1 tomcat10-10.1.52/webapps/docs/images/asf-logo.svg | 253 tomcat10-10.1.52/webapps/docs/index.xml | 3 tomcat10-10.1.52/webapps/docs/introduction.xml | 6 tomcat10-10.1.52/webapps/docs/jasper-howto.xml | 2 tomcat10-10.1.52/webapps/docs/jndi-datasource-examples-howto.xml | 4 tomcat10-10.1.52/webapps/docs/jndi-resources-howto.xml | 25 tomcat10-10.1.52/webapps/docs/logging.xml | 21 tomcat10-10.1.52/webapps/docs/manager-howto.xml | 11 tomcat10-10.1.52/webapps/docs/maven-jars.xml | 1 tomcat10-10.1.52/webapps/docs/mbeans-descriptors-howto.xml | 1 tomcat10-10.1.52/webapps/docs/proxy-howto.xml | 1 tomcat10-10.1.52/webapps/docs/realm-howto.xml | 3 tomcat10-10.1.52/webapps/docs/rewrite.xml | 25 tomcat10-10.1.52/webapps/docs/security-howto.xml | 115 tomcat10-10.1.52/webapps/docs/security-manager-howto.xml | 2 tomcat10-10.1.52/webapps/docs/setup.xml | 1 tomcat10-10.1.52/webapps/docs/ssi-howto.xml | 1 tomcat10-10.1.52/webapps/docs/ssl-howto.xml | 35 tomcat10-10.1.52/webapps/docs/tomcat-docs.xsl | 3 tomcat10-10.1.52/webapps/docs/tribes/introduction.xml | 1 tomcat10-10.1.52/webapps/docs/windows-service-howto.xml | 1 tomcat10-10.1.52/webapps/examples/META-INF/context.xml | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/CookieExample.java | 2 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/HelloWorldExample.java | 3 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/LocalStrings_zh_CN.properties | 2 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/RequestHeaderExample.java | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/RequestInfoExample.java | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/RequestParamExample.java | 3 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/ServletToJsp.java | 1 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/SessionExample.java | 3 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java | 5 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java | 3 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java | 3 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java | 5 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/examples/FooTag.java | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/examples/LogTag.java | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/examples/ValuesTag.java | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java | 2 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/listeners/ContextListener.java | 2 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/listeners/SessionListener.java | 2 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/num/NumberGuessBean.java | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/util/HTMLFilter.java | 3 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/validators/DebugValidator.java | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.java | 8 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java | 4 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.java | 8 tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java | 30 tomcat10-10.1.52/webapps/examples/jsp/index.html | 1 tomcat10-10.1.52/webapps/examples/jsp/jsp2/jspx/textRotate.jspx | 5 tomcat10-10.1.52/webapps/examples/jsp/num/numguess.html | 4 tomcat10-10.1.52/webapps/examples/jsp/num/numguess.jsp | 2 tomcat10-10.1.52/webapps/examples/servlets/index.html | 1 tomcat10-10.1.52/webapps/host-manager/META-INF/context.xml | 4 tomcat10-10.1.52/webapps/host-manager/WEB-INF/jsp/401.jsp | 1 tomcat10-10.1.52/webapps/host-manager/WEB-INF/jsp/403.jsp | 1 tomcat10-10.1.52/webapps/host-manager/WEB-INF/jsp/404.jsp | 1 tomcat10-10.1.52/webapps/host-manager/WEB-INF/manager.xml | 4 tomcat10-10.1.52/webapps/host-manager/images/asf-logo.svg | 253 tomcat10-10.1.52/webapps/manager/META-INF/context.xml | 4 tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/401.jsp | 1 tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/403.jsp | 1 tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/404.jsp | 1 tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/connectorCerts.jsp | 5 tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/connectorCiphers.jsp | 5 tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/connectorTrustedCerts.jsp | 5 tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/sessionDetail.jsp | 10 tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/sessionsList.jsp | 8 tomcat10-10.1.52/webapps/manager/images/asf-logo.svg | 253 1957 files changed, 65220 insertions(+), 44345 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp1o_em1fe/tomcat10_10.1.34-0+deb12u2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp1o_em1fe/tomcat10_10.1.52-1~deb12u1.dsc: no acceptable signature found diff -Nru tomcat10-10.1.34/.github/workflows/ci.yml tomcat10-10.1.52/.github/workflows/ci.yml --- tomcat10-10.1.34/.github/workflows/ci.yml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/.github/workflows/ci.yml 2026-01-23 19:33:36.000000000 +0000 @@ -30,7 +30,7 @@ matrix: isMain: - ${{ contains(github.ref, 'main') }} - java: [ 23 ] + java: [ 26-ea ] os: [ ubuntu-latest ] name: JDK${{ matrix.java }} ${{ matrix.os }} runs-on: ${{ matrix.os }} @@ -46,11 +46,10 @@ run: | ant -noinput echoproperties deploy embed test-nio test-status env: - ANT_OPTS: -Dtest.openssl.exists=false -Dtest.excludePerformance=true -Dtest.exclude=jakarta/servlet/http/TestHttpServletDoHeadValidWrite0.java,jakarta/servlet/http/TestHttpServletDoHeadValidWrite1023.java,jakarta/servlet/http/TestHttpServletDoHeadValidWrite1024.java,jakarta/servlet/http/TestHttpServletDoHeadValidWrite1025.java,jakarta/servlet/http/TestHttpServletDoHeadValidWrite1.java,jakarta/servlet/http/TestHttpServletDoHeadValidWrite511.java,jakarta/servlet/http/TestHttpServletDoHeadValidWrite512.java,jakarta/servlet/http/TestHttpServletDoHeadValidWrite513.java,jakarta/servlet/http/TestHttpServletResponseSendError.java,org/apache/catalina/authenticator/TestFormAuthenticatorA.java,org/apache/catalina/authenticator/TestFormAuthenticatorB.java,org/apache/catalina/authenticator/TestFormAuthenticatorC.java,org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java,org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java,org/apache/catalina/core/TestAsyncContextImpl.java,org/apache/catalina/core/TestAsyncContextStateChanges.java,org/apache/catalina/core/TestStandardContextResources.java,org/apache/catalina/core/TestStandardWrapper.java,org/apache/catalina/loader/TestVirtualContext.java,org/apache/catalina/mapper/TestMapperWebapps.java,org/apache/catalina/nonblocking/TestNonBlockingAPI.java,org/apache/catalina/servlets/TestDefaultServletEncodingPassThroughBom.java,org/apache/catalina/servlets/TestDefaultServletEncodingWithBom.java,org/apache/catalina/servlets/TestDefaultServletEncodingWithoutBom.java,org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java,org/apache/catalina/servlets/TestDefaultServlet.java,org/apache/catalina/servlets/TestDefaultServletOptions.java,org/apache/catalina/servlets/TestWebdavServletOptionsFile.java,org/apache/catalina/startup/TestContextConfig.java,org/apache/catalina/startup/TestHostConfigAutomaticDeploymentA.java,org/apache/catalina/startup/TestHostConfigAutomaticDeploymentB.java,org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC.java,org/apache/catalina/valves/rewrite/TestRewriteValve.java,org/apache/catalina/valves/TestStuckThreadDetectionValve.java,org/apache/coyote/ajp/TestAbstractAjpProcessor.java,org/apache/coyote/http11/filters/TestChunkedInputFilter.java,org/apache/coyote/http11/TestHttp11InputBufferCRLF.java,org/apache/coyote/http11/TestHttp11InputBuffer.java,org/apache/coyote/http11/TestHttp11Processor.java,org/apache/coyote/http2/TestAsync.java,org/apache/coyote/http2/TestHttp2ConnectionTimeouts.java,org/apache/coyote/http2/TestHttp2Limits.java,org/apache/coyote/http2/TestHttp2Section_6_8.java,org/apache/coyote/http2/TestHttp2Timeouts.java,org/apache/coyote/http2/TestStreamQueryString.java,org/apache/el/TestELInJsp.java,org/apache/jasper/compiler/TestCompiler.java,org/apache/jasper/compiler/TestEncodingDetector.java,org/apache/jasper/compiler/TestGenerator.java,org/apache/jasper/compiler/TestJspConfig.java,org/apache/jasper/compiler/TestJspDocumentParser.java,org/apache/jasper/compiler/TestValidator.java,org/apache/jasper/optimizations/TestELInterpreterTagSetters.java,org/apache/jasper/optimizations/TestStringInterpreterTagSetters.java,org/apache/jasper/runtime/TestCustomHttpJspPage.java,org/apache/jasper/runtime/TestJspContextWrapper.java,org/apache/jasper/runtime/TestJspRuntimeLibrary.java,org/apache/jasper/runtime/TestPageContextImpl.java,org/apache/jasper/servlet/TestTldScanner.java,org/apache/naming/resources/TestWarDirContext.java,org/apache/naming/TestEnvEntry.java,org/apache/tomcat/util/net/TestClientCert.java,org/apache/tomcat/util/net/TestCustomSslTrustManager.java,org/apache/tomcat/util/net/TestSSLHostConfigCompat.java,org/apache/tomcat/util/net/TestSsl.java,org/apache/tomcat/websocket/server/TestSlowClient.java,org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServerDeadlock.java,org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java,org/apache/tomcat/websocket/TestWsWebSocketContainer.java + ANT_OPTS: -Dtest.openssl.exists=false -Dtest.excludePerformance=true -Dtest.profile=smoke - continue-on-error: - true - name: Upload logs + if: ${{ !cancelled() }} uses: actions/upload-artifact@v4 with: name: JDK${{ matrix.java }}-${{ matrix.os }}-logs diff -Nru tomcat10-10.1.34/.gitignore tomcat10-10.1.52/.gitignore --- tomcat10-10.1.34/.gitignore 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/.gitignore 2026-01-23 19:33:36.000000000 +0000 @@ -51,3 +51,4 @@ modules/jdbc-pool/bin modules/jdbc-pool/includes webapps/docs/jdbc-pool.xml +/test/org/apache/tomcat/util/net/ocsp/ocsp-work/ diff -Nru tomcat10-10.1.34/BUILDING.txt tomcat10-10.1.52/BUILDING.txt --- tomcat10-10.1.34/BUILDING.txt 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/BUILDING.txt 2026-01-23 19:33:36.000000000 +0000 @@ -249,19 +249,42 @@ cd ${tomcat.source} ant embed +(5.4) Building the Windows installer + +The Windows installer uses the NSIS installer framework. +It can be build on Windows, on any other platform which provides +the Wine Windows emulator or the NSIS binary "makensis". + +Linux and MacOS are platforms, on which you can install Wine or +"makensis". + +Selecting between Wine and makensis on non-Windows platforms can +be done by setting the ant property "nsis.tool" to either "wine" +or "makensis" in build.properties. + +If you want to sign the installer, you will need to set some +properties with names prefixed with "codesigning" in your build.properties. +For details see the targets "jsign-installer" and "jsign-uninstaller" +in build.xml and the default property values in build.properties.default. + (6) Building a full release (as provided via the ASF download pages) + 1. Remark on building the Windows installer + A full release includes the Windows installer which requires a Windows - environment to be available to create it. If not building in a Windows - environment, the build scripts assume that Wine is available. If this is not - the case, the skip.installer property may be set to skip the creation of the - Windows installer. + environment, an installed Wine emulator or an installed native + "makensis" binary from the NSIS project. Creating a reproducible + installer using the "makensis" option needs a special build of "makensis". + For details see below. - Provided that Wine is available on non-Windows platforms, a full release - build may be made on Windows, Linux or MacOS. + Provided that Wine or "makensis" is available on non-Windows platforms, + a full release build may be made on Windows, Linux or MacOS. - 1. Configure GPG, if needed + If you do not want to build the Windows installer, the skip.installer + property may be set to skip the creation of the Windows installer. + + 2. Configure GPG, if needed If the released artifacts have to be cryptographically signed with a PGP signature, like the official ASF releases are, the following @@ -270,22 +293,23 @@ # Location of GPG executable (used only for releases) gpg.exec=/path/to/gpg - You do not need it if you do not plan to sign the release. - - If "gpg.exec" property does not point to an existing file, it will be - ignored and this feature will be deactivated. - You will be prompted for the GPG passphrase when the release build starts, unless "gpg.passphrase" property is set. - 2. If building the Windows installer + The ant target "verify-release" also uses the property "gpg.exec" to + call the GPG executable. If you want to use "verify-release" but you + do not want to sign the release artefacts, set "gpg.exec" and also + "gpg.sign.files=false". + + + 3. If building the Windows installer on Windows If running the build in a UAC enabled environment, building the Windows installer requires elevated privileges. The simplest way to do this is to open the command prompt used for the build with the "Run as administrator" option. - 3. Configure the code signing service + 4. Configure the code signing service ASF committers performing official releases will need to configure the code signing service so that the Windows installer is signed during the build @@ -297,10 +321,57 @@ # Code signing of Windows installer do.codesigning=true codesigning.storepass=request-via-pmc + codesigning.keypass=request-via-pmc Release managers will be provided with the necessary credentials by the PMC. - 4. Build the release: + If you want to verify the installer from a release by rebuilding it, + you can use the detached signatures provided in the official releases. + In this case you have to use the sources from the source release + archive, which contains these signatures. You also have to build + on Windows, use Wine, or use a special build of makensis to create + a reproducible installer, that fits the signature files. + + 5. Using a special "makensis" build on non-Windows (optional) + + If you want to build a reproducible installer on non-Windows + by using "makensis" instead of "Wine", you need to build + "makensis" from a source download yourself. To build "makensis", + you need python, scons and a C compiler. You can build "makensis" + via an Ant target: + + ant local-makensis + + or you can build it manually using the following recipe. + + First detect the needed version by looking for nsis.version + in build.properties.default. + + Download and extract NSIS for Windows by running "ant download-dist". Note + the NSIS installation path from the output or by checking "nsis.bin.home" in + build.properties.default. For example by default: + nsis.bin.home=${base.path}/nsis-${nsis.version} + + The corresponding sources will be downloaded to "nsis.src.home" in + build.properties.default. For example by default: + nsis.src.home=${base.path}/nsis-${nsis.version}-src + + Install the scons Python software construction tool. + + Run the following command in the extracted NSIS source directory: + + scons UNICODE=yes PREFIX=${base.path}/nsis-${nsis.version}/Bin SKIPPLUGINS=all SKIPUTILS=all SKIPMISC=all NSIS_CONFIG_CONST_DATA_PATH=no VERSION=${nsis.version} install-compiler + + You need to specify the correct value in VERSION (this ends up in the installer binary + so it needs to match). The PREFIX is not important, but you need to make sure + that the created binary "makensis" gets finally put into the "Bin" directory of + the NSIS binary distribution that "ant download-dist" installed. When using + "NSIS_CONFIG_CONST_DATA_PATH=no" the binary is independent of the value of "PREFIX". + + In addition set the ant property "nsis.tool" to "makensis" in + build.properties. + + 6. Build the release: Apache Tomcat releases are fully reproducible. @@ -469,7 +540,16 @@ This is configured by setting "test.threads" property. The recommended value is one thread per core. - 6. Optional support is provided for the Cobertura code coverage tool. + 6. Test output can be suppressed for cleaner console output, especially useful + when running tests with multiple threads: + + ant test -Dtest.silent=true + + This suppresses JUnit console output while still saving all test results to + individual log files in output/build/logs. The test-status target will still + display a summary of any failures or skipped tests. + + 7. Optional support is provided for the Cobertura code coverage tool. NOTE: Cobertura is licensed under GPL v2 with parts of it being under Apache License v1.1. See https://cobertura.github.io/cobertura/ for details. diff -Nru tomcat10-10.1.34/CONTRIBUTING.md tomcat10-10.1.52/CONTRIBUTING.md --- tomcat10-10.1.34/CONTRIBUTING.md 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/CONTRIBUTING.md 2026-01-23 19:33:36.000000000 +0000 @@ -43,7 +43,7 @@ is pretty new to the project, so if there aren't any issues in the filter feel free to ask on the [dev list](https://tomcat.apache.org/lists.html#tomcat-dev). -* [Beginner issues](https://bz.apache.org/bugzilla/buglist.cgi?bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=NEEDINFO&keywords=Beginner&keywords_type=allwords&list_id=160824&product=Tomcat%207&product=Tomcat%208.5&product=Tomcat%209&query_format=advanced) - +* [Beginner issues](https://bz.apache.org/bugzilla/buglist.cgi?bug_status=__open__&keywords=Beginner&keywords_type=allwords&product=Tomcat%209&product=Tomcat%2010&product=Tomcat%2011&query_format=advanced) - issues which should only require a few lines of code, and a test or two to resolve. diff -Nru tomcat10-10.1.34/MERGE.txt tomcat10-10.1.52/MERGE.txt --- tomcat10-10.1.34/MERGE.txt 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/MERGE.txt 2026-01-23 19:33:36.000000000 +0000 @@ -55,7 +55,7 @@ Sub-tree: src/main/java/org/apache/commons/fileupload The SHA1 ID / tag for the most recent commit to be merged to Tomcat is: -7a8c3241cfa8d036452cd4fc3f92d57cff189bca (2023-09-16) +rel/commons-fileupload-1.6.0 (2025-06-05) Note: Tomcat's copy of fileupload also includes classes copied manually from Commons IO. @@ -67,7 +67,7 @@ Sub-tree src/main/java/org/apache/commons/pool2 The SHA1 ID / tag for the most recent commit to be merged to Tomcat is: -rel/commons-pool-2.12.0 (2023-09-30) +rel/commons-pool-2.13.1 (2025-12-30) DBCP2 No unused code removed @@ -75,4 +75,4 @@ src/main/java/org/apache/commons/dbcp2 src/main/resources/org/apache/commons/dbcp2 The SHA1 ID / tag for the most recent commit to be merged to Tomcat is: -rel/commons-dbcp-2.13.0 (2024-12-02) +rel/commons-dbcp-2.14.0 (2025-12-16) diff -Nru tomcat10-10.1.34/NOTICE tomcat10-10.1.52/NOTICE --- tomcat10-10.1.34/NOTICE 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/NOTICE 2026-01-23 19:33:36.000000000 +0000 @@ -1,5 +1,5 @@ Apache Tomcat -Copyright 1999-2024 The Apache Software Foundation +Copyright 1999-2026 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (https://www.apache.org/). diff -Nru tomcat10-10.1.34/README.md tomcat10-10.1.52/README.md --- tomcat10-10.1.34/README.md 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/README.md 2026-01-23 19:33:36.000000000 +0000 @@ -57,9 +57,7 @@ ### Support and Mailing List Information * Free community support is available through the -[tomcat-users](https://tomcat.apache.org/lists.html#tomcat-users) email list and -a dedicated [IRC channel](https://tomcat.apache.org/irc.html) (#tomcat on -Freenode). +[tomcat-users](https://tomcat.apache.org/lists.html#tomcat-users) email list. * If you want freely available support for running Apache Tomcat, please see the resources page [here](https://tomcat.apache.org/findhelp.html). diff -Nru tomcat10-10.1.34/RUNNING.txt tomcat10-10.1.52/RUNNING.txt --- tomcat10-10.1.34/RUNNING.txt 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/RUNNING.txt 2026-01-23 19:33:36.000000000 +0000 @@ -305,8 +305,8 @@ - + diff -Nru tomcat10-10.1.34/bin/catalina.bat tomcat10-10.1.52/bin/catalina.bat --- tomcat10-10.1.34/bin/catalina.bat 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/bin/catalina.bat 2026-01-23 19:33:36.000000000 +0000 @@ -207,6 +207,13 @@ rem Do this here so custom URL handles (specifically 'war:...') can be used in the security policy set "JAVA_OPTS=%JAVA_OPTS% -Djava.protocol.handler.pkgs=org.apache.catalina.webresources" +rem Disable the global canonical file name cache to protect against CVE-2024-56337 +rem Note: The cache is disabled by default in Java 12 to 20 inclusive +rem The cache is removed in Java 21 onwards +rem Need to set this here as java.io.FileSystem caches this in a static field during class +rem initialisation so it needs to be set before any file system access +set "JAVA_OPTS=%JAVA_OPTS% -Dsun.io.useCanonCaches=false" + if not "%CATALINA_LOGGING_CONFIG%" == "" goto noJuliConfig set CATALINA_LOGGING_CONFIG=-Dnop if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig @@ -217,8 +224,9 @@ set LOGGING_MANAGER=-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager :noJuliManager -rem Configure module start-up parameters +rem Configure module start-up parameters - ensure to keep it in line with service.bat set "JAVA_OPTS=%JAVA_OPTS% --add-opens=java.base/java.lang=ALL-UNNAMED" +set "JAVA_OPTS=%JAVA_OPTS% --add-opens=java.base/java.lang.reflect=ALL-UNNAMED" set "JAVA_OPTS=%JAVA_OPTS% --add-opens=java.base/java.io=ALL-UNNAMED" set "JAVA_OPTS=%JAVA_OPTS% --add-opens=java.base/java.util=ALL-UNNAMED" set "JAVA_OPTS=%JAVA_OPTS% --add-opens=java.base/java.util.concurrent=ALL-UNNAMED" diff -Nru tomcat10-10.1.34/bin/catalina.sh tomcat10-10.1.52/bin/catalina.sh --- tomcat10-10.1.34/bin/catalina.sh 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/bin/catalina.sh 2026-01-23 19:33:36.000000000 +0000 @@ -255,6 +255,13 @@ # Do this here so custom URL handles (specifically 'war:...') can be used in the security policy JAVA_OPTS="$JAVA_OPTS -Djava.protocol.handler.pkgs=org.apache.catalina.webresources" +# Disable the global canonical file name cache to protect against CVE-2024-56337 +# Note: The cache is disabled by default in Java 12 to 20 inclusive +# The cache is removed in Java 21 onwards +# Need to set this here as java.io.FileSystem caches this in a static field during class +# initialisation so it needs to be set before any file system access +JAVA_OPTS="$JAVA_OPTS -Dsun.io.useCanonCaches=false" + # Set juli LogManager config file if it is present and an override has not been issued if [ -z "$CATALINA_LOGGING_CONFIG" ]; then if [ -r "$CATALINA_BASE"/conf/logging.properties ]; then @@ -292,6 +299,7 @@ # Add the module start-up parameters required by Tomcat JAVA_OPTS="$JAVA_OPTS --add-opens=java.base/java.lang=ALL-UNNAMED" +JAVA_OPTS="$JAVA_OPTS --add-opens=java.base/java.lang.reflect=ALL-UNNAMED" JAVA_OPTS="$JAVA_OPTS --add-opens=java.base/java.io=ALL-UNNAMED" JAVA_OPTS="$JAVA_OPTS --add-opens=java.base/java.util=ALL-UNNAMED" JAVA_OPTS="$JAVA_OPTS --add-opens=java.base/java.util.concurrent=ALL-UNNAMED" diff -Nru tomcat10-10.1.34/bin/service.bat tomcat10-10.1.52/bin/service.bat --- tomcat10-10.1.34/bin/service.bat 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/bin/service.bat 2026-01-23 19:33:36.000000000 +0000 @@ -200,6 +200,14 @@ ) ) +rem Configure JAVA 9 specific start-up parameters - ensure to keep it in line with catalina.bat +set "JVM9_OPTIONS=--add-opens=java.base/java.lang=ALL-UNNAMED" +set "JVM9_OPTIONS=%JVM9_OPTIONS%;--add-opens=java.base/java.lang.reflect=ALL-UNNAMED" +set "JVM9_OPTIONS=%JVM9_OPTIONS%;--add-opens=java.base/java.io=ALL-UNNAMED" +set "JVM9_OPTIONS=%JVM9_OPTIONS%;--add-opens=java.base/java.util=ALL-UNNAMED" +set "JVM9_OPTIONS=%JVM9_OPTIONS%;--add-opens=java.base/java.util.concurrent=ALL-UNNAMED" +set "JVM9_OPTIONS=%JVM9_OPTIONS%;--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED" + "%EXECUTABLE%" //IS//%SERVICE_NAME% ^ --Description "Apache Tomcat @VERSION@ Server - https://tomcat.apache.org/" ^ --DisplayName "Apache Tomcat @VERSION_MAJOR_MINOR@ %SERVICE_NAME%" ^ @@ -217,8 +225,8 @@ --StopClass org.apache.catalina.startup.Bootstrap ^ --StartParams start ^ --StopParams stop ^ - --JvmOptions "-Dcatalina.home=%CATALINA_HOME%;-Dcatalina.base=%CATALINA_BASE%;-Djava.io.tmpdir=%CATALINA_BASE%\temp;-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager;-Djava.util.logging.config.file=%CATALINA_BASE%\conf\logging.properties;%JvmArgs%" ^ - --JvmOptions9 "--add-opens=java.base/java.lang=ALL-UNNAMED#--add-opens=java.base/java.io=ALL-UNNAMED#--add-opens=java.base/java.util=ALL-UNNAMED#--add-opens=java.base/java.util.concurrent=ALL-UNNAMED#--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED" ^ + --JvmOptions "-Dcatalina.home=%CATALINA_HOME%;-Dcatalina.base=%CATALINA_BASE%;-Djava.io.tmpdir=%CATALINA_BASE%\temp;-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager;-Djava.util.logging.config.file=%CATALINA_BASE%\conf\logging.properties;-Dsun.io.useCanonCaches=false;%JvmArgs%" ^ + --JvmOptions9 "%JVM9_OPTIONS%" ^ --Startup "%SERVICE_STARTUP_MODE%" ^ --JvmMs "%JvmMs%" ^ --JvmMx "%JvmMx%" diff -Nru tomcat10-10.1.34/build.properties.default tomcat10-10.1.52/build.properties.default --- tomcat10-10.1.34/build.properties.default 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/build.properties.default 2026-01-23 19:33:36.000000000 +0000 @@ -31,13 +31,16 @@ # ----- Version Control Flags ----- version.major=10 version.minor=1 -version.build=34 +version.build=52 version.patch=0 version.suffix= version.dev=-dev # ----- Build tools ----- ant.version.required=1.10.2 +# Which tool to use for building Windows installer +# on unix platform: wine or makensis. +nsis.tool=wine # ----- Build control flags ----- compile.debug=true @@ -62,6 +65,7 @@ # works better for Tomcat. javadoc.failonerror=true javadoc.failonwarning=false +javadoc.locale=en # ----- Test configuration ----- execute.test.nio=true @@ -72,6 +76,8 @@ test.accesslog=false # Display the tests output on the console test.verbose=true +# Suppress JUnit console output (useful for parallel testing) +test.silent=false # Number of parallel threads to use for testing. The recommended value is one # thread per core. @@ -84,13 +90,20 @@ # MacOS requires non-default settings to test FFM with OpenSSL. Eg: #openssl.ffm.1=-Dorg.apache.tomcat.util.openssl.USE_SYSTEM_LOAD_LIBRARY=true #openssl.ffm.2=-Dorg.apache.tomcat.util.openssl.LIBRARY_NAME=ssl +# Allows loading the crypto library using the JVM and the given name +# if using org.apache.tomcat.util.openssl.USE_SYSTEM_LOAD_LIBRARY +# (otherwise it is loaded later by ssl) +#openssl.ffm.3=-Dorg.apache.tomcat.util.openssl.CRYPTO_LIBRARY_NAME=crypto openssl.ffm.1=-DNoop1 openssl.ffm.2=-DNoop2 +openssl.ffm.3=-DNoop3 # ----- Release build settings ----- # Location of GPG executable gpg.exec=/path/to/gpg +# Release artefact signing with gpg +gpg.sign.files=true # Code signing of Windows installer # See https://infra.apache.org/digicert-use.html for setup instructions @@ -169,14 +182,14 @@ jdt.loc.2=https://download.eclipse.org/eclipse/downloads/drops4/${jdt.release}/ecj-${jdt.version}.jar # ----- Tomcat native library ----- -tomcat-native.version=2.0.8 -tomcat-native-openssl.version=3.0.14 +tomcat-native.version=2.0.12 +tomcat-native-openssl.version=3.5.4 tomcat-native.src.checksum.enabled=true tomcat-native.src.checksum.algorithm=SHA-512 -tomcat-native.src.checksum.value=fd45533b9c34b008717d18ed49334c7286b93c849c487c1c42746f2998cc4a6ff0362e536a8b5124c6539847a92a9f7631c7638a21cd5d22134fe1a9bb0f0702 +tomcat-native.src.checksum.value=d937e04f7c9f0fa6ef82b32928fa2d59dbdac45cb58c7ba8eff4338fbd942297b1c9512a0a8ff80cf758d9b6ca5cc5cba8cefdc91507318b72afc56888aa1f3c tomcat-native.win.checksum.enabled=true tomcat-native.win.checksum.algorithm=SHA-512 -tomcat-native.win.checksum.value=a4a8816668f14a7461711e25cb9277534981936c9e6f8b00ae55084cb265dc1d89ad07fa508ae2e1f7832236dafafbdd9d76a313c87f34e00ecfdfe75776638a +tomcat-native.win.checksum.value=f743c151a1d48a1967c08f01986b1a30176cc8a388ad760cb8aac19e6956e5630d7ddff54782c2e136e8f247809d573fd48a31ed1a756923a3ad0954e2d4a3fe tomcat-native.home=${base.path}/tomcat-native-${tomcat-native.version} tomcat-native.tar.gz=${tomcat-native.home}/tomcat-native.tar.gz tomcat-native.loc.1=${base-tomcat.loc.1}/tomcat-connectors/native/${tomcat-native.version}/source/tomcat-native-${tomcat-native.version}-src.tar.gz @@ -185,35 +198,33 @@ tomcat-native.win.2=${base-tomcat.loc.2}/tomcat-connectors/native/${tomcat-native.version}/binaries/tomcat-native-${tomcat-native.version}-openssl-${tomcat-native-openssl.version}-win32-bin.zip # ----- NSIS, version 3.0 or later ----- -nsis.version=3.10 +nsis.version=3.11 nsis.checksum.enabled=true -nsis.checksum.algorithm=MD5|SHA-1 -nsis.checksum.value=e3e2803a13ead75e4471a51069d04c20|22cf776b463c6c845444328341219167abf399dc -nsis.home=${base.path}/nsis-${nsis.version} -nsis.exe=${nsis.home}/makensis.exe -nsis.arch.dir=x86-unicode/ -nsis.installoptions.dll=${nsis.home}/Plugins/${nsis.arch.dir}InstallOptions.dll -nsis.nsexec.dll=${nsis.home}/Plugins/${nsis.arch.dir}nsExec.dll -nsis.nsisdl.dll=${nsis.home}/Plugins/${nsis.arch.dir}NSISdl.dll -nsis.system.dll=${nsis.home}/Plugins/${nsis.arch.dir}System.dll -nsis.nsdialogs.dll=${nsis.home}/Plugins/${nsis.arch.dir}nsDialogs.dll -nsis.loc=${base-sf.loc}/nsis/nsis-${nsis.version}.zip +nsis.bin.checksum.algorithm=MD5|SHA-1 +nsis.bin.checksum.value=b7c063bee3afc8127dca0fd64c4e22ce|ef7ff767e5cbd9edd22add3a32c9b8f4500bb10d +nsis.bin.home=${base.path}/nsis-${nsis.version} +nsis.executable.windows=${nsis.bin.home}/makensis.exe +nsis.bin.loc=${base-sf.loc}/nsis/nsis-${nsis.version}.zip +nsis.src.checksum.algorithm=MD5|SHA-1 +nsis.src.checksum.value=63bdc1b7676c96486532c98c0b4d2fb5|63ddba57fe46b1e0a4810ece2a7980f069c031b6 +nsis.src.home=${base.path}/nsis-${nsis.version}-src +nsis.src.loc=${base-sf.loc}/nsis/nsis-${nsis.version}-src.tar.bz2 # ----- Commons Daemon, version 1.2.0 or later ----- -commons-daemon.version=1.4.0 +commons-daemon.version=1.5.1 -# checksum for commons-daemon-1.4.0-bin.tar.gz +# checksum for commons-daemon-1.5.1-bin.tar.gz commons-daemon.bin.checksum.enabled=true commons-daemon.bin.checksum.algorithm=SHA-512 -commons-daemon.bin.checksum.value=15fccd35a711f91e5b4466d56f50585c7ae3a787a39c16e006617c86b9e9feee9fbf902582b08c2e896ca6a655500d805fdbb9c97f04f70321631168b8d42c81 +commons-daemon.bin.checksum.value=740b832b8a7b5df8f2aa8029cf6ee88b8be6f4f5e42e34587a70bbf2cbe5b7e7dd75da375d2f3f1f7f2e6a5d89b5ceba5433499773852df67e8bbeb88b466786 -# checksums for commons-daemon-1.4.0-native-src.tar.gz, commons-daemon-1.4.0-bin-windows.zip +# checksums for commons-daemon-1.5.1-native-src.tar.gz, commons-daemon-1.5.1-bin-windows.zip commons-daemon.native.src.checksum.enabled=true commons-daemon.native.src.checksum.algorithm=SHA-512 -commons-daemon.native.src.checksum.value=8a54200d547ef7ee647e8d4910fd3cb55bf7d8fc75de8f0e01bc701ef0b386ddc3843e6c9189e34d2afd62060fb6299ea83c421cf60c7d105d04cb45904500d3 +commons-daemon.native.src.checksum.value=ced2238b4fb47a208e7c9aa435e7981b5d42fd9e07d11d7f72da2b7239c388a3b7d6b7ce3424624aecfbe7a8471ee316cdd46a040fadb129096705fa3325129b commons-daemon.native.win.checksum.enabled=true commons-daemon.native.win.checksum.algorithm=SHA-512 -commons-daemon.native.win.checksum.value=5974d638994cbf821c17d0fc6b69bace08b0314ea5614c1a57175a02cda7c57a6b8ee49f8892206061f9d3385da5841db31d9ce9b3ce74cf4afc10ad8eeeee68 +commons-daemon.native.win.checksum.value=26cef55a0237aab449d2133e56815393729c3965c6fd063326bccdb569be91ddfe500b247ba3df3e9baac511452825d3d0333d165abd24932717a372a294a463 commons-daemon.home=${base.path}/commons-daemon-${commons-daemon.version} commons-daemon.jar=${commons-daemon.home}/commons-daemon-${commons-daemon.version}.jar @@ -247,10 +258,10 @@ hamcrest.loc=${base-maven.loc}/org/hamcrest/hamcrest/${hamcrest.version}/hamcrest-${hamcrest.version}.jar # ----- EasyMock, version 5.0.0 or later ----- -easymock.version=5.5.0 +easymock.version=5.6.0 easymock.checksum.enabled=true easymock.checksum.algorithm=MD5|SHA-1 -easymock.checksum.value=f1f7e94f70903b0c37a65dd2a011e31a|b07e2bcbc7b6bcae190a18b9ced742b6fd9ceec3 +easymock.checksum.value=2be7351f227b2022faf4230ceb576e0a|f8e15a47aac9838ee36be6c3eddc50bb78a06191 easymock.home=${base.path}/easymock-${easymock.version} easymock.jar=${easymock.home}/easymock-${easymock.version}.jar easymock.loc=${base-maven.loc}/org/easymock/easymock/${easymock.version}/easymock-${easymock.version}.jar @@ -265,76 +276,77 @@ objenesis.loc=${base-maven.loc}/org/objenesis/objenesis/${objenesis.version}/objenesis-${objenesis.version}.jar # ----- byte-buddy, used by EasyMock, version 1.12.18 or later ----- -bytebuddy.version=1.15.10 +bytebuddy.version=1.18.3 bytebuddy.checksum.enabled=true -bytebuddy.checksum.algorithm=MD5|SHA-1 -bytebuddy.checksum.value=0b029979f5bf509510f91c1a49fd4adb|635c873fadd853c084f84fdc3cbd58c5dd8537f9 +bytebuddy.checksum.algorithm=SHA-512 +bytebuddy.checksum.value=8f35c806a25d9089a08d12a7aaf22c5bea2f356c432a21655f30a7935918b6385e1e080180b6ef5ad3638796fc3a7243220dfec08c31c1195416e6790fd797af bytebuddy.home=${base.path}/byte-buddy-${bytebuddy.version} bytebuddy.jar=${bytebuddy.home}/byte-buddy-${bytebuddy.version}.jar bytebuddy.loc=${base-maven.loc}/net/bytebuddy/byte-buddy/${bytebuddy.version}/byte-buddy-${bytebuddy.version}.jar # ----- UnboundID, used by unit tests, version 5.1.4 or later ----- -unboundid.version=7.0.1 +unboundid.version=7.0.4 unboundid.checksum.enabled=true -unboundid.checksum.algorithm=SHA-512 -unboundid.checksum.value=6bd8681b89886989a65316bbe03429263c0f8b442ac92e17d55dffa1035f12d8fc0b363fba8a23cac7a1d6fc93f1c50233ace83ca8769f5a0a3eee8544a9a5cb +unboundid.checksum.algorithm=MD5|SHA-1 +unboundid.checksum.value=7006a217741934517b1cdd6aa12c6d9f|2fe2d5461a87a58aee97f836e3af63ef8ce7b29e unboundid.home=${base.path}/unboundid-${unboundid.version} unboundid.jar=${unboundid.home}/unboundid-ldapsdk-${unboundid.version}.jar unboundid.loc=${base-maven.loc}/com/unboundid/unboundid-ldapsdk/${unboundid.version}/unboundid-ldapsdk-${unboundid.version}.jar # ----- Checkstyle, version 6.16 or later ----- -checkstyle.version=10.20.2 +checkstyle.version=12.3.1 checkstyle.checksum.enabled=true checkstyle.checksum.algorithm=SHA-512 -checkstyle.checksum.value=e7de3f1745b3dcd0b56993f30f01453bf4d44935cacc83863cd84c94453fdf7486dbd1610bd95e9aa8083104b06af69416cfb7f39ca5b8112cb17fa36028e279 +checkstyle.checksum.value=8406977982d6e5eb9dba561ccb319daa9d98fc7172217f0c36644d5830121124181752c425dcf34fb7579232983d923e430a5d6b119272ab8615e7b1e4f10bf1 checkstyle.home=${base.path}/checkstyle-${checkstyle.version} checkstyle.jar=${checkstyle.home}/checkstyle-${checkstyle.version}-all.jar checkstyle.loc=${base-gh.loc}/checkstyle/checkstyle/releases/download/checkstyle-${checkstyle.version}/checkstyle-${checkstyle.version}-all.jar # ----- JaCoCo code coverage tool ----- -jacoco.version=0.8.12 +jacoco.version=0.8.14 jacoco.checksum.enabled=true jacoco.checksum.algorithm=MD5|SHA-1 -jacoco.checksum.value=a85698213c36c6c964b1d4011a5f8770|c77282468d7e311b7e3e4b03dc9a8c7837902b4b +jacoco.checksum.value=1da00355854e56fe99b6c5b31224ea79|93f2e83229e20e02c556e412b041eff31132e60b jacoco.home=${base.path}/jacoco-${jacoco.version} jacoco.jar=${jacoco.home}/lib/jacocoant.jar jacoco.loc=${base-maven.loc}/org/jacoco/jacoco/${jacoco.version}/jacoco-${jacoco.version}.zip # ----- SpotBugs (originally FindBugs) ----- -spotbugs.version=4.8.6 +spotbugs.version=4.9.8 spotbugs.checksum.enabled=true spotbugs.checksum.algorithm=SHA-512 -spotbugs.checksum.value=2fe8083fe52bd04b6b1ac8fe9ea3c1ae544aa6eb07ea0d602a46d54d0537355e8a79af687ee2f96cbd7bd59d60d74609e4b89df97ec935387fc6cf925e0a3dd6 +spotbugs.checksum.value=9d0dde900b3e1330ca23b81ad9aaf292d2d9d144ff1db9d397bcbe58c5ebd86dcb36e3d83ba7c3043039d2557a9bd3af35f9dd14297fc5e908745924b9c51678 spotbugs.home=${base.path}/spotbugs-${spotbugs.version} spotbugs.jar=${spotbugs.home}/lib/spotbugs-ant.jar spotbugs.loc=${base-maven.loc}/com/github/spotbugs/spotbugs/${spotbugs.version}/spotbugs-${spotbugs.version}.tgz # ----- bnd, version 6.3.0 or later ----- # ----- provides OSGI metadata for JARs ----- -bnd.version=7.1.0 +bnd.version=7.2.0 bnd.checksum.enabled=true bnd.checksum.algorithm=MD5|SHA-1 -bnd.checksum.value=9cee533d5f3973d6135e557934160180|49e4ebe633c608c498cbfc7d7a4e9dda5fefa2fc +bnd.checksum.value=dea22b7afa9de21e1adb27d2e835a94c|af26ddc466eb178963d4eb800d2824f488037aec bnd.home=${base.path}/bnd-${bnd.version} bnd.jar=${bnd.home}/biz.aQute.bnd-${bnd.version}.jar bnd.loc=${base-maven.loc}/biz/aQute/bnd/biz.aQute.bnd/${bnd.version}/biz.aQute.bnd-${bnd.version}.jar # ----- Tomcat Migration Tool for Jakarta EE ----- -migration-lib.version=1.0.8 +migration-lib.version=1.0.10 migration-lib.checksum.enabled=true migration-lib.checksum.algorithm=MD5|SHA-1 -migration-lib.checksum.value=bc5265465d7c641bbd5c9f2b057decc1|56eb518000183b5f3eface92fb9e9ccd1cbaee09 +migration-lib.checksum.value=264a836032d45f600e01a95b3d5925e3|c86a0981ae313658222fb576595c0546542c4911 migration-lib.home=${base.path}/migration-${migration-lib.version} migration-lib.jar=${migration-lib.home}/jakartaee-migration-${migration-lib.version}-shaded.jar migration-lib.loc=${base-maven.loc}/org/apache/tomcat/jakartaee-migration/${migration-lib.version}/jakartaee-migration-${migration-lib.version}-shaded.jar # ----- JSign, version 4.1 or later ----- -jsign.version=6.0 +# Note: There are known issues with Tomcat and Jsign 7.0 and 7.1 +jsign.version=7.4 jsign.checksum.enabled=true jsign.checksum.algorithm=MD5|SHA-1 -jsign.checksum.value=c14fe256b5bc42dc6934d3ce7b659cdf|d2f1a60711c3b51123f84cd9e04dd9d482d95f5e +jsign.checksum.value=18b96a09ba9e1f0bdba58775810550f5|09705503ca76807141398ded4b35ce3f987a6903 jsign.home=${base.path}/jsign-${jsign.version} jsign.jar=${jsign.home}/jsign-${jsign.version}.jar diff -Nru tomcat10-10.1.34/build.properties.release tomcat10-10.1.52/build.properties.release --- tomcat10-10.1.34/build.properties.release 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/build.properties.release 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,7 @@ version.dev= # Ensure consistent timestamps for reproducible builds. -ant.tstamp.now.iso=2024-12-05T16:01:16Z +ant.tstamp.now.iso=2026-01-23T19:29:07Z # Enable insertion of detached signatures into the Windows installer. do.codesigning=true @@ -41,14 +41,14 @@ # # Java Name: OpenJDK 64-Bit Server VM # Java Vendor: Eclipse Adoptium -# Java Version: 23.0.1+11 +# Java Version: 25.0.1+8-LTS # The following is provided for information only. Builds will be repeatable # whether or not the build environment is consistent with this information. # -# OS: aarch64 Mac OS X 15.1.1 +# OS: aarch64 Mac OS X 15.7.3 # File encoding: UTF-8 # # Release Manager: schultz -release-java-version=23.0.1+11 +release-java-version=25.0.1+8-LTS release-ant-version=1.10.15 diff -Nru tomcat10-10.1.34/build.xml tomcat10-10.1.52/build.xml --- tomcat10-10.1.34/build.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/build.xml 2026-01-23 19:33:36.000000000 +0000 @@ -198,7 +198,7 @@ - + @@ -312,11 +312,14 @@ + + + @@ -453,12 +456,16 @@ + + - + + + @@ -468,6 +475,7 @@ + @@ -545,6 +553,7 @@ + @@ -560,6 +569,7 @@ + @@ -633,7 +643,6 @@ - @@ -919,6 +928,8 @@ + + @@ -926,6 +937,8 @@ + + @@ -954,6 +967,8 @@ + + @@ -1924,7 +1939,7 @@ - + @@ -1941,11 +1956,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + depends="-test-name-default,test-nio,test-nio2,coverage-report,test-status" /> + + + depends="-test-name-default,test-only-nio,test-only-nio2,test-status" /> @@ -1983,13 +2099,13 @@ + depends="-test-name-default,setup-jacoco,test-compile,deploy,test-openssl-exists" if="${execute.test.nio}"> + depends="-test-name-default,setup-jacoco,test-openssl-exists" if="${execute.test.nio}"> @@ -2031,6 +2147,8 @@ + + - + @@ -2066,6 +2185,7 @@ + @@ -2097,14 +2217,14 @@ - - + + - - + + + + - - + + + + + + + + + + depends="dist-static,-installer-checks" + unless="${skip.installer}"> - - - - - + + + + - - - - - - + + + + + + + + + - - + + @@ -2636,44 +2829,87 @@ - - - - - - + + + + + + + - - - - - - + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + Signing ${file} in ${dir}, using/saving signature in ${savedir} + + + + + + - - - + + + - + - - - - - - - - - - - - - - + + + + + + - + + + + + + + + - - - - + depends="-uninstaller-build,-uninstaller-jsign,-installer-build,-installer-jsign" + unless="${skip.installer}"> + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -2858,7 +3119,7 @@ - + - + - -gpg.exec.available=${gpg.exec.available} -gpg.exec=${gpg.exec} - Enter GPG passphrase - + + + + + + + + gpg.exec.available=${gpg.exec.available} + gpg.exec=${gpg.exec} + Enter GPG passphrase: - + + + @@ -3396,6 +3664,11 @@ + + + + + - - - + + + - - + + + + + + + + + + + @@ -3822,6 +4104,23 @@ + + + + + + + + + + + + + + @@ -3961,8 +4260,14 @@ IntelliJ IDEA project directory created. + The SDK was set to "${build.java.version}" so make sure that your IDE has an SDK with that name, -or update the Project Settings accordingly. +or update the Project Settings accordingly. Choose [File > Project Structure] to open the settings dialog +and set the SDK level, language level, etc. as needed. + +Some folders have been excluded by default as they require the latest SDK version and enabling of preview +features. You can remove or comment out the exclusions to enable the directories in the .idea/tomcat.iml +file, or via the IDEA UI by right-clicking folders, and choosing [Mark Directory as > Cancel Exclusion] @@ -4258,6 +4563,86 @@ + + + + + + + + + + + + + + + + + + + + + + + +Valid signature for @{src-or-bin}/@{basefile} + + + + + +********************************************** +********************************************** +Invalid signature for @{src-or-bin}/@{basefile} +********************************************** +********************************************** + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Unable to locate release signature for @{basefile} + + + + @@ -4272,6 +4657,16 @@ + + + + + + + + + + @@ -4283,6 +4678,17 @@ + + + + + + + + + + + @@ -4294,6 +4700,23 @@ + +Don't worry if there are a bunch of "WARNING: untrusted key" warnings below. +It's just because the KEYS -> apache-keys import doesn't contain any ownertrust +information. + + + + + + + + + + + + + - - - - - + + + + + default @@ -846,6 +846,10 @@ video/x-msvideo + avif + image/avif + + avx video/x-rad-screenplay @@ -2150,6 +2154,10 @@ text/plain + jxl + image/jxl + + kar audio/midi @@ -2318,6 +2326,14 @@ audio/mpeg + m2t + video/mp2t + + + m2ts + video/mp2t + + m2v video/mpeg @@ -2687,7 +2703,7 @@ mts - model/vnd.mts + video/mp2t mus @@ -3727,6 +3743,14 @@ application/x-sql + sqlite + application/vnd.sqlite3 + + + sqlite3 + application/vnd.sqlite3 + + src application/x-wais-source @@ -3975,6 +3999,10 @@ application/x-msterminal + ts + video/mp2t + + tsd application/timestamped-data diff -Nru tomcat10-10.1.34/debian/changelog tomcat10-10.1.52/debian/changelog --- tomcat10-10.1.34/debian/changelog 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/changelog 2026-02-03 13:18:04.000000000 +0000 @@ -1,4 +1,64 @@ -tomcat10 (10.1.34-0+deb12u2) bookworm-security; urgency=high +tomcat10 (10.1.52-1~deb12u1) bookworm-security; urgency=medium + + * Backport 10.1.52. to bookworm. + * Fix CVE-2025-46701, CVE-2025-48976, CVE-2025-48988, CVE-2025-48989, + CVE-2025-49125, CVE-2025-52520, CVE-2025-53506, CVE-2025-55668, + CVE-2025-55752, CVE-2025-55754, CVE-2025-61795, CVE-2025-31650 and + CVE-2025-31651. + Several security vulnerabilities have been found in Tomcat 10, a Java web + server and servlet engine. This update improves the handling of + HTTP/2 connections and corrects various flaws which can lead to + uncontrolled resource consumption and a denial of service. + + -- Markus Koschany Tue, 03 Feb 2026 14:18:04 +0100 + +tomcat10 (10.1.52-1~deb13u1) trixie-security; urgency=medium + + * Backport 10.1.52. to trixie. + * Fix CVE-2025-46701, CVE-2025-48976, CVE-2025-48988, CVE-2025-48989, + CVE-2025-49125, CVE-2025-52520, CVE-2025-53506, CVE-2025-55668, + CVE-2025-55752, CVE-2025-55754 and CVE-2025-61795. + Several security vulnerabilities have been found in Tomcat 10, a Java web + server and servlet engine. This update improves the handling of + HTTP/2 connections and corrects various flaws which can lead to + uncontrolled resource consumption and a denial of service. + + -- Markus Koschany Tue, 03 Feb 2026 13:29:34 +0100 + +tomcat10 (10.1.52-1) unstable; urgency=medium + + * New upstream version 10.1.52. + - Fix CVE-2025-61795: denial-of-service (Closes: #1119294) + - Fix CVE-2025-48989: "made you reset attack" (Closes: #1111096) + * Declare compliance with Debian Policy 4.7.3. + * Refresh the patches. + + -- Markus Koschany Wed, 28 Jan 2026 04:45:47 +0100 + +tomcat10 (10.1.46-1) unstable; urgency=medium + + * New upstream release + - Refreshed the patches + + -- Emmanuel Bourg Mon, 29 Sep 2025 13:43:22 +0200 + +tomcat10 (10.1.40-1) unstable; urgency=medium + + * New upstream release + - Refreshed the patches + * Look for OpenJDK from 11 to 25 when starting the server + * Standards-Version updated to 4.7.2 + + -- Emmanuel Bourg Thu, 03 Apr 2025 22:07:33 +0200 + +tomcat10 (10.1.35-1) unstable; urgency=medium + + * New upstream version 10.1.35. + * Refresh the patches. + + -- Markus Koschany Sun, 09 Feb 2025 00:19:28 +0100 + +tomcat10 (10.1.34-1) unstable; urgency=medium * Team upload. * Fix CVE-2025-24813: diff -Nru tomcat10-10.1.34/debian/copyright tomcat10-10.1.52/debian/copyright --- tomcat10-10.1.34/debian/copyright 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/copyright 2026-02-03 13:18:04.000000000 +0000 @@ -4,7 +4,7 @@ Files-Excluded: */taglibs-standard-*.jar Files: * -Copyright: 2000-2024, The Apache Software Foundation. +Copyright: 2000-2026, The Apache Software Foundation. 2002, International Business Machines Corporation. License: Apache-2.0 @@ -48,8 +48,8 @@ 2011-2015, Miguel Landaeta 2013, Jakub Adam 2013-2014, Gianfranco Costamagna - 2013-2018, Emmanuel Bourg - 2016-2024, Markus Koschany + 2013-2025, Emmanuel Bourg + 2016-2026, Markus Koschany License: Apache-2.0 License: Apache-2.0 diff -Nru tomcat10-10.1.34/debian/libexec/tomcat-locate-java.sh tomcat10-10.1.52/debian/libexec/tomcat-locate-java.sh --- tomcat10-10.1.34/debian/libexec/tomcat-locate-java.sh 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/libexec/tomcat-locate-java.sh 2026-02-03 13:18:04.000000000 +0000 @@ -13,7 +13,7 @@ # This function sets the variable JDK_DIRS find_jdks() { - for java_version in 21 20 19 17 11 8 + for java_version in 25 24 23 22 21 17 11 do for jvmdir in /usr/lib/jvm/java-${java_version}-openjdk-* \ /usr/lib/jvm/jdk-${java_version}-oracle-* \ diff -Nru tomcat10-10.1.34/debian/patches/0004-split-deploy-webapps-target-from-deploy-target.patch tomcat10-10.1.52/debian/patches/0004-split-deploy-webapps-target-from-deploy-target.patch --- tomcat10-10.1.34/debian/patches/0004-split-deploy-webapps-target-from-deploy-target.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0004-split-deploy-webapps-target-from-deploy-target.patch 2026-02-03 13:18:04.000000000 +0000 @@ -6,9 +6,11 @@ build.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) +diff --git a/build.xml b/build.xml +index 9a666f4..c83ae09 100644 --- a/build.xml +++ b/build.xml -@@ -1512,7 +1512,7 @@ +@@ -1527,7 +1527,7 @@ @@ -17,7 +19,7 @@ description="Default. Builds a working Tomcat instance"> -@@ -1546,6 +1546,10 @@ +@@ -1561,6 +1561,10 @@ diff -Nru tomcat10-10.1.34/debian/patches/0005-skip-test-failures.patch tomcat10-10.1.52/debian/patches/0005-skip-test-failures.patch --- tomcat10-10.1.34/debian/patches/0005-skip-test-failures.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0005-skip-test-failures.patch 2026-02-03 13:18:04.000000000 +0000 @@ -7,9 +7,11 @@ build.xml | 2 ++ 1 file changed, 2 insertions(+) +diff --git a/build.xml b/build.xml +index c83ae09..2dce95e 100644 --- a/build.xml +++ b/build.xml -@@ -1982,8 +1982,10 @@ +@@ -2098,8 +2098,10 @@ diff -Nru tomcat10-10.1.34/debian/patches/0009-Use-java.security.policy-file-in-catalina.sh.patch tomcat10-10.1.52/debian/patches/0009-Use-java.security.policy-file-in-catalina.sh.patch --- tomcat10-10.1.34/debian/patches/0009-Use-java.security.policy-file-in-catalina.sh.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0009-Use-java.security.policy-file-in-catalina.sh.patch 2026-02-03 13:18:04.000000000 +0000 @@ -11,9 +11,11 @@ bin/catalina.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) +diff --git a/bin/catalina.sh b/bin/catalina.sh +index abc43a6..7dc0821 100755 --- a/bin/catalina.sh +++ b/bin/catalina.sh -@@ -348,7 +348,7 @@ if [ "$1" = "debug" ] ; then +@@ -356,7 +356,7 @@ if [ "$1" = "debug" ] ; then -classpath "$CLASSPATH" \ -sourcepath "$CATALINA_HOME"/../../java \ -Djava.security.manager \ @@ -22,7 +24,7 @@ -Dcatalina.base="$CATALINA_BASE" \ -Dcatalina.home="$CATALINA_HOME" \ -Djava.io.tmpdir="$CATALINA_TMPDIR" \ -@@ -375,7 +375,7 @@ elif [ "$1" = "run" ]; then +@@ -383,7 +383,7 @@ elif [ "$1" = "run" ]; then eval exec "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \ -classpath "\"$CLASSPATH\"" \ -Djava.security.manager \ @@ -31,7 +33,7 @@ -Dcatalina.base="\"$CATALINA_BASE\"" \ -Dcatalina.home="\"$CATALINA_HOME\"" \ -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \ -@@ -454,7 +454,7 @@ elif [ "$1" = "start" ] ; then +@@ -462,7 +462,7 @@ elif [ "$1" = "start" ] ; then eval $_NOHUP "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \ -classpath "\"$CLASSPATH\"" \ -Djava.security.manager \ diff -Nru tomcat10-10.1.34/debian/patches/0010-debianize-build-xml.patch tomcat10-10.1.52/debian/patches/0010-debianize-build-xml.patch --- tomcat10-10.1.34/debian/patches/0010-debianize-build-xml.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0010-debianize-build-xml.patch 2026-02-03 13:18:04.000000000 +0000 @@ -8,9 +8,11 @@ build.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) +diff --git a/build.xml b/build.xml +index 2dce95e..31c9129 100644 --- a/build.xml +++ b/build.xml -@@ -1015,7 +1015,7 @@ +@@ -1030,7 +1030,7 @@ @@ -19,7 +21,7 @@ diff -Nru tomcat10-10.1.34/debian/patches/0013-dont-look-for-build-properties-in-user-home.patch tomcat10-10.1.52/debian/patches/0013-dont-look-for-build-properties-in-user-home.patch --- tomcat10-10.1.34/debian/patches/0013-dont-look-for-build-properties-in-user-home.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0013-dont-look-for-build-properties-in-user-home.patch 2026-02-03 13:18:04.000000000 +0000 @@ -10,9 +10,11 @@ build.xml | 1 - 1 file changed, 1 deletion(-) +diff --git a/build.xml b/build.xml +index 31c9129..404ba8c 100644 --- a/build.xml +++ b/build.xml -@@ -858,7 +858,6 @@ +@@ -867,7 +867,6 @@ diff -Nru tomcat10-10.1.34/debian/patches/0018-fix-manager-webapp.patch tomcat10-10.1.52/debian/patches/0018-fix-manager-webapp.patch --- tomcat10-10.1.34/debian/patches/0018-fix-manager-webapp.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0018-fix-manager-webapp.patch 2026-02-03 13:18:04.000000000 +0000 @@ -13,6 +13,8 @@ webapps/host-manager/WEB-INF/manager.xml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) +diff --git a/conf/catalina.policy b/conf/catalina.policy +index 6a82bcb..9ca7802 100644 --- a/conf/catalina.policy +++ b/conf/catalina.policy @@ -188,7 +188,7 @@ grant { @@ -24,7 +26,7 @@ permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.ha.session"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; -@@ -196,7 +196,7 @@ grant codeBase "file:${catalina.base}/we +@@ -196,7 +196,7 @@ grant codeBase "file:${catalina.base}/webapps/manager/-" { permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.util"; permission org.apache.catalina.security.DeployXmlPermission "manager"; }; @@ -33,7 +35,7 @@ permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.ha.session"; permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.manager"; -@@ -211,10 +211,10 @@ grant codeBase "file:${catalina.home}/we +@@ -211,10 +211,10 @@ grant codeBase "file:${catalina.home}/webapps/manager/-" { // - default CATALINA_HOME == CATALINA_BASE // - CATALINA_HOME != CATALINA_BASE, per instance Host Manager in CATALINA_BASE // - CATALINA_HOME != CATALINA_BASE, shared Host Manager in CATALINA_HOME @@ -46,16 +48,18 @@ permission org.apache.catalina.security.DeployXmlPermission "host-manager"; }; -@@ -260,4 +260,4 @@ grant codeBase "file:${catalina.home}/we +@@ -260,4 +260,4 @@ grant codeBase "file:${catalina.home}/webapps/host-manager/-" { // // The permissions granted to a specific JAR // grant codeBase "war:file:${catalina.base}/webapps/examples.war*/WEB-INF/lib/foo.jar" { -// }; \ No newline at end of file +// }; +diff --git a/webapps/docs/manager-howto.xml b/webapps/docs/manager-howto.xml +index 3bd65b9..07512b0 100644 --- a/webapps/docs/manager-howto.xml +++ b/webapps/docs/manager-howto.xml -@@ -74,7 +74,7 @@ configuration file in the +@@ -73,7 +73,7 @@ configuration file in the $CATALINA_BASE/conf/[enginename]/[hostname] folder. Here is an example:

- +diff --git a/java/org/apache/catalina/util/ServerInfo.properties b/java/org/apache/catalina/util/ServerInfo.properties +index 3aa34f4..c8dff06 100644 --- a/java/org/apache/catalina/util/ServerInfo.properties +++ b/java/org/apache/catalina/util/ServerInfo.properties @@ -13,7 +13,7 @@ diff -Nru tomcat10-10.1.34/debian/patches/0021-dont-test-unsupported-ciphers.patch tomcat10-10.1.52/debian/patches/0021-dont-test-unsupported-ciphers.patch --- tomcat10-10.1.34/debian/patches/0021-dont-test-unsupported-ciphers.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0021-dont-test-unsupported-ciphers.patch 2026-02-03 13:18:04.000000000 +0000 @@ -12,9 +12,11 @@ .../tomcat/util/net/openssl/ciphers/TesterOpenSSL.java | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) +diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java +index 9b9eb5e..9d81307 100644 --- a/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java +++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java -@@ -76,7 +76,7 @@ +@@ -76,7 +76,7 @@ public class TestCipher { // OpenSSL does not include ECDH/ECDHE ciphers in all and there is no // EC alias. Use aRSA. // OpenSSL 1.0.0 onwards does not include eNULL in all. @@ -23,9 +25,11 @@ Set expectedCipherSuites = new HashSet<>(); for (Cipher cipher : Cipher.values()) { +diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java +index 1c2b946..8bb4315 100644 --- a/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java +++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java -@@ -573,7 +573,7 @@ +@@ -573,7 +573,7 @@ public class TestOpenSSLCipherConfigurationParser { private void testSpecification(String specification) throws Exception { // Filter out cipher suites that OpenSSL does not implement @@ -34,9 +38,11 @@ List jsseCipherListFromOpenSSL = OpenSSLCipherConfigurationParser.parseExpression(openSSLCipherList); List jsseCipherListFromParser = +diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java +index 1c1cf5a..0dc9db8 100644 --- a/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java +++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java -@@ -107,6 +107,24 @@ +@@ -105,6 +105,24 @@ public class TesterOpenSSL { unimplemented.add(Cipher.SSL2_RC4_128_EXPORT40_WITH_MD5); unimplemented.add(Cipher.SSL2_IDEA_128_CBC_WITH_MD5); unimplemented.add(Cipher.SSL2_DES_192_EDE3_CBC_WITH_MD5); diff -Nru tomcat10-10.1.34/debian/patches/0023-disable-shutdown-by-socket.patch tomcat10-10.1.52/debian/patches/0023-disable-shutdown-by-socket.patch --- tomcat10-10.1.34/debian/patches/0023-disable-shutdown-by-socket.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0023-disable-shutdown-by-socket.patch 2026-02-03 13:18:04.000000000 +0000 @@ -7,6 +7,8 @@ conf/server.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) +diff --git a/conf/server.xml b/conf/server.xml +index 0ab6264..cf8f617 100644 --- a/conf/server.xml +++ b/conf/server.xml @@ -19,7 +19,7 @@ diff -Nru tomcat10-10.1.34/debian/patches/0024-systemd-log-formatter.patch tomcat10-10.1.52/debian/patches/0024-systemd-log-formatter.patch --- tomcat10-10.1.34/debian/patches/0024-systemd-log-formatter.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0024-systemd-log-formatter.patch 2026-02-03 13:18:04.000000000 +0000 @@ -8,6 +8,9 @@ 1 file changed, 109 insertions(+) create mode 100644 java/org/apache/juli/SystemdFormatter.java +diff --git a/java/org/apache/juli/SystemdFormatter.java b/java/org/apache/juli/SystemdFormatter.java +new file mode 100644 +index 0000000..014a193 --- /dev/null +++ b/java/org/apache/juli/SystemdFormatter.java @@ -0,0 +1,109 @@ diff -Nru tomcat10-10.1.34/debian/patches/0025-invalid-configuration-exit-status.patch tomcat10-10.1.52/debian/patches/0025-invalid-configuration-exit-status.patch --- tomcat10-10.1.34/debian/patches/0025-invalid-configuration-exit-status.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0025-invalid-configuration-exit-status.patch 2026-02-03 13:18:04.000000000 +0000 @@ -8,16 +8,18 @@ java/org/apache/catalina/startup/Bootstrap.java | 4 ++++ 1 file changed, 4 insertions(+) +diff --git a/java/org/apache/catalina/startup/Bootstrap.java b/java/org/apache/catalina/startup/Bootstrap.java +index 8dfc1fb..7250ff7 100644 --- a/java/org/apache/catalina/startup/Bootstrap.java +++ b/java/org/apache/catalina/startup/Bootstrap.java @@ -470,6 +470,10 @@ public final class Bootstrap { - } else if (command.equals("start")) { - daemon.setAwait(true); - daemon.load(args); + case "start": + daemon.setAwait(true); + daemon.load(args); + if (null == daemon.getServer()) { + log.fatal("Cannot start server. Server instance is not configured."); + System.exit(1); + } - daemon.start(); - if (null == daemon.getServer()) { - System.exit(1); + daemon.start(); + if (null == daemon.getServer()) { + System.exit(1); diff -Nru tomcat10-10.1.34/debian/patches/0026-easymock4-compatibility.patch tomcat10-10.1.52/debian/patches/0026-easymock4-compatibility.patch --- tomcat10-10.1.34/debian/patches/0026-easymock4-compatibility.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0026-easymock4-compatibility.patch 2026-02-03 13:18:04.000000000 +0000 @@ -5,12 +5,13 @@ Forwarded: no --- .../valves/TestCrawlerSessionManagerValve.java | 28 +++++++++++----------- - test/org/apache/catalina/valves/TestSSLValve.java | 4 ++-- - 2 files changed, 16 insertions(+), 16 deletions(-) + 1 file changed, 14 insertions(+), 14 deletions(-) +diff --git a/test/org/apache/catalina/valves/TestCrawlerSessionManagerValve.java b/test/org/apache/catalina/valves/TestCrawlerSessionManagerValve.java +index c813156..484ebf8 100644 --- a/test/org/apache/catalina/valves/TestCrawlerSessionManagerValve.java +++ b/test/org/apache/catalina/valves/TestCrawlerSessionManagerValve.java -@@ -57,13 +57,13 @@ public class TestCrawlerSessionManagerVa +@@ -57,13 +57,13 @@ public class TestCrawlerSessionManagerValve { CrawlerSessionManagerValve valve = new CrawlerSessionManagerValve(); valve.setCrawlerIps("216\\.58\\.206\\.174"); valve.setCrawlerUserAgents(valve.getCrawlerUserAgents()); @@ -26,7 +27,7 @@ EasyMock.verify(request, session); } -@@ -73,13 +73,13 @@ public class TestCrawlerSessionManagerVa +@@ -73,13 +73,13 @@ public class TestCrawlerSessionManagerValve { CrawlerSessionManagerValve valve = new CrawlerSessionManagerValve(); valve.setCrawlerIps("216\\.58\\.206\\.174"); valve.setCrawlerUserAgents(valve.getCrawlerUserAgents()); @@ -42,7 +43,7 @@ EasyMock.verify(request, session); } -@@ -90,7 +90,7 @@ public class TestCrawlerSessionManagerVa +@@ -90,7 +90,7 @@ public class TestCrawlerSessionManagerValve { valve.setCrawlerUserAgents(valve.getCrawlerUserAgents()); valve.setHostAware(true); valve.setContextAware(true); @@ -51,7 +52,7 @@ verifyCrawlingLocalhost(valve, "localhost"); verifyCrawlingLocalhost(valve, "example.invalid"); -@@ -102,7 +102,7 @@ public class TestCrawlerSessionManagerVa +@@ -102,7 +102,7 @@ public class TestCrawlerSessionManagerValve { valve.setCrawlerUserAgents(valve.getCrawlerUserAgents()); valve.setHostAware(true); valve.setContextAware(true); @@ -60,7 +61,7 @@ verifyCrawlingContext(valve, "/examples"); verifyCrawlingContext(valve, null); -@@ -113,7 +113,7 @@ public class TestCrawlerSessionManagerVa +@@ -113,7 +113,7 @@ public class TestCrawlerSessionManagerValve { CrawlerSessionManagerValve valve = new CrawlerSessionManagerValve(); valve.setCrawlerIps("216\\.58\\.206\\.174"); valve.setCrawlerUserAgents(valve.getCrawlerUserAgents()); @@ -69,7 +70,7 @@ valve.setSessionInactiveInterval(0); StandardSession session = new StandardSession(TEST_MANAGER); session.setId("id"); -@@ -123,7 +123,7 @@ public class TestCrawlerSessionManagerVa +@@ -123,7 +123,7 @@ public class TestCrawlerSessionManagerValve { EasyMock.replay(request); @@ -78,7 +79,7 @@ EasyMock.verify(request); -@@ -142,7 +142,7 @@ public class TestCrawlerSessionManagerVa +@@ -142,7 +142,7 @@ public class TestCrawlerSessionManagerValve { EasyMock.replay(request, session); @@ -87,7 +88,7 @@ EasyMock.verify(request, session); } -@@ -156,14 +156,14 @@ public class TestCrawlerSessionManagerVa +@@ -156,14 +156,14 @@ public class TestCrawlerSessionManagerValve { EasyMock.replay(request, session); @@ -102,18 +103,18 @@ - HttpSession session = EasyMock.createMock(HttpSession.class); + HttpSession session = (HttpSession) EasyMock.createMock(HttpSession.class); if (isBot) { - EasyMock.expect(session.getId()).andReturn("id").times(2); + EasyMock.expect(session.getId()).andReturn("id").times(1); session.setAttribute(EasyMock.eq(valve.getClass().getName()), -@@ -182,7 +182,7 @@ public class TestCrawlerSessionManagerVa +@@ -182,7 +182,7 @@ public class TestCrawlerSessionManagerValve { private Request createRequestExpectations(String ip, HttpSession session, boolean isBot, String hostname, String contextPath, String userAgent) { - Request request = EasyMock.createMock(Request.class); + Request request = (Request) EasyMock.createMock(Request.class); EasyMock.expect(request.getRemoteAddr()).andReturn(ip); + EasyMock.expect(request.getRemoteAddr()).andReturn(ip); EasyMock.expect(request.getHost()).andReturn(simpleHostWithName(hostname)); - EasyMock.expect(request.getContext()).andReturn(simpleContextWithName(contextPath)); -@@ -196,7 +196,7 @@ public class TestCrawlerSessionManagerVa +@@ -198,7 +198,7 @@ public class TestCrawlerSessionManagerValve { } private Host simpleHostWithName(String hostname) { @@ -122,7 +123,7 @@ EasyMock.expect(host.getName()).andReturn(hostname); EasyMock.replay(host); return host; -@@ -206,7 +206,7 @@ public class TestCrawlerSessionManagerVa +@@ -208,7 +208,7 @@ public class TestCrawlerSessionManagerValve { if (contextPath == null) { return null; } @@ -131,23 +132,3 @@ EasyMock.expect(context.getName()).andReturn(contextPath); EasyMock.replay(context); return context; ---- a/test/org/apache/catalina/valves/TestSSLValve.java -+++ b/test/org/apache/catalina/valves/TestSSLValve.java -@@ -37,7 +37,7 @@ public class TestSSLValve { - public static class MockRequest extends Request { - - public MockRequest() { -- super(EasyMock.createMock(Connector.class)); -+ super((Connector) EasyMock.createMock(Connector.class)); - setCoyoteRequest(new org.apache.coyote.Request()); - } - -@@ -94,7 +94,7 @@ public class TestSSLValve { - private SSLValve valve = new SSLValve(); - - private MockRequest mockRequest = new MockRequest(); -- private Valve mockNext = EasyMock.createMock(Valve.class); -+ private Valve mockNext = (Valve) EasyMock.createMock(Valve.class); - - - @Before diff -Nru tomcat10-10.1.34/debian/patches/0030-eclipse-jdt-classpath.patch tomcat10-10.1.52/debian/patches/0030-eclipse-jdt-classpath.patch --- tomcat10-10.1.34/debian/patches/0030-eclipse-jdt-classpath.patch 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/0030-eclipse-jdt-classpath.patch 2026-02-03 13:18:04.000000000 +0000 @@ -0,0 +1,21 @@ +From: Emmanuel Bourg +Date: Wed, 28 Jan 2026 04:57:59 +0100 +Subject: Updates the Eclipse Compiler classpath + +Forwarded: no +--- + build.xml | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/build.xml b/build.xml +index 5f30263..7d05c45 100644 +--- a/build.xml ++++ b/build.xml +@@ -233,6 +233,7 @@ + + + ++ + + + diff -Nru tomcat10-10.1.34/debian/patches/disable-jacoco.patch tomcat10-10.1.52/debian/patches/disable-jacoco.patch --- tomcat10-10.1.34/debian/patches/disable-jacoco.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/disable-jacoco.patch 2026-02-03 13:18:04.000000000 +0000 @@ -1,15 +1,17 @@ From: Markus Koschany -Date: Wed, 1 Feb 2023 00:11:33 +0100 -Subject: disable-jacoco +Date: Wed, 28 Jan 2026 04:59:30 +0100 +Subject: disable jacoco Forwarded: not-needed --- build.xml | 12 ------------ 1 file changed, 12 deletions(-) +diff --git a/build.xml b/build.xml +index 7d05c45..969b037 100644 --- a/build.xml +++ b/build.xml -@@ -2049,10 +2049,6 @@ +@@ -2167,10 +2167,6 @@ @@ -17,10 +19,10 @@ - enabled="${test.coverage}" - destfile="${coverage.datafile}" - > - @@ -28,7 +30,7 @@ -@@ -3581,15 +3576,8 @@ +@@ -3853,15 +3848,8 @@ Configured for ${release.asfusername} to release Tomcat ${version.major}.${versi diff -Nru tomcat10-10.1.34/debian/patches/exclude-TestJNDIRealmIntegration.patch tomcat10-10.1.52/debian/patches/exclude-TestJNDIRealmIntegration.patch --- tomcat10-10.1.34/debian/patches/exclude-TestJNDIRealmIntegration.patch 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/exclude-TestJNDIRealmIntegration.patch 2026-02-03 13:18:04.000000000 +0000 @@ -1,5 +1,5 @@ From: Markus Koschany -Date: Fri, 24 Sep 2021 16:29:54 +0200 +Date: Wed, 28 Jan 2026 05:12:44 +0100 Subject: exclude TestJNDIRealmIntegration Exclude TestJNDIRealmIntegration.java because it FTBFS due to missing @@ -7,20 +7,22 @@ Forwarded: not-needed --- - build.xml | 1 + - 1 file changed, 1 insertion(+) + build.xml | 2 ++ + 1 file changed, 2 insertions(+) +diff --git a/build.xml b/build.xml +index 969b037..5c56c29 100644 --- a/build.xml +++ b/build.xml -@@ -1929,6 +1929,7 @@ +@@ -1945,6 +1945,7 @@ - + +
-@@ -2118,6 +2119,7 @@ +@@ -2235,6 +2236,7 @@ tests. See below for more details. --> diff -Nru tomcat10-10.1.34/debian/patches/series tomcat10-10.1.52/debian/patches/series --- tomcat10-10.1.34/debian/patches/series 2025-04-01 21:03:17.000000000 +0000 +++ tomcat10-10.1.52/debian/patches/series 2026-02-03 13:18:04.000000000 +0000 @@ -10,6 +10,6 @@ 0025-invalid-configuration-exit-status.patch 0026-easymock4-compatibility.patch 0021-dont-test-unsupported-ciphers.patch -exclude-TestJNDIRealmIntegration.patch +0030-eclipse-jdt-classpath.patch disable-jacoco.patch -CVE-2025-24813.patch +exclude-TestJNDIRealmIntegration.patch diff -Nru tomcat10-10.1.34/java/jakarta/el/ELContextListener.java tomcat10-10.1.52/java/jakarta/el/ELContextListener.java --- tomcat10-10.1.34/java/jakarta/el/ELContextListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/el/ELContextListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,9 +16,6 @@ */ package jakarta.el; -/** - * @author Jacob Hookom [jacob/hookom.net] - */ public interface ELContextListener extends java.util.EventListener { void contextCreated(ELContextEvent event); diff -Nru tomcat10-10.1.34/java/jakarta/el/ELResolver.java tomcat10-10.1.52/java/jakarta/el/ELResolver.java --- tomcat10-10.1.34/java/jakarta/el/ELResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/el/ELResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,9 +18,6 @@ import java.util.Iterator; -/** - * @author Jacob Hookom [jacob/hookom.net] - */ public abstract class ELResolver { public static final String TYPE = "type"; diff -Nru tomcat10-10.1.34/java/jakarta/el/ExpressionFactory.java tomcat10-10.1.52/java/jakarta/el/ExpressionFactory.java --- tomcat10-10.1.34/java/jakarta/el/ExpressionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/el/ExpressionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -360,8 +360,8 @@ } } catch (FileNotFoundException e) { // Should not happen - ignore it if it does - } catch (IOException e) { - throw new ELException(Util.message(null, "expressionFactory.readFailed", PROPERTY_FILE), e); + } catch (IOException ioe) { + throw new ELException(Util.message(null, "expressionFactory.readFailed", PROPERTY_FILE), ioe); } } return null; @@ -375,4 +375,4 @@ return null; } -} +} \ No newline at end of file diff -Nru tomcat10-10.1.34/java/jakarta/el/ExpressionFactoryCache.java tomcat10-10.1.52/java/jakarta/el/ExpressionFactoryCache.java --- tomcat10-10.1.34/java/jakarta/el/ExpressionFactoryCache.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/el/ExpressionFactoryCache.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * 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 jakarta.el; + +import java.lang.ref.WeakReference; +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicReference; + +class ExpressionFactoryCache { + + private final AtomicReference>> factoryCache; + + ExpressionFactoryCache() { + factoryCache = new AtomicReference<>(new WeakHashMap<>()); + } + + /** + * Retrieves the currently cached ExpressionFactory, or creates a new one if required. Reads from an immutable + * WeakHashMap (which is threadsafe); in the rare cases that mutation is required, a new copy is created and + * modified, then swapped in via AtomicReference. Key performance characteristics: + *
    + *
  1. Reads are uncontended on an immutable object. (threadsafe)
  2. + *
  3. Writes are performed by copying an immutable object, then inserting. (threadsafe)
  4. + *
  5. The new object is swapped in via AtomicReference, or re-attempted if the data has since changed. + * (threadsafe)
  6. + *
  7. A single call will create 0 or 1 instances of ExpressionFactory. Simultaneous initialization by multiple + * threads may create 2+ instances of ExpressionFactory, but excess instances are short-lived and harmless. + * (memorysafe)
  8. + *
  9. ClassLoaders are weakly held (memorysafe)
  10. + *
  11. ExpressionFactorys are weakly held (memorysafe)
  12. + *
  13. No objects are allocated on cache hits (the common case)
  14. + *
+ * + * @param cl The classloader for which the cached {@code ExpressionFactory} is to be created or retrieved + * + * @return The cached {@code ExpressionFactory} for the given {@code ClassLoader} + */ + ExpressionFactory getOrCreateExpressionFactory(ClassLoader cl) { + WeakHashMap> cache; + WeakHashMap> newCache; + ExpressionFactory factory = null; + WeakReference factoryRef; + do { + // cache cannot be null + cache = factoryCache.get(); + + factoryRef = cache.get(cl); + // factoryRef can be null (could be uninitialized, or the GC cleaned the weak ref) + if (factoryRef != null) { + factory = factoryRef.get(); + // factory can be null (GC may have cleaned the ref) + if (factory != null) { + return factory; + } + } + + // something somewhere was uninitialized or GCd + if (factory == null) { + // only create an instance on the first iteration of the loop + factory = ExpressionFactory.newInstance(); + } + factoryRef = new WeakReference<>(factory); + newCache = new WeakHashMap<>(cache); + + newCache.put(cl, factoryRef); + } while (!factoryCache.compareAndSet(cache, newCache)); + + return factory; + } +} diff -Nru tomcat10-10.1.34/java/jakarta/el/ImportHandler.java tomcat10-10.1.52/java/jakarta/el/ImportHandler.java --- tomcat10-10.1.34/java/jakarta/el/ImportHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/el/ImportHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -136,7 +136,7 @@ standardPackages.put("jakarta.servlet.jsp", servletJspClassNames); Set javaLangClassNames = new HashSet<>(); - // Based on Java 21 EA29 + // Based on Java 26 EA26 // Interfaces javaLangClassNames.add("Appendable"); javaLangClassNames.add("AutoCloseable"); @@ -144,6 +144,7 @@ javaLangClassNames.add("Cloneable"); javaLangClassNames.add("Comparable"); javaLangClassNames.add("Iterable"); + javaLangClassNames.add("LazyConstant"); javaLangClassNames.add("ProcessHandle"); javaLangClassNames.add("ProcessHandle.Info"); javaLangClassNames.add("Readable"); @@ -168,6 +169,7 @@ javaLangClassNames.add("Enum"); javaLangClassNames.add("Enum.EnumDesc"); javaLangClassNames.add("Float"); + javaLangClassNames.add("IO"); javaLangClassNames.add("InheritableThreadLocal"); javaLangClassNames.add("Integer"); javaLangClassNames.add("Long"); @@ -190,6 +192,7 @@ javaLangClassNames.add("ScopedValue.Carrier"); javaLangClassNames.add("SecurityManager"); javaLangClassNames.add("Short"); + javaLangClassNames.add("StableValue"); javaLangClassNames.add("StackTraceElement"); javaLangClassNames.add("StackWalker"); javaLangClassNames.add("StrictMath"); @@ -400,6 +403,18 @@ clazzes.put(name, clazz); return clazz; } + // Might be an inner class + StringBuilder sb = new StringBuilder(className); + int replacementPosition = sb.lastIndexOf("."); + while (replacementPosition > -1) { + sb.setCharAt(replacementPosition, '$'); + clazz = findClass(sb.toString(), true); + if (clazz != null) { + clazzes.put(name, clazz); + return clazz; + } + replacementPosition = sb.lastIndexOf(".", replacementPosition); + } } // Search the package imports - note there may be multiple matches diff -Nru tomcat10-10.1.34/java/jakarta/el/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/jakarta/el/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/jakarta/el/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/el/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -46,6 +46,7 @@ propertyNotWritable=属性 [{1}] 在类型 [{0}] 上不可写 propertyReadError=在类型 [{0}] 上读取 [{1}] 时出错 propertyWriteError=在类型 [{0}] 上写入 [{1}] 时出错 +resolverNotWritable=类型[{0}]的ELResolver不可写入 staticFieldELResolver.methodNotFound=在类 [{1}] 上,找不到名为 [{0}] 的匹配公共静态方法 staticFieldELResolver.notFound=在(导出的 Java 9+ )类 [{1}] 上找不到名为 [{0}] 的公共静态字段 diff -Nru tomcat10-10.1.34/java/jakarta/el/Util.java tomcat10-10.1.52/java/jakarta/el/Util.java --- tomcat10-10.1.34/java/jakarta/el/Util.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/el/Util.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,7 +16,6 @@ */ package jakarta.el; -import java.lang.ref.WeakReference; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -33,10 +32,6 @@ import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; class Util { @@ -101,9 +96,7 @@ } } - - private static final CacheValue nullTcclFactory = new CacheValue(); - private static final Map factoryCache = new ConcurrentHashMap<>(); + private static final ExpressionFactoryCache factoryCache = new ExpressionFactoryCache(); /** * Provides a per class loader cache of ExpressionFactory instances without pinning any in memory as that could @@ -113,101 +106,7 @@ ClassLoader tccl = getContextClassLoader(); - CacheValue cacheValue = null; - ExpressionFactory factory = null; - - if (tccl == null) { - cacheValue = nullTcclFactory; - } else { - CacheKey key = new CacheKey(tccl); - cacheValue = factoryCache.get(key); - if (cacheValue == null) { - CacheValue newCacheValue = new CacheValue(); - cacheValue = factoryCache.putIfAbsent(key, newCacheValue); - if (cacheValue == null) { - cacheValue = newCacheValue; - } - } - } - - final Lock readLock = cacheValue.getLock().readLock(); - readLock.lock(); - try { - factory = cacheValue.getExpressionFactory(); - } finally { - readLock.unlock(); - } - - if (factory == null) { - final Lock writeLock = cacheValue.getLock().writeLock(); - writeLock.lock(); - try { - factory = cacheValue.getExpressionFactory(); - if (factory == null) { - factory = ExpressionFactory.newInstance(); - cacheValue.setExpressionFactory(factory); - } - } finally { - writeLock.unlock(); - } - } - - return factory; - } - - - /** - * Key used to cache default ExpressionFactory information per class loader. The class loader reference is never - * {@code null}, because {@code null} tccl is handled separately. - */ - private static class CacheKey { - private final int hash; - private final WeakReference ref; - - CacheKey(ClassLoader key) { - hash = key.hashCode(); - ref = new WeakReference<>(key); - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof CacheKey)) { - return false; - } - ClassLoader thisKey = ref.get(); - if (thisKey == null) { - return false; - } - return thisKey == ((CacheKey) obj).ref.get(); - } - } - - private static class CacheValue { - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - private WeakReference ref; - - CacheValue() { - } - - public ReadWriteLock getLock() { - return lock; - } - - public ExpressionFactory getExpressionFactory() { - return ref != null ? ref.get() : null; - } - - public void setExpressionFactory(ExpressionFactory factory) { - ref = new WeakReference<>(factory); - } + return factoryCache.getOrCreateExpressionFactory(tccl); } @@ -231,7 +130,7 @@ try { Method method = clazz.getMethod(methodName, paramTypes); return getMethod(clazz, base, method); - } catch (NoSuchMethodException | SecurityException e) { + } catch (NoSuchMethodException | SecurityException ignore) { // Fall through to broader, slower logic } } diff -Nru tomcat10-10.1.34/java/jakarta/servlet/LocalStrings_ru.properties tomcat10-10.1.52/java/jakarta/servlet/LocalStrings_ru.properties --- tomcat10-10.1.34/java/jakarta/servlet/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/servlet/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -17,3 +17,9 @@ # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations httpMethodConstraintElement.invalidMethod=Ошибочный HTTP метод + +value.false=ложный +value.true=истина + +wrapper.nullRequest=запрос не может быть null +wrapper.nullResponse=Ответ не может быть null diff -Nru tomcat10-10.1.34/java/jakarta/servlet/http/HttpServlet.java tomcat10-10.1.52/java/jakarta/servlet/http/HttpServlet.java --- tomcat10-10.1.34/java/jakarta/servlet/http/HttpServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/servlet/http/HttpServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -683,9 +683,8 @@ if (REQUEST_FACADE_CLAZZ.isAssignableFrom(req.getClass())) { try { return ((Boolean) GET_ALLOW_TRACE.invoke(req, (Object[]) null)).booleanValue(); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ignore) { // Should never happen given the checks in place. - // Ignore } } } @@ -892,11 +891,11 @@ Writer osw = null; try { osw = new OutputStreamWriter(out, encoding); - } catch (UnsupportedEncodingException e) { - // Impossible. - // The same values were used in the constructor. If this method - // gets called then the constructor must have succeeded so the - // above call must also succeed. + } catch (UnsupportedEncodingException ignore) { + /* + * Impossible. The same values were used in the constructor. If this method gets called then the + * constructor must have succeeded so the above call must also succeed. + */ } pw = new PrintWriter(osw); } diff -Nru tomcat10-10.1.34/java/jakarta/servlet/http/HttpServletRequest.java tomcat10-10.1.52/java/jakarta/servlet/http/HttpServletRequest.java --- tomcat10-10.1.34/java/jakarta/servlet/http/HttpServletRequest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/servlet/http/HttpServletRequest.java 2026-01-23 19:33:36.000000000 +0000 @@ -294,8 +294,8 @@ String getRequestedSessionId(); /** - * Returns the part of this request's URL from the protocol name up to the query string in the first line of the - * HTTP request. The web container does not decode this String. For example: + * Returns the URI path part of this request's URL which starts after the authority (if any) and ends before the + * query string delimiter ({@code ?}), if any. The web container does not decode this String. For example: * * * @@ -317,7 +317,8 @@ *

* To reconstruct a URL with a scheme and host, use {@link #getRequestURL}. * - * @return a String containing the part of the URL from the protocol name up to the query string + * @return a String containing the path part of the URL from after the authority to before the query + * string * * @see #getRequestURL */ diff -Nru tomcat10-10.1.34/java/jakarta/servlet/http/LocalStrings_ja.properties tomcat10-10.1.52/java/jakarta/servlet/http/LocalStrings_ja.properties --- tomcat10-10.1.34/java/jakarta/servlet/http/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/servlet/http/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -28,7 +28,7 @@ http.method_delete_not_supported=HTTPのDELETEメソッドは、このURLではサポートされていません。 http.method_get_not_supported=HTTPのGETメソッドは、このURLではサポートされていません。 -http.method_not_implemented=メソッド [{0}] は RFC 2068 には定義されておらず、サーブレット API ではサポートされません +http.method_not_implemented=メソッド [{0}] はこの URI のこのサーブレットでは実装されていません http.method_post_not_supported=HTTPのPOSTメソッドは、このURLではサポートされていません。 http.method_put_not_supported=HTTPのPUTメソッドは、このURLではサポートされていません。 http.non_http=リクエストが HTTP リクエストではない、あるいはレスポンスが HTTP レスポンスではありません。 diff -Nru tomcat10-10.1.34/java/jakarta/servlet/http/LocalStrings_ru.properties tomcat10-10.1.52/java/jakarta/servlet/http/LocalStrings_ru.properties --- tomcat10-10.1.34/java/jakarta/servlet/http/LocalStrings_ru.properties 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/servlet/http/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,21 @@ +# 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. + +# Do not edit this file directly. +# To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations + +http.method_get_not_supported=HTTP метод GET не поддерживается этим URL +http.method_post_not_supported=HTTP метод POST не поддерживается этим URL +http.method_put_not_supported=HTTP метод PUT не поддерживается этим URL diff -Nru tomcat10-10.1.34/java/jakarta/servlet/jsp/LocalStrings_ru.properties tomcat10-10.1.52/java/jakarta/servlet/jsp/LocalStrings_ru.properties --- tomcat10-10.1.34/java/jakarta/servlet/jsp/LocalStrings_ru.properties 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/servlet/jsp/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,19 @@ +# 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. + +# Do not edit this file directly. +# To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations + +el.unknown.identifier=Неизвестный идентификатор diff -Nru tomcat10-10.1.34/java/jakarta/servlet/jsp/tagext/BodyContent.java tomcat10-10.1.52/java/jakarta/servlet/jsp/tagext/BodyContent.java --- tomcat10-10.1.34/java/jakarta/servlet/jsp/tagext/BodyContent.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/servlet/jsp/tagext/BodyContent.java 2026-01-23 19:33:36.000000000 +0000 @@ -71,7 +71,7 @@ public void clearBody() { try { this.clear(); - } catch (IOException ex) { + } catch (IOException ioe) { // TODO -- clean this one up. throw new Error("internal error!;"); } diff -Nru tomcat10-10.1.34/java/jakarta/websocket/ContainerProvider.java tomcat10-10.1.52/java/jakarta/websocket/ContainerProvider.java --- tomcat10-10.1.34/java/jakarta/websocket/ContainerProvider.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/websocket/ContainerProvider.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,7 +47,7 @@ Class clazz = (Class) Class.forName(DEFAULT_PROVIDER_CLASS_NAME); result = clazz.getConstructor().newInstance(); - } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { + } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException ignore) { // No options left. Just return null. } } diff -Nru tomcat10-10.1.34/java/jakarta/websocket/server/ServerEndpointConfig.java tomcat10-10.1.52/java/jakarta/websocket/server/ServerEndpointConfig.java --- tomcat10-10.1.34/java/jakarta/websocket/server/ServerEndpointConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/jakarta/websocket/server/ServerEndpointConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -182,7 +182,7 @@ @SuppressWarnings("unchecked") Class clazz = (Class) Class.forName(DEFAULT_IMPL_CLASSNAME); result = clazz.getConstructor().newInstance(); - } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { + } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException ignore) { // No options left. Just return null. } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Authenticator.java tomcat10-10.1.52/java/org/apache/catalina/Authenticator.java --- tomcat10-10.1.34/java/org/apache/catalina/Authenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Authenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,6 @@ /** * An Authenticator is a component (usually a Valve or Container) that provides some sort of authentication * service. - * - * @author Craig R. McClanahan */ public interface Authenticator { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Cluster.java tomcat10-10.1.52/java/org/apache/catalina/Cluster.java --- tomcat10-10.1.34/java/org/apache/catalina/Cluster.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Cluster.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ * support different ways to communicate within the Cluster. A Cluster implementation is responsible for setting up a * way to communicate within the Cluster and also supply "ClientApplications" with ClusterSender used when * sending information in the Cluster and ClusterInfo used for receiving information in the Cluster. - * - * @author Bip Thelin - * @author Remy Maucherat */ public interface Cluster extends Contained { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Contained.java tomcat10-10.1.52/java/org/apache/catalina/Contained.java --- tomcat10-10.1.34/java/org/apache/catalina/Contained.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Contained.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,13 +17,8 @@ package org.apache.catalina; /** - *

* Decoupling interface which specifies that an implementing class is associated with at most one * Container instance. - *

- * - * @author Craig R. McClanahan - * @author Peter Donald */ public interface Contained { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Container.java tomcat10-10.1.52/java/org/apache/catalina/Container.java --- tomcat10-10.1.34/java/org/apache/catalina/Container.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Container.java 2026-01-23 19:33:36.000000000 +0000 @@ -60,9 +60,6 @@ *
  • Resources - JNDI directory context enabling access to static resources, enabling custom linkages to * existing server components when Catalina is embedded in a larger server. * - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public interface Container extends Lifecycle { @@ -331,7 +328,7 @@ /** * Add a new child Container to those associated with this Container, if supported. Prior to adding this Container * to the set of children, the child's setParent() method must be called, with this Container as an - * argument. This method may thrown an IllegalArgumentException if this Container chooses not to be + * argument. This method may throw an IllegalArgumentException if this Container chooses not to be * attached to the specified Container, in which case it is not added * * @param child New child Container to be added @@ -429,7 +426,7 @@ * * @param request Request (associated with the response) to log * @param response Response (associated with the request) to log - * @param time Time taken to process the request/response in milliseconds (use 0 if not known) + * @param time Time taken to process the request/response in nanoseconds (use 0 if not known) * @param useDefault Flag that indicates that the request/response should be logged in the engine's default access * log */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ContainerEvent.java tomcat10-10.1.52/java/org/apache/catalina/ContainerEvent.java --- tomcat10-10.1.34/java/org/apache/catalina/ContainerEvent.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ContainerEvent.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * General event for notifying listeners of significant changes on a Container. - * - * @author Craig R. McClanahan */ public final class ContainerEvent extends EventObject { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ContainerListener.java tomcat10-10.1.52/java/org/apache/catalina/ContainerListener.java --- tomcat10-10.1.34/java/org/apache/catalina/ContainerListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ContainerListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Interface defining a listener for significant Container generated events. Note that "container start" and "container * stop" events are normally LifecycleEvents, not ContainerEvents. - * - * @author Craig R. McClanahan */ public interface ContainerListener { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ContainerServlet.java tomcat10-10.1.52/java/org/apache/catalina/ContainerServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/ContainerServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ContainerServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ * A ContainerServlet is a servlet that has access to Catalina internal functionality, and is loaded from the * Catalina class loader instead of the web application class loader. The property setter methods must be called by the * container whenever a new instance of this servlet is put into service. - * - * @author Craig R. McClanahan */ public interface ContainerServlet { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Context.java tomcat10-10.1.52/java/org/apache/catalina/Context.java --- tomcat10-10.1.34/java/org/apache/catalina/Context.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Context.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Set; +import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRegistration; @@ -36,6 +37,7 @@ import org.apache.tomcat.ContextBind; import org.apache.tomcat.InstanceManager; import org.apache.tomcat.JarScanner; +import org.apache.tomcat.util.buf.EncodedSolidusHandling; import org.apache.tomcat.util.descriptor.web.ApplicationParameter; import org.apache.tomcat.util.descriptor.web.ErrorPage; import org.apache.tomcat.util.descriptor.web.FilterDef; @@ -49,8 +51,8 @@ /** * A Context is a Container that represents a servlet context, and therefore an individual web application, in * the Catalina servlet engine. It is therefore useful in almost every deployment of Catalina (even if a Connector - * attached to a web server (such as Apache) uses the web server's facilities to identify the appropriate Wrapper to - * handle this request. It also provides a convenient mechanism to use Interceptors that see every request processed by + * attached to a web server such as Apache uses the web server's facilities to identify the appropriate Wrapper to + * handle this request). It also provides a convenient mechanism to use Interceptors that see every request processed by * this particular web application. *

    * The parent Container attached to a Context is generally a Host, but may be some other implementation, or may be @@ -59,8 +61,6 @@ * The child containers attached to a Context are generally implementations of Wrapper (representing individual servlet * definitions). *

    - * - * @author Craig R. McClanahan */ public interface Context extends Container, ContextBind { @@ -130,7 +130,7 @@ * * @param listeners The set of instantiated listener objects. */ - void setApplicationEventListeners(Object listeners[]); + void setApplicationEventListeners(Object[] listeners); /** @@ -148,7 +148,7 @@ * * @param listeners The set of instantiated listener objects. */ - void setApplicationLifecycleListeners(Object listeners[]); + void setApplicationLifecycleListeners(Object[] listeners); /** @@ -316,7 +316,7 @@ * Configures if a / is added to the end of the session cookie path to ensure browsers, particularly IE, don't send * a session cookie for context /foo with requests intended for context /foobar. * - * @param sessionCookiePathUsesTrailingSlash true if the slash is should be added, otherwise + * @param sessionCookiePathUsesTrailingSlash true if the slash should be added, otherwise * false */ void setSessionCookiePathUsesTrailingSlash(boolean sessionCookiePathUsesTrailingSlash); @@ -945,19 +945,19 @@ /** - * @return the set of application listener class names configured for this application. + * @return the array of application listener class names configured for this application. */ String[] findApplicationListeners(); /** - * @return the set of application parameters for this application. + * @return the array of application parameters for this application. */ ApplicationParameter[] findApplicationParameters(); /** - * @return the set of security constraints for this web application. If there are none, a zero-length array is + * @return the array of security constraints for this web application. If there are none, a zero-length array is * returned. */ SecurityConstraint[] findConstraints(); @@ -984,7 +984,7 @@ /** - * @return the set of defined error pages for all specified error codes and exception types. + * @return the array of defined error pages for all specified error codes and exception types. */ ErrorPage[] findErrorPages(); @@ -998,13 +998,13 @@ /** - * @return the set of defined filters for this Context. + * @return the array of defined filters for this Context. */ FilterDef[] findFilterDefs(); /** - * @return the set of filter mappings for this Context. + * @return the array of filter mappings for this Context. */ FilterMap[] findFilterMaps(); @@ -1097,7 +1097,8 @@ /** - * @return the set of watched resources for this Context. If none are defined, a zero length array will be returned. + * @return the array of watched resources for this Context. If none are defined, a zero length array will be + * returned. */ String[] findWatchedResources(); @@ -1106,25 +1107,26 @@ * @return true if the specified welcome file is defined for this Context; otherwise return * false. * - * @param name Welcome file to verify + * @param name the welcome file to verify */ boolean findWelcomeFile(String name); /** - * @return the set of welcome files defined for this Context. If none are defined, a zero-length array is returned. + * @return the array of welcome files defined for this Context. If none are defined, a zero-length array is + * returned. */ String[] findWelcomeFiles(); /** - * @return the set of LifecycleListener classes that will be added to newly created Wrappers automatically. + * @return the array of LifecycleListener classes that will be added to newly created Wrappers automatically. */ String[] findWrapperLifecycles(); /** - * @return the set of ContainerListener classes that will be added to newly created Wrappers automatically. + * @return the array of ContainerListener classes that will be added to newly created Wrappers automatically. */ String[] findWrapperListeners(); @@ -1887,4 +1889,70 @@ */ @Deprecated void setUseBloomFilterForArchives(boolean useBloomFilterForArchives); + + + /** + * Obtain the current configuration for the handling of encoded reverse solidus (%5c - \) characters in paths used + * to obtain {@link RequestDispatcher} instances for this {@link Context}. + * + * @return Obtain the current configuration for the handling of encoded reverse solidus characters + */ + default String getEncodedReverseSolidusHandling() { + return EncodedSolidusHandling.DECODE.getValue(); + } + + + /** + * Configure the handling for encoded reverse solidus (%5c - \) characters in paths used to obtain + * {@link RequestDispatcher} instances for this {@link Context}. + * + * @param encodedReverseSolidusHandling One of the values of {@link EncodedSolidusHandling} + */ + default void setEncodedReverseSolidusHandling(String encodedReverseSolidusHandling) { + throw new UnsupportedOperationException(); + } + + + /** + * Obtain the current configuration for the handling of encoded reverse solidus (%5c - \) characters in paths used + * to obtain {@link RequestDispatcher} instances for this {@link Context}. + * + * @return Obtain the current configuration for the handling of encoded reverse solidus characters + */ + default EncodedSolidusHandling getEncodedReverseSolidusHandlingEnum() { + return EncodedSolidusHandling.DECODE; + } + + + /** + * Obtain the current configuration for the handling of encoded solidus (%2f - /) characters in paths used to obtain + * {@link RequestDispatcher} instances for this {@link Context}. + * + * @return Obtain the current configuration for the handling of encoded solidus characters + */ + default String getEncodedSolidusHandling() { + return EncodedSolidusHandling.DECODE.getValue(); + } + + + /** + * Configure the handling for encoded solidus (%2f - /) characters in paths used to obtain {@link RequestDispatcher} + * instances for this {@link Context}. + * + * @param encodedSolidusHandling One of the values of {@link EncodedSolidusHandling} + */ + default void setEncodedSolidusHandling(String encodedSolidusHandling) { + throw new UnsupportedOperationException(); + } + + + /** + * Obtain the current configuration for the handling of encoded solidus (%2f - /) characters in paths used to obtain + * {@link RequestDispatcher} instances for this {@link Context}. + * + * @return Obtain the current configuration for the handling of encoded solidus characters + */ + default EncodedSolidusHandling getEncodedSolidusHandlingEnum() { + return EncodedSolidusHandling.DECODE; + } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Engine.java tomcat10-10.1.52/java/org/apache/catalina/Engine.java --- tomcat10-10.1.34/java/org/apache/catalina/Engine.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Engine.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,8 +32,6 @@ *

    * If used, an Engine is always the top level Container in a Catalina hierarchy. Therefore, the implementation's * setParent() method should throw IllegalArgumentException. - * - * @author Craig R. McClanahan */ public interface Engine extends Container { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Globals.java tomcat10-10.1.52/java/org/apache/catalina/Globals.java --- tomcat10-10.1.34/java/org/apache/catalina/Globals.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Globals.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,6 @@ /** * Global constants that are applicable to multiple packages within Catalina. - * - * @author Craig R. McClanahan */ public final class Globals { @@ -156,7 +154,10 @@ /** * The subject under which the AccessControlContext is running. + * + * @deprecated Will be removed from Tomcat 11 onwards when support for the SecurityManager is removed. */ + @Deprecated public static final String SUBJECT_ATTR = "javax.security.auth.subject"; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Group.java tomcat10-10.1.52/java/org/apache/catalina/Group.java --- tomcat10-10.1.34/java/org/apache/catalina/Group.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Group.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ * group inherits the {@link Role}s assigned to the group. *

    * - * @author Craig R. McClanahan - * * @since 4.1 */ public interface Group extends Principal { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Host.java tomcat10-10.1.52/java/org/apache/catalina/Host.java --- tomcat10-10.1.34/java/org/apache/catalina/Host.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Host.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,8 +37,6 @@ *

    * The child containers attached to a Host are generally implementations of Context (representing an individual servlet * context). - * - * @author Craig R. McClanahan */ public interface Host extends Container { @@ -88,7 +86,7 @@ /** * @return an absolute {@link File} for the appBase of this Host. The file will be canonical if possible. There is - * no guarantee that that the appBase exists. + * no guarantee that the appBase exists. */ File getAppBaseFile(); @@ -110,7 +108,7 @@ /** * @return an absolute {@link File} for the legacy (Java EE) appBase of this Host. The file will be canonical if - * possible. There is no guarantee that that the appBase exists. + * possible. There is no guarantee that the appBase exists. */ File getLegacyAppBaseFile(); @@ -193,7 +191,7 @@ /** * @return the executor that is used for starting and stopping contexts. This is primarily for use by components - * deploying contexts that want to do this in a multi-threaded manner. + * deploying contexts that want to do this in a multithreaded manner. */ ExecutorService getStartStopExecutor(); @@ -243,7 +241,7 @@ /** - * @return the set of alias names for this Host. If none are defined, a zero length array is returned. + * @return the array of alias names for this Host. If none are defined, a zero length array is returned. */ String[] findAliases(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Lifecycle.java tomcat10-10.1.52/java/org/apache/catalina/Lifecycle.java --- tomcat10-10.1.34/java/org/apache/catalina/Lifecycle.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Lifecycle.java 2026-01-23 19:33:36.000000000 +0000 @@ -75,8 +75,6 @@ * * The {@link LifecycleEvent}s fired during state changes are defined in the methods that trigger the changed. No * {@link LifecycleEvent}s are fired if the attempted transition is not valid. - * - * @author Craig R. McClanahan */ public interface Lifecycle { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/LifecycleEvent.java tomcat10-10.1.52/java/org/apache/catalina/LifecycleEvent.java --- tomcat10-10.1.34/java/org/apache/catalina/LifecycleEvent.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/LifecycleEvent.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * General event for notifying listeners of significant changes on a component that implements the Lifecycle interface. - * - * @author Craig R. McClanahan */ public final class LifecycleEvent extends EventObject { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/LifecycleException.java tomcat10-10.1.52/java/org/apache/catalina/LifecycleException.java --- tomcat10-10.1.34/java/org/apache/catalina/LifecycleException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/LifecycleException.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * General purpose exception that is thrown to indicate a lifecycle related problem. Such exceptions should generally be * considered fatal to the operation of the application containing this component. - * - * @author Craig R. McClanahan */ public final class LifecycleException extends Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/LifecycleListener.java tomcat10-10.1.52/java/org/apache/catalina/LifecycleListener.java --- tomcat10-10.1.34/java/org/apache/catalina/LifecycleListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/LifecycleListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,11 +18,9 @@ /** - * Interface defining a listener for significant events (including "component start" and "component stop" generated by a - * component that implements the Lifecycle interface. The listener will be fired after the associated state change has + * Interface defining a listener for significant events (including "component start" and "component stop") generated by + * a component that implements the Lifecycle interface. The listener will be fired after the associated state change has * taken place. - * - * @author Craig R. McClanahan */ public interface LifecycleListener { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Loader.java tomcat10-10.1.52/java/org/apache/catalina/Loader.java --- tomcat10-10.1.34/java/org/apache/catalina/Loader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Loader.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,8 +35,6 @@ *

  • Based on a policy chosen by the implementation, must call the Context.reload() method on the owning * Context when a change to one or more of the class files loaded by this class loader is detected. * - * - * @author Craig R. McClanahan */ public interface Loader { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Manager.java tomcat10-10.1.52/java/org/apache/catalina/Manager.java --- tomcat10-10.1.34/java/org/apache/catalina/Manager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Manager.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,6 @@ *
  • Must allow a call to stop() to be followed by a call to start() on the same * Manager instance. * - * - * @author Craig R. McClanahan */ public interface Manager { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Pipeline.java tomcat10-10.1.52/java/org/apache/catalina/Pipeline.java --- tomcat10-10.1.34/java/org/apache/catalina/Pipeline.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Pipeline.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,21 +19,15 @@ import java.util.Set; /** - *

    * Interface describing a collection of Valves that should be executed in sequence when the invoke() method * is invoked. It is required that a Valve somewhere in the pipeline (usually the last one) must process the request and * create the corresponding response, rather than trying to pass the request on. - *

    *

    * There is generally a single Pipeline instance associated with each Container. The container's normal request * processing functionality is generally encapsulated in a container-specific Valve, which should always be executed at * the end of a pipeline. To facilitate this, the setBasic() method is provided to set the Valve instance * that will always be executed last. Other Valves will be executed in the order that they were added, before the basic * Valve is executed. - *

    - * - * @author Craig R. McClanahan - * @author Peter Donald */ public interface Pipeline extends Contained { @@ -80,8 +74,8 @@ /** - * @return the set of Valves in the pipeline associated with this Container, including the basic Valve (if any). If - * there are no such Valves, a zero-length array is returned. + * @return the array of Valves in the pipeline associated with this Container, including the basic Valve (if any). + * If there are no such Valves, a zero-length array is returned. */ Valve[] getValves(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Realm.java tomcat10-10.1.52/java/org/apache/catalina/Realm.java --- tomcat10-10.1.34/java/org/apache/catalina/Realm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Realm.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,8 +32,6 @@ * A Realm is a read-only facade for an underlying security realm used to authenticate individual users, and * identify the security roles associated with those users. Realms can be attached at any Container level, but will * typically only be attached to a Context, or higher level, Container. - * - * @author Craig R. McClanahan */ public interface Realm extends Contained { @@ -164,7 +162,7 @@ * * @return the associated principal, or {@code null} if there is none */ - Principal authenticate(X509Certificate certs[]); + Principal authenticate(X509Certificate[] certs); /** diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Role.java tomcat10-10.1.52/java/org/apache/catalina/Role.java --- tomcat10-10.1.34/java/org/apache/catalina/Role.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Role.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ * Principals. *

    * - * @author Craig R. McClanahan - * * @since 4.1 */ public interface Role extends Principal { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Server.java tomcat10-10.1.52/java/org/apache/catalina/Server.java --- tomcat10-10.1.34/java/org/apache/catalina/Server.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Server.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,8 +34,6 @@ * In between, the implementation must open a server socket on the port number specified by the port * property. When a connection is accepted, the first line is read and compared with the specified shutdown command. If * the command matches, shutdown of the server is initiated. - * - * @author Craig R. McClanahan */ public interface Server extends Lifecycle { @@ -231,7 +229,7 @@ /** - * @return the set of Services defined within this Server. + * @return the array of Services defined within this Server. */ Service[] findServices(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Service.java tomcat10-10.1.52/java/org/apache/catalina/Service.java --- tomcat10-10.1.34/java/org/apache/catalina/Service.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Service.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ *

    * A given JVM can contain any number of Service instances; however, they are completely independent of each other and * share only the basic JVM facilities and classes on the system class path. - * - * @author Craig R. McClanahan */ public interface Service extends Lifecycle { @@ -101,7 +99,7 @@ /** * Find and return the set of Connectors associated with this Service. * - * @return the set of associated Connectors + * @return the array of associated Connectors */ Connector[] findConnectors(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Session.java tomcat10-10.1.52/java/org/apache/catalina/Session.java --- tomcat10-10.1.34/java/org/apache/catalina/Session.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Session.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ /** * A Session is the Catalina-internal facade for an HttpSession that is used to maintain state * information between requests for a particular user of a web application. - * - * @author Craig R. McClanahan */ public interface Session { @@ -58,6 +56,10 @@ */ String SESSION_PASSIVATED_EVENT = "passivateSession"; + /** + * The SessionEvent event type when a session changes its sessionId. + */ + String SESSION_CHANGED_ID_EVENT = "changeSessionId"; // ------------------------------------------------------------- Properties diff -Nru tomcat10-10.1.34/java/org/apache/catalina/SessionEvent.java tomcat10-10.1.52/java/org/apache/catalina/SessionEvent.java --- tomcat10-10.1.34/java/org/apache/catalina/SessionEvent.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/SessionEvent.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,6 @@ /** * General event for notifying listeners of significant changes on a Session. - * - * @author Craig R. McClanahan */ public final class SessionEvent extends EventObject { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/SessionListener.java tomcat10-10.1.52/java/org/apache/catalina/SessionListener.java --- tomcat10-10.1.34/java/org/apache/catalina/SessionListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/SessionListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,6 @@ /** * Interface defining a listener for significant Session generated events. - * - * @author Craig R. McClanahan */ public interface SessionListener extends EventListener { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Store.java tomcat10-10.1.52/java/org/apache/catalina/Store.java --- tomcat10-10.1.34/java/org/apache/catalina/Store.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Store.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,13 +25,9 @@ * A Store is the abstraction of a Catalina component that provides persistent storage and loading of Sessions * and their associated user data. Implementations are free to save and load the Sessions to any media they wish, but it * is assumed that saved Sessions are persistent across server or context restarts. - * - * @author Craig R. McClanahan */ public interface Store { - // ------------------------------------------------------------- Properties - /** * @return the Manager instance associated with this Store. */ @@ -54,9 +50,6 @@ int getSize() throws IOException; - // --------------------------------------------------------- Public Methods - - /** * Add a property change listener to this component. * @@ -77,6 +70,11 @@ /** * Load and return the Session associated with the specified session identifier from this Store, without removing * it. If there is no such stored Session, return null. + *

    + * Implementations should expect, and correctly handle, concurrent calls to any method but in particular calls to + * {@code #load(String)}, {@code #save(Session)} and {@code #remove(String)} for the same session. + *

    + * The session ID is user provided so stores must treat it as untrusted data. * * @param id Session identifier of the session to load * @@ -91,6 +89,11 @@ /** * Remove the Session with the specified session identifier from this Store, if present. If no such Session is * present, this method takes no action. + *

    + * Implementations should expect, and correctly handle, concurrent calls to any method but in particular calls to + * {@code #load(String)}, {@code #save(Session)} and {@code #remove(String)} for the same session. + *

    + * The session ID is user provided so stores must treat it as untrusted data. * * @param id Session identifier of the Session to be removed * @@ -118,12 +121,13 @@ /** * Save the specified Session into this Store. Any previously saved information for the associated session * identifier is replaced. + *

    + * Implementations should expect, and correctly handle, concurrent calls to any method but in particular calls to + * {@code #load(String)}, {@code #save(Session)} and {@code #remove(String)} for the same session. * * @param session Session to be saved * * @exception IOException if an input/output error occurs */ void save(Session session) throws IOException; - - } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/User.java tomcat10-10.1.52/java/org/apache/catalina/User.java --- tomcat10-10.1.34/java/org/apache/catalina/User.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/User.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ * {@link Group}s through which they inherit additional security roles, and is optionally assigned a set of specific * {@link Role}s. * - * @author Craig R. McClanahan - * * @since 4.1 */ public interface User extends Principal { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/UserDatabase.java tomcat10-10.1.52/java/org/apache/catalina/UserDatabase.java --- tomcat10-10.1.34/java/org/apache/catalina/UserDatabase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/UserDatabase.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ * along with definitions of corresponding {@link Role}s, and referenced by a {@link Realm} for authentication and * access control. * - * @author Craig R. McClanahan - * * @since 4.1 */ public interface UserDatabase { @@ -116,7 +114,7 @@ /** - * @return the {@link User} with the specified user name, if any; otherwise return null. + * @return the {@link User} with the specified username, if any; otherwise return null. * * @param username Name of the user to return */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Valve.java tomcat10-10.1.52/java/org/apache/catalina/Valve.java --- tomcat10-10.1.34/java/org/apache/catalina/Valve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Valve.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,17 +24,12 @@ import org.apache.catalina.connector.Response; /** - *

    * A Valve is a request processing component associated with a particular Container. A series of Valves are * generally associated with each other into a Pipeline. The detailed contract for a Valve is included in the * description of the invoke() method below. - *

    + *

    * HISTORICAL NOTE: The "Valve" name was assigned to this concept because a valve is what you use in a real world * pipeline to control and/or modify flows through it. - * - * @author Craig R. McClanahan - * @author Gunnar Rjnning - * @author Peter Donald */ public interface Valve { @@ -81,7 +76,7 @@ * control to the caller. *

  • Examine the properties of the specified Request and Response, wrap either or both of these objects to * supplement their functionality, and pass them on. - *
  • If the corresponding Response was not generated (and control was not returned, call the next Valve in the + *
  • If the corresponding Response was not generated (and control was not returned), call the next Valve in the * pipeline (if there is one) by executing getNext().invoke(). *
  • Examine, but not modify, the properties of the resulting Response (which was created by a subsequently * invoked Valve or Container). diff -Nru tomcat10-10.1.34/java/org/apache/catalina/WebResourceRoot.java tomcat10-10.1.52/java/org/apache/catalina/WebResourceRoot.java --- tomcat10-10.1.34/java/org/apache/catalina/WebResourceRoot.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/WebResourceRoot.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,10 @@ import java.util.List; import java.util.Set; +import org.apache.catalina.util.ResourceSet; + /** - * Represents the complete set of resources for a web application. The resources for a web application comprise of + * Represents the complete set of resources for a web application. The resources for a web application consist of * multiple ResourceSets and when looking for a Resource, the ResourceSets are processed in the following order: *
      *
    1. Pre - Resources defined by the <PreResource> element in the web application's context.xml. Resources will @@ -62,7 +64,7 @@ * adds all sorts complications including: - which ResourceSet to write to - unexpected behaviour when deleting a * resource from one ResourceSet since that may unmask a resource in a lower priority ResourceSet so what was a delete * looks like a replace with the user having no idea where the 'new' resource came from - how to handle PUT when the - * target is read-only but it could be written to a higher priority ResourceSet that is read-write + * target is read-only, but it could be written to a higher priority ResourceSet that is read-write */ public interface WebResourceRoot extends Lifecycle { /** @@ -95,8 +97,8 @@ * calls to this method until the web application is reloaded. No guarantee is made as to what the search order for * JAR files may be. * - * @param path The path of the class loader resource of interest relative to the the root of class loader resources - * for this web application. + * @param path The path of the class loader resource of interest relative to the root of class loader resources for + * this web application. * * @return The object that represents the class loader resource at the given path */ @@ -190,7 +192,7 @@ * @param base The location of the resources * @param archivePath The path within the resource to the archive where the content is to be found. If there is no * archive then this should be null. - * @param internalPath The path within the archive (or the resource if the archivePath is null where + * @param internalPath The path within the archive (or the resource if the archivePath is null) where * the content is to be found. It must start with '/'. */ void createWebResourceSet(ResourceSetType type, String webAppMount, String base, String archivePath, @@ -205,7 +207,7 @@ void addPreResources(WebResourceSet webResourceSet); /** - * @return the list of WebResourceSet configured to this web application as a 'Pre' resource. + * @return the array of WebResourceSet configured to this web application as a 'Pre' resource. */ WebResourceSet[] getPreResources(); @@ -217,7 +219,7 @@ void addJarResources(WebResourceSet webResourceSet); /** - * @return the list of WebResourceSet configured to this web application as a 'Jar' resource. + * @return the array of WebResourceSet configured to this web application as a 'Jar' resource. */ WebResourceSet[] getJarResources(); @@ -229,7 +231,7 @@ void addPostResources(WebResourceSet webResourceSet); /** - * @return the list of WebResourceSet configured to this web application as a 'Post' resource. + * @return the array of WebResourceSet configured to this web application as a 'Post' resource. */ WebResourceSet[] getPostResources(); @@ -246,14 +248,16 @@ void setContext(Context context); /** - * Configure if this resources allow the use of symbolic links. + * Configure if this web application allows the use of symbolic links by default. Individual {@link ResourceSet}s + * may override this setting. * * @param allowLinking true if symbolic links are allowed. */ void setAllowLinking(boolean allowLinking); /** - * Determine if this resources allow the use of symbolic links. + * Determine if this web application allows the use of symbolic links by default. Individual {@link ResourceSet}s + * may override this setting. * * @return true if symbolic links are allowed */ @@ -317,7 +321,7 @@ /** * Controls whether the track locked files feature is enabled. If enabled, all calls to methods that return objects - * that lock a file and need to be closed to release that lock (e.g. {@link WebResource#getInputStream()} will + * that lock a file and need to be closed to release that lock (e.g. {@link WebResource#getInputStream()}) will * perform a number of additional tasks. *
        *
      • The stack trace at the point where the method was called will be recorded and associated with the returned @@ -380,7 +384,7 @@ void deregisterTrackedResource(TrackedWebResource trackedResource); /** - * @return the set of {@link WebResourceSet#getBaseUrl()} for all {@link WebResourceSet}s used by this root. + * @return the list of {@link WebResourceSet#getBaseUrl()} for all {@link WebResourceSet}s used by this root. */ List getBaseUrls(); @@ -393,7 +397,7 @@ /** * Obtain the current caching strategy. *

        - * The default implementation returns {@code null}. Sub-classes wishing to utilise a {@link CacheStrategy} should + * The default implementation returns {@code null}. Subclasses wishing to utilise a {@link CacheStrategy} should * provide an appropriate implementation. * * @return the current caching strategy or {@code null} if no strategy has been configured @@ -405,7 +409,7 @@ /** * Set the current caching strategy. *

        - * The default implementation is a NO-OP. Sub-classes wishing to utilise a {@link CacheStrategy} should provide an + * The default implementation is a NO-OP. Subclasses wishing to utilise a {@link CacheStrategy} should provide an * appropriate implementation. * * @param strategy The new strategy to use or {@code null} for no strategy @@ -414,6 +418,23 @@ // NO-OP } + /** + * Set if the main resources are read only. + * + * @param readOnly the value + */ + default void setReadOnly(boolean readOnly) { + // NO-OP + } + + /** + * @return {@code true} if the main resources are read only, otherwise {@code false}. The default implementation + * returns {@code false}. + */ + default boolean isReadOnly() { + return false; + } + enum ResourceSetType { PRE, RESOURCE_JAR, diff -Nru tomcat10-10.1.34/java/org/apache/catalina/WebResourceSet.java tomcat10-10.1.52/java/org/apache/catalina/WebResourceSet.java --- tomcat10-10.1.34/java/org/apache/catalina/WebResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/WebResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -135,4 +135,19 @@ * resources. */ void gc(); + + /** + * Configure if this {@code ResourceSet} allows the use of symbolic links. + * + * @param allowLinking true if symbolic links are allowed. + */ + void setAllowLinking(boolean allowLinking); + + /** + * Determine if this {@code ResourceSet} allows the use of symbolic links. If {@link #setAllowLinking(boolean)} has + * not been called for this instance, the value of {@link WebResourceRoot#getAllowLinking()} is returned. + * + * @return true if symbolic links are allowed + */ + boolean getAllowLinking(); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/Wrapper.java tomcat10-10.1.52/java/org/apache/catalina/Wrapper.java --- tomcat10-10.1.34/java/org/apache/catalina/Wrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/Wrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,8 +36,6 @@ *

        * Child Containers are not allowed on Wrapper implementations, so the addChild() method should throw an * IllegalArgumentException. - * - * @author Craig R. McClanahan */ public interface Wrapper extends Container { @@ -226,7 +224,7 @@ /** - * @return the set of security role reference names associated with this servlet, if any; otherwise return a + * @return the array of security role reference names associated with this servlet, if any; otherwise return a * zero-length array. */ String[] findSecurityReferences(); @@ -293,14 +291,14 @@ /** - * @return the multi-part configuration for the associated Servlet. If no multi-part configuration has been defined, + * @return the multipart configuration for the associated Servlet. If no multipart configuration has been defined, * then null will be returned. */ MultipartConfigElement getMultipartConfigElement(); /** - * Set the multi-part configuration for the associated Servlet. To clear the multi-part configuration specify + * Set the multipart configuration for the associated Servlet. To clear the multipart configuration specify * null as the new value. * * @param multipartConfig The configuration associated with the Servlet diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/AbstractCatalinaTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/AbstractCatalinaTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/AbstractCatalinaTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/AbstractCatalinaTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,6 +28,7 @@ import java.net.URLConnection; import org.apache.catalina.util.IOTools; +import org.apache.tomcat.util.http.Method; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; @@ -35,8 +36,6 @@ * Abstract base class for Ant tasks that interact with the Manager web application for dynamically deploying * and undeploying applications. These tasks require Ant 1.4 or later. * - * @author Craig R. McClanahan - * * @since 4.1 */ public abstract class AbstractCatalinaTask extends BaseRedirectorHelperTask { @@ -169,7 +168,6 @@ public void execute(String command, InputStream istream, String contentType, long contentLength) throws BuildException { - URLConnection conn = null; InputStreamReader reader = null; try { // Set up authorization with our credentials @@ -177,8 +175,7 @@ // Create a connection for this command URI uri = new URI(url + command); - uri.parseServerAuthority(); - conn = uri.toURL().openConnection(); + URLConnection conn = uri.parseServerAuthority().toURL().openConnection(); HttpURLConnection hconn = (HttpURLConnection) conn; // Set up standard connection characteristics @@ -189,7 +186,7 @@ preAuthenticate(); hconn.setDoOutput(true); - hconn.setRequestMethod("PUT"); + hconn.setRequestMethod(Method.PUT); if (contentType != null) { hconn.setRequestProperty("Content-Type", contentType); } @@ -200,7 +197,7 @@ } } else { hconn.setDoOutput(false); - hconn.setRequestMethod("GET"); + hconn.setRequestMethod(Method.GET); } hconn.setRequestProperty("User-Agent", "Catalina-Ant-Task/1.0"); @@ -209,13 +206,8 @@ // Send the request data (if any) if (istream != null) { - try (OutputStream ostream = hconn.getOutputStream()) { + try (istream; OutputStream ostream = hconn.getOutputStream()) { IOTools.flow(istream, ostream); - } finally { - try { - istream.close(); - } catch (Exception e) { - } } } @@ -271,7 +263,6 @@ } catch (IOException ioe) { // Ignore } - reader = null; } if (istream != null) { try { @@ -294,12 +285,10 @@ * appropriate Authorization when the next request is made (that includes a request body). */ private void preAuthenticate() throws IOException, URISyntaxException { - URLConnection conn = null; // Create a connection for this command URI uri = new URI(url); - uri.parseServerAuthority(); - conn = uri.toURL().openConnection(); + URLConnection conn = uri.parseServerAuthority().toURL().openConnection(); HttpURLConnection hconn = (HttpURLConnection) conn; // Set up standard connection characteristics @@ -307,7 +296,7 @@ hconn.setDoInput(true); hconn.setUseCaches(false); hconn.setDoOutput(false); - hconn.setRequestMethod("OPTIONS"); + hconn.setRequestMethod(Method.OPTIONS); hconn.setRequestProperty("User-Agent", "Catalina-Ant-Task/1.0"); // Establish the connection with the server diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/BaseRedirectorHelperTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/BaseRedirectorHelperTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/BaseRedirectorHelperTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/BaseRedirectorHelperTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,14 +30,12 @@ /** * Abstract base class to add output redirection support for Catalina Ant tasks. These tasks require Ant 1.5 or later. *
        - * WARNING: due to depends chain, Ant could call a Task more than once and this can affect the output - * redirection when configured. If you are collecting the output in a property, it will collect the output of only the - * first run, since Ant properties are immutable and once created they cannot be changed.
        + * WARNING: due to dependency chain, Ant could call a Task more than once and this can affect the + * output redirection when configured. If you are collecting the output in a property, it will collect the output of + * only the first run, since Ant properties are immutable and once created they cannot be changed.
        * If you are collecting output in a file the file will be overwritten with the output of the last run, unless you set * append="true", in which case each run will append it's output to the file. * - * @author Gabriele Garuglieri - * * @since 5.5 */ public abstract class BaseRedirectorHelperTask extends Task { @@ -63,7 +61,7 @@ /** * Whether to fail (with a BuildException) if ManagerServlet returns an error. The default behavior is to do so. * This flag does not control parameters checking. If the task is called with wrong or invalid parameters, it will - * throw BuildException independently from the setting of this flag. + * throw a BuildException regardless of the setting of this flag. */ protected boolean failOnError = true; @@ -218,8 +216,8 @@ redirectOutput = true; } /* - * Due to depends chain, Ant could call the Task more than once, this is to prevent that we attempt to configure - * uselessly more than once the Redirector. + * Due to dependency chain, Ant could call the Task more than once, this is to prevent that we attempt to + * configure uselessly more than once the Redirector. */ redirectorConfigured = true; } @@ -256,8 +254,8 @@ log("Error closing redirector: " + ioe.getMessage(), Project.MSG_ERR); } /* - * Due to depends chain, Ant could call the Task more than once, this is to prevent that we attempt to reuse the - * previously closed Streams. + * Due to dependency chain, Ant could call the Task more than once, this is to prevent that we attempt to reuse + * the previously closed Streams. */ redirectOutStream = null; redirectOutPrintStream = null; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/DeployTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/DeployTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/DeployTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/DeployTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,8 +32,6 @@ /** * Ant task that implements the /deploy command, supported by the Tomcat manager application. * - * @author Craig R. McClanahan - * * @since 4.1 */ public class DeployTask extends AbstractCatalinaCommandTask { @@ -145,15 +143,15 @@ FileChannel fsChannel = fsInput.getChannel(); contentLength = fsChannel.size(); stream = new BufferedInputStream(fsInput, 1024); - } catch (IOException e) { + } catch (IOException ioe) { if (fsInput != null) { try { fsInput.close(); - } catch (IOException ioe) { + } catch (IOException ignore) { // Ignore } } - throw new BuildException(e); + throw new BuildException(ioe); } } contentType = "application/octet-stream"; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/JKStatusUpdateTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/JKStatusUpdateTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/JKStatusUpdateTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/JKStatusUpdateTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,6 @@ /** * Ant task that implements the /status command, supported by the mod_jk status (1.2.9) application. * - * @author Peter Rossbach - * * @since 5.5.9 */ public class JKStatusUpdateTask extends AbstractCatalinaTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/JMXGetTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/JMXGetTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/JMXGetTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/JMXGetTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ /** * Ant task that implements the JMX Get command (/jmxproxy/?get) supported by the Tomcat manager * application. - * - * @author Peter Rossbach */ public class JMXGetTask extends AbstractCatalinaTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/JMXQueryTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/JMXQueryTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/JMXQueryTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/JMXQueryTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ /** * Ant task that implements the JMX Query command (/jmxproxy/?qry) supported by the Tomcat manager * application. - * - * @author Vivek Chopra */ public class JMXQueryTask extends AbstractCatalinaTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/JMXSetTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/JMXSetTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/JMXSetTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/JMXSetTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ /** * Ant task that implements the JMX Set command (/jmxproxy/?set) supported by the Tomcat manager * application. - * - * @author Vivek Chopra */ public class JMXSetTask extends AbstractCatalinaTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/ListTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/ListTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/ListTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/ListTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ /** * Ant task that implements the /list command, supported by the Tomcat manager application. * - * @author Craig R. McClanahan - * * @since 4.1 */ public class ListTask extends AbstractCatalinaTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/ReloadTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/ReloadTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/ReloadTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/ReloadTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ /** * Ant task that implements the /reload command, supported by the Tomcat manager application. * - * @author Craig R. McClanahan - * * @since 4.1 */ public class ReloadTask extends AbstractCatalinaCommandTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/ResourcesTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/ResourcesTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/ResourcesTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/ResourcesTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ /** * Ant task that implements the /resources command, supported by the Tomcat manager application. * - * @author Craig R. McClanahan - * * @since 4.1 */ public class ResourcesTask extends AbstractCatalinaTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/ServerinfoTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/ServerinfoTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/ServerinfoTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/ServerinfoTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,19 +16,13 @@ */ package org.apache.catalina.ant; - import org.apache.tools.ant.BuildException; - /** * Ant task that implements the /serverinfo command supported by the Tomcat manager application. - * - * @author Vivek Chopra */ public class ServerinfoTask extends AbstractCatalinaTask { - // Public Methods - /** * Execute the requested operation. * @@ -36,9 +30,7 @@ */ @Override public void execute() throws BuildException { - super.execute(); execute("/serverinfo"); - } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/SessionsTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/SessionsTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/SessionsTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/SessionsTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,18 +16,13 @@ */ package org.apache.catalina.ant; - import org.apache.tools.ant.BuildException; - /** * Ant task that implements the /sessions command supported by the Tomcat manager application. - * - * @author Vivek Chopra */ public class SessionsTask extends AbstractCatalinaCommandTask { - protected String idle = null; public String getIdle() { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/StartTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/StartTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/StartTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/StartTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ /** * Ant task that implements the /start command, supported by the Tomcat manager application. * - * @author Craig R. McClanahan - * * @since 4.1 */ public class StartTask extends AbstractCatalinaCommandTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/StopTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/StopTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/StopTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/StopTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ /** * Ant task that implements the /stop command, supported by the Tomcat manager application. * - * @author Craig R. McClanahan - * * @since 4.1 */ public class StopTask extends AbstractCatalinaCommandTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/UndeployTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/UndeployTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/UndeployTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/UndeployTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ /** * Ant task that implements the /undeploy command, supported by the Tomcat manager application. * - * @author Craig R. McClanahan - * * @since 4.1 */ public class UndeployTask extends AbstractCatalinaCommandTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/ValidatorTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/ValidatorTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/ValidatorTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/ValidatorTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,8 +32,6 @@ /** * Task for validating a web application deployment descriptor, using XML schema validation. * - * @author Remy Maucherat - * * @since 5.0 */ public class ValidatorTask extends BaseRedirectorHelperTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorCondition.java tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorCondition.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorCondition.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorCondition.java 2026-01-23 19:33:36.000000000 +0000 @@ -77,8 +77,6 @@ * NOTE: For numeric expressions the type must be set and use xml entities as operations.
        * As type we currently support long and double. * - * @author Peter Rossbach - * * @since 5.5.10 */ public class JMXAccessorCondition extends JMXAccessorConditionBase { @@ -171,7 +169,7 @@ * @return true if there is no unless condition, or there is a named property but it doesn't exist */ protected boolean testUnlessCondition() { - if (unlessCondition == null || "".equals(unlessCondition)) { + if (unlessCondition == null || unlessCondition.isEmpty()) { return true; } return getProject().getProperty(unlessCondition) == null; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorCreateTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,16 +46,14 @@ * </jmxCreate/> * *

        - * WARNINGNot all Tomcat MBeans can create remotely and autoregister by its parents! Please, use the MBeanFactory - * operation to generate valves and realms. + * WARNINGNot all Tomcat MBeans can create remotely and auto register by its parents! Please, use the + * MBeanFactory operation to generate valves and realms. *

        *

        - * First call to a remote MBeanserver save the JMXConnection a reference jmx.server + * First call to a remote MBean server save the JMXConnection a reference jmx.server *

        * These tasks require Ant 1.6 or later interface. * - * @author Peter Rossbach - * * @since 5.5.12 */ public class JMXAccessorCreateTask extends JMXAccessorTask { @@ -137,8 +135,8 @@ * @throws Exception Error creating MBean */ protected void jmxCreate(MBeanServerConnection jmxServerConnection, String name) throws Exception { - Object argsA[] = null; - String sigA[] = null; + Object[] argsA = null; + String[] sigA = null; if (args != null) { argsA = new Object[args.size()]; sigA = new String[args.size()]; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorEqualsCondition.java tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorEqualsCondition.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorEqualsCondition.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorEqualsCondition.java 2026-01-23 19:33:36.000000000 +0000 @@ -54,8 +54,6 @@ * </target> * * - * @author Peter Rossbach - * * @since 5.5.10 */ public class JMXAccessorEqualsCondition extends JMXAccessorConditionBase { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorGetTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,8 +49,6 @@ *

        * These tasks require Ant 1.6 or later interface. * - * @author Peter Rossbach - * * @since 5.5.10 */ public class JMXAccessorGetTask extends JMXAccessorTask { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorInvokeTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,7 +29,7 @@ /** * Access JMX JSR 160 MBeans Server. *
          - *
        • open more then one JSR 160 rmi connection
        • + *
        • open more than one JSR 160 rmi connection
        • *
        • Get/Set Mbeans attributes
        • *
        • Call Mbean Operation with arguments
        • *
        • Argument values can be converted from string to int,long,float,double,boolean,ObjectName or InetAddress
        • @@ -53,33 +53,35 @@ * * * - *
        • Create new AccessLogger at localhost + *
        • Create new AccessLogger at localhost + * + *
            *   <jmx:invoke
            *           name="Catalina:type=MBeanFactory"
            *           operation="createAccessLoggerValve"
          - *           resultproperty="accessLoggerObjectName"
          - *       >
          + *           resultproperty="accessLoggerObjectName">
            *         <arg value="Catalina:type=Host,host=localhost"/>
            *   </jmx:invoke>
          + * 
          + * + *
        • + *
        • Remove existing AccessLogger at localhost * - *
        • - *
        • Remove existing AccessLogger at localhost + *
            *   <jmx:invoke
            *           name="Catalina:type=MBeanFactory"
          - *           operation="removeValve"
          - *       >
          + *           operation="removeValve">
            *         <arg value="Catalina:type=Valve,name=AccessLogValve,host=localhost"/>
            *   </jmx:invoke>
          + * 
          * - *
        • + * *
        *

        * First call to a remote MBeanserver save the JMXConnection a referenz jmx.server *

        * These tasks require Ant 1.6 or later interface. * - * @author Peter Rossbach - * * @since 5.5.10 */ public class JMXAccessorInvokeTask extends JMXAccessorTask { @@ -153,8 +155,8 @@ if (args == null) { result = jmxServerConnection.invoke(new ObjectName(name), operation, null, null); } else { - Object argsA[] = new Object[args.size()]; - String sigA[] = new String[args.size()]; + Object[] argsA = new Object[args.size()]; + String[] sigA = new String[args.size()]; for (int i = 0; i < args.size(); i++) { Arg arg = args.get(i); if (arg.getType() == null) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorQueryTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,8 +51,6 @@ * The property manager.length show the size of the result and with manager.[0..length].name the resulted ObjectNames * are saved. These tasks require Ant 1.6 or later interface. * - * @author Peter Rossbach - * * @since 5.5.10 */ public class JMXAccessorQueryTask extends JMXAccessorTask { @@ -101,8 +99,7 @@ * @return null (no error message to report other than exception) */ protected String jmxQuery(MBeanServerConnection jmxServerConnection, String qry) { - String isError = null; - Set names = null; + Set names; String resultproperty = getResultproperty(); try { names = jmxServerConnection.queryNames(new ObjectName(qry), null); @@ -118,7 +115,7 @@ if (resultproperty != null) { int oindex = 0; - String pname = null; + String pname; for (ObjectName oname : names) { pname = resultproperty + "." + Integer.toString(oindex) + "."; oindex++; @@ -128,14 +125,14 @@ } } } - return isError; + return null; } protected void bindAttributes(MBeanServerConnection jmxServerConnection, String pname, ObjectName oname) { try { MBeanInfo minfo = jmxServerConnection.getMBeanInfo(oname); - MBeanAttributeInfo attrs[] = minfo.getAttributes(); - Object value = null; + MBeanAttributeInfo[] attrs = minfo.getAttributes(); + Object value; for (MBeanAttributeInfo attr : attrs) { if (!attr.isReadable()) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorSetTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,7 +34,7 @@ *
      • Bind Get result as Ant properties
      • *
      *

      - * Examples: Set an Mbean Manager attribute maxActiveSessions. Set this attribute with fresh jmx connection without save + * Examples: Set a Mbean Manager attribute maxActiveSessions. Set this attribute with fresh jmx connection without save * reference *

      * @@ -51,12 +51,10 @@ * /> * *

      - * First call to a remote MBeanserver save the JMXConnection a referenz jmx.server + * First call to a remote MBean server save the JMXConnection a reference jmx.server *

      * These tasks require Ant 1.6 or later interface. * - * @author Peter Rossbach - * * @since 5.5.10 */ public class JMXAccessorSetTask extends JMXAccessorTask { @@ -184,7 +182,7 @@ ObjectName oname = new ObjectName(name); String mattrType = null; MBeanInfo minfo = jmxServerConnection.getMBeanInfo(oname); - MBeanAttributeInfo attrs[] = minfo.getAttributes(); + MBeanAttributeInfo[] attrs = minfo.getAttributes(); for (int i = 0; mattrType == null && i < attrs.length; i++) { if (attribute.equals(attrs[i].getName())) { mattrType = attrs[i].getType(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -77,8 +77,6 @@ * execute when property exist and with unless when property not exists.
      * NOTE : These tasks require Ant 1.6 or later interface. * - * @author Peter Rossbach - * * @since 5.5.10 */ public class JMXAccessorTask extends BaseRedirectorHelperTask { @@ -308,7 +306,7 @@ public void execute() throws BuildException { if (testIfCondition() && testUnlessCondition()) { try { - String error = null; + String error; MBeanServerConnection jmxServerConnection = getJMXConnection(); error = jmxExecute(jmxServerConnection); @@ -370,7 +368,7 @@ * @return true if there is no if condition, or the named property exists */ protected boolean testIfCondition() { - if (ifCondition == null || "".equals(ifCondition)) { + if (ifCondition == null || ifCondition.isEmpty()) { return true; } return getProperty(ifCondition) != null; @@ -382,7 +380,7 @@ * @return true if there is no unless condition, or there is a named property but it doesn't exist */ protected boolean testUnlessCondition() { - if (unlessCondition == null || "".equals(unlessCondition)) { + if (unlessCondition == null || unlessCondition.isEmpty()) { return true; } return getProperty(unlessCondition) == null; @@ -409,7 +407,7 @@ public static MBeanServerConnection accessJMXConnection(Project project, String url, String host, String port, String username, String password, String refId) throws MalformedURLException, IOException { MBeanServerConnection jmxServerConnection = null; - boolean isRef = project != null && refId != null && refId.length() > 0; + boolean isRef = project != null && refId != null && !refId.isEmpty(); if (isRef) { Object pref = project.getReference(refId); try { @@ -442,7 +440,7 @@ MBeanServerConnection jmxServerConnection = null; if (isUseRef()) { - Object pref = null; + Object pref; if (getProject() != null) { pref = getProject().getReference(getRef()); if (pref != null) { @@ -639,7 +637,7 @@ if (delim != null) { StringTokenizer tokenizer = new StringTokenizer(result.toString(), delim); int size = 0; - for (; tokenizer.hasMoreTokens();) { + while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); if (setProperty(propertyPrefix + "." + size, token)) { size++; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java --- tomcat10-10.1.34/java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ant/jmx/JMXAccessorUnregisterTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,8 +45,6 @@ *

      * These tasks require Ant 1.6 or later interface. * - * @author Peter Rossbach - * * @since 5.5.12 */ public class JMXAccessorUnregisterTask extends JMXAccessorTask { @@ -74,12 +72,11 @@ * @throws Exception An error occurred */ protected String jmxUuregister(MBeanServerConnection jmxServerConnection, String name) throws Exception { - String error = null; if (isEcho()) { handleOutput("Unregister MBean " + name); } jmxServerConnection.unregisterMBean(new ObjectName(name)); - return error; + return null; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/AuthenticatorBase.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/AuthenticatorBase.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/AuthenticatorBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -70,6 +70,7 @@ import org.apache.tomcat.util.descriptor.web.LoginConfig; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.RequestUtil; import org.apache.tomcat.util.res.StringManager; @@ -85,8 +86,6 @@ *

      * USAGE CONSTRAINT: This Valve is only useful when processing HTTP requests. Requests of any other type will * simply be passed through. - * - * @author Craig R. McClanahan */ public abstract class AuthenticatorBase extends ValveBase implements Authenticator, RegistrationListener { @@ -143,7 +142,7 @@ * Should a session always be used once a user is authenticated? This may offer some performance benefits since the * session can then be used to cache the authenticated Principal, hence removing the need to authenticate the user * via the Realm on every request. This may be of help for combinations such as BASIC authentication used with the - * JNDIRealm or DataSourceRealms. However there will also be the performance cost of creating and GC'ing the + * JNDIRealm or DataSourceRealms. However, there will also be the performance cost of creating and GC'ing the * session. By default, a session will not be created. */ protected boolean alwaysUseSession = false; @@ -211,7 +210,6 @@ * {@code remote-user} and {@code auth-type} to a reverse proxy. This is useful, e.g., for access log consistency or * other decisions to make. */ - protected boolean sendAuthInfoResponseHeaders = false; protected SessionIdGeneratorBase sessionIdGenerator = null; @@ -221,6 +219,8 @@ */ protected SingleSignOn sso = null; + private SsoReauthenticationMode ssoReauthenticationMode = SsoReauthenticationMode.DEFAULT; + private AllowCorsPreflight allowCorsPreflight = AllowCorsPreflight.NEVER; private volatile String jaspicAppContextID = null; @@ -230,6 +230,15 @@ // ------------------------------------------------------------- Properties + public String getSsoReauthenticationMode() { + return ssoReauthenticationMode.name().toLowerCase(Locale.ENGLISH); + } + + public void setSsoReauthenticationMode(String ssoReauthenticationMode) { + this.ssoReauthenticationMode = + SsoReauthenticationMode.valueOf(ssoReauthenticationMode.trim().toUpperCase(Locale.ENGLISH)); + } + public String getAllowCorsPreflight() { return allowCorsPreflight.name().toLowerCase(Locale.ENGLISH); } @@ -420,7 +429,7 @@ } /** - * Sets the flag whether authentication information will be send to a reverse proxy on a forwarded request. + * Sets the flag whether authentication information will be sent to a reverse proxy on a forwarded request. * * @param sendAuthInfoResponseHeaders {@code true} if response headers shall be sent, {@code false} otherwise */ @@ -486,7 +495,7 @@ // Make sure that constrained resources are not cached by web proxies // or browsers as caching can provide a security hole - if (constraints != null && disableProxyCaching && !"POST".equalsIgnoreCase(request.getMethod())) { + if (constraints != null && disableProxyCaching && !Method.POST.equals(request.getMethod())) { if (securePagesWithPragma) { // Note: These can cause problems with downloading files with IE response.setHeader("Pragma", "No-cache"); @@ -609,7 +618,7 @@ if (allowCorsPreflight != AllowCorsPreflight.NEVER) { // First check to see if this is a CORS Preflight request // This is a subset of the tests in CorsFilter.checkRequestType - if ("OPTIONS".equals(request.getMethod())) { + if (Method.OPTIONS.equals(request.getMethod())) { String originHeader = request.getHeader(CorsFilter.REQUEST_HEADER_ORIGIN); if (originHeader != null && !originHeader.isEmpty() && RequestUtil.isValidOrigin(originHeader) && !RequestUtil.isSameOrigin(request, originHeader)) { @@ -721,17 +730,18 @@ private CallbackHandler createCallbackHandler() { - CallbackHandler callbackHandler = null; + CallbackHandler callbackHandler; Class clazz = null; try { clazz = Class.forName(jaspicCallbackHandlerClass, true, Thread.currentThread().getContextClassLoader()); - } catch (ClassNotFoundException e) { - // Proceed with the retry below + } catch (ClassNotFoundException ignore) { + // Not found in the context class loader (web application class loader). Re-try below. } try { if (clazz == null) { + // Look in the same class loader that loaded this class - usually Tomcat's common loader. clazz = Class.forName(jaspicCallbackHandlerClass); } callbackHandler = (CallbackHandler) clazz.getConstructor().newInstance(); @@ -751,12 +761,12 @@ // ------------------------------------------------------ Protected Methods /** - * Provided for sub-classes to implement their specific authentication mechanism. + * Provided for subclasses to implement their specific authentication mechanism. * * @param request The request that triggered the authentication * @param response The response associated with the request * - * @return {@code true} if the the user was authenticated, otherwise {@code + * @return {@code true} if the user was authenticated, otherwise {@code * false}, in which case an authentication challenge will have been written to the response * * @throws IOException If an I/O problem occurred during the authentication process @@ -822,7 +832,7 @@ if (requirePrincipal) { return false; } - } else if (cachedAuth == false || !principal.getUserPrincipal().equals(request.getUserPrincipal())) { + } else if (!cachedAuth || !principal.getUserPrincipal().equals(request.getUserPrincipal())) { // Skip registration if authentication credentials were // cached and the Principal did not change. @@ -878,40 +888,87 @@ * Check to see if the user has already been authenticated earlier in the processing chain or if there is enough * information available to authenticate the user without requiring further user interaction. * - * @param request The current request - * @param response The current response - * @param useSSO Should information available from SSO be used to attempt to authenticate the current user? + * @param request The current request + * @param response The current response + * @param useSsoCachedUserAndPassword Should the user and password available from SSO be used to attempt to + * authenticate the current user? * * @return true if the user was authenticated via the cache, otherwise false */ - protected boolean checkForCachedAuthentication(Request request, HttpServletResponse response, boolean useSSO) { + protected boolean checkForCachedAuthentication(Request request, HttpServletResponse response, + boolean useSsoCachedUserAndPassword) { + + /* + * There are two methods for authentication caching implemented by the SSO Valve. The first caches the + * authenticated Principal returned by the Realm. The second caches the user name and password passed to the + * Realm that were used for authentication. + * + * If cached authentication is not available or fails for any reason, the Authenticator will attempt the normal + * authentication process for the Authenticator. + * + * Which cached authentication methods are used depends on the configuration of the SSO Valve and/or the + * Authenticator. + * + * If the SSO Valve is configured to require re-authentication, any cached Principal will not be used unless the + * Authenticator is explicitly configured (via ssoReauthenticationMode) to use it. + * + * If the SSO Valve is configured to require re-authentication, whether the cached user name and password can be + * used will be determined by the calling Authenticator type unless the Authenticator's ssoReauthenticationMode + * is explicitly configured. + */ + + // Determine which - if any - checks for cached authentication will be made. + boolean checkPrincipal = false; + boolean checkPassword = false; - // Has the user already been authenticated? - Principal principal = request.getUserPrincipal(); + // Will be null if SSO is not configured or there is no current SSO session String ssoId = (String) request.getNote(Constants.REQ_SSOID_NOTE); - if (principal != null) { - if (log.isDebugEnabled()) { - log.debug(sm.getString("authenticator.check.found", principal.getName())); - } - // Associate the session with any existing SSO session. Even if - // useSSO is false, this will ensure coordinated session - // invalidation at log out. + + if (sso == null) { + // There is no SSO - check in case some other component has set the Principal + checkPrincipal = true; + } else if (ssoReauthenticationMode == SsoReauthenticationMode.DEFAULT && !sso.getRequireReauthentication() || + ssoReauthenticationMode == SsoReauthenticationMode.PRINCIPAL) { + checkPrincipal = true; + // If checkPrincipal is enabled then checkPassword is enabled if there is an SSO session if (ssoId != null) { - associate(ssoId, request.getSessionInternal(true)); + checkPassword = true; } - return true; + } else if (ssoId != null && (ssoReauthenticationMode == SsoReauthenticationMode.PASSWORD || + sso.getRequireReauthentication() && useSsoCachedUserAndPassword)) { + checkPassword = true; } - // Is there an SSO session against which we can try to reauthenticate? - if (useSSO && ssoId != null) { + // Check for a cached Principal. Most likely from SSO but could be another component. + if (checkPrincipal) { + if (ssoId != null && sso != null && sso.getRequireReauthentication()) { + // There is a valid SSO session but SSO Valve won't have cached the Principal. + sso.populateRequestFromSsoEntry(request, ssoId); + } + + // Has the user already been authenticated? + Principal principal = request.getUserPrincipal(); + if (principal != null) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("authenticator.check.found", principal.getName())); + } + // Associate the session with any existing SSO session. Even if + // useSSO is false, this will ensure coordinated session + // invalidation at log out. + if (ssoId != null) { + associate(ssoId, request.getSessionInternal(true)); + } + return true; + } + } + + // Check for a user and password cached by SSO + if (checkPassword) { if (log.isDebugEnabled()) { log.debug(sm.getString("authenticator.check.sso", ssoId)); } /* - * Try to reauthenticate using data cached by SSO. If this fails, either the original SSO logon was of - * DIGEST or SSL (which we can't reauthenticate ourselves because there is no cached username and password), - * or the realm denied the user's reauthentication for some reason. In either case we have to prompt the - * user for a logon + * Try to reauthenticate using data cached by SSO. If this fails we have to prompt the user for credentials. */ if (reauthenticateFromSSO(ssoId, request)) { return true; @@ -929,14 +986,14 @@ Principal authorized = context.getRealm().authenticate(username); if (authorized == null) { // Realm doesn't recognise user. Create a user with no roles - // from the authenticated user name + // from the authenticated username if (log.isDebugEnabled()) { log.debug(sm.getString("authenticator.check.authorizeFail", username)); } authorized = new GenericPrincipal(username); } String authType = request.getAuthType(); - if (authType == null || authType.length() == 0) { + if (authType == null || authType.isEmpty()) { authType = getAuthMethod(); } register(request, response, authorized, authType, username, null); @@ -1208,7 +1265,7 @@ // path, if there is one Container parent = context.getParent(); while ((sso == null) && (parent != null)) { - Valve valves[] = parent.getPipeline().getValves(); + Valve[] valves = parent.getPipeline().getValves(); for (Valve valve : valves) { if (valve instanceof SingleSignOn) { sso = (SingleSignOn) valve; @@ -1294,4 +1351,12 @@ FILTER, ALWAYS } + + + protected enum SsoReauthenticationMode { + DEFAULT, + PRINCIPAL, + PASSWORD, + FULL + } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/BasicAuthenticator.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/BasicAuthenticator.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/BasicAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/BasicAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,8 +34,6 @@ /** * An Authenticator and Valve implementation of HTTP BASIC Authentication, as outlined in RFC 7617: "The * 'Basic' HTTP Authentication Scheme" - * - * @author Craig R. McClanahan */ public class BasicAuthenticator extends AuthenticatorBase { @@ -52,7 +50,7 @@ public void setCharset(String charsetString) { - // Only acceptable options are null, "" or "UTF-8" (case insensitive) + // Only acceptable options are null, "" or "UTF-8" (case-insensitive) if (charsetString == null || charsetString.isEmpty()) { charset = StandardCharsets.ISO_8859_1; } else if ("UTF-8".equalsIgnoreCase(charsetString)) { @@ -103,7 +101,7 @@ if (authorization != null) { authorization.toBytes(); ByteChunk authorizationBC = authorization.getByteChunk(); - BasicCredentials credentials = null; + BasicCredentials credentials; try { credentials = new BasicCredentials(authorizationBC, charset, getTrimCredentials()); String username = credentials.getUsername(); @@ -116,7 +114,7 @@ } } catch (IllegalArgumentException iae) { if (log.isDebugEnabled()) { - log.debug(sm.getString("basicAuthenticator.invalidAuthorization", iae.getMessage())); + log.debug(sm.getString("basicAuthenticator.invalidAuthorization"), iae); } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/Constants.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -74,7 +74,7 @@ /** * If the cache property of the authenticator is set, and the current request is part of a session, the - * user name used to authenticate this user will be cached under this key to avoid the need for repeated calls to + * username used to authenticate this user will be cached under this key to avoid the need for repeated calls to * Realm.authenticate(). */ public static final String SESS_USERNAME_NOTE = "org.apache.catalina.session.USERNAME"; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/DigestAuthenticator.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/DigestAuthenticator.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/DigestAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/DigestAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,9 +47,6 @@ /** * An Authenticator and Valve implementation of HTTP DIGEST Authentication, as outlined in RFC 7616: "HTTP * Digest Authentication" - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class DigestAuthenticator extends AuthenticatorBase { @@ -208,7 +205,7 @@ public String getAlgorithms() { StringBuilder result = new StringBuilder(); - StringUtils.join(algorithms, ',', (x) -> x.getRfcName(), result); + StringUtils.join(algorithms, ',', AuthDigest::getRfcName, result); return result.toString(); } @@ -266,14 +263,16 @@ @Override protected boolean doAuthenticate(Request request, HttpServletResponse response) throws IOException { - // NOTE: We don't try to reauthenticate using any existing SSO session, - // because that will only work if the original authentication was - // BASIC or FORM, which are less secure than the DIGEST auth-type - // specified for this webapp - // - // Change to true below to allow previous FORM or BASIC authentications - // to authenticate users for this webapp - // TODO make this a configurable attribute (in SingleSignOn??) + /* + * Reauthentication using the cached user name and password (if any) is not enabled for DIGEST authentication. + * This was an historical design decision made because DIGEST authentication is viewed as more secure than + * BASIC/FORM. + * + * However, reauthentication was introduced to handle the case where the Realm took additional actions on + * authentication. Reauthenticating with the cached user name and password should be sufficient for DIGEST in + * that scenario. However, the original behaviour to reauthenticate has been retained in case of any (very + * unlikely) backwards compatibility issues. + */ if (checkForCachedAuthentication(request, response, false)) { return true; } @@ -375,7 +374,7 @@ String ipTimeKey = request.getRemoteAddr() + ":" + currentTime + ":" + getKey(); - // Note: The digest used to generate the nonce is independent of the the digest used for authentication. + // Note: The digest used to generate the nonce is independent of the digest used for authentication. byte[] buffer = ConcurrentMessageDigest.digest(NONCE_DIGEST, ipTimeKey.getBytes(StandardCharsets.ISO_8859_1)); String nonce = currentTime + ":" + HexUtils.toHexString(buffer); @@ -498,7 +497,7 @@ private final long nonceValidity; private final String key; private final Map nonces; - private boolean validateUri = true; + private final boolean validateUri; private String userName = null; private String method = null; @@ -539,7 +538,7 @@ Map directives; try { directives = Authorization.parseAuthorizationDigest(new StringReader(authorization)); - } catch (IOException e) { + } catch (IOException ioe) { return false; } @@ -597,7 +596,7 @@ absolute.append("://"); absolute.append(host); absolute.append(uriQuery); - if (!uri.equals(absolute.toString())) { + if (!uri.contentEquals(absolute)) { return false; } } else { @@ -637,7 +636,7 @@ } } String serverIpTimeKey = request.getRemoteAddr() + ":" + nonceTime + ":" + key; - // Note: The digest used to generate the nonce is independent of the the digest used for authentication/ + // Note: The digest used to generate the nonce is independent of the digest used for authentication/ byte[] buffer = ConcurrentMessageDigest.digest(NONCE_DIGEST, serverIpTimeKey.getBytes(StandardCharsets.ISO_8859_1)); String digestServerIpTimeKey = HexUtils.toHexString(buffer); @@ -687,11 +686,7 @@ } // Validate algorithm is one of the algorithms configured for the authenticator - if (!algorithms.contains(algorithm)) { - return false; - } - - return true; + return algorithms.contains(algorithm); } public boolean isNonceStale() { @@ -713,7 +708,7 @@ public static class NonceInfo { private final long timestamp; - private final boolean seen[]; + private final boolean[] seen; private final int offset; private int count = 0; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/FormAuthenticator.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/FormAuthenticator.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/FormAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/FormAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,14 +42,12 @@ import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.descriptor.web.LoginConfig; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; /** * An Authenticator and Valve implementation of FORM BASED Authentication, as described in the Servlet API * Specification. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class FormAuthenticator extends AuthenticatorBase { @@ -81,7 +79,7 @@ // ------------------------------------------------------------- Properties /** - * Return the character encoding to use to read the user name and password. + * Return the character encoding to use to read the username and password. * * @return The name of the character encoding */ @@ -91,7 +89,7 @@ /** - * Set the character encoding to be used to read the user name and password. + * Set the character encoding to be used to read the username and password. * * @param encoding The name of the encoding to use */ @@ -136,7 +134,7 @@ * Configures the maximum session timeout to be used during authentication if the authentication process creates a * session. * - * @param authenticationSessionTimeout The maximum session timeout to use duriing authentication if the + * @param authenticationSessionTimeout The maximum session timeout to use during authentication if the * authentication process creates a session */ public void setAuthenticationSessionTimeout(int authenticationSessionTimeout) { @@ -161,7 +159,7 @@ // References to objects we will need later Session session = null; - Principal principal = null; + Principal principal; // Have we authenticated this user before but have caching disabled? if (!cache) { @@ -227,9 +225,9 @@ // No -- Save this request and redirect to the form login page if (!loginAction) { // If this request was to the root of the context without a trailing - // '/', need to redirect to add it else the submit of the login form + // '/', need to redirect to add it else the submission of the login form // may not go to the correct web application - if (request.getServletPath().length() == 0 && request.getPathInfo() == null) { + if (request.getServletPath().isEmpty() && request.getPathInfo() == null) { StringBuilder location = new StringBuilder(requestURI); location.append('/'); if (request.getQueryString() != null) { @@ -247,7 +245,7 @@ try { saveRequest(request, session); } catch (IOException ioe) { - log.debug(sm.getString("authenticator.requestBodyTooBig")); + log.debug(sm.getString("authenticator.requestBodyTooBig"), ioe); response.sendError(HttpServletResponse.SC_FORBIDDEN, sm.getString("authenticator.requestBodyTooBig")); return false; } @@ -303,7 +301,7 @@ // the landing page String uri = request.getContextPath() + landingPage; SavedRequest saved = new SavedRequest(); - saved.setMethod("GET"); + saved.setMethod(Method.GET); saved.setRequestURI(uri); saved.setDecodedRequestURI(uri); request.getSessionInternal(true).setNote(Constants.FORM_REQUEST_NOTE, saved); @@ -328,7 +326,7 @@ // the landing page String uri = request.getContextPath() + landingPage; SavedRequest saved = new SavedRequest(); - saved.setMethod("GET"); + saved.setMethod(Method.GET); saved.setRequestURI(uri); saved.setDecodedRequestURI(uri); session.setNote(Constants.FORM_REQUEST_NOTE, saved); @@ -364,17 +362,7 @@ // a resource is protected for some HTTP methods but not protected for // GET which is used after authentication when redirecting to the // protected resource. - // TODO: This is similar to the FormAuthenticator.matchRequest() logic - // Is there a way to remove the duplication? - Session session = request.getSessionInternal(false); - if (session != null) { - SavedRequest savedRequest = (SavedRequest) session.getNote(Constants.FORM_REQUEST_NOTE); - if (savedRequest != null && decodedRequestURI.equals(savedRequest.getDecodedRequestURI())) { - return true; - } - } - - return false; + return matchRequest(request, false); } @@ -429,7 +417,7 @@ } String loginPage = config.getLoginPage(); - if (loginPage == null || loginPage.length() == 0) { + if (loginPage == null || loginPage.isEmpty()) { String msg = sm.getString("formAuthenticator.noLoginPage", context.getName()); log.warn(msg); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg); @@ -450,7 +438,7 @@ // Always use GET for the login page, regardless of the method used String oldMethod = request.getMethod(); - request.getCoyoteRequest().method().setString("GET"); + request.getCoyoteRequest().setMethod(Method.GET); RequestDispatcher disp = context.getServletContext().getRequestDispatcher(loginPage); try { @@ -466,7 +454,7 @@ response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg); } finally { // Restore original method so that it is written into access log - request.getCoyoteRequest().method().setString(oldMethod); + request.getCoyoteRequest().setMethod(oldMethod); } } @@ -485,7 +473,7 @@ throws IOException { String errorPage = config.getErrorPage(); - if (errorPage == null || errorPage.length() == 0) { + if (errorPage == null || errorPage.isEmpty()) { String msg = sm.getString("formAuthenticator.noErrorPage", context.getName()); log.warn(msg); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg); @@ -508,15 +496,21 @@ } + protected boolean matchRequest(Request request) { + return matchRequest(request, true); + } + /** * Does this request match the saved one (so that it must be the redirect we signaled after successful - * authentication? + * authentication?) * * @param request The request to be verified + * @param strict true to check for a valid Principal and valid Session ID, false to only + * check for a valid saved request and matching URI * * @return true if the requests matched the saved one */ - protected boolean matchRequest(Request request) { + protected boolean matchRequest(Request request, boolean strict) { // Has a session been created? Session session = request.getSessionInternal(false); if (session == null) { @@ -529,17 +523,19 @@ return false; } - // Is there a saved principal? - if (cache && session.getPrincipal() == null || !cache && request.getPrincipal() == null) { - return false; - } - - // Does session id match? - if (getChangeSessionIdOnAuthentication()) { - String expectedSessionId = (String) session.getNote(Constants.SESSION_ID_NOTE); - if (expectedSessionId == null || !expectedSessionId.equals(request.getRequestedSessionId())) { + if (strict) { + // Is there a saved principal? + if (cache && session.getPrincipal() == null || !cache && request.getPrincipal() == null) { return false; } + + // Does session id match? + if (getChangeSessionIdOnAuthentication()) { + String expectedSessionId = (String) session.getNote(Constants.SESSION_ID_NOTE); + if (expectedSessionId == null || !expectedSessionId.equals(request.getRequestedSessionId())) { + return false; + } + } } // Does the request URI match? @@ -592,7 +588,7 @@ String method = saved.getMethod(); MimeHeaders rmh = request.getCoyoteRequest().getMimeHeaders(); rmh.recycle(); - boolean cacheable = "GET".equalsIgnoreCase(method) || "HEAD".equalsIgnoreCase(method); + boolean cacheable = Method.GET.equals(method) || Method.HEAD.equals(method); Iterator names = saved.getHeaderNames(); while (names.hasNext()) { String name = names.next(); @@ -626,7 +622,7 @@ // If no content type specified, use default for POST String savedContentType = saved.getContentType(); - if (savedContentType == null && "POST".equalsIgnoreCase(method)) { + if (savedContentType == null && Method.POST.equals(method)) { savedContentType = Globals.CONTENT_TYPE_FORM_URL_ENCODING; } @@ -634,7 +630,7 @@ request.getCoyoteRequest().setContentType(contentType); } - request.getCoyoteRequest().method().setString(method); + request.getCoyoteRequest().setMethod(method); // The method, URI, queryString and protocol are normally stored as // bytes in the HttpInputBuffer and converted lazily to String. At this // point, the method has already been set as String in the line above @@ -642,14 +638,14 @@ // HttpInputBuffer. Processing the saved request body will overwrite // these bytes. Configuring the HttpInputBuffer to retain these bytes as // it would in a normal request would require some invasive API changes. - // Therefore force the conversion to String now so the correct values + // Therefore, force the conversion to String now so the correct values // are presented if the application requests them. request.getCoyoteRequest().requestURI().toStringType(); request.getCoyoteRequest().queryString().toStringType(); request.getCoyoteRequest().protocol().toStringType(); - if (saved.getOriginalMaxInactiveInterval() > 0) { - session.setMaxInactiveInterval(saved.getOriginalMaxInactiveInterval()); + if (saved.getOriginalMaxInactiveIntervalOptional() != null) { + session.setMaxInactiveInterval(saved.getOriginalMaxInactiveIntervalOptional().intValue()); } return true; @@ -668,7 +664,7 @@ // Create and populate a SavedRequest object for this request SavedRequest saved = new SavedRequest(); - Cookie cookies[] = request.getCookies(); + Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { saved.addCookie(cookie); @@ -721,17 +717,20 @@ if (session instanceof HttpSession) { if (((HttpSession) session).isNew()) { int originalMaxInactiveInterval = session.getMaxInactiveInterval(); - if (originalMaxInactiveInterval > getAuthenticationSessionTimeout()) { + if (originalMaxInactiveInterval > getAuthenticationSessionTimeout() || + originalMaxInactiveInterval <= 0) { saved.setOriginalMaxInactiveInterval(originalMaxInactiveInterval); session.setMaxInactiveInterval(getAuthenticationSessionTimeout()); } - } else if (previousSavedRequest != null && previousSavedRequest.getOriginalMaxInactiveInterval() > 0) { + } else if (previousSavedRequest != null && + previousSavedRequest.getOriginalMaxInactiveIntervalOptional() != null) { /* * The user may have refreshed the browser page during authentication. Transfer the original max * inactive interval from previous saved request to current one else, once authentication is completed, - * the session will retain the the shorter authentication session timeout + * the session will retain the shorter authentication session timeout */ - saved.setOriginalMaxInactiveInterval(previousSavedRequest.getOriginalMaxInactiveInterval()); + saved.setOriginalMaxInactiveInterval( + previousSavedRequest.getOriginalMaxInactiveIntervalOptional().intValue()); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -42,7 +42,7 @@ authenticator.userDataPermissionFail=User data does not comply with the constraints of the resource authenticator.userPermissionFail=User [{0}] does not have authorization to access the resource -basicAuthenticator.invalidAuthorization=Invalid Authorization: [{0}] +basicAuthenticator.invalidAuthorization=Invalid Authorization header basicAuthenticator.invalidCharset=The only permitted values are null, the empty string or UTF-8 basicAuthenticator.notBase64=Basic Authorization credentials are not Base64 basicAuthenticator.notBasic=Authorization header method is not ''Basic'' @@ -76,9 +76,12 @@ singleSignOn.debug.principalNotFound=SSO did not find a cached Principal. Erasing SSO cookie for session [{0}] singleSignOn.debug.register=SSO registering SSO session [{0}] for user [{1}] with authentication type [{2}] singleSignOn.debug.removeSession=SSO removing application session [{0}] from SSO session [{1}] +singleSignOn.debug.sessionChangedId=SSO changing sessionID in session [{0}, oldSessionId {1}] from SSO session [{2}] singleSignOn.debug.sessionLogout=SSO processing a log out for SSO session [{0}] and application session [{1}] singleSignOn.debug.sessionTimeout=SSO processing a time out for SSO session [{0}] and application session [{1}] singleSignOn.debug.update=SSO updating SSO session [{0}] to authentication type [{1}] +singleSignOn.duplicateRealm=SSO found a realm defined on context [{0}], this will conflict with principals defined in the main realm +singleSignOn.noRealm=This SSO [{0}] has no realm associated with it singleSignOn.sessionExpire.contextNotFound=SSO unable to expire session [{0}] because the Context could not be found singleSignOn.sessionExpire.engineNull=SSO unable to expire session [{0}] because the Engine was null singleSignOn.sessionExpire.hostNotFound=SSO unable to expire session [{0}] because the Host could not be found @@ -92,6 +95,6 @@ spnegoAuthenticator.ticketValidateFail=Failed to validate client supplied ticket sslAuthenticatorValve.authFailed=Authentication with the provided certificates failed -sslAuthenticatorValve.http2=The context [{0}] in virtual host [{1}] is configured to use CLIENT-CERT authentication and [{2}] is configured to support HTTP/2. Use of CLIENT-CERT authentication is not compatible with the use of HTTP/2. +sslAuthenticatorValve.http2=The context [{0}] in virtual host [{1}] is configured to use CLIENT-CERT authentication and [{2}] is configured to support HTTP/2. Use of CLIENT-CERT authentication is not compatible with the use of HTTP/2 unless certificateVerification is set to required. sslAuthenticatorValve.noCertificates=No certificates are included with this request -sslAuthenticatorValve.tls13=The context [{0}] in virtual host [{1}] is configured to use CLIENT-CERT authentication and [{2}] is configured to support TLS 1.3 using JSSE. Use of CLIENT-CERT authentication is not compatible with the use of TLS 1.3 and JSSE. +sslAuthenticatorValve.tls13=The context [{0}] in virtual host [{1}] is configured to use CLIENT-CERT authentication and [{2}] is configured to support TLS 1.3 using JSSE. Use of CLIENT-CERT authentication is not compatible with the use of TLS 1.3 and JSSE unless certificateVerification is set to required. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -42,7 +42,7 @@ authenticator.userDataPermissionFail=Les données envoyées par l'utilisateur ne répondent pas aux contraintes définies pour la ressource authenticator.userPermissionFail=L''utilisateur [{0}] n''a pas l''autorisation d''accéder à la ressource -basicAuthenticator.invalidAuthorization=L''autorisation est invalide: [{0}] +basicAuthenticator.invalidAuthorization=L'autorisation est invalide basicAuthenticator.invalidCharset=Les seules valeurs permises sont null, la chaîne vide, ou des caractères UTF-8 basicAuthenticator.notBase64=Les informations d'identification Basic ne sont pas encodées en Base64 basicAuthenticator.notBasic=La méthode d'authentification n'est pas ''Basic'' @@ -76,9 +76,12 @@ singleSignOn.debug.principalNotFound=Le SSO n''a pas trouvé de principal en cache, le cookie SSO de la session [{0}] est effacé singleSignOn.debug.register=Enregistrement de la session SSO [{0}] pour l''utilisateur [{1}] avec le type d''authentification [{2}] singleSignOn.debug.removeSession=Le SSO retire la session applicative [{0}] de la session SSO [{1}] +singleSignOn.debug.sessionChangedId=Changement de l''id de session [{0}, ancien id {1}] dans la session SSO [{2}] singleSignOn.debug.sessionLogout=Le SSO effectue une déconnection pour la session SSO [{0}] et la session [{1}] de l''application singleSignOn.debug.sessionTimeout=Le SSO traite un timeout pour la session SSO [{0}] et la session [{1}] de l''application singleSignOn.debug.update=Le SSO met à jour la session SSO [{0}] avec le type d''authentification [{1}] +singleSignOn.duplicateRealm=Le SSO a trouvé un royaume défini sur le contexte [{0}] qui va entrer en conflit avec les principals définis dans le royaume du SSO +singleSignOn.noRealm=Ce SSO [{0}] n''a aucun royaume associé avec lui singleSignOn.sessionExpire.contextNotFound=Le SSO n''a pu faire expirer la session [{0}] parce que le contexte n''a pas été trouvé singleSignOn.sessionExpire.engineNull=Le SSO n''a pu faire expirer la session [{0}] parce que le moteur est null singleSignOn.sessionExpire.hostNotFound=SSO ne peut pas faire expirer le session [{0}] parce que l''hôte ("Host") n''a pas été trouvé diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -42,7 +42,7 @@ authenticator.userDataPermissionFail=ユーザデータがリソースの制約に従っていません authenticator.userPermissionFail=ユーザ [{0}] にはリソースへのアクセス権限がありません -basicAuthenticator.invalidAuthorization=無効な認証: [{0}] +basicAuthenticator.invalidAuthorization=無効な認証 basicAuthenticator.invalidCharset=指定できる値は、null、空の文字列またはUTF-8です。 basicAuthenticator.notBase64=Basic認証の資格情報がBase64ではありません basicAuthenticator.notBasic=認証ヘッダメソッドが ''Basic'' ではありません @@ -76,9 +76,12 @@ singleSignOn.debug.principalNotFound=SSO はキャッシュされたプリンシパルを検出しませんでした。セッション [{0}] の SSO Cookie を消去しています singleSignOn.debug.register=SSO は認証タイプ [{2}] のユーザー [{1}] の SSO セッション [{0}] を登録しています singleSignOn.debug.removeSession=SSOはSSOセッション [{1}] からアプリケーションセッション [{0}] を削除しています +singleSignOn.debug.sessionChangedId=SSO は SSO セッション [{2}] からセッション [{0}、oldSessionId {1}] のセッション ID を変更しています singleSignOn.debug.sessionLogout=SSOはSSOセッション[{0}]とアプリケーションセッション[{1}]をログアウト処理しています singleSignOn.debug.sessionTimeout=SSOはSSOセッション[{0}]とアプリケーションセッション[{1}]のタイムアウトを処理しています singleSignOn.debug.update=SSOはSSOセッション [{0}] を認証タイプ [{1}] に更新します +singleSignOn.duplicateRealm=SSO はコンテキスト [{0}] で定義されたレルムを検出しました。これはメインレルムで定義されたプリンシパルと競合します +singleSignOn.noRealm=このSSO [{0}]にはレルムが関連付けられていません singleSignOn.sessionExpire.contextNotFound=Context が見つからないため、SSO はセッション [{0}] を破棄できません singleSignOn.sessionExpire.engineNull=Engine が null だったため、SSO はセッション [{0}] を破棄できません singleSignOn.sessionExpire.hostNotFound=ホストが見つからないため SSO セッション [{0}] を破棄できません diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,4 +16,7 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +authenticator.check.found=Уже аутентифицирован [{0}] authenticator.noAuthHeader=Заголовок авторизации не был отправлен клиентом + +basicAuthenticator.invalidCharset=Единственные разрешенные значения это null, пустая строка или UTF-8 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,8 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +authenticator.authentication=认证类型为[{1}]的认证主体[{0}] +authenticator.authenticationFail=用户认证失败 authenticator.certificates=此请求中没有客户端证书链 authenticator.changeSessionId=在身份验证时, 会话 ID 从 [{0}] 更改为 [{1}] authenticator.check.authorize=用户名[{0}]从连接器获得,并被信任为有效。从Tomcat领域获取此用户的角色。 @@ -32,6 +34,7 @@ authenticator.notContext=配置错误:必须被附属于一个上下文 authenticator.requestBodyTooBig=请求正文太大,无法在身份验证过程中进行缓存 authenticator.sessionExpired=已超出登录过程所允许的时间。 如果您希望继续,则必须单击两次后退并重新单击您请求的链接或先关闭然后重新打开浏览器 +authenticator.sso=找到SSO [{0}] authenticator.tomcatPrincipalLogoutFail=使用TomcatPrincipal实例注销失败 authenticator.unauthorized=无法使用提供的凭据进行身份验证 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/NonLoginAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * An Authenticator and Valve implementation that checks only security constraints not involving user * authentication. - * - * @author Craig R. McClanahan */ public final class NonLoginAuthenticator extends AuthenticatorBase { @@ -86,7 +84,7 @@ } // No Principal means the user is not already authenticated - // and so will not be assigned any roles. It is safe to + // and so will not be assigned any roles. It is safe // to say the user is now authenticated because access to // protected resources will only be allowed with a matching role. // i.e. SC_FORBIDDEN (403 status) will be generated later. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/SSLAuthenticator.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/SSLAuthenticator.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/SSLAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/SSLAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,12 +37,11 @@ import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.net.Constants; import org.apache.tomcat.util.net.SSLHostConfig; +import org.apache.tomcat.util.net.SSLHostConfig.CertificateVerification; /** * An Authenticator and Valve implementation of authentication that utilizes SSL certificates to identify * client users. - * - * @author Craig R. McClanahan */ public class SSLAuthenticator extends AuthenticatorBase { @@ -60,14 +59,17 @@ @Override protected boolean doAuthenticate(Request request, HttpServletResponse response) throws IOException { - // NOTE: We don't try to reauthenticate using any existing SSO session, - // because that will only work if the original authentication was - // BASIC or FORM, which are less secure than the CLIENT-CERT auth-type - // specified for this webapp - // - // Change to true below to allow previous FORM or BASIC authentications - // to authenticate users for this webapp - // TODO make this a configurable attribute (in SingleSignOn??) + /* + * Reauthentication using the cached user name and password (if any) is not enabled for CLIENT-CERT + * authentication. This was an historical design decision made because CLIENT-CERT authentication is viewed as + * more secure than BASIC/FORM. + * + * However, reauthentication was introduced to handle the case where the Realm took additional actions on + * authentication. Reauthenticating with the cached user name and password may not be sufficient for CLIENT-CERT + * since it will not make any TLS information (client certificate etc) available that a web application may + * depend on. Therefore, the reauthentication behaviour for CLIENT-CERT is to perform a normal CLIENT-CERT + * authentication. + */ if (checkForCachedAuthentication(request, response, false)) { return true; } @@ -77,7 +79,7 @@ containerLog.trace(" Looking up certificates"); } - X509Certificate certs[] = getRequestCertificates(request); + X509Certificate[] certs = getRequestCertificates(request); if ((certs == null) || (certs.length < 1)) { if (containerLog.isDebugEnabled()) { @@ -128,7 +130,7 @@ */ protected X509Certificate[] getRequestCertificates(final Request request) throws IllegalStateException { - X509Certificate certs[] = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR); + X509Certificate[] certs = (X509Certificate[]) request.getAttribute(Globals.CERTIFICATES_ATTR); if ((certs == null) || (certs.length < 1)) { try { @@ -175,28 +177,52 @@ Connector[] connectors = engine.getService().findConnectors(); for (Connector connector : connectors) { - // First check for upgrade - UpgradeProtocol[] upgradeProtocols = connector.findUpgradeProtocols(); - for (UpgradeProtocol upgradeProtocol : upgradeProtocols) { - if ("h2".equals(upgradeProtocol.getAlpnName())) { - log.warn(sm.getString("sslAuthenticatorValve.http2", context.getName(), host.getName(), connector)); + /* + * There are two underlying issues here. + * + * 1. JSSE does not implement post-handshake authentication (PHA) for TLS 1.3. That means CLIENT-CERT + * authentication will only work if the virtual host requires a certificate OR the client never requests a + * protected resource. + * + * 2. HTTP/2 does not permit re-negotiation nor PHA. That means CLIENT-CERT authentication will only work if + * the virtual host requires a certificate OR the client never requests a protected resource. + * + * We can't rely on the client never requesting a protected resource but we can check if all the virtual + * hosts are configured to require a certificate. + */ + boolean allHostsRequireCertificate = true; + for (SSLHostConfig sslHostConfig : connector.findSslHostConfigs()) { + if (sslHostConfig.getCertificateVerification() != CertificateVerification.REQUIRED) { + allHostsRequireCertificate = false; break; } } - // Then check for TLS 1.3 - SSLHostConfig[] sslHostConfigs = connector.findSslHostConfigs(); - for (SSLHostConfig sslHostConfig : sslHostConfigs) { - if (!sslHostConfig.isTls13RenegotiationAvailable()) { - String[] enabledProtocols = sslHostConfig.getEnabledProtocols(); - if (enabledProtocols == null) { - // Possibly boundOnInit is used, so use the less accurate protocols - enabledProtocols = sslHostConfig.getProtocols().toArray(new String[0]); + // Only need to check for use of HTTP/2 or TLS 1.3 if one or more hosts doesn't require a certificate + if (!allHostsRequireCertificate) { + // Check if the Connector is configured to support upgrade to HTTP/2 + UpgradeProtocol[] upgradeProtocols = connector.findUpgradeProtocols(); + for (UpgradeProtocol upgradeProtocol : upgradeProtocols) { + if ("h2".equals(upgradeProtocol.getAlpnName())) { + log.warn(sm.getString("sslAuthenticatorValve.http2", context.getName(), host.getName(), + connector)); + break; } - for (String enbabledProtocol : enabledProtocols) { - if (Constants.SSL_PROTO_TLSv1_3.equals(enbabledProtocol)) { - log.warn(sm.getString("sslAuthenticatorValve.tls13", context.getName(), host.getName(), - connector)); + } + + // Check if any of the virtual hosts support TLS 1.3 without supporting PHA + for (SSLHostConfig sslHostConfig : connector.findSslHostConfigs()) { + if (!sslHostConfig.isTls13RenegotiationAvailable()) { + String[] enabledProtocols = sslHostConfig.getEnabledProtocols(); + if (enabledProtocols == null) { + // Possibly boundOnInit is used, so use the less accurate protocols + enabledProtocols = sslHostConfig.getProtocols().toArray(new String[0]); + } + for (String enabledProtocol : enabledProtocols) { + if (Constants.SSL_PROTO_TLSv1_3.equals(enabledProtocol)) { + log.warn(sm.getString("sslAuthenticatorValve.tls13", context.getName(), host.getName(), + connector)); + } } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/SavedRequest.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/SavedRequest.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/SavedRequest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/SavedRequest.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,8 +35,6 @@ *

      * IMPLEMENTATION NOTE - It is assumed that this object is accessed only from the context of a single thread, so * no synchronization around internal collection classes is performed. - * - * @author Craig R. McClanahan */ public final class SavedRequest implements Serializable { @@ -181,13 +179,26 @@ /** * The original maxInactiveInterval for the session. */ - private int originalMaxInactiveInterval = -1; + private Integer originalMaxInactiveInterval = null; - public int getOriginalMaxInactiveInterval() { + public Integer getOriginalMaxInactiveIntervalOptional() { return originalMaxInactiveInterval; } + /** + * Obtain the original session maxInactiveInterval. + * + * @return the original session maxInactiveInterval + * + * @deprecated This method will be removed in Tomcat 12.0.x onwards. Use + * {@link SavedRequest#getOriginalMaxInactiveIntervalOptional()} + */ + @Deprecated + public int getOriginalMaxInactiveInterval() { + return (originalMaxInactiveInterval == null) ? -1 : originalMaxInactiveInterval.intValue(); + } + public void setOriginalMaxInactiveInterval(int originalMaxInactiveInterval) { - this.originalMaxInactiveInterval = originalMaxInactiveInterval; + this.originalMaxInactiveInterval = Integer.valueOf(originalMaxInactiveInterval); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/SingleSignOn.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOn.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/SingleSignOn.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOn.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,6 +28,7 @@ import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Engine; +import org.apache.catalina.Host; import org.apache.catalina.LifecycleException; import org.apache.catalina.Manager; import org.apache.catalina.Realm; @@ -50,8 +51,33 @@ *

    2. The web applications themselves must use one of the standard Authenticators found in the * org.apache.catalina.authenticator package.
    3. * - * - * @author Craig R. McClanahan + *

      + * On first authentication to any web application, an SSO session is created and the authenticated Principal, the + * authentication type and the plain text user name and password used to authenticate (if available) are cached using a + * key based on the SSO session. On subsequent requests to a web application on the Host where this Valve is configured, + * the cached authenticated Principal and the authentication type are added to the request by the SSO Valve and no + * further authentication takes place. + *

      + * In some scenarios, adding the authenticated Principal and the authentication type is insufficient. This usually + * occurs when the web application depends on additional actions the Realm takes on authentication which are bypassed by + * the SSO Valve. Examples of this include the Realm setting security credentials on the request thread to support EJB + * access or the CLIENT-CERT authenticator providing the client certificate and other TLS attributes. To address this, + * the {@code requireReauthentication} flag can be set to {@code true} which will cause the SSO Valve not to set the + * cached Principal and authentication type on the request and the web application authenticator will authenticate the + * request. By default this reauthentication will occur in the following ways: + *

        + *
      • BASIC - call the realm using the plain text user name and password cached by the SSO Valve if available. If not + * cached, obtain those values from the request. If not present in the request, request them from the user agent.
      • + *
      • FORM - call the realm using the plain text user name and password cached by the SSO Valve if available. If not + * cached, request them from the user agent.
      • + *
      • DIGEST - call the realm using the credentials present in the request. If not present in the request, request them + * from the user agent.
      • + *
      • CLIENT-CERT - call the realm using the credentials present in the TLS connection. If not present in the TLS + * connection, request them from the user agent.
      • + *
      • SPNEGO - request authentication credentials from the user agent.
      • + *
      + * Note that this means that enabling reauthentication only makes sense if there are two or more web applications in the + * Host that use BASIC or FORM. If that is not the case, the SSO Valve will just add processing overhead. */ public class SingleSignOn extends ValveBase { @@ -112,7 +138,7 @@ * @param cookieDomain cookie domain name */ public void setCookieDomain(String cookieDomain) { - if (cookieDomain != null && cookieDomain.trim().length() == 0) { + if (cookieDomain != null && cookieDomain.trim().isEmpty()) { this.cookieDomain = null; } else { this.cookieDomain = cookieDomain; @@ -225,7 +251,7 @@ containerLog.trace(sm.getString("singleSignOn.debug.cookieCheck")); } Cookie cookie = null; - Cookie cookies[] = request.getCookies(); + Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie value : cookies) { if (cookieName.equals(value.getName())) { @@ -299,7 +325,7 @@ * Process a session destroyed event by removing references to that session from the caches and - if the session * destruction is the result of a logout - destroy the associated SSO session. * - * @param ssoId The ID of the SSO session which which the destroyed session was associated + * @param ssoId The id of the SSO session with which the destroyed session was associated * @param session The session that has been destroyed */ public void sessionDestroyed(String ssoId, Session session) { @@ -313,7 +339,7 @@ // session was logged out, we'll log out of all session associated with // the SSO. if (((session.getMaxInactiveInterval() > 0) && - (session.getIdleTimeInternal() >= session.getMaxInactiveInterval() * 1000)) || + (session.getIdleTimeInternal() >= session.getMaxInactiveInterval() * 1000L)) || (!session.getManager().getContext().getState().isAvailable())) { if (containerLog.isDebugEnabled()) { containerLog.debug(sm.getString("singleSignOn.debug.sessionTimeout", ssoId, session)); @@ -384,7 +410,7 @@ // Expire any associated sessions Set ssoKeys = sso.findSessions(); - if (ssoKeys.size() == 0) { + if (ssoKeys.isEmpty()) { if (containerLog.isDebugEnabled()) { containerLog.debug(sm.getString("singleSignOn.debug.deregisterNone", ssoId)); } @@ -423,11 +449,11 @@ containerLog.warn(sm.getString("singleSignOn.sessionExpire.managerNotFound", key)); return; } - Session session = null; + Session session; try { session = manager.findSession(key.getSessionId()); - } catch (IOException e) { - containerLog.warn(sm.getString("singleSignOn.sessionExpire.managerError", key), e); + } catch (IOException ioe) { + containerLog.warn(sm.getString("singleSignOn.sessionExpire.managerError", key), ioe); return; } if (session == null) { @@ -439,8 +465,8 @@ /** - * Attempts reauthentication to the given Realm using the credentials associated with the single - * sign-on session identified by argument ssoId. + * Attempts reauthentication to the given Realm using the cached plain text credentials associated with + * the single sign-on session identified by argument ssoId. *

      * If reauthentication is successful, the Principal and authorization type associated with the SSO * session will be bound to the given Request object via calls to {@link Request#setAuthType @@ -480,6 +506,15 @@ } + protected void populateRequestFromSsoEntry(Request request, String ssoId) { + SingleSignOnEntry entry = cache.get(ssoId); + if (entry != null) { + request.setAuthType(entry.getAuthType()); + request.setUserPrincipal(entry.getPrincipal()); + } + } + + /** * Register the specified Principal as being associated with the specified value for the single sign on identifier. * @@ -556,9 +591,9 @@ // Remove the inactive session from SingleSignOnEntry entry.removeSession(session); - // If there are not sessions left in the SingleSignOnEntry, + // If there are no sessions left in the SingleSignOnEntry, // deregister the entry. - if (entry.findSessions().size() == 0) { + if (entry.findSessions().isEmpty()) { deregister(ssoId); } } @@ -571,12 +606,39 @@ @Override protected void startInternal() throws LifecycleException { - Container c = getContainer(); - while (c != null && !(c instanceof Engine)) { - c = c.getParent(); - } - if (c != null) { - engine = (Engine) c; + Container container = getContainer(); + while (container != null && !(container instanceof Engine)) { + container = container.getParent(); + } + if (container != null) { + engine = (Engine) container; + } + // Starting with the associated container, verify it has a realm associated, + // and that no child container returns a different realm + container = getContainer(); + Realm containerRealm = container.getRealm(); + if (containerRealm == null) { + containerLog.warn(sm.getString("singleSignOn.noRealm", container.getName())); + } else { + if (container instanceof Engine) { + for (Container host : engine.findChildren()) { + if (host.getRealm() != containerRealm) { + containerLog.warn(sm.getString("singleSignOn.duplicateRealm", host.getName())); + } else { + for (Container context : host.findChildren()) { + if (context.getRealm() != containerRealm) { + containerLog.warn(sm.getString("singleSignOn.duplicateRealm", context.getName())); + } + } + } + } + } else if (container instanceof Host) { + for (Container context : container.findChildren()) { + if (context.getRealm() != containerRealm) { + containerLog.warn(sm.getString("singleSignOn.duplicateRealm", context.getName())); + } + } + } } super.startInternal(); } @@ -587,4 +649,27 @@ super.stopInternal(); engine = null; } + + protected void sessionChangedId(String ssoId, Session session, String oldSessionId) { + if (containerLog.isDebugEnabled()) { + containerLog.debug(sm.getString("singleSignOn.debug.sessionChangedId", session, oldSessionId, ssoId)); + } + + SingleSignOnEntry entry = cache.get(ssoId); + if (entry == null) { + return; + } + + /* + * Associate the new sessionId with this SingleSignOnEntry. A SessionListener will be registered for the new + * sessionID. If not, then we would not notice any subsequent Session.SESSION_DESTROYED_EVENT for the session. + */ + entry.addSession(this, ssoId, session); + + /* + * Remove the obsolete sessionId from the SingleSignOnEntry. The sessionId part of the SingleSignOnSessionKey is + * final. + */ + entry.removeSession(session, oldSessionId); + } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/SingleSignOnEntry.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOnEntry.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/SingleSignOnEntry.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOnEntry.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,8 +34,6 @@ * AuthenticatorBase subclasses that need it in order to perform reauthentications when SingleSignOn is in * use. * - * @author B Stansberry, based on work by Craig R. McClanahan - * * @see SingleSignOn * @see AuthenticatorBase#reauthenticateFromSSO */ @@ -102,9 +100,21 @@ } /** + * Removes the given Session from the list of those associated with this SSO, using the previous + * sessionId + * + * @param session the Session to remove. + * @param oldSessionId the previous sessionId of the Session to remove. + */ + public void removeSession(Session session, String oldSessionId) { + SingleSignOnSessionKey key = new SingleSignOnSessionKey(session, oldSessionId); + sessionKeys.remove(key); + } + + /** * Returns the HTTP Session identifiers associated with this SSO. * - * @return The identifiers for the HTTP sessions that are current associated with this SSo entry + * @return the identifiers for the HTTP sessions that are currently associated with this SSO entry */ public Set findSessions() { return sessionKeys.keySet(); @@ -148,9 +158,9 @@ } /** - * Gets the user name provided by the user as part of the authentication process. + * Gets the username provided by the user as part of the authentication process. * - * @return The user name that was authenticated as part of the authentication that triggered the creation of the SSO + * @return The username that was authenticated as part of the authentication that triggered the creation of the SSO * entry */ public String getUsername() { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/SingleSignOnListener.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOnListener.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/SingleSignOnListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOnListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,7 +38,8 @@ @Override public void sessionEvent(SessionEvent event) { - if (!Session.SESSION_DESTROYED_EVENT.equals(event.getType())) { + final String type = event.getType(); + if (!(Session.SESSION_DESTROYED_EVENT.equals(type) || Session.SESSION_CHANGED_ID_EVENT.equals(type))) { return; } @@ -56,6 +57,15 @@ if (sso == null) { return; } - sso.sessionDestroyed(ssoId, session); + + switch (type) { + case Session.SESSION_CHANGED_ID_EVENT: + sso.sessionChangedId(ssoId, session, (String) event.getData()); + break; + + case Session.SESSION_DESTROYED_EVENT: + sso.sessionDestroyed(ssoId, session); + break; + } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/SingleSignOnSessionKey.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOnSessionKey.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/SingleSignOnSessionKey.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/SingleSignOnSessionKey.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,6 +41,13 @@ this.hostName = context.getParent().getName(); } + public SingleSignOnSessionKey(Session session, String sessionId) { + this.sessionId = sessionId; + Context context = session.getManager().getContext(); + this.contextName = context.getName(); + this.hostName = context.getParent().getName(); + } + public String getSessionId() { return sessionId; } @@ -90,13 +97,10 @@ return false; } if (hostName == null) { - if (other.hostName != null) { - return false; - } - } else if (!hostName.equals(other.hostName)) { - return false; + return other.hostName == null; + } else { + return hostName.equals(other.hostName); } - return true; } @Override @@ -104,14 +108,6 @@ // Session ID is 32. Standard text is 36. Host could easily be 20+. // Context could be anything from 0 upwards. 128 seems like a reasonable // size to accommodate most cases without being too big. - StringBuilder sb = new StringBuilder(128); - sb.append("Host: ["); - sb.append(hostName); - sb.append("], Context: ["); - sb.append(contextName); - sb.append("], SessionID: ["); - sb.append(sessionId); - sb.append(']'); - return sb.toString(); + return "Host: [" + hostName + "], Context: [" + contextName + "], SessionID: [" + sessionId + ']'; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/SpnegoAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -86,7 +86,7 @@ } public void setNoKeepAliveUserAgents(String noKeepAliveUserAgents) { - if (noKeepAliveUserAgents == null || noKeepAliveUserAgents.length() == 0) { + if (noKeepAliveUserAgents == null || noKeepAliveUserAgents.isEmpty()) { this.noKeepAliveUserAgents = null; } else { this.noKeepAliveUserAgents = Pattern.compile(noKeepAliveUserAgents); @@ -135,7 +135,17 @@ @Override protected boolean doAuthenticate(Request request, HttpServletResponse response) throws IOException { - if (checkForCachedAuthentication(request, response, true)) { + /* + * Reauthentication using the cached user name and password (if any) is not enabled for SPNEGO authentication. + * This is because the delegated credentials will nto be available unless a normal SPNEGO authentication takes + * place. + * + * Reauthentication was introduced to handle the case where the Realm took additional actions on authentication. + * Reauthenticating with the cached user name and password may not be sufficient for SPNEGO since it will not + * make the delegated credentials available that a web application may depend on. Therefore, the + * reauthentication behaviour for SPNEGO is to perform a normal SPNEGO authentication. + */ + if (checkForCachedAuthentication(request, response, false)) { return true; } @@ -184,8 +194,8 @@ LoginContext lc = null; GSSContext gssContext = null; - byte[] outToken = null; - Principal principal = null; + byte[] outToken; + Principal principal; try { try { lc = new LoginContext(getLoginConfigName()); @@ -299,10 +309,10 @@ /** * This class implements a hack around an incompatibility between the SPNEGO implementation in Windows and the * SPNEGO implementation in Java 8 update 40 onwards. It was introduced by the change to fix this bug: - * https://bugs.openjdk.java.net/browse/JDK-8048194 (note: the change applied is not the one suggested in the bug - * report) + * JDK-8048194 (note: the change applied is not the + * one suggested in the bug report) *

      - * It is not clear to me if Windows, Java or Tomcat is at fault here. I think it is Java but I could be wrong. + * It is not clear to me if Windows, Java or Tomcat is at fault here. I think it is Java, but I could be wrong. *

      * This hack works by re-ordering the list of mechTypes in the NegTokenInit token. */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/AuthConfigFactoryImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,18 +1,18 @@ -/** - * 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 +/* + * 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 + * 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. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.apache.catalina.authenticator.jaspic; @@ -62,7 +62,7 @@ private static final String SERVLET_LAYER_ID = "HttpServlet"; - private static String DEFAULT_REGISTRATION_ID = getRegistrationID(null, null); + private static final String DEFAULT_REGISTRATION_ID = getRegistrationID(null, null); private final Map layerAppContextRegistrations = new ConcurrentHashMap<>(); private final Map appContextRegistrations = new ConcurrentHashMap<>(); @@ -122,7 +122,7 @@ private AuthConfigProvider createAuthConfigProvider(String className, Map properties) throws SecurityException { Class clazz = null; - AuthConfigProvider provider = null; + AuthConfigProvider provider; try { clazz = Class.forName(className, true, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { @@ -158,14 +158,14 @@ private void addRegistrationContextImpl(String layer, String appContext, String registrationID, RegistrationContextImpl registrationContextImpl) { - RegistrationContextImpl previous = null; + RegistrationContextImpl previous; // Add the registration, noting any registration it replaces if (layer != null && appContext != null) { previous = layerAppContextRegistrations.put(registrationID, registrationContextImpl); } else if (layer == null && appContext != null) { previous = appContextRegistrations.put(registrationID, registrationContextImpl); - } else if (layer != null && appContext == null) { + } else if (layer != null) { previous = layerRegistrations.put(registrationID, registrationContextImpl); } else { previous = defaultRegistration.put(registrationID, registrationContextImpl); @@ -360,10 +360,10 @@ private static String getRegistrationID(String layer, String appContext) { - if (layer != null && layer.length() == 0) { + if (layer != null && layer.isEmpty()) { throw new IllegalArgumentException(sm.getString("authConfigFactoryImpl.zeroLengthMessageLayer")); } - if (appContext != null && appContext.length() == 0) { + if (appContext != null && appContext.isEmpty()) { throw new IllegalArgumentException(sm.getString("authConfigFactoryImpl.zeroLengthAppContext")); } return (layer == null ? "" : layer) + ":" + (appContext == null ? "" : appContext); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/CallbackHandlerImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -126,7 +126,7 @@ mergedRoles.addAll(Arrays.asList(groups)); } - if (mergedRoles.size() == 0) { + if (mergedRoles.isEmpty()) { mergedRoles = Collections.emptyList(); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,4 +18,6 @@ authConfigFactoryImpl.zeroLengthAppContext=Название контекста приложения нулевой длины является недействительным +persistentProviderRegistrations.deleteFail=Временный файл [{0}] не может быть удален persistentProviderRegistrations.existsDeleteFail=Временный файл [{0}] уже существует и не может быть удалён +persistentProviderRegistrations.moveFail=Невозможно переместить [{0}] в [{1}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/PersistentProviderRegistrations.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/PersistentProviderRegistrations.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/PersistentProviderRegistrations.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/PersistentProviderRegistrations.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ -/** +/* * 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. @@ -141,12 +141,12 @@ writer.write(" \n"); } writer.write("\n"); - } catch (IOException e) { + } catch (IOException ioe) { if (!configFileNew.delete()) { Log log = LogFactory.getLog(PersistentProviderRegistrations.class); log.warn(sm.getString("persistentProviderRegistrations.deleteFail", configFileNew.getAbsolutePath())); } - throw new SecurityException(e); + throw new SecurityException(ioe); } // Move the current file out of the way @@ -244,7 +244,7 @@ /** * Used by IntrospectionUtils via reflection. * - * @param name - the name of of the property to set on this object + * @param name - the name of the property to set on this object * @param value - the value to set * * @see #addProperty(String, String) diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/SimpleAuthConfigProvider.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/SimpleAuthConfigProvider.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/SimpleAuthConfigProvider.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/SimpleAuthConfigProvider.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ -/** +/* * 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. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthConfig.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ -/** +/* * 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. @@ -39,7 +39,7 @@ */ public class SimpleServerAuthConfig implements ServerAuthConfig { - private static StringManager sm = StringManager.getManager(SimpleServerAuthConfig.class); + private static final StringManager sm = StringManager.getManager(SimpleServerAuthConfig.class); private static final String SERVER_AUTH_MODULE_KEY_PREFIX = "org.apache.catalina.authenticator.jaspic.ServerAuthModule."; @@ -116,9 +116,7 @@ module.initialize(null, null, handler, mergedProperties); modules.add(module); } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) { - AuthException ae = new AuthException(); - ae.initCause(e); - throw ae; + throw new AuthException(e); } // Look for the next module @@ -127,7 +125,7 @@ moduleClassName = mergedProperties.get(key); } - if (modules.size() == 0) { + if (modules.isEmpty()) { throw new AuthException(sm.getString("simpleServerAuthConfig.noModules")); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthContext.java tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthContext.java --- tomcat10-10.1.34/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/authenticator/jaspic/SimpleServerAuthContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ -/** +/* * 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. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/ClientAbortException.java tomcat10-10.1.52/java/org/apache/catalina/connector/ClientAbortException.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/ClientAbortException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/ClientAbortException.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Extend IOException to identify it as being caused by an abort of a request by a remote client. - * - * @author Glenn L. Nielsen */ public final class ClientAbortException extends BadRequestException { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/Connector.java tomcat10-10.1.52/java/org/apache/catalina/connector/Connector.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/Connector.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/Connector.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,22 +29,22 @@ import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleState; import org.apache.catalina.Service; -import org.apache.catalina.core.AprStatus; import org.apache.catalina.util.LifecycleMBeanBase; import org.apache.coyote.AbstractProtocol; import org.apache.coyote.Adapter; import org.apache.coyote.ProtocolHandler; import org.apache.coyote.UpgradeProtocol; import org.apache.coyote.http11.AbstractHttp11JsseProtocol; -import org.apache.coyote.http11.AbstractHttp11Protocol; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jni.AprStatus; import org.apache.tomcat.util.IntrospectionUtils; import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.CharsetUtil; import org.apache.tomcat.util.buf.EncodedSolidusHandling; import org.apache.tomcat.util.buf.StringUtils; import org.apache.tomcat.util.compat.JreCompat; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.net.SSLHostConfig; import org.apache.tomcat.util.net.openssl.OpenSSLImplementation; import org.apache.tomcat.util.net.openssl.OpenSSLStatus; @@ -53,9 +53,6 @@ /** * Implementation of a Coyote connector. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class Connector extends LifecycleMBeanBase { @@ -64,6 +61,22 @@ public static final String INTERNAL_EXECUTOR_NAME = "Internal"; + private static final boolean aprStatusPresent; + + static { + /* + * The AprStatus class has to be in the org.apache.tomcat.jni package so it can be referenced by the OpenSSL + * clean-up code to avoid a race condition on shutdown between the AprLifecycleListener shutting down the Tomcat + * Native library along with any remaining open connections and the OpenSSL clean-up code shutting down an + * individual connection that can trigger a JVM crash. + * + * In some deployment scenarios AprStatus is not present - e.g. because tomcat-jni.jar is not present. To avoid + * a CNFE in this class on Connector initialisation when AprStatus is not present - and ugly work-arounds that + * try loading the class and catching the exception - use getResource() to see if AprStatus is present. + */ + aprStatusPresent = + (Connector.class.getClassLoader().getResource("org/apache/tomcat/jni/AprStatus.class") != null); + } // ------------------------------------------------------------ Constructor @@ -212,6 +225,10 @@ */ protected int maxParameterCount = 10000; + private int maxPartCount = 50; + + private int maxPartHeaderSize = 512; + /** * Maximum size of a POST which will be automatically parsed by the container. 2 MiB by default. */ @@ -227,7 +244,7 @@ * Comma-separated list of HTTP methods that will be parsed according to POST-style rules for * application/x-www-form-urlencoded request bodies. */ - protected String parseBodyMethods = "POST"; + protected String parseBodyMethods = Method.POST; /** * A Set of methods determined by {@link #parseBodyMethods}. @@ -272,7 +289,13 @@ /** - * The behavior when an encoded solidus (slash) is submitted. + * The behavior when an encoded reverse solidus (backslash - \) is submitted. + */ + private EncodedSolidusHandling encodedReverseSolidusHandling = EncodedSolidusHandling.DECODE; + + + /** + * The behavior when an encoded solidus (slash - /) is submitted. */ private EncodedSolidusHandling encodedSolidusHandling = EncodedSolidusHandling.REJECT; @@ -476,6 +499,26 @@ } + public int getMaxPartCount() { + return maxPartCount; + } + + + public void setMaxPartCount(int maxPartCount) { + this.maxPartCount = maxPartCount; + } + + + public int getMaxPartHeaderSize() { + return maxPartHeaderSize; + } + + + public void setMaxPartHeaderSize(int maxPartHeaderSize) { + this.maxPartHeaderSize = maxPartHeaderSize; + } + + /** * @return the maximum size of a POST which will be automatically parsed by the container. */ @@ -535,7 +578,7 @@ methodSet.addAll(Arrays.asList(StringUtils.splitCommaSeparated(methods))); } - if (methodSet.contains("TRACE")) { + if (methodSet.contains(Method.TRACE)) { throw new IllegalArgumentException(sm.getString("coyoteConnector.parseBodyMethodNoTrace")); } @@ -658,7 +701,7 @@ */ public void setProxyName(String proxyName) { - if (proxyName != null && proxyName.length() > 0) { + if (proxyName != null && !proxyName.isEmpty()) { this.proxyName = proxyName; } else { this.proxyName = null; @@ -869,6 +912,21 @@ } + public String getEncodedReverseSolidusHandling() { + return encodedReverseSolidusHandling.getValue(); + } + + + public void setEncodedReverseSolidusHandling(String encodedReverseSolidusHandling) { + this.encodedReverseSolidusHandling = EncodedSolidusHandling.fromString(encodedReverseSolidusHandling); + } + + + public EncodedSolidusHandling getEncodedReverseSolidusHandlingInternal() { + return encodedReverseSolidusHandling; + } + + public String getEncodedSolidusHandling() { return encodedSolidusHandling.getValue(); } @@ -949,7 +1007,7 @@ } else if (addressObj != null) { address = addressObj.toString(); } - if (address.length() > 0) { + if (!address.isEmpty()) { sb.append(",address="); sb.append(ObjectName.quote(address)); } @@ -1004,25 +1062,21 @@ setParseBodyMethods(getParseBodyMethods()); } - if (JreCompat.isJre22Available() && OpenSSLStatus.getUseOpenSSL() && OpenSSLStatus.isAvailable() && - protocolHandler instanceof AbstractHttp11Protocol) { - // Use FFM and OpenSSL if available - AbstractHttp11JsseProtocol jsseProtocolHandler = (AbstractHttp11JsseProtocol) protocolHandler; - if (jsseProtocolHandler.isSSLEnabled() && jsseProtocolHandler.getSslImplementationName() == null) { - // OpenSSL is compatible with the JSSE configuration, so use it if it is available - jsseProtocolHandler - .setSslImplementationName("org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation"); - } - } else if (AprStatus.isAprAvailable() && AprStatus.getUseOpenSSL() && - protocolHandler instanceof AbstractHttp11Protocol) { - // Use tomcat-native and OpenSSL otherwise, if available + if (protocolHandler instanceof AbstractHttp11JsseProtocol) { AbstractHttp11JsseProtocol jsseProtocolHandler = (AbstractHttp11JsseProtocol) protocolHandler; if (jsseProtocolHandler.isSSLEnabled() && jsseProtocolHandler.getSslImplementationName() == null) { - // OpenSSL is compatible with the JSSE configuration, so use it if APR is available - jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName()); + // If SSL is enabled and a specific implementation isn't specified, select the correct default. + if (JreCompat.isJre22Available() && OpenSSLStatus.getUseOpenSSL() && OpenSSLStatus.isAvailable()) { + // Use FFM and OpenSSL if available + jsseProtocolHandler.setSslImplementationName( + "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation"); + } else if (aprStatusPresent && AprStatus.isAprAvailable() && AprStatus.getUseOpenSSL()) { + // Use tomcat-native and OpenSSL otherwise, if available + jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName()); + } + // Otherwise the default JSSE will be used } } - // Otherwise the default JSSE will be used try { protocolHandler.init(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteAdapter.java tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteAdapter.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteAdapter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteAdapter.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,6 +48,7 @@ import org.apache.tomcat.util.buf.CharChunk; import org.apache.tomcat.util.buf.HexUtils; import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.ServerCookie; import org.apache.tomcat.util.http.ServerCookies; import org.apache.tomcat.util.net.SSLSupport; @@ -57,9 +58,6 @@ /** * Implementation of a request processor which delegates the processing to a Coyote processor. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class CoyoteAdapter implements Adapter { @@ -137,7 +135,7 @@ } } else if (status == SocketEvent.ERROR) { // An I/O error occurred on a non-container thread which means - // that the socket needs to be closed so set success to false to + // that the socket needs to be closed so set success as false to // trigger a close success = false; Throwable t = (Throwable) req.getAttribute(RequestDispatcher.ERROR_EXCEPTION); @@ -255,14 +253,14 @@ if (error.get()) { if (request.isAsyncCompleting() || request.isAsyncDispatching()) { // Connection will be forcibly closed which will prevent completion/dispatch happening at the usual - // point. Trigger post processing here. + // point. Trigger post-processing here. res.action(ActionCode.ASYNC_POST_PROCESS, null); } success = false; } - } catch (IOException e) { + } catch (IOException ioe) { + // Issues that should be logged will have already been logged success = false; - // Ignore } catch (Throwable t) { ExceptionUtils.handleThrowable(t); success = false; @@ -373,8 +371,8 @@ response.finishResponse(); } - } catch (IOException e) { - // Ignore + } catch (IOException ignore) { + // Issues that should be logged will have already been logged } finally { AtomicBoolean error = new AtomicBoolean(false); res.action(ActionCode.IS_ERROR, error); @@ -595,7 +593,7 @@ // Check for ping OPTIONS * request if (undecodedURI.equals("*")) { - if (req.method().equals("OPTIONS")) { + if (Method.OPTIONS.equals(req.getMethod())) { StringBuilder allow = new StringBuilder(); allow.append("GET, HEAD, POST, PUT, DELETE, OPTIONS"); // Trace if allowed @@ -614,7 +612,7 @@ MessageBytes decodedURI = req.decodedURI(); // Filter CONNECT method - if (req.method().equals("CONNECT")) { + if (Method.CONNECT.equals(req.getMethod())) { response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, sm.getString("coyoteAdapter.connect")); } else { // No URI for CONNECT requests @@ -635,7 +633,8 @@ // %xx decoding of the URL try { req.getURLDecoder().convert(decodedURI.getByteChunk(), - connector.getEncodedSolidusHandlingInternal()); + connector.getEncodedSolidusHandlingInternal(), + connector.getEncodedReverseSolidusHandlingInternal()); } catch (IOException ioe) { response.sendError(400, sm.getString("coyoteAdapter.invalidURIWithMessage", ioe.getMessage())); } @@ -644,7 +643,7 @@ // Character decoding convertURI(decodedURI, request); // URIEncoding values are limited to US-ASCII supersets. - // Therefore it is not necessary to check that the URI remains + // Therefore, it is not necessary to check that the URI remains // normalized after character decoding } else { response.sendError(400, sm.getString("coyoteAdapter.invalidURI")); @@ -652,18 +651,17 @@ } else { /* * The URI is chars or String, and has been sent using an in-memory protocol handler. The following - * assumptions are made: - req.requestURI() has been set to the 'original' non-decoded, non-normalized - * URI - req.decodedURI() has been set to the decoded, normalized form of req.requestURI() - - * 'suspicious' URI filtering - if required - has already been performed + * assumptions are made: + * + * - req.requestURI() has been set to the 'original' non-decoded, non-normalized URI that includes path + * parameters (if any) + * + * - req.decodedURI() has been set to the decoded, normalized form of req.requestURI() with any path + * parameters removed + * + * - 'suspicious' URI filtering, if required, has already been performed */ decodedURI.toChars(); - // Remove all path parameters; any needed path parameter should be set - // using the request object rather than passing it in the URL - CharChunk uriCC = decodedURI.getCharChunk(); - int semicolon = uriCC.indexOf(';'); - if (semicolon > 0) { - decodedURI.setChars(uriCC.getBuffer(), uriCC.getStart(), semicolon); - } } } @@ -693,7 +691,7 @@ } while (mapRequired) { - // This will map the the latest version by default + // This will map the latest version by default connector.getService().getMapper().map(serverName, decodedURI, version, request.getMappingData()); // If there is no context at this point, either this is a 404 @@ -782,8 +780,8 @@ // point. try { Thread.sleep(1000); - } catch (InterruptedException e) { - // Should never happen + } catch (InterruptedException ignore) { + // Should never happen but, if it does, just continue looping } // Reset mapping request.getMappingData().recycle(); @@ -813,26 +811,26 @@ } // Filter TRACE method - if (!connector.getAllowTrace() && req.method().equals("TRACE")) { + if (!connector.getAllowTrace() && Method.TRACE.equals(req.getMethod())) { Wrapper wrapper = request.getWrapper(); - String header = null; + StringBuilder header = null; if (wrapper != null) { String[] methods = wrapper.getServletMethods(); if (methods != null) { for (String method : methods) { - if ("TRACE".equals(method)) { + if (Method.TRACE.equals(method)) { continue; } if (header == null) { - header = method; + header = new StringBuilder(method); } else { - header += ", " + method; + header.append(", ").append(method); } } } } if (header != null) { - res.addHeader("Allow", header); + res.addHeader("Allow", header.toString()); } response.sendError(405, sm.getString("coyoteAdapter.trace")); // Safe to skip the remainder of this method. @@ -868,7 +866,7 @@ // result preventing excessive calls to the Realm. } else { // The connector isn't configured for authorization. Create a - // user without any roles using the supplied user name. + // user without any roles using the supplied username. request.setUserPrincipal(new CoyotePrincipal(username)); } } @@ -1075,7 +1073,7 @@ /** - * Character conversion of the a US-ASCII MessageBytes. + * Character conversion of the US-ASCII MessageBytes. * * @param mb The MessageBytes instance containing the bytes that should be converted to chars */ @@ -1125,15 +1123,13 @@ return false; } - int pos = 0; - int index = 0; - - // The URL must start with '/' (or '\' that will be replaced soon) if (b[start] != (byte) '/' && b[start] != (byte) '\\') { return false; } + int pos; + // Replace '\' with '/' // Check for null byte for (pos = start; pos < end; pos++) { @@ -1171,7 +1167,7 @@ uriBC.setEnd(end); - index = 0; + int index = 0; // Resolve occurrences of "/./" in the normalized path while (true) { @@ -1238,11 +1234,10 @@ byte[] bytes = undecodedURI.getBytes(); int start = undecodedURI.getStart(); int end = undecodedURI.getEnd(); - int segmentStart = -1; - int segmentEnd = -1; // Find first segment - segmentStart = undecodedURI.indexOf('/', 0); + int segmentStart = undecodedURI.indexOf('/', 0); + int segmentEnd = -1; if (segmentStart > -1) { segmentEnd = undecodedURI.indexOf('/', segmentStart + 1); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteInputStream.java tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteInputStream.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteInputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteInputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,6 @@ /** * This class handles reading bytes. - * - * @author Remy Maucherat */ public class CoyoteInputStream extends ServletInputStream { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteOutputStream.java tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteOutputStream.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteOutputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteOutputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,9 +27,6 @@ /** * Coyote implementation of the servlet output stream. - * - * @author Costin Manolache - * @author Remy Maucherat */ public class CoyoteOutputStream extends ServletOutputStream { @@ -81,8 +78,7 @@ try { ob.writeByte(i); } catch (IOException ioe) { - ob.setErrorException(ioe); - throw ioe; + handleIOException(ioe); } if (nonBlocking) { checkRegisterForWrite(); @@ -102,8 +98,7 @@ try { ob.write(b, off, len); } catch (IOException ioe) { - ob.setErrorException(ioe); - throw ioe; + handleIOException(ioe); } if (nonBlocking) { checkRegisterForWrite(); @@ -117,8 +112,7 @@ try { ob.write(from); } catch (IOException ioe) { - ob.setErrorException(ioe); - throw ioe; + handleIOException(ioe); } if (nonBlocking) { checkRegisterForWrite(); @@ -135,8 +129,7 @@ try { ob.flush(); } catch (IOException ioe) { - ob.setErrorException(ioe); - throw ioe; + handleIOException(ioe); } if (nonBlocking) { checkRegisterForWrite(); @@ -175,8 +168,7 @@ try { ob.close(); } catch (IOException ioe) { - ob.setErrorException(ioe); - throw ioe; + handleIOException(ioe); } } @@ -193,5 +185,29 @@ public void setWriteListener(WriteListener listener) { ob.setWriteListener(listener); } + + + private void handleIOException(IOException ioe) throws IOException { + try { + ob.setErrorException(ioe); + } catch (NullPointerException npe) { + /* + * Ignore. + * + * An IOException on a non-container thread during asynchronous Servlet processing will trigger a dispatch + * to a container thread that will complete the asynchronous processing and recycle the request, response + * and associated objects including the OutputBuffer. Depending on timing it is possible that the + * OutputBuffer will have been cleared by the time the call above is made - resulting in an NPE. + * + * If the OutputBuffer is null then there is no need to call setErrorException(). Catching and ignoring the + * NPE is (for now at least) a simpler solution than adding locking to OutputBuffer to ensure it is non-null + * and remains non-null while setErrorException() is called. + * + * The longer term solution is likely a refactoring and clean-up of error handling for asynchronous requests + * but that is potentially a significant piece of work. + */ + } + throw ioe; + } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/CoyotePrincipal.java tomcat10-10.1.52/java/org/apache/catalina/connector/CoyotePrincipal.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/CoyotePrincipal.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/CoyotePrincipal.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,6 @@ /** * Generic implementation of java.security.Principal that is used to represent principals authenticated * at the protocol handler level. - * - * @author Remy Maucherat */ public class CoyotePrincipal implements Principal, Serializable { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteReader.java tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteReader.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteReader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteReader.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,6 @@ /** * Coyote implementation of the buffered reader. - * - * @author Remy Maucherat */ public class CoyoteReader extends BufferedReader { @@ -116,12 +114,6 @@ @Override - public boolean markSupported() { - return true; - } - - - @Override public void mark(int readAheadLimit) throws IOException { ib.mark(readAheadLimit); } @@ -140,8 +132,6 @@ lineBuffer = new char[MAX_LINE_LENGTH]; } - String result = null; - int pos = 0; int end = -1; int skip = -1; @@ -192,6 +182,7 @@ } } + String result; if (aggregator == null) { result = new String(lineBuffer, 0, end); } else { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteWriter.java tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteWriter.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/CoyoteWriter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/CoyoteWriter.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,6 @@ /** * Coyote implementation of the servlet writer. - * - * @author Remy Maucherat */ public class CoyoteWriter extends PrintWriter { @@ -91,8 +89,8 @@ try { ob.flush(); - } catch (IOException e) { - setErrorException(e); + } catch (IOException ioe) { + setErrorException(ioe); } } @@ -105,7 +103,7 @@ // so the stream can be reused. We close ob. try { ob.close(); - } catch (IOException ex) { + } catch (IOException ignore) { // Ignore } error = false; @@ -129,15 +127,15 @@ try { ob.write(c); - } catch (IOException e) { - setErrorException(e); + } catch (IOException ioe) { + setErrorException(ioe); } } @Override - public void write(char buf[], int off, int len) { + public void write(char[] buf, int off, int len) { if (error) { return; @@ -145,15 +143,15 @@ try { ob.write(buf, off, len); - } catch (IOException e) { - setErrorException(e); + } catch (IOException ioe) { + setErrorException(ioe); } } @Override - public void write(char buf[]) { + public void write(char[] buf) { write(buf, 0, buf.length); } @@ -167,8 +165,8 @@ try { ob.write(s, off, len); - } catch (IOException e) { - setErrorException(e); + } catch (IOException ioe) { + setErrorException(ioe); } } @@ -224,7 +222,7 @@ @Override - public void print(char s[]) { + public void print(char[] s) { write(s); } @@ -293,7 +291,7 @@ @Override - public void println(char c[]) { + public void println(char[] c) { print(c); println(); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/InputBuffer.java tomcat10-10.1.52/java/org/apache/catalina/connector/InputBuffer.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/InputBuffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/InputBuffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,8 +48,6 @@ * The buffer used by Tomcat request. This is a derivative of the Tomcat 3.3 OutputBuffer, adapted to handle input * instead of output. This allows complete recycling of the facade objects (the ServletInputStream and the * BufferedReader). - * - * @author Remy Maucherat */ public class InputBuffer extends Reader implements ByteChunk.ByteInputChannel, ApplicationBufferHandler { @@ -60,6 +58,8 @@ private static final Log log = LogFactory.getLog(InputBuffer.class); + private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); + public static final int DEFAULT_BUFFER_SIZE = 8 * 1024; // The buffer can be used for byte[] and char[] reading @@ -76,8 +76,11 @@ // ----------------------------------------------------- Instance Variables - /** - * The byte buffer. + /* + * The byte buffer. Data is always injected into this class by calling {@link #setByteBuffer(ByteBuffer)} rather + * than copying data into any existing buffer. It is initialised to an empty buffer as there are code paths that + * access the buffer when it is expected to be empty and an empty buffer gives cleaner code than lots of null + * checks. */ private ByteBuffer bb; @@ -151,8 +154,8 @@ public InputBuffer(int size) { this.size = size; - bb = ByteBuffer.allocate(size); - clear(bb); + // Will be replaced when there is data to read so initialise to empty buffer. + bb = EMPTY_BUFFER; cb = CharBuffer.allocate(size); clear(cb); readLimit = size; @@ -191,7 +194,11 @@ } readLimit = size; markPos = -1; - clear(bb); + /* + * This buffer will have been replaced if there was data to read so re-initialise to an empty buffer to clear + * any reference to an injected buffer. + */ + bb = EMPTY_BUFFER; closed = false; if (conv != null) { @@ -273,7 +280,7 @@ // threads for each stream. For HTTP/2 the two operations have to be // performed atomically else it is possible for the connection thread to // read more data in to the buffer after the stream thread checks for - // available network data but before it registers for read. + // available network data, but before it registers for read. if (availableInThisBuffer() > 0) { return true; } @@ -612,20 +619,14 @@ private boolean checkByteBufferEof() throws IOException { if (bb.remaining() == 0) { - int n = realReadBytes(); - if (n < 0) { - return true; - } + return realReadBytes() < 0; } return false; } private boolean checkCharBufferEof() throws IOException { if (cb.remaining() == 0) { - int n = realReadChars(); - if (n < 0) { - return true; - } + return realReadChars() < 0; } return false; } @@ -660,7 +661,6 @@ tmp.flip(); tmp.position(oldPosition); cb = tmp; - tmp = null; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -62,6 +62,7 @@ coyoteRequest.noAsync=Unable to start async because the following classes in the processing chain do not support async [{0}] coyoteRequest.noMultipartConfig=Unable to process parts as no multi-part configuration has been provided coyoteRequest.parseParameters=Exception thrown whilst processing POSTed parameters +coyoteRequest.partsParseException=Exception [{0}] occurred parsing parts and will be thrown coyoteRequest.postTooLarge=Parameters were not parsed because the size of the posted data was too big. Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs. coyoteRequest.sendfileNotCanonical=Unable to determine canonical name of file [{0}] specified for use with sendfile coyoteRequest.sessionCreateCommitted=Cannot create a session after the response has been committed @@ -91,6 +92,7 @@ request.fragmentInDispatchPath=The fragment in dispatch path [{0}] has been removed request.illegalWrap=The request wrapper must wrap the request obtained from getRequest() request.notAsync=It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false) +request.partCleanup.failed=Unable to delete temporary file for uploaded part after multi-part processing failed request.session.failed=Failed to load session [{0}] due to [{1}] requestFacade.nullRequest=The request object has been recycled and is no longer associated with this facade diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_es.properties tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_es.properties --- tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_es.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_es.properties 2026-01-23 19:33:36.000000000 +0000 @@ -38,7 +38,7 @@ coyoteRequest.attributeEvent=Excepción lanzada mediante el escuchador de eventos de atributos coyoteRequest.authenticate.ise=No puedo llamar a authenticate() tras haberse acometido la respuesta coyoteRequest.changeSessionId=No se puede cambiar el ID de sesión. No hay sesión asociada con esta solicitud -coyoteRequest.chunkedPostTooLarge=No se han analizado los parámetros porque la medida de los datos enviados meiante "post" era demasiado grande. Debido a que este requerimiento es una parte del original, no puede ser procesado. Utiliza el atributo "maxPostSize" del conector para resolver esta situación, en caso de que la aplicación deba de aceptar POSTs mayores. +coyoteRequest.chunkedPostTooLarge=No se han analizado los parámetros porque la medida de los datos enviados meiante POST era demasiado grande. Debido a que este requerimiento es una parte del original, no puede ser procesado. Utiliza el atributo "maxPostSize" del conector para resolver esta situación, en caso de que la aplicación deba de aceptar POSTs mayores. coyoteRequest.filterAsyncSupportUnknown=Imposible determinar si algún filtro no soporta procesamiento asincrónico coyoteRequest.getInputStream.ise=getReader() ya ha sido llamado para este requerimiento coyoteRequest.getReader.ise=getInputStream() ya ha sido llamado para este requerimiento diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -62,6 +62,7 @@ coyoteRequest.noAsync=Impossible de démarrer le mode asynchrone car les classes [{0}] de la chaîne de traitement ne le supportent pas coyoteRequest.noMultipartConfig=Impossible de traiter des parties, parce qu'aucune configuration multi-parties n'a été fournie coyoteRequest.parseParameters=Exception lors du traitement des paramètres envoyés par POST +coyoteRequest.partsParseException=Une exception [{0}] s''est produite lors du traitement des portions du message et sera lancée coyoteRequest.postTooLarge=Les paramètres n'ont pas été évalués car la taille des données postées est trop important. Utilisez l'attribut maxPostSize du connecteur pour corriger ce problème si votre application doit accepter des POSTs importants. coyoteRequest.sendfileNotCanonical=Impossible d''obtenir le nom canonique du fichier [{0}] qui a été donné pour le sendfile coyoteRequest.sessionCreateCommitted=Impossible de créer une session après que la réponse ait été envoyée @@ -91,6 +92,7 @@ request.fragmentInDispatchPath=Le fragment dans le chemin de dispatch [{0}] a été enlevé request.illegalWrap=L'enrobeur de la réponse doit enrober la requête obtenue à partir de getRequest() request.notAsync=Il est interdit d'appeler cette méthode si la requête actuelle n'est pas en mode asynchrone (isAsyncStarted() a renvoyé false) +request.partCleanup.failed=Impossible d'effacer le fichier temporaire pour la partie envoyée après l'échec de traitement du multi partie request.session.failed=Erreur de chargement de la session [{0}] à cause de [{1}] requestFacade.nullRequest=L'objet requête a été recyclé et n'est plus associé à cette façade diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -62,8 +62,9 @@ coyoteRequest.noAsync=処理チェーン内の次のクラスが非同期をサポートしていないため、非同期を開始できません [{0}] coyoteRequest.noMultipartConfig=multi-part 構成が提供されていないため、partを処理できません coyoteRequest.parseParameters=POST パラメーターの処理中に例外を投げました。 +coyoteRequest.partsParseException=パートの解析中に例外 [{0}] が発生しました coyoteRequest.postTooLarge=POSTされたデータが大きすぎたので、パラメータが構文解析できませんでした。そのアプリケーションが巨大なPOSTを受け付けねばならない場合には、これを解決するためにコネクタのmaxPostSize属性を使用してください。 -coyoteRequest.sendfileNotCanonical=sendfile に指定されたファイル [{0}] の正式名を取得できません +coyoteRequest.sendfileNotCanonical=sendfile に指定されたファイル [{0}] の正規名を取得できません coyoteRequest.sessionCreateCommitted=レスポンスをコミットした後でセッションを作成できません coyoteRequest.sessionEndAccessFail=リクエストの再利用中に行ったセッションへのアクセス終了処理で例外が送出されました。 coyoteRequest.setAttribute.namenull=setAttributeを名前を指定せずに呼び出すことはできません @@ -91,6 +92,7 @@ request.fragmentInDispatchPath=ディスパッチパス [{0}] 中のフラグメントは除去されました request.illegalWrap=リクエストラッパーは getRequest() で取得したリクエストをラップしなければなりません。 request.notAsync=非同期モードではないリクエストでこのメソッドを呼び出すことはできません。(例えば isAsyncStarted() が false を返す場合) +request.partCleanup.failed=マルチパート処理が失敗した後、アップロードされたパートのテンポラリファイルを削除できませんでした request.session.failed=[{1}]が原因で、セッション[{0}]の読み込みに失敗しました requestFacade.nullRequest=リクエストオブジェクトは回収されこのファサードに関連付けられなくなりました。 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -25,5 +25,6 @@ coyoteInputStream.nbNotready=В неблокирующем режиме невозможно читать из ServletInputStream до тех пор пока не завершится предыдущее чтение и IsReady() не вернёт true +coyoteRequest.changeSessionId=Невозможно изменить ID сессии. Нет сессии связаной с этим запросом. coyoteRequest.sendfileNotCanonical=Невозможно определить каноническое имя файла [{0}] указанное для использования с sendfile coyoteRequest.sessionEndAccessFail=Исключение вызвало прекращение доступа к сессии при очистке запроса diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/connector/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -52,6 +52,7 @@ coyoteRequest.authenticate.ise=):提交响应后无法调用authenticate() coyoteRequest.changeSessionId=无法更改 session ID。 没有与此请求关联的 session。 coyoteRequest.chunkedPostTooLarge=由于请求参数数据太大,导致参数不能解析。因为当前请求是块状请求,后续也不会处理。如果应用程序需要接收大的POST请求,可以使用连接器的maxPostSize解决它。 +coyoteRequest.deletePartFailed=删除[{0}]部分的临时文件失败 coyoteRequest.filterAsyncSupportUnknown=无法确定是否有任何过滤器不支持异步处理 coyoteRequest.getContextPath.ise=找不到规范上下文路径[{0}]与用户代理[{1}]提供的URI之间的匹配项。 coyoteRequest.getInputStream.ise=已为此请求调用getReader() diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/OutputBuffer.java tomcat10-10.1.52/java/org/apache/catalina/connector/OutputBuffer.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/OutputBuffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/OutputBuffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,14 +37,12 @@ import org.apache.coyote.Response; import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.C2BConverter; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.res.StringManager; /** * The buffer used by Tomcat response. This is a derivative of the Tomcat 3.3 OutputBuffer, with the removal of some of * the state handling (which in Coyote is mostly the Processor's responsibility). - * - * @author Costin Manolache - * @author Remy Maucherat */ public class OutputBuffer extends Writer { @@ -238,16 +236,12 @@ // - the content length has not been explicitly set // AND // - some content has been written OR this is NOT a HEAD request - if ((!coyoteResponse.isCommitted()) && (coyoteResponse.getContentLengthLong() == -1) && - ((bb.remaining() > 0 || !coyoteResponse.getRequest().method().equals("HEAD")))) { + if (!coyoteResponse.isCommitted() && coyoteResponse.getContentLengthLong() == -1 && + (bb.remaining() > 0 || !Method.HEAD.equals(coyoteResponse.getRequest().getMethod()))) { coyoteResponse.setContentLength(bb.remaining()); } - if (coyoteResponse.getStatus() == HttpServletResponse.SC_SWITCHING_PROTOCOLS) { - doFlush(true); - } else { - doFlush(false); - } + doFlush(coyoteResponse.getStatus() == HttpServletResponse.SC_SWITCHING_PROTOCOLS); closed = true; // The request should have been completely read by the time the response @@ -333,24 +327,24 @@ try { coyoteResponse.doWrite(buf); } catch (CloseNowException e) { - // Catch this sub-class as it requires specific handling. + // Catch this subclass as it requires specific handling. // Examples where this exception is thrown: // - HTTP/2 stream timeout // Prevent further output for this response closed = true; throw e; - } catch (IOException e) { + } catch (IOException ioe) { // An IOException on a write is almost always due to // the remote client aborting the request. Wrap this // so that it can be handled better by the error dispatcher. - throw new ClientAbortException(e); + throw new ClientAbortException(ioe); } } } - public void write(byte b[], int off, int len) throws IOException { + public void write(byte[] b, int off, int len) throws IOException { if (suspended) { return; @@ -372,7 +366,7 @@ } - private void writeBytes(byte b[], int off, int len) throws IOException { + private void writeBytes(byte[] b, int off, int len) throws IOException { if (closed) { return; @@ -445,12 +439,12 @@ } if (from.remaining() > 0) { flushByteBuffer(); - } else if (conv.isUndeflow() && bb.limit() > bb.capacity() - 4) { + } else if (conv.isUnderflow() && bb.limit() > bb.capacity() - 4) { // Handle an edge case. There are no more chars to write at the // moment but there is a leftover character in the converter // which must be part of a surrogate pair. The byte buffer does // not have enough space left to output the bytes for this pair - // once it is complete )it will require 4 bytes) so flush now to + // once it is complete (it will require 4 bytes) so flush now to // prevent the bytes for the leftover char and the rest of the // surrogate pair yet to be written from being lost. // See TestOutputBuffer#testUtf8SurrogateBody() @@ -478,7 +472,7 @@ @Override - public void write(char c[]) throws IOException { + public void write(char[] c) throws IOException { if (suspended) { return; @@ -490,7 +484,7 @@ @Override - public void write(char c[], int off, int len) throws IOException { + public void write(char[] c, int off, int len) throws IOException { if (suspended) { return; @@ -667,7 +661,7 @@ * * @throws IOException Writing overflow data to the output channel failed */ - public void append(byte src[], int off, int len) throws IOException { + public void append(byte[] src, int off, int len) throws IOException { if (bb.remaining() == 0) { appendByteArray(src, off, len); } else { @@ -690,7 +684,7 @@ * * @throws IOException Writing overflow data to the output channel failed */ - public void append(char src[], int off, int len) throws IOException { + public void append(char[] src, int off, int len) throws IOException { // if we have limit and we're below if (len <= cb.capacity() - cb.limit()) { transfer(src, off, len, cb); @@ -741,7 +735,7 @@ } - private void appendByteArray(byte src[], int off, int len) throws IOException { + private void appendByteArray(byte[] src, int off, int len) throws IOException { if (len == 0) { return; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/Request.java tomcat10-10.1.52/java/org/apache/catalina/connector/Request.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/Request.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/Request.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,6 +35,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; @@ -110,6 +111,7 @@ import org.apache.tomcat.util.http.fileupload.FileItem; import org.apache.tomcat.util.http.fileupload.FileUpload; import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory; +import org.apache.tomcat.util.http.fileupload.impl.FileCountLimitExceededException; import org.apache.tomcat.util.http.fileupload.impl.InvalidContentTypeException; import org.apache.tomcat.util.http.fileupload.impl.SizeException; import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext; @@ -123,9 +125,6 @@ /** * Wrapper object for the Coyote request. - * - * @author Remy Maucherat - * @author Craig R. McClanahan */ public class Request implements HttpServletRequest { @@ -142,6 +141,11 @@ */ public Request(Connector connector) { this.connector = connector; + if (connector != null) { + maxParameterCount = connector.getMaxParameterCount(); + maxPartCount = connector.getMaxPartCount(); + maxPartHeaderSize = connector.getMaxPartHeaderSize(); + } } @@ -410,14 +414,22 @@ private HttpServletRequest applicationRequest = null; + /** + * The maximum number of request parameters + */ + private int maxParameterCount = -1; + + private int maxPartCount = -1; + + private int maxPartHeaderSize = -1; // --------------------------------------------------------- Public Methods - protected void addPathParameter(String name, String value) { + public void addPathParameter(String name, String value) { coyoteRequest.addPathParameter(name, value); } - protected String getPathParameter(String name) { + public String getPathParameter(String name) { return coyoteRequest.getPathParameter(name); } @@ -440,6 +452,15 @@ userPrincipal = null; subject = null; parametersParsed = false; + if (connector != null) { + maxParameterCount = connector.getMaxParameterCount(); + maxPartCount = connector.getMaxPartCount(); + maxPartHeaderSize = connector.getMaxPartHeaderSize(); + } else { + maxParameterCount = -1; + maxPartCount = -1; + maxPartHeaderSize = -1; + } if (parts != null) { for (Part part : parts) { try { @@ -504,7 +525,7 @@ } - protected void recycleSessionInfo() { + public void recycleSessionInfo() { if (session != null) { try { session.endAccess(); @@ -566,7 +587,7 @@ * this request */ public boolean getDiscardFacades() { - return (connector == null) ? true : connector.getDiscardFacades(); + return connector == null || connector.getDiscardFacades(); } @@ -828,6 +849,36 @@ } + /** + * Set the maximum number of request parameters (GET plus POST including multipart) for a single request. + * + * @param maxParameterCount The maximum number of request parameters + */ + public void setMaxParameterCount(int maxParameterCount) { + this.maxParameterCount = maxParameterCount; + } + + + /** + * Set the maximum number of parts for a single multipart request. + * + * @param maxPartCount The maximum number of request parts + */ + public void setMaxPartCount(int maxPartCount) { + this.maxPartCount = maxPartCount; + } + + + /** + * Set the maximum header size per part for a single multipart request. + * + * @param maxPartHeaderSize The maximum size of the headers for one part + */ + public void setMaxPartHeaderSize(int maxPartHeaderSize) { + this.maxPartHeaderSize = maxPartHeaderSize; + } + + // ------------------------------------------------- ServletRequest Methods @Override @@ -899,8 +950,8 @@ * {@inheritDoc} *

      * The attribute names returned will only be those for the attributes set via {@link #setAttribute(String, Object)}. - * Tomcat internal attributes will not be included although they are accessible via {@link #getAttribute(String)}. - * The Tomcat internal attributes include: + * Tomcat internal attributes will not be included even though they are accessible via + * {@link #getAttribute(String)}. The Tomcat internal attributes include: *

        *
      • {@link Globals#DISPATCHER_TYPE_ATTR}
      • *
      • {@link Globals#DISPATCHER_REQUEST_PATH_ATTR}
      • @@ -1020,7 +1071,7 @@ parseLocales(); } - if (locales.size() > 0) { + if (!locales.isEmpty()) { return locales.get(0); } @@ -1035,7 +1086,7 @@ parseLocales(); } - if (locales.size() > 0) { + if (!locales.isEmpty()) { return Collections.enumeration(locales); } ArrayList results = new ArrayList<>(); @@ -1280,7 +1331,7 @@ // Add the path info, if there is any String pathInfo = getPathInfo(); - String requestPath = null; + String requestPath; if (pathInfo == null) { requestPath = servletPath; @@ -1289,7 +1340,7 @@ } int pos = requestPath.lastIndexOf('/'); - String relative = null; + String relative; if (context.getDispatchersUseEncodedPaths()) { if (pos >= 0) { relative = URLEncoder.DEFAULT.encode(requestPath.substring(0, pos + 1), StandardCharsets.UTF_8) + path; @@ -1380,8 +1431,8 @@ String canonicalPath; try { canonicalPath = new File(value.toString()).getCanonicalPath(); - } catch (IOException e) { - throw new SecurityException(sm.getString("coyoteRequest.sendfileNotCanonical", value), e); + } catch (IOException ioe) { + throw new SecurityException(sm.getString("coyoteRequest.sendfileNotCanonical", value), ioe); } // Sendfile is performed in Tomcat's security context so need to // check if the web app is permitted to access the file while still @@ -1415,12 +1466,12 @@ if (context == null) { return; } - Object listeners[] = context.getApplicationEventListeners(); + Object[] listeners = context.getApplicationEventListeners(); if (listeners == null || listeners.length == 0) { return; } boolean replaced = (oldValue != null); - ServletRequestAttributeEvent event = null; + ServletRequestAttributeEvent event; if (replaced) { event = new ServletRequestAttributeEvent(context.getServletContext(), getRequest(), name, oldValue); } else { @@ -1456,7 +1507,7 @@ */ private void notifyAttributeRemoved(String name, Object value) { Context context = getContext(); - Object listeners[] = context.getApplicationEventListeners(); + Object[] listeners = context.getApplicationEventListeners(); if (listeners == null || listeners.length == 0) { return; } @@ -1610,11 +1661,7 @@ @Override public DispatcherType getDispatcherType() { - if (internalDispatcherType == null) { - return DispatcherType.REQUEST; - } - - return this.internalDispatcherType; + return Objects.requireNonNullElse(internalDispatcherType, DispatcherType.REQUEST); } @@ -1794,6 +1841,7 @@ * * @param principal The user Principal */ + @SuppressWarnings("deprecation") public void setUserPrincipal(final Principal principal) { if (Globals.IS_SECURITY_ENABLED && principal != null) { if (subject == null) { @@ -1934,8 +1982,8 @@ int pos = 0; if (!getContext().getAllowMultipleLeadingForwardSlashInPath()) { // Ensure that the returned value only starts with a single '/'. - // This prevents the value being misinterpreted as a protocol- - // relative URI if used with sendRedirect(). + // This prevents the value being misinterpreted as a protocol-relative + // URI if used with sendRedirect(). do { pos++; } while (pos < uri.length() && uri.charAt(pos) == '/'); @@ -1999,7 +2047,7 @@ return input; } StringBuilder result = new StringBuilder(input.length()); - result.append(input.substring(0, nextSemiColon)); + result.append(input, 0, nextSemiColon); while (true) { int nextSlash = input.indexOf('/', nextSemiColon); if (nextSlash == -1) { @@ -2010,7 +2058,7 @@ result.append(input.substring(nextSlash)); break; } else { - result.append(input.substring(nextSlash, nextSemiColon)); + result.append(input, nextSlash, nextSemiColon); } } @@ -2112,7 +2160,7 @@ @Override public String getMethod() { - return coyoteRequest.method().toStringType(); + return coyoteRequest.getMethod(); } @@ -2254,27 +2302,25 @@ Session session = null; try { session = manager.findSession(requestedSessionId); - } catch (IOException e) { - // Can't find the session + } catch (IOException ignore) { + // Error looking up session. Treat it as not found. } if ((session == null) || !session.isValid()) { // Check for parallel deployment contexts - if (getMappingData().contexts == null) { - return false; - } else { + if (getMappingData().contexts != null) { for (int i = (getMappingData().contexts.length); i > 0; i--) { Context ctxt = getMappingData().contexts[i - 1]; try { if (ctxt.getManager().findSession(requestedSessionId) != null) { return true; } - } catch (IOException e) { - // Ignore + } catch (IOException ignore) { + // Error looking up session. Treat it as not found. } } - return false; } + return false; } return true; @@ -2387,7 +2433,7 @@ public void changeSessionId(String newSessionId) { // This should only ever be called if there was an old session ID but // double check to be sure - if (requestedSessionId != null && requestedSessionId.length() > 0) { + if (requestedSessionId != null && !requestedSessionId.isEmpty()) { requestedSessionId = newSessionId; } @@ -2397,7 +2443,7 @@ return; } - if (response != null) { + if (response != null && context != null) { Cookie newCookie = ApplicationSessionCookieConfig.createSessionCookie(context, newSessionId, isSecure()); response.addSessionCookieInternal(newCookie); } @@ -2488,6 +2534,11 @@ parseParts(true); if (partsParseException != null) { + Context context = getContext(); + if (context != null && context.getLogger().isDebugEnabled()) { + context.getLogger() + .debug(sm.getString("coyoteRequest.partsParseException", partsParseException.getMessage())); + } if (partsParseException instanceof IOException) { throw (IOException) partsParseException; } else if (partsParseException instanceof IllegalStateException) { @@ -2518,15 +2569,33 @@ } else { if (explicit) { partsParseException = new IllegalStateException(sm.getString("coyoteRequest.noMultipartConfig")); - return; } else { parts = Collections.emptyList(); - return; } + return; } } - int maxParameterCount = getConnector().getMaxParameterCount(); + /* + * When the request body is multipart/form-data, both the parts and the query string count towards + * maxParameterCount. If parseParts() is called before getParameterXXX() then the parts will be parsed before + * the query string. Otherwise, the query string will be parsed first. + * + * maxParameterCount must be respected regardless of which is parsed first. + * + * maxParameterCount is reset from the Connector at the start of every request. + * + * If parts are parsed first, non-file parts will be added to the parameter map and any files will reduce + * maxParameterCount by 1 so that when the query string is parsed the difference between the size of the + * parameter map and maxParameterCount will be the original maxParameterCount less the number of parts. i.e. the + * maxParameterCount applied to the query string will be the original maxParameterCount less the number of + * parts. + * + * If the query string is parsed first, all parameters will be added to the parameter map and, ignoring + * maxPartCount, the part limit will be set to the original maxParameterCount less the size of the parameter + * map. i.e. the maxParameterCount applied to the parts will be the original maxParameterCount less the number + * of query parameters. + */ Parameters parameters = coyoteRequest.getParameters(); parameters.setLimit(maxParameterCount); @@ -2575,35 +2644,53 @@ upload.setFileItemFactory(factory); upload.setFileSizeMax(mce.getMaxFileSize()); upload.setSizeMax(mce.getMaxRequestSize()); - if (maxParameterCount > -1) { - // There is a limit. The limit for parts needs to be reduced by - // the number of parameters we have already parsed. - // Must be under the limit else parsing parameters would have - // triggered an exception. - upload.setFileCountMax(maxParameterCount - parameters.size()); + upload.setPartHeaderSizeMax(maxPartHeaderSize); + /* + * There are two independent limits on the number of parts. + * + * 1. The limit based on parameters. This is maxParameterCount less the number of parameters already + * processed. + * + * 2. The limit based on parts. This is maxPartCount. + * + * The lower of these two limits will be applied to this request. + * + * Note: Either of both limits may be set to -1 (unlimited). + */ + int partLimit = maxParameterCount; + if (partLimit > -1) { + partLimit = partLimit - parameters.size(); } + int maxPartCount = this.maxPartCount; + if (maxPartCount > -1) { + if (partLimit < 0 || partLimit > maxPartCount) { + partLimit = maxPartCount; + } + } + upload.setFileCountMax(partLimit); parts = new ArrayList<>(); + List items = null; try { - List items = upload.parseRequest(new ServletRequestContext(this)); + items = upload.parseRequest(new ServletRequestContext(this)); int maxPostSize = getConnector().getMaxPostSize(); - int postSize = 0; + long postSize = 0; Charset charset = getCharset(); for (FileItem item : items) { ApplicationPart part = new ApplicationPart(item, location); - parts.add(part); if (part.getSubmittedFileName() == null) { String name = part.getName(); if (maxPostSize >= 0) { // Have to calculate equivalent size. Not completely // accurate but close enough. - postSize += name.getBytes(charset).length; + // Name + postSize = Math.addExact(postSize, name.getBytes(charset).length); // Equals sign - postSize++; + postSize = Math.addExact(postSize, 1); // Value length - postSize += part.getSize(); + postSize = Math.addExact(postSize, part.getSize()); // Value separator - postSize++; + postSize = Math.addExact(postSize, 1); if (postSize > maxPostSize) { parameters.setParseFailedReason(FailReason.POST_TOO_LARGE); throw new IllegalStateException(sm.getString("coyoteRequest.maxPostSizeExceeded")); @@ -2616,24 +2703,46 @@ // Not possible } parameters.addParameter(name, value); + } else { + // Adjust the limit to account for a file part which is not added to the parameter map. + maxParameterCount--; } + parts.add(part); } success = true; } catch (InvalidContentTypeException e) { parameters.setParseFailedReason(FailReason.INVALID_CONTENT_TYPE); partsParseException = new ServletException(e); - } catch (SizeException e) { + } catch (SizeException | FileCountLimitExceededException e) { parameters.setParseFailedReason(FailReason.POST_TOO_LARGE); checkSwallowInput(); partsParseException = new IllegalStateException(e); - } catch (IOException e) { + } catch (IOException ioe) { parameters.setParseFailedReason(FailReason.IO_ERROR); - partsParseException = e; + partsParseException = ioe; } catch (IllegalStateException e) { // addParameters() will set parseFailedReason checkSwallowInput(); partsParseException = e; + } finally { + /* + * GC will delete any temporary copies of uploaded files left in the work directory but if we know that + * the upload has failed then explicitly clean up now. + */ + if (!success) { + parts.clear(); + if (items != null) { + for (FileItem item : items) { + try { + item.delete(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.warn(sm.getString("request.partCleanup.failed"), t); + } + } + } + } } } finally { // This might look odd but is correct. setParseFailedReason() only @@ -2684,11 +2793,11 @@ if (requestedSessionId != null) { try { session = manager.findSession(requestedSessionId); - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(sm.getString("request.session.failed", requestedSessionId, e.getMessage()), e); + log.debug(sm.getString("request.session.failed", requestedSessionId, ioe.getMessage()), ioe); } else { - log.info(sm.getString("request.session.failed", requestedSessionId, e.getMessage())); + log.info(sm.getString("request.session.failed", requestedSessionId, ioe.getMessage())); } session = null; } @@ -2697,6 +2806,8 @@ } if (session != null) { session.access(); + // The client has chosen to join the session + session.setNew(false); return session; } } @@ -2720,7 +2831,7 @@ } else if (("/".equals(context.getSessionCookiePath()) && isRequestedSessionIdFromCookie())) { /* * This is the common(ish) use case: using the same session ID with multiple web applications on the same - * host. Typically this is used by Portlet implementations. It only works if sessions are tracked via + * host. Typically, this is used by Portlet implementations. It only works if sessions are tracked via * cookies. The cookie must have a path of "/" else it won't be provided for requests to all web * applications. * @@ -2737,9 +2848,8 @@ found = true; break; } - } catch (IOException e) { - // Ignore. Problems with this manager will be - // handled elsewhere. + } catch (IOException ignore) { + // Error looking up session. Treat it as not found. } } } @@ -2849,7 +2959,7 @@ scookie.getValue().getByteChunk().setCharset(getCookieProcessor().getCharset()); cookie.setValue(unescape(scookie.getValue().toString())); cookies[idx++] = cookie; - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException ignore) { // Ignore bad cookie } } @@ -2868,14 +2978,30 @@ parametersParsed = true; + /* + * When the request body is multipart/form-data, both the parts and the query string count towards + * maxParameterCount. If parseParts() is called before getParameterXXX() then the parts will be parsed before + * the query string. Otherwise, the query string will be parsed first. + * + * maxParameterCount must be respected regardless of which is parsed first. + * + * maxParameterCount is reset from the Connector at the start of every request. + * + * If parts are parsed first, non-file parts will be added to the parameter map and any files will reduce + * maxParameterCount by 1 so that when the query string is parsed the difference between the size of the + * parameter map and maxParameterCount will be the original maxParameterCount less the number of parts. i.e. the + * maxParameterCount applied to the query string will be the original maxParameterCount less the number of + * parts. + * + * If the query string is parsed first, all parameters will be added to the parameter map and, ignoring + * maxPartCount, the part limit will be set to the original maxParameterCount less the size of the parameter + * map. i.e. the maxParameterCount applied to the parts will be the original maxParameterCount less the number + * of query parameters. + */ Parameters parameters = coyoteRequest.getParameters(); boolean success = false; try { // Set this every time in case limit has been changed via JMX - int maxParameterCount = getConnector().getMaxParameterCount(); - if (parts != null && maxParameterCount > 0) { - maxParameterCount -= parts.size(); - } parameters.setLimit(maxParameterCount); // getCharacterEncoding() may have been overridden to search for @@ -2939,17 +3065,18 @@ } try { readPostBodyFully(formData, len); - } catch (IOException e) { + } catch (IOException ioe) { // Client disconnect Context context = getContext(); if (context != null && context.getLogger().isDebugEnabled()) { - context.getLogger().debug(sm.getString("coyoteRequest.parseParameters"), e); + context.getLogger().debug(sm.getString("coyoteRequest.parseParameters"), ioe); } parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT); return; } parameters.processParameters(formData, 0, len); - } else if ("chunked".equalsIgnoreCase(coyoteRequest.getHeader("transfer-encoding"))) { + } else if (coyoteRequest.protocol().equals("HTTP/2.0") || + "chunked".equalsIgnoreCase(coyoteRequest.getHeader("transfer-encoding"))) { byte[] formData = null; try { formData = readChunkedPostBody(); @@ -2961,12 +3088,12 @@ context.getLogger().debug(sm.getString("coyoteRequest.parseParameters"), ise); } return; - } catch (IOException e) { + } catch (IOException ioe) { // Client disconnect parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT); Context context = getContext(); if (context != null && context.getLogger().isDebugEnabled()) { - context.getLogger().debug(sm.getString("coyoteRequest.parseParameters"), e); + context.getLogger().debug(sm.getString("coyoteRequest.parseParameters"), ioe); } return; } @@ -3111,7 +3238,7 @@ List acceptLanguages; try { acceptLanguages = AcceptLanguage.parse(new StringReader(value)); - } catch (IOException e) { + } catch (IOException ioe) { // Mal-formed headers are ignore. Do the same in the unlikely event // of an IOException. return; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/RequestFacade.java tomcat10-10.1.52/java/org/apache/catalina/connector/RequestFacade.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/RequestFacade.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/RequestFacade.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,9 +49,6 @@ /** * Facade class that wraps a Coyote request object. All methods are delegated to the wrapped request. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class RequestFacade implements HttpServletRequest { @@ -208,7 +205,7 @@ /** * The wrapped request. */ - protected Request request = null; + protected Request request; /** diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/Response.java tomcat10-10.1.52/java/org/apache/catalina/connector/Response.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/Response.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/Response.java 2026-01-23 19:33:36.000000000 +0000 @@ -67,9 +67,6 @@ /** * Wrapper object for the Coyote response. - * - * @author Remy Maucherat - * @author Craig R. McClanahan */ public class Response implements HttpServletResponse { @@ -714,11 +711,7 @@ log.warn(sm.getString("coyoteResponse.encoding.invalid", encoding), e); return; } - if (encoding == null) { - isCharacterEncodingSet = false; - } else { - isCharacterEncodingSet = true; - } + isCharacterEncodingSet = encoding != null; } @@ -886,7 +879,7 @@ @Override public void addDateHeader(String name, long value) { - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { return; } @@ -911,7 +904,7 @@ private void addHeader(String name, String value, Charset charset) { - if (name == null || name.length() == 0 || value == null) { + if (name == null || name.isEmpty() || value == null) { return; } @@ -954,7 +947,7 @@ @Override public void addIntHeader(String name, int value) { - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { return; } @@ -1194,7 +1187,7 @@ @Override public void setDateHeader(String name, long value) { - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { return; } @@ -1214,7 +1207,7 @@ @Override public void setHeader(String name, String value) { - if (name == null || name.length() == 0 || value == null) { + if (name == null || name.isEmpty() || value == null) { return; } @@ -1241,7 +1234,7 @@ @Override public void setIntHeader(String name, int value) { - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { return; } @@ -1328,7 +1321,7 @@ private static boolean doIsEncodeable(Context context, Request hreq, Session session, String location) { // Is this a valid absolute URL? - URL url = null; + URL url; try { URI uri = new URI(location); url = uri.toURL(); @@ -1370,9 +1363,7 @@ return false; } String tok = ";" + SessionConfig.getSessionUriParamName(context) + "=" + session.getIdInternal(); - if (file.indexOf(tok, contextPath.length()) >= 0) { - return false; - } + return file.indexOf(tok, contextPath.length()) < 0; } // This URL belongs to our web application, so it is encodeable @@ -1395,7 +1386,7 @@ protected String toAbsolute(String location) { if (location == null) { - return location; + return null; } boolean leadingSlash = location.startsWith("/"); @@ -1410,8 +1401,8 @@ redirectURLCC.append(':'); redirectURLCC.append(location, 0, location.length()); return redirectURLCC.toString(); - } catch (IOException e) { - throw new IllegalArgumentException(location, e); + } catch (IOException ioe) { + throw new IllegalArgumentException(location, ioe); } } else if (leadingSlash || !UriUtil.hasScheme(location)) { @@ -1452,8 +1443,8 @@ redirectURLCC.append(location, 0, location.length()); normalize(redirectURLCC); - } catch (IOException e) { - throw new IllegalArgumentException(location, e); + } catch (IOException ioe) { + throw new IllegalArgumentException(location, ioe); } return redirectURLCC.toString(); @@ -1487,15 +1478,14 @@ if (cc.endsWith("/.") || cc.endsWith("/..")) { try { cc.append('/'); - } catch (IOException e) { - throw new IllegalArgumentException(cc.toString(), e); + } catch (IOException ioe) { + throw new IllegalArgumentException(cc.toString(), ioe); } } char[] c = cc.getChars(); int start = cc.getStart(); int end = cc.getEnd(); - int index = 0; int startIndex = 0; // Advance past the first three / characters (should place index just @@ -1506,7 +1496,7 @@ } // Remove /./ - index = startIndex; + int index = startIndex; while (true) { index = cc.indexOf("/./", 0, 3, index); if (index < 0) { @@ -1569,10 +1559,7 @@ return false; } pos = uri.indexOf('/', pos + 3); - if (pos < 0) { - return false; - } - return true; + return pos >= 0; } /** diff -Nru tomcat10-10.1.34/java/org/apache/catalina/connector/ResponseFacade.java tomcat10-10.1.52/java/org/apache/catalina/connector/ResponseFacade.java --- tomcat10-10.1.34/java/org/apache/catalina/connector/ResponseFacade.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/connector/ResponseFacade.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,8 +37,6 @@ /** * Facade class that wraps a Coyote response object. All methods are delegated to the wrapped response. - * - * @author Remy Maucherat */ public class ResponseFacade implements HttpServletResponse { @@ -122,7 +120,7 @@ /** * The wrapped response. */ - protected Response response = null; + protected Response response; // --------------------------------------------------------- Public Methods diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/AccessLogAdapter.java tomcat10-10.1.52/java/org/apache/catalina/core/AccessLogAdapter.java --- tomcat10-10.1.34/java/org/apache/catalina/core/AccessLogAdapter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/AccessLogAdapter.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,7 +37,7 @@ public void add(AccessLog log) { Objects.requireNonNull(log); - AccessLog newArray[] = Arrays.copyOf(logs, logs.length + 1); + AccessLog[] newArray = Arrays.copyOf(logs, logs.length + 1); newArray[newArray.length - 1] = log; logs = newArray; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationContext.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationContext.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -83,9 +83,6 @@ /** * Standard implementation of ServletContext that represents a web application's execution environment. An * instance of this class is associated with each instance of StandardContext. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class ApplicationContext implements ServletContext { @@ -161,7 +158,7 @@ /** * Session Cookie config */ - private SessionCookieConfig sessionCookieConfig; + private final SessionCookieConfig sessionCookieConfig; /** * Session tracking modes @@ -200,7 +197,7 @@ return null; } - Context child = null; + Context child; try { // Look for an exact match Container host = context.getParent(); @@ -314,7 +311,7 @@ return null; } String extension = file.substring(period + 1); - if (extension.length() < 1) { + if (extension.isEmpty()) { return null; } return context.findMimeMapping(extension); @@ -372,36 +369,32 @@ queryString = null; } + // From this point, the removal of path parameters, decoding and normalization is only for mapping purposes. // Remove path parameters - String uriNoParams = stripPathParams(uri); + String uriToMap = org.apache.catalina.util.RequestUtil.stripPathParams(uri, null); + + // Decode only if the uri derived from the provided path is expected to be encoded + if (getContext().getDispatchersUseEncodedPaths()) { + uriToMap = UDecoder.URLDecode(uriToMap, StandardCharsets.UTF_8, context.getEncodedSolidusHandlingEnum(), + context.getEncodedReverseSolidusHandlingEnum()); + } // Then normalize - String normalizedUri = RequestUtil.normalize(uriNoParams); - if (normalizedUri == null) { + uriToMap = RequestUtil.normalize(uriToMap); + if (uriToMap == null) { + getContext().getLogger().warn(sm.getString("applicationContext.illegalDispatchPath", path), + new IllegalArgumentException()); return null; } - // Mapping is against the normalized uri - + /* + * uri is passed to the constructor for ApplicationDispatcher and is ultimately used as the value for + * getRequestURI() which returns encoded values. getContextPath() returns a decoded value. uri may be encoded or + * not. Need to prepend the context path to uri and ensure the result is correctly encoded. + */ if (getContext().getDispatchersUseEncodedPaths()) { - // Decode - String decodedUri = UDecoder.URLDecode(normalizedUri, StandardCharsets.UTF_8); - - // Security check to catch attempts to encode /../ sequences - normalizedUri = RequestUtil.normalize(decodedUri); - if (!decodedUri.equals(normalizedUri)) { - getContext().getLogger().warn(sm.getString("applicationContext.illegalDispatchPath", path), - new IllegalArgumentException()); - return null; - } - - // URI needs to include the context path uri = URLEncoder.DEFAULT.encode(getContextPath(), StandardCharsets.UTF_8) + uri; } else { - // uri is passed to the constructor for ApplicationDispatcher and is - // ultimately used as the value for getRequestURI() which returns - // encoded values. Therefore, since the value passed in for path - // was decoded, encode uri here. uri = URLEncoder.DEFAULT.encode(getContextPath() + uri, StandardCharsets.UTF_8); } @@ -422,7 +415,7 @@ CharChunk uriCC = uriMB.getCharChunk(); try { uriCC.append(context.getPath()); - uriCC.append(normalizedUri); + uriCC.append(uriToMap); service.getMapper().map(context, uriMB, mappingData); if (mappingData.wrapper == null) { return null; @@ -453,34 +446,6 @@ } - // Package private to facilitate testing - static String stripPathParams(String input) { - // Shortcut - if (input.indexOf(';') < 0) { - return input; - } - - StringBuilder sb = new StringBuilder(input.length()); - int pos = 0; - int limit = input.length(); - while (pos < limit) { - int nextSemiColon = input.indexOf(';', pos); - if (nextSemiColon < 0) { - nextSemiColon = limit; - } - sb.append(input.substring(pos, nextSemiColon)); - int followingSlash = input.indexOf('/', nextSemiColon); - if (followingSlash < 0) { - pos = limit; - } else { - pos = followingSlash; - } - } - - return sb.toString(); - } - - @Override public URL getResource(String path) throws MalformedURLException { @@ -584,20 +549,18 @@ @Override public void removeAttribute(String name) { - Object value = null; - // Remove the specified attribute // Check for read only attribute if (readOnlyAttributes.containsKey(name)) { return; } - value = attributes.remove(name); + Object value = attributes.remove(name); if (value == null) { return; } // Notify interested application event listeners - Object listeners[] = context.getApplicationEventListeners(); + Object[] listeners = context.getApplicationEventListeners(); if (listeners == null || listeners.length == 0) { return; } @@ -643,11 +606,11 @@ boolean replaced = oldValue != null; // Notify interested application event listeners - Object listeners[] = context.getApplicationEventListeners(); + Object[] listeners = context.getApplicationEventListeners(); if (listeners == null || listeners.length == 0) { return; } - ServletContextAttributeEvent event = null; + ServletContextAttributeEvent event; if (replaced) { event = new ServletContextAttributeEvent(context.getServletContext(), name, oldValue); } else { @@ -703,7 +666,7 @@ private FilterRegistration.Dynamic addFilter(String filterName, String filterClass, Filter filter) throws IllegalStateException { - if (filterName == null || filterName.equals("")) { + if (filterName == null || filterName.isEmpty()) { throw new IllegalArgumentException(sm.getString("applicationContext.invalidFilterName", filterName)); } @@ -786,11 +749,11 @@ throw new IllegalArgumentException(sm.getString("applicationContext.addJspFile.iae", jspFile)); } - String jspServletClassName = null; Map jspFileInitParams = new HashMap<>(); Wrapper jspServlet = (Wrapper) context.findChild("jsp"); + String jspServletClassName; if (jspServlet == null) { // No JSP servlet currently defined. // Use default JSP Servlet class name @@ -816,7 +779,7 @@ private ServletRegistration.Dynamic addServlet(String servletName, String servletClass, Servlet servlet, Map initParams) throws IllegalStateException { - if (servletName == null || servletName.equals("")) { + if (servletName == null || servletName.isEmpty()) { throw new IllegalArgumentException(sm.getString("applicationContext.invalidServletName", servletName)); } @@ -913,7 +876,7 @@ supportedSessionTrackingModes.add(SessionTrackingMode.COOKIE); } - // SSL not enabled by default as it can only used on its own + // SSL not enabled by default as it can only be used on its own // Context > Host > Engine > Service Connector[] connectors = service.findConnectors(); // Need at least one SSL enabled connector to use the SSL session ID. @@ -954,7 +917,7 @@ } } - // Check SSL has not be configured with anything else + // Check SSL has not been configured with anything else if (sessionTrackingModes.contains(SessionTrackingMode.SSL)) { if (sessionTrackingModes.size() > 1) { throw new IllegalArgumentException( diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationContextFacade.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationContextFacade.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationContextFacade.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationContextFacade.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,8 +51,6 @@ /** * Facade object which masks the internal ApplicationContext object from the web application. - * - * @author Remy Maucherat */ public class ApplicationContextFacade implements ServletContext { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationDispatcher.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationDispatcher.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationDispatcher.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationDispatcher.java 2026-01-23 19:33:36.000000000 +0000 @@ -56,8 +56,6 @@ * resource. This implementation allows application level servlets to wrap the request and/or response objects that are * passed on to the called resource, as long as the wrapping classes extend * jakarta.servlet.ServletRequestWrapper and jakarta.servlet.ServletResponseWrapper. - * - * @author Craig R. McClanahan */ final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher { @@ -124,13 +122,13 @@ /** * The outermost request that will be passed on to the invoked servlet. */ - ServletRequest outerRequest = null; + ServletRequest outerRequest; /** * The outermost response that will be passed on to the invoked servlet. */ - ServletResponse outerResponse = null; + ServletResponse outerResponse; /** * The request wrapper we have created and installed (if any). @@ -146,7 +144,7 @@ /** * Are we performing an include() instead of a forward()? */ - boolean including = false; + boolean including; /** * Outermost HttpServletRequest in the chain @@ -275,11 +273,7 @@ if (response.isCommitted()) { throw new IllegalStateException(sm.getString("applicationDispatcher.forward.ise")); } - try { - response.resetBuffer(); - } catch (IllegalStateException e) { - throw e; - } + response.resetBuffer(); // Set up to handle the specified request and response State state = new State(request, response, false); @@ -290,25 +284,20 @@ } wrapResponse(state); - // Handle an HTTP named dispatcher forward + ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state); + HttpServletRequest hrequest = state.hrequest; if (servletPath == null && pathInfo == null) { + // Handle an HTTP named dispatcher forward - ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state); - HttpServletRequest hrequest = state.hrequest; wrequest.setRequestURI(hrequest.getRequestURI()); wrequest.setContextPath(hrequest.getContextPath()); wrequest.setServletPath(hrequest.getServletPath()); wrequest.setPathInfo(hrequest.getPathInfo()); wrequest.setQueryString(hrequest.getQueryString()); - processRequest(request, response, state); - } - - // Handle an HTTP path-based forward - else { + } else { + // Handle an HTTP path-based forward - ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state); - HttpServletRequest hrequest = state.hrequest; if (hrequest.getAttribute(FORWARD_REQUEST_URI) == null) { wrequest.setAttribute(FORWARD_REQUEST_URI, hrequest.getRequestURI()); wrequest.setAttribute(FORWARD_CONTEXT_PATH, hrequest.getContextPath()); @@ -328,8 +317,8 @@ } wrequest.setMapping(mapping); - processRequest(request, response, state); } + processRequest(request, response, state); if (request.isAsyncStarted()) { // An async request was started during the forward, don't close the @@ -358,7 +347,7 @@ } if (!finished) { // Servlet SRV.6.2.2. The Request/Response may have been wrapped - // and may no longer be instance of RequestFacade + // and may no longer be an instance of RequestFacade if (wrapper.getLogger().isDebugEnabled()) { wrapper.getLogger().debug(sm.getString("applicationDispatcher.customResponse", response.getClass())); } @@ -373,7 +362,7 @@ } catch (IllegalStateException | IOException f) { // Ignore } - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } @@ -396,13 +385,7 @@ DispatcherType disInt = (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR); if (disInt != null) { - boolean doInvoke = true; - - if (context.getFireRequestListenersOnForwards() && !context.fireRequestInitEvent(request)) { - doInvoke = false; - } - - if (doInvoke) { + if (!context.getFireRequestListenersOnForwards() || context.fireRequestInitEvent(request)) { if (disInt != DispatcherType.ERROR) { state.outerRequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, getCombinedPath()); state.outerRequest.setAttribute(Globals.DISPATCHER_TYPE_ATTR, DispatcherType.FORWARD); @@ -468,23 +451,17 @@ // Create a wrapped response to use for this request wrapResponse(state); - // Handle an HTTP named dispatcher include + ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state); if (name != null) { + // Handle an HTTP named dispatcher include - ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state); wrequest.setAttribute(Globals.NAMED_DISPATCHER_ATTR, name); if (servletPath != null) { wrequest.setServletPath(servletPath); } - wrequest.setAttribute(Globals.DISPATCHER_TYPE_ATTR, DispatcherType.INCLUDE); - wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, getCombinedPath()); - invoke(state.outerRequest, state.outerResponse, state); - } - - // Handle an HTTP path based include - else { + } else { + // Handle an HTTP path based include - ApplicationHttpRequest wrequest = (ApplicationHttpRequest) wrapRequest(state); String contextPath = context.getPath(); if (requestURI != null) { wrequest.setAttribute(INCLUDE_REQUEST_URI, requestURI); @@ -506,10 +483,10 @@ wrequest.setAttribute(INCLUDE_MAPPING, mapping); } - wrequest.setAttribute(Globals.DISPATCHER_TYPE_ATTR, DispatcherType.INCLUDE); - wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, getCombinedPath()); - invoke(state.outerRequest, state.outerResponse, state); } + wrequest.setAttribute(Globals.DISPATCHER_TYPE_ATTR, DispatcherType.INCLUDE); + wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, getCombinedPath()); + invoke(state.outerRequest, state.outerResponse, state); } @@ -615,12 +592,12 @@ wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException", wrapper.getName()), StandardWrapper.getRootCause(e)); servletException = e; - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException", wrapper.getName()), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + wrapper.getLogger().error(sm.getString("applicationDispatcher.allocateException", wrapper.getName()), t); servletException = - new ServletException(sm.getString("applicationDispatcher.allocateException", wrapper.getName()), e); - servlet = null; + new ServletException(sm.getString("applicationDispatcher.allocateException", wrapper.getName()), t); + // servlet = null; is already done so no need to do it explicitly } // Get the FilterChain Here @@ -635,9 +612,9 @@ // Servlet Service Method is called by the FilterChain } catch (BadRequestException | CloseNowException e) { ioException = e; - } catch (IOException e) { - wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException", wrapper.getName()), e); - ioException = e; + } catch (IOException ioe) { + wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException", wrapper.getName()), ioe); + ioException = ioe; } catch (UnavailableException e) { wrapper.getLogger().error(sm.getString("applicationDispatcher.serviceException", wrapper.getName()), e); servletException = e; @@ -667,11 +644,11 @@ } catch (ServletException e) { wrapper.getLogger().error(sm.getString("applicationDispatcher.deallocateException", wrapper.getName()), e); servletException = e; - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - wrapper.getLogger().error(sm.getString("applicationDispatcher.deallocateException", wrapper.getName()), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + wrapper.getLogger().error(sm.getString("applicationDispatcher.deallocateException", wrapper.getName()), t); servletException = new ServletException( - sm.getString("applicationDispatcher.deallocateException", wrapper.getName()), e); + sm.getString("applicationDispatcher.deallocateException", wrapper.getName()), t); } // Reset the old context class loader @@ -810,14 +787,12 @@ } // Instantiate a new wrapper at this point and insert it in the chain - ServletRequest wrapper = null; - if (current instanceof ApplicationHttpRequest || current instanceof Request || - current instanceof HttpServletRequest) { + ServletRequest wrapper; + if (current instanceof HttpServletRequest) { // Compute a crossContext flag HttpServletRequest hcurrent = (HttpServletRequest) current; boolean crossContext = false; - if (state.outerRequest instanceof ApplicationHttpRequest || state.outerRequest instanceof Request || - state.outerRequest instanceof HttpServletRequest) { + if (state.outerRequest instanceof HttpServletRequest) { HttpServletRequest houterRequest = (HttpServletRequest) state.outerRequest; Object contextPath = houterRequest.getAttribute(INCLUDE_CONTEXT_PATH); if (contextPath == null) { @@ -870,9 +845,8 @@ } // Instantiate a new wrapper at this point and insert it in the chain - ServletResponse wrapper = null; - if (current instanceof ApplicationHttpResponse || current instanceof Response || - current instanceof HttpServletResponse) { + ServletResponse wrapper; + if (current instanceof HttpServletResponse) { wrapper = new ApplicationHttpResponse((HttpServletResponse) current, state.including); } else { wrapper = new ApplicationResponse(current, state.including); @@ -891,7 +865,7 @@ ServletRequest originalRequest = ApplicationFilterChain.getLastServicedRequest(); ServletResponse originalResponse = ApplicationFilterChain.getLastServicedResponse(); - // Some forwards, eg from valves will not set original values + // Some forwards, e.g. from valves will not set original values if (originalRequest == null || originalResponse == null) { return; } @@ -905,7 +879,7 @@ originalRequest = ((ServletRequestWrapper) originalRequest).getRequest(); } // compare with the dispatched request - while (!same) { + while (true) { if (originalRequest.equals(dispatchedRequest)) { same = true; } @@ -928,7 +902,7 @@ originalResponse = ((ServletResponseWrapper) originalResponse).getResponse(); } // compare with the dispatched response - while (!same) { + while (true) { if (originalResponse.equals(dispatchedResponse)) { same = true; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationFilterChain.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationFilterChain.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationFilterChain.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationFilterChain.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,8 +40,6 @@ * Implementation of jakarta.servlet.FilterChain used to manage the execution of a set of filters for a * particular request. When the set of defined filters has all been executed, the next call to doFilter() * will execute the servlet's service() method itself. - * - * @author Craig R. McClanahan */ public final class ApplicationFilterChain implements FilterChain { @@ -165,10 +163,10 @@ } } catch (IOException | ServletException | RuntimeException e) { throw e; - } catch (Throwable e) { - e = ExceptionUtils.unwrapInvocationTargetException(e); - ExceptionUtils.handleThrowable(e); - throw new ServletException(sm.getString("filterChain.filter"), e); + } catch (Throwable t) { + t = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(t); + throw new ServletException(sm.getString("filterChain.filter"), t); } return; } @@ -196,10 +194,10 @@ } } catch (IOException | ServletException | RuntimeException e) { throw e; - } catch (Throwable e) { - e = ExceptionUtils.unwrapInvocationTargetException(e); - ExceptionUtils.handleThrowable(e); - throw new ServletException(sm.getString("filterChain.servlet"), e); + } catch (Throwable t) { + t = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(t); + throw new ServletException(sm.getString("filterChain.servlet"), t); } finally { if (dispatcherWrapsSameObject) { lastServicedRequest.set(null); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationFilterConfig.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationFilterConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationFilterConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationFilterConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,8 +49,6 @@ /** * Implementation of a jakarta.servlet.FilterConfig useful in managing the filter instances instantiated * when a web application is first started. - * - * @author Craig R. McClanahan */ public final class ApplicationFilterConfig implements FilterConfig, Serializable { @@ -179,13 +177,8 @@ @Override public String toString() { - StringBuilder sb = new StringBuilder("ApplicationFilterConfig["); - sb.append("name="); - sb.append(filterDef.getFilterName()); - sb.append(", filterClass="); - sb.append(filterDef.getFilterClass()); - sb.append(']'); - return sb.toString(); + return "ApplicationFilterConfig[" + "name=" + filterDef.getFilterName() + ", filterClass=" + + filterDef.getFilterClass() + ']'; } // --------------------------------------------------------- Public Methods @@ -237,7 +230,7 @@ filter.init(this); } finally { String capturedlog = SystemLogHandler.stopCapture(); - if (capturedlog != null && capturedlog.length() > 0) { + if (capturedlog != null && !capturedlog.isEmpty()) { getServletContext().log(capturedlog); } } @@ -310,7 +303,7 @@ String domain = context.getParent().getParent().getName(); String webMod = "//" + hostName + parentName; - String onameStr = null; + String onameStr; String filterName = filterDef.getFilterName(); if (Util.objectNameValueNeedsQuote(filterName)) { filterName = ObjectName.quote(filterName); @@ -324,9 +317,9 @@ } try { oname = new ObjectName(onameStr); - Registry.getRegistry(null, null).registerComponent(this, oname, null); - } catch (Exception ex) { - log.warn(sm.getString("applicationFilterConfig.jmxRegisterFail", getFilterClass(), getFilterName()), ex); + Registry.getRegistry(null).registerComponent(this, oname, null); + } catch (Exception e) { + log.warn(sm.getString("applicationFilterConfig.jmxRegisterFail", getFilterClass(), getFilterName()), e); } } @@ -335,13 +328,13 @@ // unregister this component if (oname != null) { try { - Registry.getRegistry(null, null).unregisterComponent(oname); + Registry.getRegistry(null).unregisterComponent(oname); if (log.isDebugEnabled()) { log.debug(sm.getString("applicationFilterConfig.jmxUnregister", getFilterClass(), getFilterName())); } - } catch (Exception ex) { + } catch (Exception e) { log.warn(sm.getString("applicationFilterConfig.jmxUnregisterFail", getFilterClass(), getFilterName()), - ex); + e); } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationFilterFactory.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationFilterFactory.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationFilterFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationFilterFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,9 +31,6 @@ /** * Factory for the creation and caching of Filters and creation of Filter Chains. - * - * @author Greg Murray - * @author Remy Maucherat */ public final class ApplicationFilterFactory { @@ -62,7 +59,7 @@ } // Create and initialize a filter chain object - ApplicationFilterChain filterChain = null; + ApplicationFilterChain filterChain; if (request instanceof Request) { Request req = (Request) request; if (Globals.IS_SECURITY_ENABLED) { @@ -86,7 +83,7 @@ // Acquire the filter mappings for this Context StandardContext context = (StandardContext) wrapper.getParent(); filterChain.setDispatcherWrapsSameObject(context.getDispatcherWrapsSameObject()); - FilterMap filterMaps[] = context.findFilterMaps(); + FilterMap[] filterMaps = context.findFilterMaps(); // If there are no filter mappings, we are done if (filterMaps == null || filterMaps.length == 0) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationHttpRequest.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationHttpRequest.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationHttpRequest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationHttpRequest.java 2026-01-23 19:33:36.000000000 +0000 @@ -63,9 +63,6 @@ * WARNING: Due to Java's lack of support for multiple inheritance, all of the logic in * ApplicationRequest is duplicated in ApplicationHttpRequest. Make sure that you keep these * two classes in synchronization when making changes! - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ class ApplicationHttpRequest extends HttpServletRequestWrapper { @@ -74,7 +71,7 @@ /** * The set of attribute names that are special for request dispatchers. */ - protected static final String specials[] = + protected static final String[] specials = { RequestDispatcher.INCLUDE_REQUEST_URI, RequestDispatcher.INCLUDE_CONTEXT_PATH, RequestDispatcher.INCLUDE_SERVLET_PATH, RequestDispatcher.INCLUDE_PATH_INFO, RequestDispatcher.INCLUDE_QUERY_STRING, RequestDispatcher.INCLUDE_MAPPING, @@ -93,7 +90,7 @@ } private static final int shortestSpecialNameLength = - specialsMap.keySet().stream().mapToInt(s -> s.length()).min().getAsInt(); + specialsMap.keySet().stream().mapToInt(String::length).min().getAsInt(); private static final int SPECIALS_FIRST_FORWARD_INDEX = 6; @@ -346,7 +343,7 @@ // Add the path info, if there is any String pathInfo = getPathInfo(); - String requestPath = null; + String requestPath; if (pathInfo == null) { requestPath = servletPath; @@ -355,7 +352,7 @@ } int pos = requestPath.lastIndexOf('/'); - String relative = null; + String relative; if (context.getDispatchersUseEncodedPaths()) { if (pos >= 0) { relative = URLEncoder.DEFAULT.encode(requestPath.substring(0, pos + 1), StandardCharsets.UTF_8) + path; @@ -550,7 +547,7 @@ if (localSession != null && !localSession.isValid()) { localSession = null; } - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } if (localSession == null && create) { @@ -596,14 +593,10 @@ Session session = null; try { session = manager.findSession(requestedSessionId); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } - if ((session != null) && session.isValid()) { - return true; - } else { - return false; - } + return (session != null) && session.isValid(); } else { return super.isRequestedSessionIdValid(); @@ -732,7 +725,12 @@ return; } - parameters = new ParameterMap<>(getRequest().getParameterMap()); + Map requestParameters = getRequest().getParameterMap(); + if (requestParameters instanceof ParameterMap) { + parameters = new ParameterMap<>((ParameterMap) requestParameters); + } else { + parameters = new ParameterMap<>(requestParameters); + } mergeParameters(); ((ParameterMap) parameters).setLocked(true); parsedParams = true; @@ -825,17 +823,13 @@ */ private String[] mergeValues(String[] values1, String[] values2) { - List results = new ArrayList<>(); + List results = new ArrayList<>(); - if (values1 == null) { - // Skip - nothing to merge - } else { + if (values1 != null) { results.addAll(Arrays.asList(values1)); } - if (values2 == null) { - // Skip - nothing to merge - } else { + if (values2 != null) { results.addAll(Arrays.asList(values2)); } @@ -852,7 +846,7 @@ */ private void mergeParameters() { - if ((queryParamString == null) || (queryParamString.length() < 1)) { + if ((queryParamString == null) || (queryParamString.isEmpty())) { return; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationHttpResponse.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationHttpResponse.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationHttpResponse.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationHttpResponse.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,8 +33,6 @@ * WARNING: Due to Java's lack of support for multiple inheritance, all of the logic in * ApplicationResponse is duplicated in ApplicationHttpResponse. Make sure that you keep these * two classes in synchronization when making changes! - * - * @author Craig R. McClanahan */ class ApplicationHttpResponse extends HttpServletResponseWrapper { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationPushBuilder.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationPushBuilder.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationPushBuilder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationPushBuilder.java 2026-01-23 19:33:36.000000000 +0000 @@ -293,7 +293,7 @@ org.apache.coyote.Request pushTarget = new org.apache.coyote.Request(); - pushTarget.method().setString(method); + pushTarget.setMethod(method); // The next three are implied by the Javadoc getPath() pushTarget.serverName().setString(baseRequest.getServerName()); pushTarget.setServerPort(baseRequest.getServerPort()); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationRequest.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationRequest.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationRequest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationRequest.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,8 +36,6 @@ * WARNING: Due to Java's lack of support for multiple inheritance, all of the logic in * ApplicationRequest is duplicated in ApplicationHttpRequest. Make sure that you keep these * two classes in synchronization when making changes! - * - * @author Craig R. McClanahan */ class ApplicationRequest extends ServletRequestWrapper { @@ -59,8 +57,7 @@ */ private static final Set specialsSet = new HashSet<>(Arrays.asList(specials)); - private static final int shortestSpecialNameLength = - specialsSet.stream().mapToInt(s -> s.length()).min().getAsInt(); + private static final int shortestSpecialNameLength = specialsSet.stream().mapToInt(String::length).min().getAsInt(); /** diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationResponse.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationResponse.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationResponse.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationResponse.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,6 @@ * WARNING: Due to Java's lack of support for multiple inheritance, all of the logic in * ApplicationResponse is duplicated in ApplicationHttpResponse. Make sure that you keep these * two classes in synchronization when making changes! - * - * @author Craig R. McClanahan */ class ApplicationResponse extends ServletResponseWrapper { @@ -66,7 +64,7 @@ /** - * Disallow reset() calls on a included response. + * Disallow reset() calls on an included response. * * @exception IllegalStateException if the response has already been committed */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationSessionCookieConfig.java tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationSessionCookieConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ApplicationSessionCookieConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ApplicationSessionCookieConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,7 +43,7 @@ private final Map attributes = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private String name; - private StandardContext context; + private final StandardContext context; public ApplicationSessionCookieConfig(StandardContext context) { this.context = context; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/AprLifecycleListener.java tomcat10-10.1.52/java/org/apache/catalina/core/AprLifecycleListener.java --- tomcat10-10.1.34/java/org/apache/catalina/core/AprLifecycleListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/AprLifecycleListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,6 +19,7 @@ import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.locks.Lock; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; @@ -33,7 +34,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * Implementation of LifecycleListener that will init and and destroy APR. + * Implementation of LifecycleListener that will init and destroy APR. *

        * This listener must only be nested within {@link Server} elements. *

        @@ -63,12 +64,17 @@ // ---------------------------------------------- Constants - protected static final int TCN_REQUIRED_MAJOR = 1; - protected static final int TCN_REQUIRED_MINOR = 2; - protected static final int TCN_REQUIRED_PATCH = 34; + private static final int TCN_1_REQUIRED_MINOR = 3; + private static final int TCN_1_REQUIRED_PATCH = 4; + private static final int TCN_1_RECOMMENDED_MINOR = 3; + private static final int TCN_1_RECOMMENDED_PATCH = 4; + + protected static final int TCN_REQUIRED_MAJOR = 2; + protected static final int TCN_REQUIRED_MINOR = 0; + protected static final int TCN_REQUIRED_PATCH = 12; protected static final int TCN_RECOMMENDED_MAJOR = 2; protected static final int TCN_RECOMMENDED_MINOR = 0; - protected static final int TCN_RECOMMENDED_PV = 5; + protected static final int TCN_RECOMMENDED_PV = 12; // ---------------------------------------------- Properties @@ -99,25 +105,27 @@ private static final int FIPS_OFF = 0; - protected static final Object lock = new Object(); - - // Guarded by lock + // Guarded APRStatus.getStatusLock() private static int referenceCount = 0; private boolean instanceInitialized = false; public static boolean isAprAvailable() { // https://bz.apache.org/bugzilla/show_bug.cgi?id=48613 - if (AprStatus.isInstanceCreated()) { - synchronized (lock) { + if (org.apache.tomcat.jni.AprStatus.isInstanceCreated()) { + Lock writeLock = org.apache.tomcat.jni.AprStatus.getStatusLock().writeLock(); + writeLock.lock(); + try { init(); + } finally { + writeLock.unlock(); } } - return AprStatus.isAprAvailable(); + return org.apache.tomcat.jni.AprStatus.isAprAvailable(); } public AprLifecycleListener() { - AprStatus.setInstanceCreated(true); + org.apache.tomcat.jni.AprStatus.setInstanceCreated(true); } // ---------------------------------------------- LifecycleListener Methods @@ -131,7 +139,9 @@ public void lifecycleEvent(LifecycleEvent event) { if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType())) { - synchronized (lock) { + Lock writeLock = org.apache.tomcat.jni.AprStatus.getStatusLock().writeLock(); + writeLock.lock(); + try { instanceInitialized = true; if (!(event.getLifecycle() instanceof Server)) { log.warn(sm.getString("listener.notServer", event.getLifecycle().getClass().getSimpleName())); @@ -145,26 +155,29 @@ log.info(msg); } initInfoLogMessages.clear(); - if (AprStatus.isAprAvailable()) { + if (org.apache.tomcat.jni.AprStatus.isAprAvailable()) { try { initializeSSL(); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("aprListener.sslInit"), t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + log.error(sm.getString("aprListener.sslInit"), throwable); } } // Failure to initialize FIPS mode is fatal if (!(null == FIPSMode || "off".equalsIgnoreCase(FIPSMode)) && !isFIPSModeActive()) { - String errorMessage = sm.getString("aprListener.initializeFIPSFailed"); - Error e = new Error(errorMessage); + Error e = new Error(sm.getString("aprListener.initializeFIPSFailed")); // Log here, because thrown error might be not logged - log.fatal(errorMessage, e); + log.fatal(e.getMessage(), e); throw e; } + } finally { + writeLock.unlock(); } } else if (Lifecycle.AFTER_DESTROY_EVENT.equals(event.getType())) { - synchronized (lock) { + Lock writeLock = org.apache.tomcat.jni.AprStatus.getStatusLock().writeLock(); + writeLock.lock(); + try { // Instance may get destroyed without ever being initialized if (instanceInitialized) { referenceCount--; @@ -173,24 +186,26 @@ // Still being used return; } - if (!AprStatus.isAprAvailable()) { + if (!org.apache.tomcat.jni.AprStatus.isAprAvailable()) { return; } try { terminateAPR(); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); - log.info(sm.getString("aprListener.aprDestroy")); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + log.warn(sm.getString("aprListener.aprDestroy"), throwable); } + } finally { + writeLock.unlock(); } } } private static void terminateAPR() { - AprStatus.setAprInitialized(false); - AprStatus.setAprAvailable(false); + org.apache.tomcat.jni.AprStatus.setAprInitialized(false); + org.apache.tomcat.jni.AprStatus.setAprAvailable(false); fipsModeActive = false; sslInitialized = false; // terminate() will clean the pool // There could be unreferenced SSL_CTX still waiting for GC @@ -199,13 +214,10 @@ } private static void init() { - int rqver = TCN_REQUIRED_MAJOR * 1000 + TCN_REQUIRED_MINOR * 100 + TCN_REQUIRED_PATCH; - int rcver = TCN_RECOMMENDED_MAJOR * 1000 + TCN_RECOMMENDED_MINOR * 100 + TCN_RECOMMENDED_PV; - - if (AprStatus.isAprInitialized()) { + if (org.apache.tomcat.jni.AprStatus.isAprInitialized()) { return; } - AprStatus.setAprInitialized(true); + org.apache.tomcat.jni.AprStatus.setAprInitialized(true); try { Library.initialize(null); @@ -223,9 +235,9 @@ return; } catch (Throwable t) { // Library present but failed to load - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); - log.warn(sm.getString("aprListener.aprInitError", t.getMessage()), t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + log.warn(sm.getString("aprListener.aprInitError", throwable.getMessage()), throwable); return; } if (tcnMajor > 1 && "off".equalsIgnoreCase(SSLEngine)) { @@ -234,33 +246,63 @@ // Tomcat Native 2.x onwards requires SSL terminateAPR(); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); } return; } + + /* + * With parallel development of 1.x and 2.x there are now minimum and recommended versions for both branches. + * + * The minimum required version is increased when the Tomcat Native API is changed (typically extended) to + * include functionality that Tomcat expects to always be present. + * + * The minimum recommended version is increased when there is a change in Tomcat Native that while not required + * is recommended (such as bug fixes). + */ + int rqver; + int rcver; + if (tcnMajor == 1) { + rqver = 1000 + TCN_1_REQUIRED_MINOR * 100 + TCN_1_REQUIRED_PATCH; + rcver = 1000 + TCN_1_RECOMMENDED_MINOR * 100 + TCN_1_RECOMMENDED_PATCH; + } else { + rqver = TCN_REQUIRED_MAJOR * 1000 + TCN_REQUIRED_MINOR * 100 + TCN_REQUIRED_PATCH; + rcver = TCN_RECOMMENDED_MAJOR * 1000 + TCN_RECOMMENDED_MINOR * 100 + TCN_RECOMMENDED_PV; + } + if (tcnVersion < rqver) { - log.error(sm.getString("aprListener.tcnInvalid", Library.versionString(), - TCN_REQUIRED_MAJOR + "." + TCN_REQUIRED_MINOR + "." + TCN_REQUIRED_PATCH)); + if (tcnMajor == 1) { + log.error(sm.getString("aprListener.tcnInvalid.1", Library.versionString(), + "1." + TCN_1_REQUIRED_MINOR + "." + TCN_1_REQUIRED_PATCH)); + } else { + log.error(sm.getString("aprListener.tcnInvalid", Library.versionString(), + TCN_REQUIRED_MAJOR + "." + TCN_REQUIRED_MINOR + "." + TCN_REQUIRED_PATCH)); + } try { // Terminate the APR in case the version // is below required. terminateAPR(); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); } return; } if (tcnVersion < rcver) { - initInfoLogMessages.add(sm.getString("aprListener.tcnVersion", Library.versionString(), - TCN_RECOMMENDED_MAJOR + "." + TCN_RECOMMENDED_MINOR + "." + TCN_RECOMMENDED_PV)); + if (tcnMajor == 1) { + initInfoLogMessages.add(sm.getString("aprListener.tcnVersion.1", Library.versionString(), + "1." + TCN_1_RECOMMENDED_MINOR + "." + TCN_1_RECOMMENDED_PATCH)); + } else { + initInfoLogMessages.add(sm.getString("aprListener.tcnVersion", Library.versionString(), + TCN_RECOMMENDED_MAJOR + "." + TCN_RECOMMENDED_MINOR + "." + TCN_RECOMMENDED_PV)); + } } initInfoLogMessages .add(sm.getString("aprListener.tcnValid", Library.versionString(), Library.aprVersionString())); - AprStatus.setAprAvailable(true); + org.apache.tomcat.jni.AprStatus.setAprAvailable(true); } private static void initializeSSL() throws Exception { @@ -276,9 +318,9 @@ sslInitialized = true; String methodName = "randSet"; - Class paramTypes[] = new Class[1]; + Class[] paramTypes = new Class[1]; paramTypes[0] = String.class; - Object paramValues[] = new Object[1]; + Object[] paramValues = new Object[1]; paramValues[0] = SSLRandomSeed; Class clazz = Class.forName("org.apache.tomcat.jni.SSL"); Method method = clazz.getMethod(methodName, paramTypes); @@ -290,7 +332,7 @@ method = clazz.getMethod(methodName, paramTypes); method.invoke(null, paramValues); - AprStatus.setOpenSSLVersion(SSL.version()); + org.apache.tomcat.jni.AprStatus.setOpenSSLVersion(SSL.version()); // OpenSSL 3 onwards uses providers boolean usingProviders = tcnMajor > 1 || (tcnVersion > 1233 && (SSL.version() & 0xF0000000L) > 0x20000000); @@ -430,17 +472,17 @@ } public void setUseOpenSSL(boolean useOpenSSL) { - if (useOpenSSL != AprStatus.getUseOpenSSL()) { - AprStatus.setUseOpenSSL(useOpenSSL); + if (useOpenSSL != org.apache.tomcat.jni.AprStatus.getUseOpenSSL()) { + org.apache.tomcat.jni.AprStatus.setUseOpenSSL(useOpenSSL); } } public static boolean getUseOpenSSL() { - return AprStatus.getUseOpenSSL(); + return org.apache.tomcat.jni.AprStatus.getUseOpenSSL(); } public static boolean isInstanceCreated() { - return AprStatus.isInstanceCreated(); + return org.apache.tomcat.jni.AprStatus.isInstanceCreated(); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/AprStatus.java tomcat10-10.1.52/java/org/apache/catalina/core/AprStatus.java --- tomcat10-10.1.34/java/org/apache/catalina/core/AprStatus.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/AprStatus.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,58 +18,56 @@ /** * Holds APR status without the need to load other classes. + * + * @deprecated Unused. Use {@link org.apache.tomcat.jni.AprStatus} instead. This class will be removed in Tomcat 12 + * onwards. */ +@Deprecated public class AprStatus { - private static volatile boolean aprInitialized = false; - private static volatile boolean aprAvailable = false; - private static volatile boolean useOpenSSL = true; - private static volatile boolean instanceCreated = false; - private static volatile int openSSLVersion = 0; public static boolean isAprInitialized() { - return aprInitialized; + return org.apache.tomcat.jni.AprStatus.isAprInitialized(); } public static boolean isAprAvailable() { - return aprAvailable; + return org.apache.tomcat.jni.AprStatus.isAprAvailable(); } public static boolean getUseOpenSSL() { - return useOpenSSL; + return org.apache.tomcat.jni.AprStatus.getUseOpenSSL(); } public static boolean isInstanceCreated() { - return instanceCreated; + return org.apache.tomcat.jni.AprStatus.isInstanceCreated(); } public static void setAprInitialized(boolean aprInitialized) { - AprStatus.aprInitialized = aprInitialized; + org.apache.tomcat.jni.AprStatus.setAprInitialized(aprInitialized); } public static void setAprAvailable(boolean aprAvailable) { - AprStatus.aprAvailable = aprAvailable; + org.apache.tomcat.jni.AprStatus.setAprAvailable(aprAvailable); } public static void setUseOpenSSL(boolean useOpenSSL) { - AprStatus.useOpenSSL = useOpenSSL; + org.apache.tomcat.jni.AprStatus.setUseOpenSSL(useOpenSSL); } public static void setInstanceCreated(boolean instanceCreated) { - AprStatus.instanceCreated = instanceCreated; + org.apache.tomcat.jni.AprStatus.setInstanceCreated(instanceCreated); } /** * @return the openSSLVersion */ public static int getOpenSSLVersion() { - return openSSLVersion; + return org.apache.tomcat.jni.AprStatus.getOpenSSLVersion(); } /** * @param openSSLVersion the openSSLVersion to set */ public static void setOpenSSLVersion(int openSSLVersion) { - AprStatus.openSSLVersion = openSSLVersion; + org.apache.tomcat.jni.AprStatus.setOpenSSLVersion(openSSLVersion); } - } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/AsyncContextImpl.java tomcat10-10.1.52/java/org/apache/catalina/core/AsyncContextImpl.java --- tomcat10-10.1.34/java/org/apache/catalina/core/AsyncContextImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/AsyncContextImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -256,16 +256,14 @@ @Override public T createListener(Class clazz) throws ServletException { check(); - T listener = null; + T listener; try { listener = (T) context.getInstanceManager().newInstance(clazz.getName(), clazz.getClassLoader()); } catch (ReflectiveOperationException | NamingException e) { - ServletException se = new ServletException(e); - throw se; + throw new ServletException(e); } catch (Exception e) { ExceptionUtils.handleThrowable(e.getCause()); - ServletException se = new ServletException(e); - throw se; + throw new ServletException(e); } return listener; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ContainerBase.java tomcat10-10.1.52/java/org/apache/catalina/core/ContainerBase.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ContainerBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ContainerBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -118,8 +118,6 @@ *

  • *
    Examples of Returned Values
    * Subclasses that fire additional events should document them in the class comments of the implementation class. - * - * @author Craig R. McClanahan */ public abstract class ContainerBase extends LifecycleMBeanBase implements Container { @@ -312,7 +310,7 @@ Container current = this; while (current != null) { String name = current.getName(); - if ((name == null) || (name.equals(""))) { + if ((name == null) || (name.isEmpty())) { name = "/"; } else if (name.startsWith("##")) { name = "/" + name; @@ -363,7 +361,7 @@ @Override public void setCluster(Cluster cluster) { - Cluster oldCluster = null; + Cluster oldCluster; Lock writeLock = clusterLock.writeLock(); writeLock.lock(); try { @@ -518,7 +516,7 @@ @Override public void setRealm(Realm realm) { - Realm oldRealm = null; + Realm oldRealm; Lock l = realmLock.writeLock(); l.lock(); try { @@ -754,12 +752,12 @@ for (Future result : results) { try { result.get(); - } catch (Throwable e) { - log.error(sm.getString("containerBase.threadedStartFailed"), e); + } catch (Throwable t) { + log.error(sm.getString("containerBase.threadedStartFailed"), t); if (multiThrowable == null) { multiThrowable = new MultiThrowable(); } - multiThrowable.add(e); + multiThrowable.add(t); } } @@ -1001,7 +999,7 @@ @Override public void fireContainerEvent(String type, Object data) { - if (listeners.size() < 1) { + if (listeners.isEmpty()) { return; } @@ -1049,7 +1047,7 @@ } else if (c == null) { // May happen in unit testing and/or some embedding scenarios keyProperties.append(",container"); - keyProperties.append(containerCount++); + keyProperties.append(containerCount); keyProperties.append("=null"); break; } else { @@ -1122,7 +1120,7 @@ StringBuilder sb = new StringBuilder(); Container parent = getParent(); if (parent != null) { - sb.append(parent.toString()); + sb.append(parent); sb.append('.'); } sb.append(this.getClass().getSimpleName()); @@ -1192,7 +1190,7 @@ private static class StartChild implements Callable { - private Container child; + private final Container child; StartChild(Container child) { this.child = child; @@ -1207,7 +1205,7 @@ private static class StopChild implements Callable { - private Container child; + private final Container child; StopChild(Container child) { this.child = child; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/DefaultInstanceManager.java tomcat10-10.1.52/java/org/apache/catalina/core/DefaultInstanceManager.java --- tomcat10-10.1.34/java/org/apache/catalina/core/DefaultInstanceManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/DefaultInstanceManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -170,7 +170,7 @@ private Map assembleInjectionsFromClassHierarchy(Class clazz) { Map injections = new HashMap<>(); - Map currentInjections = null; + Map currentInjections; while (clazz != null) { currentInjections = this.injectionMap.get(clazz.getName()); if (currentInjections != null) { @@ -189,7 +189,7 @@ } /** - * Call postConstruct method on the specified instance recursively from deepest superclass to actual class. + * Call postConstruct method on the specified instance recursively from the deepest superclass to actual class. * * @param instance object to call postconstruct methods on * @param clazz (super) class to examine for postConstruct annotation. @@ -226,7 +226,7 @@ /** - * Call preDestroy method on the specified instance recursively from deepest superclass to actual class. + * Call preDestroy method on the specified instance recursively from the deepest superclass to actual class. * * @param instance object to call preDestroy methods on * @param clazz (super) class to examine for preDestroy annotation. @@ -311,30 +311,28 @@ } } Resource resourceAnnotation; - Annotation ejbAnnotation; - Annotation webServiceRefAnnotation; - Annotation persistenceContextAnnotation; - Annotation persistenceUnitAnnotation; + EJB ejbAnnotation; + WebServiceRef webServiceRefAnnotation; + PersistenceContext persistenceContextAnnotation; + PersistenceUnit persistenceUnitAnnotation; if ((resourceAnnotation = method.getAnnotation(Resource.class)) != null) { annotations.add(new AnnotationCacheEntry(method.getName(), method.getParameterTypes(), resourceAnnotation.name(), AnnotationCacheEntryType.SETTER)); } else if (EJB_PRESENT && (ejbAnnotation = method.getAnnotation(EJB.class)) != null) { annotations.add(new AnnotationCacheEntry(method.getName(), method.getParameterTypes(), - ((EJB) ejbAnnotation).name(), AnnotationCacheEntryType.SETTER)); + ejbAnnotation.name(), AnnotationCacheEntryType.SETTER)); } else if (WS_PRESENT && (webServiceRefAnnotation = method.getAnnotation(WebServiceRef.class)) != null) { annotations.add(new AnnotationCacheEntry(method.getName(), method.getParameterTypes(), - ((WebServiceRef) webServiceRefAnnotation).name(), AnnotationCacheEntryType.SETTER)); + webServiceRefAnnotation.name(), AnnotationCacheEntryType.SETTER)); } else if (JPA_PRESENT && (persistenceContextAnnotation = method.getAnnotation(PersistenceContext.class)) != null) { annotations.add(new AnnotationCacheEntry(method.getName(), method.getParameterTypes(), - ((PersistenceContext) persistenceContextAnnotation).name(), - AnnotationCacheEntryType.SETTER)); + persistenceContextAnnotation.name(), AnnotationCacheEntryType.SETTER)); } else if (JPA_PRESENT && (persistenceUnitAnnotation = method.getAnnotation(PersistenceUnit.class)) != null) { annotations.add(new AnnotationCacheEntry(method.getName(), method.getParameterTypes(), - ((PersistenceUnit) persistenceUnitAnnotation).name(), - AnnotationCacheEntryType.SETTER)); + persistenceUnitAnnotation.name(), AnnotationCacheEntryType.SETTER)); } } @@ -364,10 +362,10 @@ Field[] fields = Introspection.getDeclaredFields(clazz); for (Field field : fields) { Resource resourceAnnotation; - Annotation ejbAnnotation; - Annotation webServiceRefAnnotation; - Annotation persistenceContextAnnotation; - Annotation persistenceUnitAnnotation; + EJB ejbAnnotation; + WebServiceRef webServiceRefAnnotation; + PersistenceContext persistenceContextAnnotation; + PersistenceUnit persistenceUnitAnnotation; String fieldName = field.getName(); if (injections != null && injections.containsKey(fieldName) && !injectionsMatchedToSetter.contains(fieldName)) { @@ -377,21 +375,19 @@ annotations.add(new AnnotationCacheEntry(fieldName, null, resourceAnnotation.name(), AnnotationCacheEntryType.FIELD)); } else if (EJB_PRESENT && (ejbAnnotation = field.getAnnotation(EJB.class)) != null) { - annotations.add(new AnnotationCacheEntry(fieldName, null, ((EJB) ejbAnnotation).name(), + annotations.add(new AnnotationCacheEntry(fieldName, null, ejbAnnotation.name(), AnnotationCacheEntryType.FIELD)); } else if (WS_PRESENT && (webServiceRefAnnotation = field.getAnnotation(WebServiceRef.class)) != null) { - annotations.add(new AnnotationCacheEntry(fieldName, null, - ((WebServiceRef) webServiceRefAnnotation).name(), AnnotationCacheEntryType.FIELD)); + annotations.add(new AnnotationCacheEntry(fieldName, null, webServiceRefAnnotation.name(), + AnnotationCacheEntryType.FIELD)); } else if (JPA_PRESENT && (persistenceContextAnnotation = field.getAnnotation(PersistenceContext.class)) != null) { annotations.add(new AnnotationCacheEntry(fieldName, null, - ((PersistenceContext) persistenceContextAnnotation).name(), - AnnotationCacheEntryType.FIELD)); + persistenceContextAnnotation.name(), AnnotationCacheEntryType.FIELD)); } else if (JPA_PRESENT && (persistenceUnitAnnotation = field.getAnnotation(PersistenceUnit.class)) != null) { - annotations.add(new AnnotationCacheEntry(fieldName, null, - ((PersistenceUnit) persistenceUnitAnnotation).name(), + annotations.add(new AnnotationCacheEntry(fieldName, null, persistenceUnitAnnotation.name(), AnnotationCacheEntryType.FIELD)); } } @@ -525,7 +521,7 @@ String normalizedName = normalize(name); - if ((normalizedName != null) && (normalizedName.length() > 0)) { + if ((normalizedName != null) && (!normalizedName.isEmpty())) { lookedupResource = context.lookup(normalizedName); } else { lookedupResource = context.lookup(clazz.getName() + "/" + field.getName()); @@ -562,7 +558,7 @@ String normalizedName = normalize(name); - if ((normalizedName != null) && (normalizedName.length() > 0)) { + if ((normalizedName != null) && (!normalizedName.isEmpty())) { lookedupResource = context.lookup(normalizedName); } else { lookedupResource = context.lookup(clazz.getName() + "/" + Introspection.getPropertyName(method)); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java tomcat10-10.1.52/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java --- tomcat10-10.1.34/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -64,7 +64,7 @@ } /** - * Protect against resources being read for JAR files and, as a side-effect, the JAR file becoming locked. Note this + * Protect against resources being read for JAR files and, as a side effect, the JAR file becoming locked. Note this * disables caching for all {@link URLConnection}s, regardless of type. Defaults to true. */ private boolean urlCacheProtection = true; @@ -78,9 +78,9 @@ } /** - * The first access to {@link DriverManager} will trigger the loading of all {@link java.sql.Driver}s in the the - * current class loader. The web application level memory leak protection can take care of this in most cases but - * triggering the loading here has fewer side-effects. + * The first access to {@link DriverManager} will trigger the loading of all {@link java.sql.Driver}s in the current + * class loader. The web application level memory leak protection can take care of this in most cases but triggering + * the loading here has fewer side effects. */ private boolean driverManagerProtection = true; @@ -94,7 +94,7 @@ /** * List of comma-separated fully qualified class names to load and initialize during the startup of this Listener. - * This allows to pre-load classes that are known to provoke classloader leaks if they are loaded during a request + * This allows to preload classes that are known to provoke classloader leaks if they are loaded during a request * processing. */ private String classesToInitialize = null; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/core/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -90,9 +90,11 @@ aprListener.skipFIPSInitialization=Already in FIPS mode; skipping FIPS initialization. aprListener.sslInit=Failed to initialize the SSLEngine. aprListener.sslRequired=[{0}] is not a valid value for SSLEngine when using version [{1}] of the Tomcat Native library since SSL is required for version 2.x onwards. -aprListener.tcnInvalid=An incompatible version [{0}] of the Apache Tomcat Native library is installed, while Tomcat requires version [{1}] +aprListener.tcnInvalid=An incompatible version [{0}] of the Apache Tomcat Native library is installed, while Tomcat requires at least version [{1}] +aprListener.tcnInvalid.1=An incompatible version [{0}] of the Apache Tomcat Native library is installed, while Tomcat requires at least version [{1}] of Tomcat Native 1.x aprListener.tcnValid=Loaded Apache Tomcat Native library [{0}] using APR version [{1}]. aprListener.tcnVersion=An older version [{0}] of the Apache Tomcat Native library is installed, while Tomcat recommends a minimum version of [{1}] +aprListener.tcnVersion.1=An older version [{0}] of the Apache Tomcat Native library is installed, while Tomcat recommends a minimum version of [{1}] of Tomcat Native 1.x aprListener.tooLateForFIPSMode=Cannot setFIPSMode: SSL has already been initialized aprListener.tooLateForSSLEngine=Cannot setSSLEngine: SSL has already been initialized aprListener.tooLateForSSLRandomSeed=Cannot setSSLRandomSeed: SSL has already been initialized @@ -276,7 +278,9 @@ standardHost.notContext=Child of a Host must be a Context standardHost.nullName=Host name is required standardHost.problematicAppBase=Using an empty string for appBase on host [{0}] will set it to CATALINA_BASE, which is a bad idea +standardHost.problematicAppBaseParent=appBase on host [{0}] is a parent folder of CATALINA_BASE, which is a bad idea standardHost.problematicLegacyAppBase=Using an empty string for legacyAppBase on host [{0}] will set it to CATALINA_BASE, which is a bad idea +standardHost.problematicLegacyAppBaseParent=legacyAppBase on host [{0}] is a parent folder of CATALINA_BASE, which is a bad idea standardHostValve.customStatusFailed=Custom error page [{0}] could not be dispatched correctly standardHostValve.exception=Exception Processing [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/core/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -276,7 +276,9 @@ standardHost.notContext=Host の子供はContextでなければいけません standardHost.nullName=ホスト名が必要です standardHost.problematicAppBase=ホスト [{0}] のappBaseに空の文字列を使用すると、CATALINA_BASEに設定されますが、これは悪い考えです +standardHost.problematicAppBaseParent=Host [{0}] の appBase は CATALINA_BASE の親フォルダですが、これは適切ではありません standardHost.problematicLegacyAppBase=ホスト [{0}] のlegacyAppBaseに空の文字列を使用すると、CATALINA_BASEに設定されます。これは悪い考えです +standardHost.problematicLegacyAppBaseParent=Host [{0}] の legacyAppBase は CATALINA_BASE の親フォルダですが、これは適切ではありません standardHostValve.customStatusFailed=カスタムエラーページ [{0}] を正しくディスパッチできませんでした standardHostValve.exception=例外処理 [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/core/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -28,17 +28,22 @@ applicationHttpRequest.sessionEndAccessFail=Исключение вызвало прекращение доступа к сессии при очистке запроса aprListener.initializingFIPS=Инициализируется режим FIPS... +aprListener.tcnVersion=Была обнаружена более старая версия [{0}] библиотеки Apache Tomcat Native, в то время как Tomcat рекомендует использовать как минимум версию [{1}] filterChain.filter=При выполнении фильтра выброшено исключение +naming.addEnvEntry=Добавляется переменная окружения [{0}] naming.unbindFailed=Ошибка при отвязывании объекта: [{0}] naming.wsdlFailed=wsdl файл не найден: [{0}] standardContext.filterStart=Ошибка при старте фильтра [{0}] standardContext.invalidWrapperClass=[{0}] не является подклассом StandardWrapper +standardContext.managerFail=Невозможно запустить менеджер сессий +standardContext.notStarted=Контекст с именем [{0}] ещё небыл запущен standardContext.parameter.duplicate=Дублированный параметр инициализации контекста [{0}] standardContext.predestroy.duplicate=Дублированное определение метода @PreDestroy для класса [{0}] standardContext.securityConstraint.mixHttpMethod=Запрещено смешивать и в одной и той же коллекции веб-ресурсов +standardContext.securityConstraint.pattern=Некорректный [{0}] в ограничении безопасности standardContext.startingContext=Ошибка запуска контекста с именем [{0}] standardWrapper.allocate=Ошибка при выделении экземпляра сервлета diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/core/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -128,6 +128,8 @@ containerBase.threadedStartFailed=子容器启动失败 containerBase.threadedStopFailed=停止期间子容器失败 +contextNamingInfoListener.envEntry=添加上下文环境条目[{0}], 值是[{1}] + defaultInstanceManager.invalidAnnotation=无效注解[{0}] defaultInstanceManager.invalidInjection=方法资源注入注解无效 defaultInstanceManager.postConstructNotFound=类[{1}]的后构造方法[{0}]已在部署描述符中声明,但找不到 @@ -163,7 +165,9 @@ noPluggabilityServletContext.notAllowed=Servlet 3.0规范的第4.4节不允许从未在web.xml,web-fragment.xml文件中定义或未用@WebListener注释的ServletContextListener调用此方法。 +openssllistener.destroy=OpenSSL关闭失败 openssllistener.initializeFIPSFailed=无法进入FIPS模式 +openssllistener.java22=Java 22中的FFM API不可用,使用OpenSSL需要Apache Tomcat Native openssllistener.sslInit=OpenSSL初始化失败 propertiesRoleMappingListener.roleMappingFileEmpty=角色映射文件不能为空 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/NamingContextListener.java tomcat10-10.1.52/java/org/apache/catalina/core/NamingContextListener.java --- tomcat10-10.1.34/java/org/apache/catalina/core/NamingContextListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/NamingContextListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -76,8 +76,6 @@ /** * Helper class used to initialize and populate the JNDI context associated with each context and server. - * - * @author Remy Maucherat */ public class NamingContextListener implements LifecycleListener, PropertyChangeListener { @@ -235,7 +233,7 @@ try { createNamingContext(); } catch (NamingException e) { - log.error(sm.getString("naming.namingContextCreationFailed", e)); + log.error(sm.getString("naming.namingContextCreationFailed", container), e); } namingResources.addPropertyChangeListener(this); @@ -248,7 +246,7 @@ ContextBindings.bindClassLoader(container, token, ((Context) container).getLoader().getClassLoader()); } catch (NamingException e) { - log.error(sm.getString("naming.bindFailed", e)); + log.error(sm.getString("naming.bindFailed", container), e); } } @@ -257,7 +255,7 @@ try { ContextBindings.bindClassLoader(container, token, this.getClass().getClassLoader()); } catch (NamingException e) { - log.error(sm.getString("naming.bindFailed", e)); + log.error(sm.getString("naming.bindFailed", container), e); } if (container instanceof StandardServer) { ((StandardServer) container).setGlobalNamingContext(namingContext); @@ -297,7 +295,7 @@ // unregister mbeans. if (!objectNames.isEmpty()) { Collection names = objectNames.values(); - Registry registry = Registry.getRegistry(null, null); + Registry registry = Registry.getRegistry(null); for (ObjectName objectName : names) { registry.unregisterComponent(objectName); } @@ -360,110 +358,119 @@ */ private void processGlobalResourcesChange(String name, Object oldValue, Object newValue) { - if (name.equals("ejb")) { - if (oldValue != null) { - ContextEjb ejb = (ContextEjb) oldValue; - if (ejb.getName() != null) { - removeEjb(ejb.getName()); + switch (name) { + case "ejb": + if (oldValue != null) { + ContextEjb ejb = (ContextEjb) oldValue; + if (ejb.getName() != null) { + removeEjb(ejb.getName()); + } } - } - if (newValue != null) { - ContextEjb ejb = (ContextEjb) newValue; - if (ejb.getName() != null) { - addEjb(ejb); + if (newValue != null) { + ContextEjb ejb = (ContextEjb) newValue; + if (ejb.getName() != null) { + addEjb(ejb); + } } - } - } else if (name.equals("environment")) { - if (oldValue != null) { - ContextEnvironment env = (ContextEnvironment) oldValue; - if (env.getName() != null) { - removeEnvironment(env.getName()); + break; + case "environment": + if (oldValue != null) { + ContextEnvironment env = (ContextEnvironment) oldValue; + if (env.getName() != null) { + removeEnvironment(env.getName()); + } } - } - if (newValue != null) { - ContextEnvironment env = (ContextEnvironment) newValue; - if (env.getName() != null) { - addEnvironment(env); + if (newValue != null) { + ContextEnvironment env = (ContextEnvironment) newValue; + if (env.getName() != null) { + addEnvironment(env); + } } - } - } else if (name.equals("localEjb")) { - if (oldValue != null) { - ContextLocalEjb ejb = (ContextLocalEjb) oldValue; - if (ejb.getName() != null) { - removeLocalEjb(ejb.getName()); + break; + case "localEjb": + if (oldValue != null) { + ContextLocalEjb ejb = (ContextLocalEjb) oldValue; + if (ejb.getName() != null) { + removeLocalEjb(ejb.getName()); + } } - } - if (newValue != null) { - ContextLocalEjb ejb = (ContextLocalEjb) newValue; - if (ejb.getName() != null) { - addLocalEjb(ejb); + if (newValue != null) { + ContextLocalEjb ejb = (ContextLocalEjb) newValue; + if (ejb.getName() != null) { + addLocalEjb(ejb); + } } - } - } else if (name.equals("messageDestinationRef")) { - if (oldValue != null) { - MessageDestinationRef mdr = (MessageDestinationRef) oldValue; - if (mdr.getName() != null) { - removeMessageDestinationRef(mdr.getName()); + break; + case "messageDestinationRef": + if (oldValue != null) { + MessageDestinationRef mdr = (MessageDestinationRef) oldValue; + if (mdr.getName() != null) { + removeMessageDestinationRef(mdr.getName()); + } } - } - if (newValue != null) { - MessageDestinationRef mdr = (MessageDestinationRef) newValue; - if (mdr.getName() != null) { - addMessageDestinationRef(mdr); + if (newValue != null) { + MessageDestinationRef mdr = (MessageDestinationRef) newValue; + if (mdr.getName() != null) { + addMessageDestinationRef(mdr); + } } - } - } else if (name.equals("resource")) { - if (oldValue != null) { - ContextResource resource = (ContextResource) oldValue; - if (resource.getName() != null) { - removeResource(resource.getName()); + break; + case "resource": + if (oldValue != null) { + ContextResource resource = (ContextResource) oldValue; + if (resource.getName() != null) { + removeResource(resource.getName()); + } } - } - if (newValue != null) { - ContextResource resource = (ContextResource) newValue; - if (resource.getName() != null) { - addResource(resource); + if (newValue != null) { + ContextResource resource = (ContextResource) newValue; + if (resource.getName() != null) { + addResource(resource); + } } - } - } else if (name.equals("resourceEnvRef")) { - if (oldValue != null) { - ContextResourceEnvRef resourceEnvRef = (ContextResourceEnvRef) oldValue; - if (resourceEnvRef.getName() != null) { - removeResourceEnvRef(resourceEnvRef.getName()); + break; + case "resourceEnvRef": + if (oldValue != null) { + ContextResourceEnvRef resourceEnvRef = (ContextResourceEnvRef) oldValue; + if (resourceEnvRef.getName() != null) { + removeResourceEnvRef(resourceEnvRef.getName()); + } } - } - if (newValue != null) { - ContextResourceEnvRef resourceEnvRef = (ContextResourceEnvRef) newValue; - if (resourceEnvRef.getName() != null) { - addResourceEnvRef(resourceEnvRef); + if (newValue != null) { + ContextResourceEnvRef resourceEnvRef = (ContextResourceEnvRef) newValue; + if (resourceEnvRef.getName() != null) { + addResourceEnvRef(resourceEnvRef); + } } - } - } else if (name.equals("resourceLink")) { - if (oldValue != null) { - ContextResourceLink rl = (ContextResourceLink) oldValue; - if (rl.getName() != null) { - removeResourceLink(rl.getName()); + break; + case "resourceLink": + if (oldValue != null) { + ContextResourceLink rl = (ContextResourceLink) oldValue; + if (rl.getName() != null) { + removeResourceLink(rl.getName()); + } } - } - if (newValue != null) { - ContextResourceLink rl = (ContextResourceLink) newValue; - if (rl.getName() != null) { - addResourceLink(rl); + if (newValue != null) { + ContextResourceLink rl = (ContextResourceLink) newValue; + if (rl.getName() != null) { + addResourceLink(rl); + } } - } - } else if (name.equals("service")) { - if (oldValue != null) { - ContextService service = (ContextService) oldValue; - if (service.getName() != null) { - removeService(service.getName()); + break; + case "service": + if (oldValue != null) { + ContextService service = (ContextService) oldValue; + if (service.getName() != null) { + removeService(service.getName()); + } } - } - if (newValue != null) { - ContextService service = (ContextService) newValue; - if (service.getName() != null) { - addService(service); + if (newValue != null) { + ContextService service = (ContextService) newValue; + if (service.getName() != null) { + addService(service); + } } - } + break; } @@ -482,6 +489,13 @@ } else { compCtx = namingContext.createSubcontext("comp"); envCtx = compCtx.createSubcontext("env"); + /* + * Jakarta Platform Specification, 5.2.2: Application Component Environment Namespaces + * + * "java:module" and "java:comp" refer to the same namespace in a web module (i.e. a web application). + * Implement this by binding the "comp" sub-context we just created to the "module" name as well. + */ + namingContext.bind("module", compCtx); } int i; @@ -556,7 +570,7 @@ // Ignore because UserTransaction was obviously // added via ResourceLink } catch (NamingException e) { - log.error(sm.getString("naming.bindFailed", e)); + log.error(sm.getString("naming.bindFailed", "UserTransaction"), e); } } @@ -565,7 +579,7 @@ try { compCtx.bind("Resources", ((Context) container).getResources()); } catch (NamingException e) { - log.error(sm.getString("naming.bindFailed", e)); + log.error(sm.getString("naming.bindFailed", "Resources"), e); } } @@ -639,7 +653,7 @@ createSubcontexts(envCtx, ejb.getName()); envCtx.bind(ejb.getName(), ref); } catch (NamingException e) { - log.error(sm.getString("naming.bindFailed", e)); + log.error(sm.getString("naming.bindFailed", ejb.getName()), e); } } @@ -658,61 +672,72 @@ // initializing it. String type = env.getType(); try { - if (type.equals("java.lang.String")) { - value = env.getValue(); - } else if (type.equals("java.lang.Byte")) { - if (env.getValue() == null) { - value = Byte.valueOf((byte) 0); - } else { - value = Byte.decode(env.getValue()); - } - } else if (type.equals("java.lang.Short")) { - if (env.getValue() == null) { - value = Short.valueOf((short) 0); - } else { - value = Short.decode(env.getValue()); - } - } else if (type.equals("java.lang.Integer")) { - if (env.getValue() == null) { - value = Integer.valueOf(0); - } else { - value = Integer.decode(env.getValue()); - } - } else if (type.equals("java.lang.Long")) { - if (env.getValue() == null) { - value = Long.valueOf(0); - } else { - value = Long.decode(env.getValue()); - } - } else if (type.equals("java.lang.Boolean")) { - value = Boolean.valueOf(env.getValue()); - } else if (type.equals("java.lang.Double")) { - if (env.getValue() == null) { - value = Double.valueOf(0); - } else { - value = Double.valueOf(env.getValue()); - } - } else if (type.equals("java.lang.Float")) { - if (env.getValue() == null) { - value = Float.valueOf(0); - } else { - value = Float.valueOf(env.getValue()); - } - } else if (type.equals("java.lang.Character")) { - if (env.getValue() == null) { - value = Character.valueOf((char) 0); - } else { - if (env.getValue().length() == 1) { - value = Character.valueOf(env.getValue().charAt(0)); + switch (type) { + case "java.lang.String": + value = env.getValue(); + break; + case "java.lang.Byte": + if (env.getValue() == null) { + value = Byte.valueOf((byte) 0); } else { - throw new IllegalArgumentException(); + value = Byte.decode(env.getValue()); } - } - } else { - value = constructEnvEntry(env.getType(), env.getValue()); - if (value == null) { - log.error(sm.getString("naming.invalidEnvEntryType", env.getName())); - } + break; + case "java.lang.Short": + if (env.getValue() == null) { + value = Short.valueOf((short) 0); + } else { + value = Short.decode(env.getValue()); + } + break; + case "java.lang.Integer": + if (env.getValue() == null) { + value = Integer.valueOf(0); + } else { + value = Integer.decode(env.getValue()); + } + break; + case "java.lang.Long": + if (env.getValue() == null) { + value = Long.valueOf(0); + } else { + value = Long.decode(env.getValue()); + } + break; + case "java.lang.Boolean": + value = Boolean.valueOf(env.getValue()); + break; + case "java.lang.Double": + if (env.getValue() == null) { + value = Double.valueOf(0); + } else { + value = Double.valueOf(env.getValue()); + } + break; + case "java.lang.Float": + if (env.getValue() == null) { + value = Float.valueOf(0); + } else { + value = Float.valueOf(env.getValue()); + } + break; + case "java.lang.Character": + if (env.getValue() == null) { + value = Character.valueOf((char) 0); + } else { + if (env.getValue().length() == 1) { + value = Character.valueOf(env.getValue().charAt(0)); + } else { + throw new IllegalArgumentException(); + } + } + break; + default: + value = constructEnvEntry(env.getType(), env.getValue()); + if (value == null) { + log.error(sm.getString("naming.invalidEnvEntryType", env.getName())); + } + break; } } catch (IllegalArgumentException e) { log.error(sm.getString("naming.invalidEnvEntryValue", env.getName())); @@ -728,7 +753,7 @@ createSubcontexts(envCtx, env.getName()); envCtx.bind(env.getName(), value); } catch (NamingException e) { - log.error(sm.getString("naming.invalidEnvEntryValue", e)); + log.error(sm.getString("naming.invalidEnvEntryValue", env.getName()), e); } } } @@ -737,7 +762,7 @@ private Object constructEnvEntry(String type, String value) { try { Class clazz = Class.forName(type); - Constructor c = null; + Constructor c; try { c = clazz.getConstructor(String.class); return c.newInstance(value); @@ -819,7 +844,7 @@ log.debug(sm.getString("naming.addSlash", service.getWsdlfile())); } } catch (MalformedURLException e) { - log.error(sm.getString("naming.wsdlFailed", e)); + log.error(sm.getString("naming.wsdlFailed", service.getWsdlfile()), e); } } if (wsdlURL == null) { @@ -854,7 +879,7 @@ log.debug(sm.getString("naming.addSlash", service.getJaxrpcmappingfile())); } } catch (MalformedURLException e) { - log.error(sm.getString("naming.wsdlFailed", e)); + log.error(sm.getString("naming.wsdlFailed", service.getJaxrpcmappingfile()), e); } } if (jaxrpcURL == null) { @@ -915,7 +940,7 @@ createSubcontexts(envCtx, service.getName()); envCtx.bind(service.getName(), ref); } catch (NamingException e) { - log.error(sm.getString("naming.bindFailed", e)); + log.error(sm.getString("naming.bindFailed", service.getName()), e); } } @@ -950,7 +975,7 @@ createSubcontexts(envCtx, resource.getName()); envCtx.bind(resource.getName(), ref); } catch (NamingException e) { - log.error(sm.getString("naming.bindFailed", e)); + log.error(sm.getString("naming.bindFailed", resource.getName()), e); } if (("javax.sql.DataSource".equals(ref.getClassName()) || @@ -959,10 +984,10 @@ try { ObjectName on = createObjectName(resource); actualResource = envCtx.lookup(resource.getName()); - Registry.getRegistry(null, null).registerComponent(actualResource, on, null); + Registry.getRegistry(null).registerComponent(actualResource, on, null); objectNames.put(resource.getName(), on); } catch (Exception e) { - log.warn(sm.getString("naming.jmxRegistrationFailed", e)); + log.warn(sm.getString("naming.jmxRegistrationFailed", resource.getName()), e); } // Bug 63210. DBCP2 DataSources require an explicit close. This goes // further and cleans up and AutoCloseable DataSource by default. @@ -1002,7 +1027,7 @@ createSubcontexts(envCtx, resourceEnvRef.getName()); envCtx.bind(resourceEnvRef.getName(), ref); } catch (NamingException e) { - log.error(sm.getString("naming.bindFailed", e)); + log.error(sm.getString("naming.bindFailed", resourceEnvRef.getName()), e); } } @@ -1034,7 +1059,7 @@ createSubcontexts(envCtx, resourceLink.getName()); ctx.bind(resourceLink.getName(), ref); } catch (NamingException e) { - log.error(sm.getString("naming.bindFailed", e)); + log.error(sm.getString("naming.bindFailed", resourceLink.getName()), e); } ResourceLinkFactory.registerGlobalResourceAccess(getGlobalNamingContext(), resourceLink.getName(), @@ -1150,7 +1175,7 @@ ObjectName on = objectNames.get(name); if (on != null) { - Registry.getRegistry(null, null).unregisterComponent(on); + Registry.getRegistry(null).unregisterComponent(on); } } @@ -1197,7 +1222,7 @@ StringTokenizer tokenizer = new StringTokenizer(name, "/"); while (tokenizer.hasMoreTokens()) { String token = tokenizer.nextToken(); - if ((!token.equals("")) && (tokenizer.hasMoreTokens())) { + if ((!token.isEmpty()) && (tokenizer.hasMoreTokens())) { try { currentContext = currentContext.createSubcontext(token); } catch (NamingException e) { @@ -1211,7 +1236,7 @@ /** - * Gets look up reference from resource if exist. + * Gets look up reference from resource if it exists. * * @param resourceBase resource base object * @@ -1219,7 +1244,7 @@ */ private LookupRef lookForLookupRef(ResourceBase resourceBase) { String lookupName = resourceBase.getLookupName(); - if ((lookupName != null && !lookupName.equals(""))) { + if ((lookupName != null && !lookupName.isEmpty())) { return new LookupRef(resourceBase.getType(), lookupName); } return null; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/OpenSSLLifecycleListener.java tomcat10-10.1.52/java/org/apache/catalina/core/OpenSSLLifecycleListener.java --- tomcat10-10.1.34/java/org/apache/catalina/core/OpenSSLLifecycleListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/OpenSSLLifecycleListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -57,9 +57,9 @@ Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); openSSLLibraryClass.getMethod("init").invoke(null); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("openssllistener.sslInit"), t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + log.error(sm.getString("openssllistener.sslInit"), throwable); } } } @@ -97,9 +97,9 @@ Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); openSSLLibraryClass.getMethod("init").invoke(null); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("openssllistener.sslInit"), t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + log.error(sm.getString("openssllistener.sslInit"), throwable); initError = true; } // Failure to initialize FIPS mode is fatal @@ -123,9 +123,9 @@ Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); openSSLLibraryClass.getMethod("destroy").invoke(null); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); - log.info(sm.getString("openssllistener.destroy")); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + log.warn(sm.getString("openssllistener.destroy"), throwable); } } } @@ -139,8 +139,8 @@ Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); return (String) openSSLLibraryClass.getMethod("getSSLEngine").invoke(null); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); } } return null; @@ -151,10 +151,10 @@ try { Class openSSLLibraryClass = Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); - openSSLLibraryClass.getMethod("setSSLEngine").invoke(null, SSLEngine); + openSSLLibraryClass.getMethod("setSSLEngine", String.class).invoke(null, SSLEngine); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); } } } @@ -166,8 +166,8 @@ Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); return (String) openSSLLibraryClass.getMethod("getSSLRandomSeed").invoke(null); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); } } return null; @@ -178,10 +178,10 @@ try { Class openSSLLibraryClass = Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); - openSSLLibraryClass.getMethod("setSSLRandomSeed").invoke(null, SSLRandomSeed); + openSSLLibraryClass.getMethod("setSSLRandomSeed", String.class).invoke(null, SSLRandomSeed); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); } } } @@ -193,8 +193,8 @@ Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); return (String) openSSLLibraryClass.getMethod("getFIPSMode").invoke(null); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); } } return null; @@ -205,10 +205,10 @@ try { Class openSSLLibraryClass = Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); - openSSLLibraryClass.getMethod("setFIPSMode").invoke(null, FIPSMode); + openSSLLibraryClass.getMethod("setFIPSMode", String.class).invoke(null, FIPSMode); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); } } } @@ -220,8 +220,8 @@ Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); return ((Boolean) openSSLLibraryClass.getMethod("isFIPSModeActive").invoke(null)).booleanValue(); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); } } return false; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/PropertiesRoleMappingListener.java tomcat10-10.1.52/java/org/apache/catalina/core/PropertiesRoleMappingListener.java --- tomcat10-10.1.34/java/org/apache/catalina/core/PropertiesRoleMappingListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/PropertiesRoleMappingListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -111,9 +111,9 @@ Context context = (Context) event.getLifecycle(); try (Resource resource = context.findConfigFileResource(roleMappingFile)) { props.load(resource.getInputStream()); - } catch (IOException e) { + } catch (IOException ioe) { throw new IllegalStateException( - sm.getString("propertiesRoleMappingListener.roleMappingFileFail", roleMappingFile), e); + sm.getString("propertiesRoleMappingListener.roleMappingFileFail", roleMappingFile), ioe); } int linkCount = 0; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardContext.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardContext.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,9 +38,11 @@ import java.util.Locale; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; @@ -116,6 +118,7 @@ import org.apache.tomcat.InstanceManagerBindings; import org.apache.tomcat.JarScanner; import org.apache.tomcat.util.ExceptionUtils; +import org.apache.tomcat.util.buf.EncodedSolidusHandling; import org.apache.tomcat.util.buf.StringUtils; import org.apache.tomcat.util.compat.JreCompat; import org.apache.tomcat.util.descriptor.XmlIdentifiers; @@ -139,9 +142,6 @@ /** * Standard implementation of the Context interface. Each child container must be a Wrapper implementation to * process the requests directed to a particular servlet. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class StandardContext extends ContainerBase implements Context, NotificationEmitter { @@ -204,7 +204,7 @@ * The list of unique application listener class names configured for this application, in the order they were * encountered in the resulting merged web.xml file. */ - private CopyOnWriteArrayList applicationListeners = new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList applicationListeners = new CopyOnWriteArrayList<>(); /** * The set of application listeners that are required to have limited access to ServletContext methods. See Servlet @@ -216,34 +216,34 @@ * The list of instantiated application event listener objects. Note that SCIs and other code may use the * pluggability APIs to add listener instances directly to this list before the application starts. */ - private List applicationEventListenersList = new CopyOnWriteArrayList<>(); + private final List applicationEventListenersList = new CopyOnWriteArrayList<>(); /** * The set of instantiated application lifecycle listener objects. Note that SCIs and other code may use the * pluggability APIs to add listener instances directly to this list before the application starts. */ - private Object applicationLifecycleListenersObjects[] = new Object[0]; + private Object[] applicationLifecycleListenersObjects = new Object[0]; /** * The ordered set of ServletContainerInitializers for this web application. */ - private Map>> initializers = new LinkedHashMap<>(); + private final Map>> initializers = new LinkedHashMap<>(); /** * The set of application parameters defined for this application. */ - private ApplicationParameter applicationParameters[] = new ApplicationParameter[0]; + private ApplicationParameter[] applicationParameters = new ApplicationParameter[0]; private final Object applicationParametersLock = new Object(); /** - * The broadcaster that sends j2ee notifications. + * The broadcaster that sends EE notifications. */ - private NotificationBroadcasterSupport broadcaster = null; + private final NotificationBroadcasterSupport broadcaster; /** * The Locale to character set mapper for this application. @@ -272,7 +272,7 @@ /** * The security constraints for this web application. */ - private volatile SecurityConstraint constraints[] = new SecurityConstraint[0]; + private volatile SecurityConstraint[] constraints = new SecurityConstraint[0]; private final Object constraintsLock = new Object(); @@ -359,13 +359,13 @@ /** * The set of filter configurations (and associated filter instances) we have initialized, keyed by filter name. */ - private Map filterConfigs = new HashMap<>(); // Guarded by filterDefs + private final Map filterConfigs = new HashMap<>(); // Guarded by filterDefs /** * The set of filter definitions for this application, keyed by filter name. */ - private Map filterDefs = new HashMap<>(); + private final Map filterDefs = new HashMap<>(); /** @@ -415,13 +415,13 @@ /** * The message destinations for this web application. */ - private HashMap messageDestinations = new HashMap<>(); + private final HashMap messageDestinations = new HashMap<>(); /** * The MIME mappings for this web application, keyed by extension. */ - private Map mimeMappings = new HashMap<>(); + private final ConcurrentMap mimeMappings = new ConcurrentHashMap<>(); /** @@ -490,13 +490,13 @@ /** * The security role mappings for this application, keyed by role name (as used within the application). */ - private Map roleMappings = new HashMap<>(); + private final Map roleMappings = new HashMap<>(); /** * The security roles for this application, keyed by role name. */ - private String securityRoles[] = new String[0]; + private String[] securityRoles = new String[0]; private final Object securityRolesLock = new Object(); @@ -504,7 +504,7 @@ /** * The servlet mappings for this web application, keyed by matching pattern. */ - private Map servletMappings = new HashMap<>(); + private final Map servletMappings = new HashMap<>(); private final Object servletMappingsLock = new Object(); @@ -517,7 +517,7 @@ /** * The notification sequence number. */ - private AtomicLong sequenceNumber = new AtomicLong(0); + private final AtomicLong sequenceNumber = new AtomicLong(0); /** @@ -535,7 +535,7 @@ /** * The watched resources for this application. */ - private String watchedResources[] = new String[0]; + private String[] watchedResources = new String[0]; private final Object watchedResourcesLock = new Object(); @@ -543,7 +543,7 @@ /** * The welcome files for this application. */ - private String welcomeFiles[] = new String[0]; + private String[] welcomeFiles = new String[0]; private final Object welcomeFilesLock = new Object(); @@ -552,7 +552,7 @@ * The set of classnames of LifecycleListeners that will be added to each newly created Wrapper by * createWrapper(). */ - private String wrapperLifecycles[] = new String[0]; + private String[] wrapperLifecycles = new String[0]; private final Object wrapperLifecyclesLock = new Object(); @@ -560,7 +560,7 @@ * The set of classnames of ContainerListeners that will be added to each newly created Wrapper by * createWrapper(). */ - private String wrapperListeners[] = new String[0]; + private String[] wrapperListeners = new String[0]; private final Object wrapperListenersLock = new Object(); @@ -731,7 +731,7 @@ private JspConfigDescriptor jspConfigDescriptor = null; - private Set resourceOnlyServlets = new HashSet<>(); + private final Set resourceOnlyServlets = new HashSet<>(); private String webappVersion = ""; @@ -742,7 +742,7 @@ /** * Servlets created via {@link ApplicationContext#createServlet(Class)} for tracking purposes. */ - private Set createdServlets = new HashSet<>(); + private final Set createdServlets = new HashSet<>(); private boolean preemptiveAuthentication = false; @@ -750,8 +750,8 @@ private boolean jndiExceptionOnFailedWrite = true; - private Map postConstructMethods = new HashMap<>(); - private Map preDestroyMethods = new HashMap<>(); + private final Map postConstructMethods = new HashMap<>(); + private final Map preDestroyMethods = new HashMap<>(); private String containerSciFilter; @@ -806,9 +806,49 @@ private int notFoundClassResourceCacheSize = 1000; + private EncodedSolidusHandling encodedReverseSolidusHandling = EncodedSolidusHandling.DECODE; + + private EncodedSolidusHandling encodedSolidusHandling = EncodedSolidusHandling.DECODE; + // ----------------------------------------------------- Context Properties + @Override + public String getEncodedReverseSolidusHandling() { + return encodedReverseSolidusHandling.getValue(); + } + + + @Override + public void setEncodedReverseSolidusHandling(String encodedReverseSolidusHandling) { + this.encodedReverseSolidusHandling = EncodedSolidusHandling.fromString(encodedReverseSolidusHandling); + } + + + @Override + public EncodedSolidusHandling getEncodedReverseSolidusHandlingEnum() { + return encodedReverseSolidusHandling; + } + + + @Override + public String getEncodedSolidusHandling() { + return encodedSolidusHandling.getValue(); + } + + + @Override + public void setEncodedSolidusHandling(String encodedSolidusHandling) { + this.encodedSolidusHandling = EncodedSolidusHandling.fromString(encodedSolidusHandling); + } + + + @Override + public EncodedSolidusHandling getEncodedSolidusHandlingEnum() { + return encodedSolidusHandling; + } + + public int getNotFoundClassResourceCacheSize() { return notFoundClassResourceCacheSize; } @@ -1109,11 +1149,7 @@ @Override public void setWebappVersion(String webappVersion) { - if (null == webappVersion) { - this.webappVersion = ""; - } else { - this.webappVersion = webappVersion; - } + this.webappVersion = Objects.requireNonNullElse(webappVersion, ""); } @@ -1143,7 +1179,7 @@ } for (String servletName : resourceOnlyServlets.split(",")) { servletName = servletName.trim(); - if (servletName.length() > 0) { + if (!servletName.isEmpty()) { this.resourceOnlyServlets.add(servletName); } } @@ -1322,10 +1358,10 @@ /** * {@inheritDoc} Note that this implementation is not thread safe. If two threads call this method concurrently, the - * result may be either set of listeners or a the union of both. + * result may be either set of listeners or the union of both. */ @Override - public void setApplicationEventListeners(Object listeners[]) { + public void setApplicationEventListeners(Object[] listeners) { applicationEventListenersList.clear(); if (listeners != null && listeners.length > 0) { applicationEventListenersList.addAll(Arrays.asList(listeners)); @@ -1350,7 +1386,7 @@ @Override - public void setApplicationLifecycleListeners(Object listeners[]) { + public void setApplicationLifecycleListeners(Object[] listeners) { applicationLifecycleListenersObjects = listeners; } @@ -1774,7 +1810,7 @@ Lock writeLock = loaderLock.writeLock(); writeLock.lock(); - Loader oldLoader = null; + Loader oldLoader; try { // Change components if necessary oldLoader = this.loader; @@ -1829,7 +1865,7 @@ Lock writeLock = managerLock.writeLock(); writeLock.lock(); - Manager oldManager = null; + Manager oldManager; try { // Change components if necessary oldManager = this.manager; @@ -2311,7 +2347,7 @@ Lock writeLock = resourcesLock.writeLock(); writeLock.lock(); - WebResourceRoot oldResources = null; + WebResourceRoot oldResources; try { if (getState().isAvailable()) { throw new IllegalStateException(sm.getString("standardContext.resourcesStart")); @@ -2416,8 +2452,8 @@ if (!workDir.isAbsolute()) { try { workDir = new File(getCatalinaBase().getCanonicalFile(), getWorkDir()); - } catch (IOException e) { - log.warn(sm.getString("standardContext.workPath", getName()), e); + } catch (IOException ioe) { + log.warn(sm.getString("standardContext.workPath", getName()), ioe); } } return workDir.getAbsolutePath(); @@ -2615,7 +2651,7 @@ return; } } - ApplicationParameter results[] = Arrays.copyOf(applicationParameters, applicationParameters.length + 1); + ApplicationParameter[] results = Arrays.copyOf(applicationParameters, applicationParameters.length + 1); results[applicationParameters.length] = parameter; applicationParameters = results; } @@ -2663,9 +2699,9 @@ public void addConstraint(SecurityConstraint constraint) { // Validate the proposed constraint - SecurityCollection collections[] = constraint.findCollections(); + SecurityCollection[] collections = constraint.findCollections(); for (SecurityCollection collection : collections) { - String patterns[] = collection.findPatterns(); + String[] patterns = collection.findPatterns(); for (int j = 0; j < patterns.length; j++) { patterns[j] = adjustURLPattern(patterns[j]); if (!validateURLPattern(patterns[j])) { @@ -2789,12 +2825,8 @@ @Override public void addMimeMapping(String extension, String mimeType) { - - synchronized (mimeMappings) { - mimeMappings.put(extension.toLowerCase(Locale.ENGLISH), mimeType); - } + mimeMappings.put(extension.toLowerCase(Locale.ENGLISH), mimeType); fireContainerEvent("addMimeMapping", extension); - } @@ -2930,7 +2962,7 @@ @Override public Wrapper createWrapper() { - Wrapper wrapper = null; + Wrapper wrapper; if (wrapperClass != null) { try { wrapper = (Wrapper) wrapperClass.getConstructor().newInstance(); @@ -3050,7 +3082,7 @@ /** - * @return the set of defined message destinations for this web application. If none have been defined, a + * @return the array of defined message destinations for this web application. If none have been defined, a * zero-length array is returned. */ public MessageDestination[] findMessageDestinations() { @@ -3068,9 +3100,7 @@ @Override public String[] findMimeMappings() { - synchronized (mimeMappings) { - return mimeMappings.keySet().toArray(new String[0]); - } + return mimeMappings.keySet().toArray(new String[0]); } @@ -3088,7 +3118,7 @@ @Override public String findRoleMapping(String role) { - String realRole = null; + String realRole; synchronized (roleMappings) { realRole = roleMappings.get(role); } @@ -3259,7 +3289,7 @@ // Remove the specified parameter int j = 0; - ApplicationParameter results[] = new ApplicationParameter[applicationParameters.length - 1]; + ApplicationParameter[] results = new ApplicationParameter[applicationParameters.length - 1]; for (int i = 0; i < applicationParameters.length; i++) { if (i != n) { results[j++] = applicationParameters[i]; @@ -3306,7 +3336,7 @@ // Remove the specified constraint int j = 0; - SecurityConstraint results[] = new SecurityConstraint[constraints.length - 1]; + SecurityConstraint[] results = new SecurityConstraint[constraints.length - 1]; for (int i = 0; i < constraints.length; i++) { if (i != n) { results[j++] = constraints[i]; @@ -3365,12 +3395,8 @@ @Override public void removeMimeMapping(String extension) { - - synchronized (mimeMappings) { - mimeMappings.remove(extension); - } + mimeMappings.remove(extension); fireContainerEvent("removeMimeMapping", extension); - } @@ -3411,7 +3437,7 @@ // Remove the specified security role int j = 0; - String results[] = new String[securityRoles.length - 1]; + String[] results = new String[securityRoles.length - 1]; for (int i = 0; i < securityRoles.length; i++) { if (i != n) { results[j++] = securityRoles[i]; @@ -3430,7 +3456,7 @@ @Override public void removeServletMapping(String pattern) { - String name = null; + String name; synchronized (servletMappingsLock) { name = servletMappings.remove(pattern); } @@ -3461,7 +3487,7 @@ // Remove the specified watched resource int j = 0; - String results[] = new String[watchedResources.length - 1]; + String[] results = new String[watchedResources.length - 1]; for (int i = 0; i < watchedResources.length; i++) { if (i != n) { results[j++] = watchedResources[i]; @@ -3495,7 +3521,7 @@ // Remove the specified welcome file int j = 0; - String results[] = new String[welcomeFiles.length - 1]; + String[] results = new String[welcomeFiles.length - 1]; for (int i = 0; i < welcomeFiles.length; i++) { if (i != n) { results[j++] = welcomeFiles[i]; @@ -3533,7 +3559,7 @@ // Remove the specified lifecycle listener int j = 0; - String results[] = new String[wrapperLifecycles.length - 1]; + String[] results = new String[wrapperLifecycles.length - 1]; for (int i = 0; i < wrapperLifecycles.length; i++) { if (i != n) { results[j++] = wrapperLifecycles[i]; @@ -3569,7 +3595,7 @@ // Remove the specified listener int j = 0; - String results[] = new String[wrapperListeners.length - 1]; + String[] results = new String[wrapperListeners.length - 1]; for (int i = 0; i < wrapperListeners.length; i++) { if (i != n) { results[j++] = wrapperListeners[i]; @@ -3777,7 +3803,7 @@ */ public void add(FilterMap filterMap) { synchronized (lock) { - FilterMap results[] = Arrays.copyOf(array, array.length + 1); + FilterMap[] results = Arrays.copyOf(array, array.length + 1); results[array.length] = filterMap; array = results; } @@ -3791,7 +3817,7 @@ */ public void addBefore(FilterMap filterMap) { synchronized (lock) { - FilterMap results[] = new FilterMap[array.length + 1]; + FilterMap[] results = new FilterMap[array.length + 1]; System.arraycopy(array, 0, results, 0, insertPoint); System.arraycopy(array, insertPoint, results, insertPoint + 1, array.length - insertPoint); results[insertPoint] = filterMap; @@ -3820,7 +3846,7 @@ } // Remove the specified filter mapping - FilterMap results[] = new FilterMap[array.length - 1]; + FilterMap[] results = new FilterMap[array.length - 1]; System.arraycopy(array, 0, results, 0, n); System.arraycopy(array, n + 1, results, n, (array.length - 1) - n); array = results; @@ -3857,9 +3883,9 @@ ApplicationFilterConfig filterConfig = new ApplicationFilterConfig(this, entry.getValue()); filterConfigs.put(name, filterConfig); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); - getLogger().error(sm.getString("standardContext.filterStart", name), t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + getLogger().error(sm.getString("standardContext.filterStart", name), throwable); ok = false; } } @@ -3923,8 +3949,8 @@ } // Instantiate the required listeners - String listeners[] = findApplicationListeners(); - Object results[] = new Object[listeners.length]; + String[] listeners = findApplicationListeners(); + Object[] results = new Object[listeners.length]; boolean ok = true; for (int i = 0; i < results.length; i++) { if (getLogger().isTraceEnabled()) { @@ -3934,9 +3960,9 @@ String listener = listeners[i]; results[i] = getInstanceManager().newInstance(listener); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); - getLogger().error(sm.getString("standardContext.applicationListener", listeners[i]), t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + getLogger().error(sm.getString("standardContext.applicationListener", listeners[i]), throwable); ok = false; } } @@ -3984,14 +4010,14 @@ getServletContext(); context.setNewServletContextListenerAllowed(false); - Object instances[] = getApplicationLifecycleListeners(); + Object[] instances = getApplicationLifecycleListeners(); if (instances == null || instances.length == 0) { return ok; } ServletContextEvent event = new ServletContextEvent(getServletContext()); ServletContextEvent tldEvent = null; - if (noPluggabilityListeners.size() > 0) { + if (!noPluggabilityListeners.isEmpty()) { noPluggabilityServletContext = new NoPluggabilityServletContext(getServletContext()); tldEvent = new ServletContextEvent(noPluggabilityServletContext); } @@ -4032,7 +4058,7 @@ } boolean ok = true; - Object listeners[] = getApplicationLifecycleListeners(); + Object[] listeners = getApplicationLifecycleListeners(); if (listeners != null && listeners.length > 0) { ServletContextEvent event = new ServletContextEvent(getServletContext()); ServletContextEvent tldEvent = null; @@ -4067,10 +4093,10 @@ getInstanceManager().destroyInstance(listeners[j]); } } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); getLogger().error(sm.getString("standardContext.listenerStop", listeners[j].getClass().getName()), - t); + throwable); ok = false; } } @@ -4089,10 +4115,10 @@ getInstanceManager().destroyInstance(listeners[j]); } } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); getLogger().error(sm.getString("standardContext.listenerStop", listeners[j].getClass().getName()), - t); + throwable); ok = false; } } @@ -4162,7 +4188,7 @@ * * @return true if load on startup was considered successful */ - public boolean loadOnStartup(Container children[]) { + public boolean loadOnStartup(Container[] children) { // Collect "load on startup" servlets that need to be initialized TreeMap> map = new TreeMap<>(); @@ -4304,7 +4330,7 @@ cl.setNotFoundClassResourceCacheSize(getNotFoundClassResourceCacheSize()); } - // By calling unbindThread and bindThread in a row, we setup the + // By calling unbindThread and bindThread in a row, we set up the // current Thread CCL to be the webapp classloader unbindThread(oldCCL); oldCCL = bindThread(); @@ -4365,8 +4391,8 @@ if ((getCluster() != null) && distributable) { try { contextManager = getCluster().createManager(getName()); - } catch (Exception ex) { - log.error(sm.getString("standardContext.cluster.managerError"), ex); + } catch (Exception e) { + log.error(sm.getString("standardContext.cluster.managerError"), e); ok = false; } } else { @@ -4567,7 +4593,7 @@ private void addInjectionTarget(Injectable resource, Map> injectionMap) { List injectionTargets = resource.getInjectionTargets(); - if (injectionTargets != null && injectionTargets.size() > 0) { + if (injectionTargets != null && !injectionTargets.isEmpty()) { String jndiName = resource.getName(); for (InjectionTarget injectionTarget : injectionTargets) { String clazz = injectionTarget.getTargetClass(); @@ -4586,12 +4612,12 @@ private void mergeParameters() { Map mergedParams = new HashMap<>(); - String names[] = findParameters(); + String[] names = findParameters(); for (String s : names) { mergedParams.put(s, findParameter(s)); } - ApplicationParameter params[] = findApplicationParameters(); + ApplicationParameter[] params = findApplicationParameters(); for (ApplicationParameter param : params) { if (param.getOverride()) { mergedParams.computeIfAbsent(param.getName(), k -> param.getValue()); @@ -4633,7 +4659,7 @@ } // Once the state is set to STOPPING, the Context will report itself as - // not available and any in progress async requests will timeout + // not available and any in progress async requests will time out setState(LifecycleState.STOPPING); // Binding thread @@ -4725,8 +4751,8 @@ // This object will no longer be visible or used. try { resetContext(); - } catch (Exception ex) { - log.error(sm.getString("standardContext.resetContextFail", getName()), ex); + } catch (Exception e) { + log.error(sm.getString("standardContext.resetContextFail", getName()), e); } // reset the instance manager @@ -4741,7 +4767,7 @@ /** * Destroy needs to clean up the context completely. The problem is that undoing all the config in start() and * restoring a 'fresh' state is impossible. After stop()/destroy()/init()/start() we should have the same state as - * if a fresh start was done - i.e read modified web.xml, etc. This can only be done by completely removing the + * if a fresh start was done - i.e. read modified web.xml, etc. This can only be done by completely removing the * context object and remapping a new one, or by cleaning up everything. */ @Override @@ -4869,7 +4895,7 @@ protected String adjustURLPattern(String urlPattern) { if (urlPattern == null) { - return urlPattern; + return null; } if (urlPattern.startsWith("/") || urlPattern.startsWith("*.")) { return urlPattern; @@ -4974,9 +5000,8 @@ if (isUseNaming()) { try { ContextBindings.bindThread(this, getNamingToken()); - } catch (NamingException e) { - // Silent catch, as this is a normal case during the early - // startup stages + } catch (NamingException ignore) { + // Silent catch, as this is a normal case during the early startup stages } } @@ -5126,7 +5151,7 @@ @Override public boolean fireRequestInitEvent(ServletRequest request) { - Object instances[] = getApplicationEventListeners(); + Object[] instances = getApplicationEventListeners(); if ((instances != null) && (instances.length > 0)) { @@ -5159,7 +5184,7 @@ @Override public boolean fireRequestDestroyEvent(ServletRequest request) { - Object instances[] = getApplicationEventListeners(); + Object[] instances = getApplicationEventListeners(); if ((instances != null) && (instances.length > 0)) { @@ -5263,7 +5288,7 @@ // Acquire (or calculate) the work directory path String workDir = getWorkDir(); - if (workDir == null || workDir.length() == 0) { + if (workDir == null || workDir.isEmpty()) { // Retrieve our parent (normally a host) name String hostName = null; @@ -5280,10 +5305,10 @@ engineName = parentEngine.getName(); } } - if ((hostName == null) || (hostName.length() < 1)) { + if ((hostName == null) || (hostName.isEmpty())) { hostName = "_"; } - if ((engineName == null) || (engineName.length() < 1)) { + if ((engineName == null) || (engineName.isEmpty())) { engineName = "_"; } @@ -5293,7 +5318,7 @@ } temp = temp.replace('/', '_'); temp = temp.replace('\\', '_'); - if (temp.length() < 1) { + if (temp.isEmpty()) { temp = ContextName.ROOT_NAME; } if (hostWorkDir != null) { @@ -5307,12 +5332,13 @@ // Create this directory if necessary File dir = new File(workDir); if (!dir.isAbsolute()) { - String catalinaHomePath = null; + String catalinaHomePath; try { catalinaHomePath = getCatalinaBase().getCanonicalPath(); dir = new File(catalinaHomePath, workDir); - } catch (IOException e) { - log.warn(sm.getString("standardContext.workCreateException", workDir, catalinaHomePath, getName()), e); + } catch (IOException ioe) { + log.warn(sm.getString("standardContext.workCreateException", workDir, getCatalinaBase(), getName()), + ioe); } } if (!dir.mkdirs() && !dir.isDirectory()) { @@ -5356,7 +5382,7 @@ if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) { return false; } - if (urlPattern.equals("")) { + if (urlPattern.isEmpty()) { return true; } if (urlPattern.startsWith("*.")) { @@ -5397,25 +5423,14 @@ @Override protected String getObjectNameKeyProperties() { - - StringBuilder keyProperties = new StringBuilder("j2eeType=WebModule,"); - keyProperties.append(getObjectKeyPropertiesNameOnly()); - keyProperties.append(",J2EEApplication="); - keyProperties.append(getJ2EEApplication()); - keyProperties.append(",J2EEServer="); - keyProperties.append(getJ2EEServer()); - - return keyProperties.toString(); + return "j2eeType=WebModule," + getObjectKeyPropertiesNameOnly() + ",J2EEApplication=" + getJ2EEApplication() + + ",J2EEServer=" + getJ2EEServer(); } private String getObjectKeyPropertiesNameOnly() { StringBuilder result = new StringBuilder("name=//"); String hostname = getParent().getName(); - if (hostname == null) { - result.append("DEFAULT"); - } else { - result.append(hostname); - } + result.append(Objects.requireNonNullElse(hostname, "DEFAULT")); String contextName = getName(); if (!contextName.startsWith("/")) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardContextValve.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardContextValve.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardContextValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardContextValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,8 +34,6 @@ * Valve that implements the default basic behavior for the StandardContext container implementation. *

    * USAGE CONSTRAINT: This implementation is likely to be useful only when processing HTTP requests. - * - * @author Craig R. McClanahan */ final class StandardContextValve extends ValveBase { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardEngine.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardEngine.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardEngine.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardEngine.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,8 +45,6 @@ /** * Standard implementation of the Engine interface. Each child container must be a Host implementation to process * the specific fully qualified host name of that virtual host. - * - * @author Craig R. McClanahan */ public class StandardEngine extends ContainerBase implements Engine { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardEngineValve.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardEngineValve.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardEngineValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardEngineValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,6 @@ * Valve that implements the default basic behavior for the StandardEngine container implementation. *

    * USAGE CONSTRAINT: This implementation is likely to be useful only when processing HTTP requests. - * - * @author Craig R. McClanahan */ final class StandardEngineValve extends ValveBase { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardHost.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardHost.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardHost.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardHost.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -49,9 +50,6 @@ /** * Standard implementation of the Host interface. Each child container must be a Context implementation to * process the requests directed to a particular web application. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class StandardHost extends ContainerBase implements Host { @@ -232,6 +230,12 @@ // Ignore } + Path appBasePath = file.toPath(); + Path basePath = getCatalinaBase().toPath(); + if (basePath.startsWith(appBasePath)) { + log.warn(sm.getString("standardHost.problematicAppBaseParent", getName())); + } + this.appBaseFile = file; return file; } @@ -239,7 +243,7 @@ @Override public void setAppBase(String appBase) { - if (appBase.trim().equals("")) { + if (appBase.trim().isEmpty()) { log.warn(sm.getString("standardHost.problematicAppBase", getName())); } String oldAppBase = this.appBase; @@ -275,6 +279,12 @@ // Ignore } + Path appBasePath = file.toPath(); + Path basePath = getCatalinaBase().toPath(); + if (basePath.startsWith(appBasePath)) { + log.warn(sm.getString("standardHost.problematicLegacyAppBaseParent", getName())); + } + this.legacyAppBaseFile = file; return file; } @@ -282,7 +292,7 @@ @Override public void setLegacyAppBase(String legacyAppBase) { - if (legacyAppBase.trim().equals("")) { + if (legacyAppBase.trim().isEmpty()) { log.warn(sm.getString("standardHost.problematicLegacyAppBase", getName())); } String oldLegacyAppBase = this.legacyAppBase; @@ -311,7 +321,7 @@ if (hostConfigBase != null) { return hostConfigBase; } - String path = null; + String path; if (getXmlBase() != null) { path = getXmlBase(); } else { @@ -331,7 +341,8 @@ } try { file = file.getCanonicalFile(); - } catch (IOException e) {// ignore + } catch (IOException ignore) { + // Ignore } this.hostConfigBase = file; return file; @@ -619,7 +630,7 @@ } } // Add this alias to the list - String newAliases[] = Arrays.copyOf(aliases, aliases.length + 1); + String[] newAliases = Arrays.copyOf(aliases, aliases.length + 1); newAliases[aliases.length] = alias; aliases = newAliases; } @@ -657,7 +668,7 @@ /** - * Used to ensure the regardless of {@link Context} implementation, a record is kept of the class loader used every + * Used to ensure that regardless of {@link Context} implementation, a record is kept of the class loader used every * time a context starts. */ private class MemoryLeakTrackingListener implements LifecycleListener { @@ -679,7 +690,7 @@ * reload. Note: This method attempts to force a full garbage collection. This should be used with extreme caution * on a production system. * - * @return a list of possibly leaking contexts + * @return an array of possibly leaking contexts */ public String[] findReloadedContextMemoryLeaks() { @@ -728,7 +739,7 @@ // Remove the specified alias int j = 0; - String results[] = new String[aliases.length - 1]; + String[] results = new String[aliases.length - 1]; for (int i = 0; i < aliases.length; i++) { if (i != n) { results[j++] = aliases[i]; @@ -749,7 +760,7 @@ // Set error report valve String errorValve = getErrorReportValveClass(); - if ((errorValve != null) && (!errorValve.equals(""))) { + if ((errorValve != null) && (!errorValve.isEmpty())) { try { boolean found = false; Valve[] valves = getPipeline().getValves(); @@ -802,11 +813,7 @@ @Override protected String getObjectNameKeyProperties() { - - StringBuilder keyProperties = new StringBuilder("type=Host"); - keyProperties.append(getMBeanKeyProperties()); - - return keyProperties.toString(); + return "type=Host" + getMBeanKeyProperties(); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardHostValve.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardHostValve.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardHostValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardHostValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ package org.apache.catalina.core; import java.io.IOException; +import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import jakarta.servlet.DispatcherType; @@ -43,9 +44,6 @@ * Valve that implements the default basic behavior for the StandardHost container implementation. *

    * USAGE CONSTRAINT: This implementation is likely to be useful only when processing HTTP requests. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ final class StandardHostValve extends ValveBase { @@ -211,8 +209,8 @@ response.finishResponse(); } catch (ClientAbortException e) { // Ignore - } catch (IOException e) { - container.getLogger().warn(sm.getString("standardHostValve.exception", errorPage), e); + } catch (IOException ioe) { + container.getLogger().warn(sm.getString("standardHostValve.exception", errorPage), ioe); } } } @@ -265,8 +263,8 @@ if (custom(request, response, errorPage)) { try { response.finishResponse(); - } catch (IOException e) { - container.getLogger().warn(sm.getString("standardHostValve.exception", errorPage), e); + } catch (IOException ioe) { + container.getLogger().warn(sm.getString("standardHostValve.exception", errorPage), ioe); } } } @@ -306,11 +304,7 @@ * Need to ensure message attribute is set even if there is no message (e.g. if error was triggered by an * exception with a null message). */ - if (message == null) { - request.setAttribute(RequestDispatcher.ERROR_MESSAGE, ""); - } else { - request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message); - } + request.setAttribute(RequestDispatcher.ERROR_MESSAGE, Objects.requireNonNullElse(message, "")); if (exception != null) { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, exception); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardPipeline.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardPipeline.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardPipeline.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardPipeline.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,8 +43,6 @@ * This implementation assumes that no calls to addValve() or removeValve are allowed while a * request is currently being processed. Otherwise, the mechanism by which per-thread state is maintained will need to * be modified. - * - * @author Craig R. McClanahan */ public class StandardPipeline extends LifecycleBase implements Pipeline { @@ -105,7 +103,7 @@ Valve valve = (first != null) ? first : basic; boolean supported = true; while (supported && valve != null) { - supported = supported & valve.isAsyncSupported(); + supported = valve.isAsyncSupported(); valve = valve.getNext(); } return supported; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardServer.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardServer.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardServer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardServer.java 2026-01-23 19:33:36.000000000 +0000 @@ -62,8 +62,6 @@ /** * Standard implementation of the Server interface, available for use (but not required) when deploying and * starting Catalina. - * - * @author Craig R. McClanahan */ public final class StandardServer extends LifecycleMBeanBase implements Server { @@ -105,7 +103,7 @@ /** * Global naming resources. */ - private NamingResourcesImpl globalNamingResources = null; + private NamingResourcesImpl globalNamingResources; /** @@ -462,7 +460,7 @@ servicesWriteLock.lock(); try { - Service results[] = new Service[services.length + 1]; + Service[] results = new Service[services.length + 1]; System.arraycopy(services, 0, results, 0, services.length); results[services.length] = service; services = results; @@ -491,14 +489,14 @@ awaitSocket = null; try { s.close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignored } } t.interrupt(); try { t.join(1000); - } catch (InterruptedException e) { + } catch (InterruptedException ignore) { // Ignored } } @@ -531,9 +529,9 @@ // Set up a server socket to wait on try { awaitSocket = new ServerSocket(getPortWithOffset(), 1, InetAddress.getByName(address)); - } catch (IOException e) { + } catch (IOException ioe) { log.error(sm.getString("standardServer.awaitSocket.fail", address, String.valueOf(getPortWithOffset()), - String.valueOf(getPort()), String.valueOf(getPortOffset())), e); + String.valueOf(getPort()), String.valueOf(getPortOffset())), ioe); return; } @@ -566,12 +564,12 @@ } catch (AccessControlException ace) { log.warn(sm.getString("standardServer.accept.security"), ace); continue; - } catch (IOException e) { + } catch (IOException ioe) { if (stopAwait) { // Wait was aborted with socket.close() break; } - log.error(sm.getString("standardServer.accept.error"), e); + log.error(sm.getString("standardServer.accept.error"), ioe); break; } @@ -584,11 +582,11 @@ expected += random.nextInt() % 1024; } while (expected > 0) { - int ch = -1; + int ch; try { ch = stream.read(); - } catch (IOException e) { - log.warn(sm.getString("standardServer.accept.readError"), e); + } catch (IOException ioe) { + log.warn(sm.getString("standardServer.accept.readError"), ioe); ch = -1; } // Control character or EOF (-1) terminates loop @@ -604,7 +602,7 @@ if (socket != null) { socket.close(); } - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } @@ -627,7 +625,7 @@ if (serverSocket != null) { try { serverSocket.close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } @@ -797,7 +795,7 @@ try { // Note: Hard-coded domain used since this object is per Server/JVM ObjectName sname = new ObjectName("Catalina:type=StoreConfig"); - MBeanServer server = Registry.getRegistry(null, null).getMBeanServer(); + MBeanServer server = Registry.getRegistry(null).getMBeanServer(); if (server.isRegistered(sname)) { server.invoke(sname, "storeConfig", null, null); } else { @@ -824,7 +822,7 @@ try { // Note: Hard-coded domain used since this object is per Server/JVM ObjectName sname = new ObjectName("Catalina:type=StoreConfig"); - MBeanServer server = Registry.getRegistry(null, null).getMBeanServer(); + MBeanServer server = Registry.getRegistry(null).getMBeanServer(); if (server.isRegistered(sname)) { server.invoke(sname, "store", new Object[] { context }, new String[] { "java.lang.String" }); } else { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardService.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardService.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardService.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardService.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,8 +46,6 @@ /** * Standard implementation of the Service interface. The associated Container is generally an instance of * Engine, but this is not required. - * - * @author Craig R. McClanahan */ public class StandardService extends LifecycleMBeanBase implements Service { @@ -78,7 +76,7 @@ /** * The set of Connectors associated with this Service. */ - protected Connector connectors[] = new Connector[0]; + protected Connector[] connectors = new Connector[0]; private final ReadWriteLock connectorsLock = new ReentrantReadWriteLock(); /** @@ -206,7 +204,7 @@ writeLock.lock(); try { connector.setService(this); - Connector results[] = new Connector[connectors.length + 1]; + Connector[] results = new Connector[connectors.length + 1]; System.arraycopy(connectors, 0, results, 0, connectors.length); results[connectors.length] = connector; connectors = results; @@ -231,7 +229,7 @@ Lock readLock = connectorsLock.readLock(); readLock.lock(); try { - ObjectName results[] = new ObjectName[connectors.length]; + ObjectName[] results = new ObjectName[connectors.length]; for (int i = 0; i < results.length; i++) { results[i] = connectors[i].getObjectName(); } @@ -282,7 +280,7 @@ return; } int k = 0; - Connector results[] = new Connector[connectors.length - 1]; + Connector[] results = new Connector[connectors.length - 1]; for (int i = 0; i < connectors.length; i++) { if (i != j) { results[k++] = connectors[i]; @@ -320,10 +318,7 @@ @Override public String toString() { - StringBuilder sb = new StringBuilder("StandardService["); - sb.append(getName()); - sb.append(']'); - return sb.toString(); + return "StandardService[" + getName() + "]"; } @@ -378,7 +373,7 @@ @Override public void removeExecutor(Executor ex) { - boolean removed = false; + boolean removed; executorsLock.writeLock().lock(); try { removed = executors.remove(ex); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardWrapper.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardWrapper.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardWrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardWrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,6 +23,7 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -64,9 +65,6 @@ /** * Standard implementation of the Wrapper interface that represents an individual servlet definition. No child * Containers are allowed, and the parent Container must be a Context. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class StandardWrapper extends ContainerBase implements ServletConfig, Wrapper, NotificationEmitter { @@ -101,7 +99,7 @@ protected long available = 0L; /** - * The broadcaster that sends j2ee notifications. + * The broadcaster that sends EE notifications. */ protected final NotificationBroadcasterSupport broadcaster; @@ -200,7 +198,7 @@ protected boolean swallowOutput = false; // To support jmx attributes - protected StandardWrapperValve swValve; + StandardWrapperValve swValve; protected long loadTime = 0; protected int classLoadTime = 0; @@ -429,15 +427,20 @@ for (int i = 0; methods != null && i < methods.length; i++) { Method m = methods[i]; - if (m.getName().equals("doGet")) { - allow.add("GET"); - allow.add("HEAD"); - } else if (m.getName().equals("doPost")) { - allow.add("POST"); - } else if (m.getName().equals("doPut")) { - allow.add("PUT"); - } else if (m.getName().equals("doDelete")) { - allow.add("DELETE"); + switch (m.getName()) { + case "doGet": + allow.add("GET"); + allow.add("HEAD"); + break; + case "doPost": + allow.add("POST"); + break; + case "doPut": + allow.add("PUT"); + break; + case "doDelete": + allow.add("DELETE"); + break; } } } @@ -483,7 +486,7 @@ */ public static Throwable getRootCause(ServletException e) { Throwable rootCause = e; - Throwable rootCauseCheck = null; + Throwable rootCauseCheck; // Extra aggressive rootCause finding int loops = 0; do { @@ -579,9 +582,9 @@ countAllocated.incrementAndGet(); } catch (ServletException e) { throw e; - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - throw new ServletException(sm.getString("standardWrapper.allocate"), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + throw new ServletException(sm.getString("standardWrapper.allocate"), t); } } if (!instanceInitialized) { @@ -649,7 +652,7 @@ @Override public String findSecurityReference(String name) { - String reference = null; + String reference; referencesLock.readLock().lock(); try { @@ -715,9 +718,9 @@ try { jspMonitorON = new ObjectName(oname.toString()); - Registry.getRegistry(null, null).registerComponent(instance, jspMonitorON, null); - } catch (Exception ex) { - log.warn(sm.getString("standardWrapper.jspMonitorError", instance)); + Registry.getRegistry(null).registerComponent(instance, jspMonitorON, null); + } catch (Exception e) { + log.warn(sm.getString("standardWrapper.jspMonitorError", instance), e); } } } @@ -760,19 +763,19 @@ unavailable(null); // Restore the context ClassLoader throw new ServletException(sm.getString("standardWrapper.notServlet", servletClass), e); - } catch (Throwable e) { - e = ExceptionUtils.unwrapInvocationTargetException(e); - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); unavailable(null); // Added extra log statement for Bugzilla 36630: // https://bz.apache.org/bugzilla/show_bug.cgi?id=36630 if (log.isDebugEnabled()) { - log.debug(sm.getString("standardWrapper.instantiate", servletClass), e); + log.debug(sm.getString("standardWrapper.instantiate", servletClass), throwable); } // Restore the context ClassLoader - throw new ServletException(sm.getString("standardWrapper.instantiate", servletClass), e); + throw new ServletException(sm.getString("standardWrapper.instantiate", servletClass), throwable); } if (multipartConfigElement == null) { @@ -799,7 +802,7 @@ } finally { if (swallowOutput) { String log = SystemLogHandler.stopCapture(); - if (log != null && log.length() > 0) { + if (log != null && !log.isEmpty()) { if (getServletContext() != null) { getServletContext().log(log); } else { @@ -961,11 +964,11 @@ } } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); fireContainerEvent("unload", this); unloading = false; - throw new ServletException(sm.getString("standardWrapper.destroyException", getName()), t); + throw new ServletException(sm.getString("standardWrapper.destroyException", getName()), throwable); } finally { // Annotation processing if (!((Context) getParent()).getIgnoreAnnotations()) { @@ -979,7 +982,7 @@ // Write captured output if (swallowOutput) { String log = SystemLogHandler.stopCapture(); - if (log != null && log.length() > 0) { + if (log != null && !log.isEmpty()) { if (getServletContext() != null) { getServletContext().log(log); } else { @@ -996,7 +999,7 @@ instance = null; if (isJspServlet && jspMonitorON != null) { - Registry.getRegistry(null, null).unregisterComponent(jspMonitorON); + Registry.getRegistry(null).unregisterComponent(jspMonitorON); } unloading = false; @@ -1223,11 +1226,11 @@ if (this.getObjectName() != null) { Notification notification = new Notification("j2ee.state.stopped", this.getObjectName(), sequenceNumber++); broadcaster.sendNotification(notification); - } - // Send j2ee.object.deleted notification - Notification notification = new Notification("j2ee.object.deleted", this.getObjectName(), sequenceNumber++); - broadcaster.sendNotification(notification); + // Send j2ee.object.deleted notification + notification = new Notification("j2ee.object.deleted", this.getObjectName(), sequenceNumber++); + broadcaster.sendNotification(notification); + } } @@ -1257,11 +1260,7 @@ StringBuilder keyProperties = new StringBuilder(",WebModule=//"); String hostName = getParent().getParent().getName(); - if (hostName == null) { - keyProperties.append("DEFAULT"); - } else { - keyProperties.append(hostName); - } + keyProperties.append(Objects.requireNonNullElse(hostName, "DEFAULT")); String contextName = getParent().getName(); if (!contextName.startsWith("/")) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardWrapperFacade.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardWrapperFacade.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardWrapperFacade.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardWrapperFacade.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Facade for the StandardWrapper object. - * - * @author Remy Maucherat */ public final class StandardWrapperFacade implements ServletConfig { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/StandardWrapperValve.java tomcat10-10.1.52/java/org/apache/catalina/core/StandardWrapperValve.java --- tomcat10-10.1.34/java/org/apache/catalina/core/StandardWrapperValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/StandardWrapperValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,8 +44,6 @@ /** * Valve that implements the default basic behavior for the StandardWrapper container implementation. - * - * @author Craig R. McClanahan */ final class StandardWrapperValve extends ValveBase { @@ -122,12 +120,12 @@ StandardWrapper.getRootCause(e)); throwable = e; exception(request, response, e); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), e); - throwable = e; - exception(request, response, e); - servlet = null; + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + container.getLogger().error(sm.getString("standardWrapper.allocateException", wrapper.getName()), t); + throwable = t; + exception(request, response, t); + // servlet = null; is set here } MessageBytes requestPathMB = request.getRequestPathMB(); @@ -156,7 +154,7 @@ } } finally { String log = SystemLogHandler.stopCapture(); - if (log != null && log.length() > 0) { + if (log != null && !log.isEmpty()) { context.getLogger().info(log); } } @@ -183,11 +181,11 @@ } throwable = e; exception(request, response, e); - } catch (IOException e) { + } catch (IOException ioe) { container.getLogger() - .error(sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), e); - throwable = e; - exception(request, response, e); + .error(sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), ioe); + throwable = ioe; + exception(request, response, ioe); } catch (UnavailableException e) { container.getLogger() .error(sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), e); @@ -203,12 +201,12 @@ } throwable = e; exception(request, response, e); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); container.getLogger() - .error(sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), e); - throwable = e; - exception(request, response, e); + .error(sm.getString("standardWrapper.serviceException", wrapper.getName(), context.getName()), t); + throwable = t; + exception(request, response, t); } finally { // Release the filter chain (if any) for this request if (filterChain != null) { @@ -220,12 +218,12 @@ if (servlet != null) { wrapper.deallocate(servlet); } - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - container.getLogger().error(sm.getString("standardWrapper.deallocateException", wrapper.getName()), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + container.getLogger().error(sm.getString("standardWrapper.deallocateException", wrapper.getName()), t); if (throwable == null) { - throwable = e; - exception(request, response, e); + throwable = t; + exception(request, response, t); } } @@ -235,11 +233,11 @@ if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) { wrapper.unload(); } - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - container.getLogger().error(sm.getString("standardWrapper.unloadException", wrapper.getName()), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + container.getLogger().error(sm.getString("standardWrapper.unloadException", wrapper.getName()), t); if (throwable == null) { - exception(request, response, e); + exception(request, response, t); } } long t2 = System.currentTimeMillis(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java tomcat10-10.1.52/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java --- tomcat10-10.1.34/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/core/ThreadLocalLeakPreventionListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -73,8 +73,7 @@ stopIdleThreads((Context) lifecycle); } } catch (Exception e) { - String msg = sm.getString("threadLocalLeakPreventionListener.lifecycleEvent.error", event); - log.error(msg, e); + log.error(sm.getString("threadLocalLeakPreventionListener.lifecycleEvent.error", event), e); } } @@ -83,8 +82,7 @@ try { super.containerEvent(event); } catch (Exception e) { - String msg = sm.getString("threadLocalLeakPreventionListener.containerEvent.error", event); - log.error(msg, e); + log.error(sm.getString("threadLocalLeakPreventionListener.containerEvent.error", event), e); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/deploy/NamingResourcesImpl.java tomcat10-10.1.52/java/org/apache/catalina/deploy/NamingResourcesImpl.java --- tomcat10-10.1.34/java/org/apache/catalina/deploy/NamingResourcesImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/deploy/NamingResourcesImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -61,8 +61,6 @@ /** * Holds and manages the naming resources defined in the Jakarta EE Naming Context and their associated JNDI context. - * - * @author Remy Maucherat */ public class NamingResourcesImpl extends LifecycleMBeanBase implements Serializable, NamingResources { @@ -208,7 +206,7 @@ String ejbLink = ejb.getLink(); String lookupName = ejb.getLookupName(); - if (ejbLink != null && ejbLink.length() > 0 && lookupName != null && lookupName.length() > 0) { + if (ejbLink != null && !ejbLink.isEmpty() && lookupName != null && !lookupName.isEmpty()) { throw new IllegalArgumentException(sm.getString("namingResources.ejbLookupLink", ejb.getName())); } @@ -260,12 +258,12 @@ String lookupName = environment.getLookupName(); // Entries with injection targets but no value are effectively ignored - if (injectionTargets != null && injectionTargets.size() > 0 && (value == null || value.length() == 0)) { + if (injectionTargets != null && !injectionTargets.isEmpty() && (value == null || value.isEmpty())) { return; } // Entries with lookup-name and value are an error (EE.5.4.1.3) - if (value != null && value.length() > 0 && lookupName != null && lookupName.length() > 0) { + if (value != null && !value.isEmpty() && lookupName != null && !lookupName.isEmpty()) { throw new IllegalArgumentException( sm.getString("namingResources.envEntryLookupValue", environment.getName())); } @@ -513,8 +511,8 @@ /** - * @return the set of defined environment entries for this web application. If none have been defined, a zero-length - * array is returned. + * @return the array of defined environment entries for this web application. If none have been defined, a + * zero-length array is returned. */ public ContextEnvironment[] findEnvironments() { @@ -649,8 +647,8 @@ /** - * @return the set of resource environment reference names for this web application. If none have been specified, a - * zero-length array is returned. + * @return the array of resource environment reference names for this web application. If none have been specified, + * a zero-length array is returned. */ public ContextResourceEnvRef[] findResourceEnvRefs() { @@ -697,7 +695,7 @@ entries.remove(name); - ContextEjb ejb = null; + ContextEjb ejb; synchronized (ejbs) { ejb = ejbs.remove(name); } @@ -714,7 +712,7 @@ entries.remove(name); - ContextEnvironment environment = null; + ContextEnvironment environment; synchronized (envs) { environment = envs.remove(name); } @@ -742,7 +740,7 @@ entries.remove(name); - ContextLocalEjb localEjb = null; + ContextLocalEjb localEjb; synchronized (localEjbs) { localEjb = localEjbs.remove(name); } @@ -763,7 +761,7 @@ entries.remove(name); - MessageDestinationRef mdr = null; + MessageDestinationRef mdr; synchronized (mdrs) { mdr = mdrs.remove(name); } @@ -792,7 +790,7 @@ entries.remove(name); - ContextResource resource = null; + ContextResource resource; synchronized (resources) { resource = resources.remove(name); } @@ -820,7 +818,7 @@ entries.remove(name); - ContextResourceEnvRef resourceEnvRef = null; + ContextResourceEnvRef resourceEnvRef; synchronized (resourceEnvRefs) { resourceEnvRef = resourceEnvRefs.remove(name); } @@ -837,7 +835,7 @@ entries.remove(name); - ContextResourceLink resourceLink = null; + ContextResourceLink resourceLink; synchronized (resourceLinks) { resourceLink = resourceLinks.remove(name); } @@ -865,7 +863,7 @@ entries.remove(name); - ContextService service = null; + ContextService service; synchronized (services) { service = services.remove(name); } @@ -931,7 +929,7 @@ * Close those resources that an explicit close may help clean-up faster. */ private void cleanUp() { - if (resources.size() == 0) { + if (resources.isEmpty()) { return; } javax.naming.Context ctxt; @@ -949,7 +947,7 @@ for (ContextResource cr : resources.values()) { if (cr.getSingleton()) { String closeMethod = cr.getCloseMethod(); - if (closeMethod != null && closeMethod.length() > 0) { + if (closeMethod != null && !closeMethod.isEmpty()) { String name = cr.getName(); Object resource; try { @@ -973,14 +971,14 @@ */ private void cleanUp(Object resource, String name, String closeMethod) { // Look for a zero-arg close() method - Method m = null; + Method m; try { m = resource.getClass().getMethod(closeMethod, (Class[]) null); } catch (SecurityException e) { - log.debug(sm.getString("namingResources.cleanupCloseSecurity", closeMethod, name, container)); + log.debug(sm.getString("namingResources.cleanupCloseSecurity", closeMethod, name, container), e); return; } catch (NoSuchMethodException e) { - log.debug(sm.getString("namingResources.cleanupNoClose", name, container, closeMethod)); + log.debug(sm.getString("namingResources.cleanupNoClose", name, container, closeMethod), e); return; } try { @@ -1069,7 +1067,7 @@ return true; } - if (resource.getInjectionTargets() == null || resource.getInjectionTargets().size() == 0) { + if (resource.getInjectionTargets() == null || resource.getInjectionTargets().isEmpty()) { // No injection targets so use the defined type for the resource return true; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/AddDefaultCharsetFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/AddDefaultCharsetFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/AddDefaultCharsetFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/AddDefaultCharsetFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -63,7 +63,7 @@ @Override public void init(FilterConfig filterConfig) throws ServletException { super.init(filterConfig); - if (encoding == null || encoding.length() == 0 || encoding.equalsIgnoreCase("default")) { + if (encoding == null || encoding.isEmpty() || encoding.equalsIgnoreCase("default")) { encoding = DEFAULT_ENCODING; } else if (encoding.equalsIgnoreCase("system")) { encoding = Charset.defaultCharset().name(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/Constants.java tomcat10-10.1.52/java/org/apache/catalina/filters/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,11 +16,10 @@ */ package org.apache.catalina.filters; +import org.apache.tomcat.util.http.Method; /** * Manifest constants for this Java package. - * - * @author Craig R. McClanahan */ public final class Constants { @@ -30,7 +29,7 @@ public static final String CSRF_NONCE_SESSION_ATTR_NAME = "org.apache.catalina.filters.CSRF_NONCE"; /** - * The request attribute key under which the current requests's CSRF nonce can be found. + * The request attribute key under which the current request's CSRF nonce can be found. */ public static final String CSRF_NONCE_REQUEST_ATTR_NAME = "org.apache.catalina.filters.CSRF_REQUEST_NONCE"; @@ -44,7 +43,8 @@ */ public static final String CSRF_NONCE_REQUEST_PARAM_NAME_KEY = "org.apache.catalina.filters.CSRF_NONCE_PARAM_NAME"; - public static final String METHOD_GET = "GET"; + @Deprecated + public static final String METHOD_GET = Method.GET; public static final String CSRF_REST_NONCE_HEADER_NAME = "X-CSRF-Token"; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/CorsFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/CorsFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/CorsFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/CorsFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,9 +19,7 @@ import java.io.IOException; import java.io.ObjectInputStream; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; @@ -38,6 +36,7 @@ import org.apache.catalina.Globals; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.RequestUtil; import org.apache.tomcat.util.http.ResponseUtil; import org.apache.tomcat.util.http.parser.MediaType; @@ -402,14 +401,14 @@ // Local copy to avoid concurrency issues if getExposedHeaders() // is overridden. Collection exposedHeaders = getExposedHeaders(); - if (exposedHeaders != null && exposedHeaders.size() > 0) { + if (exposedHeaders != null && !exposedHeaders.isEmpty()) { String exposedHeadersString = join(exposedHeaders, ","); response.addHeader(RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS, exposedHeadersString); } - if ("OPTIONS".equals(method)) { + if (Method.OPTIONS.equals(method)) { // For an OPTIONS request, the response will vary based on the - // value or absence of the following headers. Hence they need be be + // value or absence of the following headers. Hence, they need to be // included in the Vary header. ResponseUtil.addVaryFieldName(response, REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD); ResponseUtil.addVaryFieldName(response, REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS); @@ -538,59 +537,47 @@ * @return the CORS type */ protected CORSRequestType checkRequestType(final HttpServletRequest request) { - CORSRequestType requestType = CORSRequestType.INVALID_CORS; if (request == null) { throw new IllegalArgumentException(sm.getString("corsFilter.nullRequest")); } String originHeader = request.getHeader(REQUEST_HEADER_ORIGIN); - // Section 6.1.1 and Section 6.2.1 - if (originHeader != null) { - if (originHeader.isEmpty()) { - requestType = CORSRequestType.INVALID_CORS; - } else if (!RequestUtil.isValidOrigin(originHeader)) { - requestType = CORSRequestType.INVALID_CORS; - } else if (RequestUtil.isSameOrigin(request, originHeader)) { - return CORSRequestType.NOT_CORS; - } else { - String method = request.getMethod(); - if (method != null) { - if ("OPTIONS".equals(method)) { - String accessControlRequestMethodHeader = - request.getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD); - if (accessControlRequestMethodHeader != null && !accessControlRequestMethodHeader.isEmpty()) { - requestType = CORSRequestType.PRE_FLIGHT; - } else if (accessControlRequestMethodHeader != null && - accessControlRequestMethodHeader.isEmpty()) { - requestType = CORSRequestType.INVALID_CORS; - } else { - requestType = CORSRequestType.ACTUAL; - } - } else if ("GET".equals(method) || "HEAD".equals(method)) { - requestType = CORSRequestType.SIMPLE; - } else if ("POST".equals(method)) { - String mediaType = MediaType.parseMediaTypeOnly(request.getContentType()); - if (mediaType == null) { - requestType = CORSRequestType.SIMPLE; - } else { - if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES.contains(mediaType)) { - requestType = CORSRequestType.SIMPLE; - } else { - requestType = CORSRequestType.ACTUAL; - } - } - } else { - requestType = CORSRequestType.ACTUAL; + if (originHeader == null) { + return CORSRequestType.NOT_CORS; + } + if (originHeader.isEmpty() || !RequestUtil.isValidOrigin(originHeader)) { + return CORSRequestType.INVALID_CORS; + } + if (RequestUtil.isSameOrigin(request, originHeader)) { + return CORSRequestType.NOT_CORS; + } + String method = request.getMethod(); + if (method == null) { + return CORSRequestType.INVALID_CORS; + } + switch (method) { + case Method.OPTIONS: + String accessControlRequestMethodHeader = + request.getHeader(REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD); + if (accessControlRequestMethodHeader != null) { + if (accessControlRequestMethodHeader.isEmpty()) { + return CORSRequestType.INVALID_CORS; } + return CORSRequestType.PRE_FLIGHT; } - } - } else { - requestType = CORSRequestType.NOT_CORS; + return CORSRequestType.ACTUAL; + case Method.GET: + case Method.HEAD: + return CORSRequestType.SIMPLE; + case Method.POST: + String mediaType = MediaType.parseMediaTypeOnly(request.getContentType()); + if (mediaType == null || SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES.contains(mediaType)) { + return CORSRequestType.SIMPLE; + } + break; } - - return requestType; + return CORSRequestType.ACTUAL; } - /** * Checks if the Origin is allowed to make a CORS request. * @@ -683,17 +670,15 @@ private Set parseStringToSet(final String data) { String[] splits; - if (data != null && data.length() > 0) { + if (data != null && !data.isEmpty()) { splits = data.split(","); } else { splits = new String[] {}; } Set set = new HashSet<>(); - if (splits.length > 0) { - for (String split : splits) { - set.add(split.trim()); - } + for (String split : splits) { + set.add(split.trim()); } return set; @@ -908,8 +893,8 @@ * * @see http://www.w3.org/TR/cors/#terminology */ - public static final Collection SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES = Collections.unmodifiableSet( - new HashSet<>(Arrays.asList(Globals.CONTENT_TYPE_FORM_URL_ENCODING, "multipart/form-data", "text/plain"))); + public static final Collection SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES = + Set.of(Globals.CONTENT_TYPE_FORM_URL_ENCODING, "multipart/form-data", "text/plain"); // ------------------------------------------------ Configuration Defaults /** diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/CsrfPreventionFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/CsrfPreventionFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/CsrfPreventionFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/CsrfPreventionFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,6 +41,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.http.Method; /** * Provides basic CSRF protection for a web application. The filter assumes that: @@ -103,7 +104,7 @@ * @param entryPoints Comma separated list of URLs to be configured as entry points. */ public void setEntryPoints(String entryPoints) { - String values[] = entryPoints.split(","); + String[] values = entryPoints.split(","); for (String value : values) { this.entryPoints.add(value.trim()); } @@ -176,7 +177,7 @@ * @return A collection of predicates representing the URL patterns. */ protected static Collection> createNoNoncePredicates(ServletContext context, String patterns) { - if (null == patterns || 0 == patterns.trim().length()) { + if (null == patterns || patterns.trim().isEmpty()) { return null; } @@ -184,7 +185,7 @@ return Collections.singleton(new PatternPredicate(patterns.substring(1, patterns.length() - 1))); } - String values[] = patterns.split(","); + String[] values = patterns.split(","); ArrayList> matchers = new ArrayList<>(values.length); for (String value : values) { @@ -209,7 +210,7 @@ * @return A Predicate which can match the specified pattern, or null if the pattern is null or blank. */ protected static Predicate createNoNoncePredicate(ServletContext context, String pattern) { - if (null == pattern || 0 == pattern.trim().length()) { + if (null == pattern || pattern.trim().isEmpty()) { return null; } if (pattern.startsWith("mime:")) { @@ -446,7 +447,7 @@ } protected boolean skipNonceCheck(HttpServletRequest request) { - if (!Constants.METHOD_GET.equals(request.getMethod())) { + if (!Method.GET.equals(request.getMethod())) { return false; } @@ -477,7 +478,7 @@ /** - * Determines whether a nonce should be created. This method is provided primarily for the benefit of sub-classes + * Determines whether a nonce should be created. This method is provided primarily for the benefit of subclasses * that wish to customise this behaviour. * * @param request The request that triggered the need to potentially create the nonce. @@ -491,7 +492,7 @@ /** * Create a new {@link NonceCache} and store in the {@link HttpSession}. This method is provided primarily for the - * benefit of sub-classes that wish to customise this behaviour. + * benefit of subclasses that wish to customise this behaviour. * * @param request The request that triggered the need to create the nonce cache. Unused by the default * implementation. @@ -511,7 +512,7 @@ /** * Obtain the {@link NonceCache} associated with the request and/or session. This method is provided primarily for - * the benefit of sub-classes that wish to customise this behaviour. + * the benefit of subclasses that wish to customise this behaviour. * * @param request The request that triggered the need to obtain the nonce cache. Unused by the default * implementation. @@ -545,6 +546,8 @@ @Override public String encodeRedirectURL(String url) { + url = removeQueryParameters(url, nonceRequestParameterName); + if (shouldAddNonce(url)) { return addNonce(super.encodeRedirectURL(url)); } else { @@ -554,6 +557,8 @@ @Override public String encodeURL(String url) { + url = removeQueryParameters(url, nonceRequestParameterName); + if (shouldAddNonce(url)) { return addNonce(super.encodeURL(url)); } else { @@ -566,21 +571,98 @@ return true; } - if (null != noNoncePatterns) { - for (Predicate p : noNoncePatterns) { - if (p.test(url)) { - return false; - } + for (Predicate p : noNoncePatterns) { + if (p.test(url)) { + return false; } } return true; } + /** + * Removes zero or more query parameters from a URL. All instances of the query parameter and any associated + * values will be removed. + * + * @param url The URL whose query parameters should be removed. + * @param parameterName The name of the parameter to remove. + * + * @return The URL without any instances of the query parameter parameterName present. + */ + public static String removeQueryParameters(String url, String parameterName) { + if (null != parameterName) { + // Check for query string + int q = url.indexOf('?'); + if (q > -1) { + + // Look for parameter end + int start = q + 1; + int pos = url.indexOf('&', start); + + int iterations = 0; + + while (-1 < pos) { + // Process all parameters + if (++iterations > 100000) { + // Just in case things get out of control + throw new IllegalStateException("Way too many loop iterations"); + } + + int eq = url.indexOf('=', start); + int paramNameEnd; + if (-1 == eq || eq > pos) { + paramNameEnd = pos; + // Found no equal sign at all or past next & ; Parameter is all name. + } else { + // Found this param's equal sign + paramNameEnd = eq; + } + if (parameterName.equals(url.substring(start, paramNameEnd))) { + // Remove the parameter + url = url.substring(0, start) + url.substring(pos + 1); // +1 to consume the & + } else { + start = pos + 1; // Go to next parameter + } + pos = url.indexOf('&', start); + } + + // Check final parameter + String paramName; + pos = url.indexOf('=', start); + + if (-1 < pos) { + paramName = url.substring(start, pos); + } else { + paramName = url.substring(start); + // No value + } + if (paramName.equals(parameterName)) { + // Remove this parameter + + // Remove any trailing ? or & as well + char c = url.charAt(start - 1); + if ('?' == c || '&' == c) { + start--; + } + + url = url.substring(0, start); + } else { + // Remove trailing ? if it's there. Is this worth it? + int length = url.length(); + if (length == q + 1) { + url = url.substring(0, length - 1); + } + } + } + } + + return url; + } + /* * Return the specified URL with the nonce added to the query string. * - * @param url URL to be modified + * @param url the URL to be modified */ private String addNonce(String url) { @@ -602,7 +684,7 @@ path = path.substring(0, question); } StringBuilder sb = new StringBuilder(path); - if (query.length() > 0) { + if (!query.isEmpty()) { sb.append(query); sb.append('&'); } else { @@ -644,10 +726,7 @@ @Override protected boolean removeEldestEntry(Map.Entry eldest) { - if (size() > cacheSize) { - return true; - } - return false; + return size() > cacheSize; } }; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java tomcat10-10.1.52/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/CsrfPreventionFilterBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -79,9 +79,7 @@ Class clazz = Class.forName(randomClass); randomSource = (Random) clazz.getConstructor().newInstance(); } catch (ReflectiveOperationException e) { - ServletException se = - new ServletException(sm.getString("csrfPrevention.invalidRandomClass", randomClass), e); - throw se; + throw new ServletException(sm.getString("csrfPrevention.invalidRandomClass", randomClass), e); } } @@ -95,12 +93,12 @@ * Generate a once time token (nonce) for authenticating subsequent requests. The nonce generation is a simplified * version of ManagerBase.generateSessionId(). * - * @param request The request. Unused in this method but present for the the benefit of sub-classes. + * @param request The request. Unused in this method but present for the benefit of subclasses. * * @return the generated nonce */ protected String generateNonce(HttpServletRequest request) { - byte random[] = new byte[16]; + byte[] random = new byte[16]; // Render the result as a String of hexadecimal digits StringBuilder buffer = new StringBuilder(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/ExpiresFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/ExpiresFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/ExpiresFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/ExpiresFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,6 +44,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.StringUtils; +import org.apache.tomcat.util.http.Method; /** *

    @@ -445,7 +446,7 @@ /** * Duration unit */ - protected enum DurationUnit { + public enum DurationUnit { DAY(Calendar.DAY_OF_YEAR), HOUR(Calendar.HOUR), MINUTE(Calendar.MINUTE), @@ -510,7 +511,7 @@ * ({@link StartingPoint#ACCESS_TIME}) or the last time the HTML-page/servlet-response was modified ( * {@link StartingPoint#LAST_MODIFICATION_TIME}). */ - protected enum StartingPoint { + public enum StartingPoint { ACCESS_TIME, LAST_MODIFICATION_TIME } @@ -1031,7 +1032,7 @@ private static final String PARAMETER_EXPIRES_EXCLUDED_RESPONSE_STATUS_CODES = "ExpiresExcludedResponseStatusCodes"; /** - * Convert a comma delimited list of numbers into an {@code int[]}. + * Convert a comma-delimited list of numbers into an {@code int[]}. * * @param commaDelimitedInts can be {@code null} * @@ -1080,7 +1081,7 @@ } /** - * Convert an array of ints into a comma delimited string + * Convert an array of ints into a comma-delimited string * * @param ints The int array * @@ -1108,7 +1109,7 @@ * @return {@code true} if the given {@code str} is {@code null} or has a zero characters length. */ protected static boolean isEmpty(String str) { - return str == null || str.length() == 0; + return str == null || str.isEmpty(); } /** @@ -1372,8 +1373,7 @@ ExpiresConfiguration expiresConfiguration = parseExpiresConfiguration(value); this.expiresConfigurationByContentType.put(contentType, expiresConfiguration); } else if (name.equalsIgnoreCase(PARAMETER_EXPIRES_DEFAULT)) { - ExpiresConfiguration expiresConfiguration = parseExpiresConfiguration(value); - this.defaultExpiresConfiguration = expiresConfiguration; + this.defaultExpiresConfiguration = parseExpiresConfiguration(value); } else if (name.equalsIgnoreCase(PARAMETER_EXPIRES_EXCLUDED_RESPONSE_STATUS_CODES)) { this.excludedResponseStatusCodes = commaDelimitedListToIntArray(value); } else { @@ -1404,7 +1404,7 @@ // Don't add cache headers unless the request is a GET or a HEAD request String method = request.getMethod(); - if (!"GET".equals(method) && !"HEAD".equals(method)) { + if (!Method.GET.equals(method) && !Method.HEAD.equals(method)) { if (log.isDebugEnabled()) { log.debug(sm.getString("expiresFilter.invalidMethod", request.getRequestURI(), method)); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/FilterBase.java tomcat10-10.1.52/java/org/apache/catalina/filters/FilterBase.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/FilterBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/FilterBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -63,7 +63,7 @@ /** * Determines if an exception when calling a setter or an unknown configuration attribute triggers the failure of - * the this filter which in turn will prevent the web application from starting. + * this filter which in turn will prevent the web application from starting. * * @return true if a problem should trigger the failure of this filter, else false */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -160,11 +160,7 @@ public void setHstsMaxAgeSeconds(int hstsMaxAgeSeconds) { - if (hstsMaxAgeSeconds < 0) { - this.hstsMaxAgeSeconds = 0; - } else { - this.hstsMaxAgeSeconds = hstsMaxAgeSeconds; - } + this.hstsMaxAgeSeconds = Math.max(hstsMaxAgeSeconds, 0); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/filters/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/filters/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -56,6 +56,7 @@ httpHeaderSecurityFilter.committed=在进入HttpHeaderSecurityFilter的时候响应消息已经提交导致不能添加响应消息头 rateLimitFilter.initialized=RateLimitFilter [{0}] 初始值是每 [{2}] 秒 [{1}] 个请求。实际值是每 [{4}] 秒 [{3}]。 [{5}]。 +rateLimitFilter.maxRequestsExceeded=[{0}] [{1}] [{2}]的请求超出了[{4}]秒时间窗口的最大允许[{3}]。 remoteCidrFilter.invalid=为[{0}]提供的配置无效。有关详细信息,请参阅以前的消息。 remoteCidrFilter.noRemoteIp=客户端没有 IP 地址,请求被拒绝。 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/RateLimitFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/RateLimitFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/RateLimitFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/RateLimitFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,17 +42,20 @@ * the bucket time ends and a new bucket starts. *

    *

    - * The RateLimiter implementation can be set via the className init param. The default implementation, - * org.apache.catalina.util.FastRateLimiter, is optimized for efficiency and low overhead so it converts - * some configured values to more efficient values. For example, a configuration of a 60 seconds time bucket is - * converted to 65.536 seconds. That allows for very fast bucket calculation using bit shift arithmetic. In order to - * remain true to the user intent, the configured number of requests is then multiplied by the same ratio, so a - * configuration of 100 Requests per 60 seconds, has the real values of 109 Requests per 65 seconds. You can specify a - * different class as long as it implements the org.apache.catalina.util.RateLimiter interface. + * The RateLimiter implementation can be set via the rateLimitClassName init param. The default + * implementation, org.apache.catalina.util.FastRateLimiter, is optimized for efficiency and low overhead + * so it converts some configured values to more efficient values. For example, a configuration of a 60 seconds time + * bucket is converted to 65.536 seconds. That allows for very fast bucket calculation using bit shift arithmetic. In + * order to remain true to the user intent, the configured number of requests is then multiplied by the same ratio, so a + * configuration of 100 Requests per 60 seconds, has the real values of 109 Requests per 65 seconds. An alternative + * implementation, org.apache.catalina.util.ExactRateLimiter, is intended to provide a less efficient but + * more accurate control, whose effective duration in seconds and number of requests configuration are consist with the + * user declared. You can specify a different class as long as it implements the + * org.apache.catalina.util.RateLimiter interface. *

    *

    * It is common to set up different restrictions for different URIs. For example, a login page or authentication script - * is typically expected to get far less requests than the rest of the application, so you can add a filter definition + * is typically expected to get far fewer requests than the rest of the application, so you can add a filter definition * that would allow only 5 requests per 15 seconds and map those URIs to it. *

    *

    @@ -71,7 +74,7 @@ * the client IP address, so if for example you are using the Remote IP Filter, then the * filter mapping for the Rate Limit Filter must come after the mapping of the Remote IP Filter to ensure that * each request has its IP address resolved before the Rate Limit Filter is applied. Failure to do so will count - * requests from different IPs in the same bucket and will result in a self inflicted DoS attack. + * requests from different IPs in the same bucket and will result in a self-inflicted DoS attack. *

    */ public class RateLimitFilter extends FilterBase { @@ -145,7 +148,7 @@ private String policyName = null; - private transient Log log = LogFactory.getLog(RateLimitFilter.class); + private final transient Log log = LogFactory.getLog(RateLimitFilter.class); private static final StringManager sm = StringManager.getManager(RateLimitFilter.class); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/RemoteAddrFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteAddrFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/RemoteAddrFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteAddrFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,8 +30,9 @@ * Concrete implementation of RequestFilter that filters based on the string representation of the remote * client's IP address. * - * @author Craig R. McClanahan + * @deprecated This Filter will be removed in Tomcat 12 onwards. Use {@link RemoteCIDRFilter} instead. */ +@Deprecated public final class RemoteAddrFilter extends RequestFilter { // Log must be non-static as loggers are created per class-loader and this diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/RemoteCIDRFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteCIDRFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/RemoteCIDRFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteCIDRFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ import java.io.PrintWriter; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import jakarta.servlet.FilterChain; @@ -31,9 +29,9 @@ import jakarta.servlet.http.HttpServletResponse; import org.apache.catalina.util.NetMask; +import org.apache.catalina.util.NetMaskSet; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; -import org.apache.tomcat.util.buf.StringUtils; public final class RemoteCIDRFilter extends FilterBase { @@ -49,14 +47,14 @@ private final Log log = LogFactory.getLog(RemoteCIDRFilter.class); // must not be static /** - * The list of allowed {@link NetMask}s + * The allowed {@link NetMask}s. */ - private final List allow = new ArrayList<>(); + private final NetMaskSet allow = new NetMaskSet(); /** - * The list of denied {@link NetMask}s + * The denied {@link NetMask}s. */ - private final List deny = new ArrayList<>(); + private final NetMaskSet deny = new NetMaskSet(); /** @@ -77,7 +75,7 @@ * @throws IllegalArgumentException One or more netmasks are invalid */ public void setAllow(final String input) { - final List messages = fillFromInput(input, allow); + final List messages = allow.addAll(input); if (messages.isEmpty()) { return; @@ -109,7 +107,7 @@ * @throws IllegalArgumentException One or more netmasks are invalid */ public void setDeny(final String input) { - final List messages = fillFromInput(input, deny); + final List messages = deny.addAll(input); if (messages.isEmpty()) { return; @@ -174,25 +172,17 @@ return false; } - for (final NetMask nm : deny) { - if (nm.matches(addr)) { - return false; - } - } - - for (final NetMask nm : allow) { - if (nm.matches(addr)) { - return true; - } + if (deny.contains(addr)) { + return false; } - // Allow if deny is specified but allow isn't - if (!deny.isEmpty() && allow.isEmpty()) { + if (allow.contains(addr)) { return true; } - // Deny this request - return false; + // Allow if deny is specified but allow isn't + // Deny this request otherwise + return !deny.isEmpty() && allow.isEmpty(); } @@ -202,35 +192,4 @@ writer.write(sm.getString("http.403")); writer.flush(); } - - - /** - * Fill a {@link NetMask} list from a string input containing a comma-separated list of (hopefully valid) - * {@link NetMask}s. - * - * @param input The input string - * @param target The list to fill - * - * @return a string list of processing errors (empty when no errors) - */ - private List fillFromInput(final String input, final List target) { - target.clear(); - if (input == null || input.isEmpty()) { - return Collections.emptyList(); - } - - final List messages = new ArrayList<>(); - NetMask nm; - - for (final String s : StringUtils.splitCommaSeparated(input)) { - try { - nm = new NetMask(s); - target.add(nm); - } catch (IllegalArgumentException e) { - messages.add(s + ": " + e.getMessage()); - } - } - - return Collections.unmodifiableList(messages); - } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/RemoteHostFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteHostFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/RemoteHostFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteHostFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,6 @@ /** * Concrete implementation of RequestFilter that filters based on the remote client's host name. - * - * @author Craig R. McClanahan */ public final class RemoteHostFilter extends RequestFilter { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/RemoteIpFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteIpFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/RemoteIpFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/RemoteIpFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -75,7 +75,7 @@ * proxies: *

    *
      - *
    • Loop on the comma delimited list of IPs and hostnames passed by the preceding load balancer or proxy in the given + *
    • Loop on the comma-delimited list of IPs and hostnames passed by the preceding load balancer or proxy in the given * request's Http header named $remoteIpHeader (default value x-forwarded-for). Values are * processed in right-to-left order.
    • *
    • For each ip/host of the list: @@ -151,7 +151,7 @@ * * * protocolHeaderHttpsValue - * Value of the protocolHeader to indicate that it is an Https request + * Value of the protocolHeader to indicate that it is a Https request * N/A * String like https or ON * https @@ -194,7 +194,8 @@ *

      * XForwardedFilter configuration: *

      - * + * + *
        * <filter>
        *    <filter-name>RemoteIpFilter</filter-name>
        *    <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
      @@ -220,7 +221,8 @@
        *    <filter-name>RemoteIpFilter</filter-name>
        *    <url-pattern>/*</url-pattern>
        *    <dispatcher>REQUEST</dispatcher>
      - * </filter-mapping>
      + * </filter-mapping>
      + * 
      * * * @@ -273,7 +275,8 @@ *

      * RemoteIpFilter configuration: *

      - * + * + *
        * <filter>
        *    <filter-name>RemoteIpFilter</filter-name>
        *    <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
      @@ -299,7 +302,8 @@
        *    <filter-name>RemoteIpFilter</filter-name>
        *    <url-pattern>/*</url-pattern>
        *    <dispatcher>REQUEST</dispatcher>
      - * </filter-mapping>
      + * </filter-mapping>
      + * 
      *
      Request Values
      * * @@ -335,7 +339,8 @@ *

      * RemoteIpFilter configuration: *

      - * + * + *
        * <filter>
        *    <filter-name>RemoteIpFilter</filter-name>
        *    <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
      @@ -361,7 +366,8 @@
        *    <filter-name>RemoteIpFilter</filter-name>
        *    <url-pattern>/*</url-pattern>
        *    <dispatcher>REQUEST</dispatcher>
      - * </filter-mapping>
      + * </filter-mapping>
      + * 
      *
      Request Values
      * * @@ -398,7 +404,8 @@ *

      * RemoteIpFilter configuration: *

      - * + * + *
        * <filter>
        *    <filter-name>RemoteIpFilter</filter-name>
        *    <filter-class>org.apache.catalina.filters.RemoteIpFilter</filter-class>
      @@ -424,7 +431,8 @@
        *    <filter-name>RemoteIpFilter</filter-name>
        *    <url-pattern>/*</url-pattern>
        *    <dispatcher>REQUEST</dispatcher>
      - * </filter-mapping>
      + * </filter-mapping>
      + * 
      *
      Request Values
      * * @@ -538,7 +546,7 @@ public Enumeration getHeaders(String name) { Map.Entry> header = getHeaderEntry(name); if (header == null || header.getValue() == null) { - return Collections.enumeration(Collections.emptyList()); + return Collections.enumeration(Collections.emptyList()); } return Collections.enumeration(header.getValue()); } @@ -742,13 +750,13 @@ /** * @see #setInternalProxies(String) */ - private Pattern internalProxies = - Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" + - "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + - "100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + - "100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" + - "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + - "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "0:0:0:0:0:0:0:1|::1"); + private Pattern internalProxies = Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" + + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + + "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + + "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" + + "0:0:0:0:0:0:0:1|::1|" + "fe[89ab]\\p{XDigit}:.*|" + "f[cd]\\p{XDigit}{2}+:.*"); /** * @see #setProtocolHeader(String) @@ -851,13 +859,13 @@ xRequest.setRemoteHost(remoteIp); } - if (proxiesHeaderValue.size() == 0) { + if (proxiesHeaderValue.isEmpty()) { xRequest.removeHeader(proxiesHeader); } else { String commaDelimitedListOfProxies = StringUtils.join(proxiesHeaderValue); xRequest.setHeader(proxiesHeader, commaDelimitedListOfProxies); } - if (newRemoteIpHeaderValue.size() == 0) { + if (newRemoteIpHeaderValue.isEmpty()) { xRequest.removeHeader(remoteIpHeader); } else { String commaDelimitedRemoteIpHeaderValue = StringUtils.join(newRemoteIpHeaderValue); @@ -897,7 +905,7 @@ } } catch (IllegalArgumentException iae) { - log.debug(sm.getString("remoteIpFilter.invalidHostHeader", hostHeaderValue, hostHeader)); + log.debug(sm.getString("remoteIpFilter.invalidHostHeader", hostHeaderValue, hostHeader), iae); } } } @@ -959,7 +967,7 @@ try { port = Integer.parseInt(portHeaderValue); } catch (NumberFormatException nfe) { - log.debug(sm.getString("remoteIpFilter.invalidPort", portHeaderValue, getPortHeader())); + log.debug(sm.getString("remoteIpFilter.invalidPort", portHeaderValue, getPortHeader()), nfe); } } } @@ -1174,7 +1182,7 @@ * @param internalProxies The regexp */ public void setInternalProxies(String internalProxies) { - if (internalProxies == null || internalProxies.length() == 0) { + if (internalProxies == null || internalProxies.isEmpty()) { this.internalProxies = null; } else { this.internalProxies = Pattern.compile(internalProxies); @@ -1227,7 +1235,7 @@ /** *

      - * Case insensitive value of the protocol header to indicate that the incoming http request uses HTTPS. + * Case-insensitive value of the protocol header to indicate that the incoming http request uses HTTPS. *

      *

      * Default value : https @@ -1250,7 +1258,7 @@ * Name of the http header that holds the list of trusted proxies that has been traversed by the http request. *

      *

      - * The value of this header can be comma delimited. + * The value of this header can be comma-delimited. *

      *

      * Default value : X-Forwarded-By @@ -1267,7 +1275,7 @@ * Name of the http header from which the remote ip is extracted. *

      *

      - * The value of this header can be comma delimited. + * The value of this header can be comma-delimited. *

      *

      * Default value : X-Forwarded-For @@ -1280,7 +1288,7 @@ } /** - * Should this filter set request attributes for IP address, Hostname, protocol and port used for the request? This + * Should this filter set request attributes for IP address, Hostname, protocol and port used for the request? These * are typically used in conjunction with an {@link AccessLog} which will otherwise log the original values. Default * is true. The attributes set are: *

        @@ -1309,7 +1317,7 @@ * @param trustedProxies The trusted proxies regexp */ public void setTrustedProxies(String trustedProxies) { - if (trustedProxies == null || trustedProxies.length() == 0) { + if (trustedProxies == null || trustedProxies.isEmpty()) { this.trustedProxies = null; } else { this.trustedProxies = Pattern.compile(trustedProxies); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/RequestDumperFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/RequestDumperFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/RequestDumperFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/RequestDumperFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,8 +46,6 @@ * org.apache.catalina.filter.RequestDumperFilter logger is directed to a dedicated file and that the * org.apache.juli.VerbatimFormatter is used. *

        - * - * @author Craig R. McClanahan */ public class RequestDumperFilter extends GenericFilter { @@ -109,7 +107,7 @@ doLog(" header", NON_HTTP_REQ_MSG); } else { doLog(" contextPath", hRequest.getContextPath()); - Cookie cookies[] = hRequest.getCookies(); + Cookie[] cookies = hRequest.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { doLog(" cookie", cookie.getName() + "=" + cookie.getValue()); @@ -229,13 +227,8 @@ } private void doLog(String attribute, String value) { - StringBuilder sb = new StringBuilder(80); - sb.append(Thread.currentThread().getName()); - sb.append(' '); - sb.append(attribute); - sb.append('='); - sb.append(value); - log.info(sb.toString()); + String sb = Thread.currentThread().getName() + " " + attribute + "=" + value; + log.info(sb); } private String getTimestamp() { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/RequestFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/RequestFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/RequestFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/RequestFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,7 +39,7 @@ *
      • The subclass extracts the request property to be filtered, and calls the common process() method. *
      • If there is a deny expression configured, the property will be compared to the expression. If a match is found, * this request will be rejected with a "Forbidden" HTTP response.
      • - *
      • If there is a allow expression configured, the property will be compared to the expression. If a match is found, + *
      • If there is an allow expression configured, the property will be compared to the expression. If a match is found, * this request will be allowed to pass through to the next filter in the current pipeline.
      • *
      • If a deny expression was specified but no allow expression, allow this request to pass through (because none of * the deny expressions matched it). @@ -94,7 +94,7 @@ * @param allow The new allow expression */ public void setAllow(String allow) { - if (allow == null || allow.length() == 0) { + if (allow == null || allow.isEmpty()) { this.allow = null; } else { this.allow = Pattern.compile(allow); @@ -120,7 +120,7 @@ * @param deny The new deny expression */ public void setDeny(String deny) { - if (deny == null || deny.length() == 0) { + if (deny == null || deny.isEmpty()) { this.deny = null; } else { this.deny = Pattern.compile(deny); @@ -223,12 +223,9 @@ } // Allow if denies specified but not allows - if (deny != null && allow == null) { - return true; - } + // Deny this request otherwise + return deny != null && allow == null; - // Deny this request - return false; } private void sendErrorWhenNotHttp(ServletResponse response) throws IOException { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/RestCsrfPreventionFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -85,9 +85,8 @@ private static final Predicate nonModifyingMethods = m -> Objects.nonNull(m) && NON_MODIFYING_METHODS_PATTERN.matcher(m).matches(); - private Set pathsAcceptingParams = new HashSet<>(); - - private String pathsDelimiter = ","; + private final Set pathsAcceptingParams = new HashSet<>(); + private static final String pathsDelimiter = ","; @Override public void init(FilterConfig filterConfig) throws ServletException { @@ -110,13 +109,10 @@ } RestCsrfPreventionStrategy strategy; - switch (mType) { - case NON_MODIFYING_METHOD: - strategy = new FetchRequest(); - break; - default: - strategy = new StateChangingRequest(); - break; + if (mType == MethodType.NON_MODIFYING_METHOD) { + strategy = new FetchRequest(); + } else { + strategy = new StateChangingRequest(); } if (!strategy.apply((HttpServletRequest) request, (HttpServletResponse) response)) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/filters/SetCharacterEncodingFilter.java tomcat10-10.1.52/java/org/apache/catalina/filters/SetCharacterEncodingFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/filters/SetCharacterEncodingFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/filters/SetCharacterEncodingFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,7 +46,7 @@ * Although this filter can be used unchanged, it is also easy to subclass it and make the selectEncoding() * method more intelligent about what encoding to choose, based on characteristics of the incoming request (such as the * values of the Accept-Language and User-Agent headers, or a value stashed in the current - * user's session. + * user's session). *

        */ public class SetCharacterEncodingFilter extends FilterBase { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterDeployer.java tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterDeployer.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterDeployer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterDeployer.java 2026-01-23 19:33:36.000000000 +0000 @@ -80,9 +80,7 @@ void backgroundProcess(); /** - * Returns the cluster the cluster deployer is associated with - * - * @return CatalinaCluster + * @return the cluster the is associated with */ CatalinaCluster getCluster(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterListener.java tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterListener.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ /** * Receive SessionID cluster change from other backup node after primary session node is failed. - * - * @author Peter Rossbach */ public abstract class ClusterListener implements ChannelListener { @@ -71,10 +69,7 @@ @Override public final boolean accept(Serializable msg, Member member) { - if (msg instanceof ClusterMessage) { - return true; - } - return false; + return msg instanceof ClusterMessage; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterManager.java tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterManager.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ /** * The common interface used by all cluster manager. This is so that we can have a more pluggable way of swapping * session managers for different algorithms. - * - * @author Peter Rossbach */ public interface ClusterManager extends Manager { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterRuleSet.java tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterRuleSet.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterRuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterRuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,11 +20,7 @@ import org.apache.tomcat.util.digester.RuleSet; /** - *

        * RuleSet for processing the contents of a Cluster definition element. - *

        - * - * @author Peter Rossbach */ public class ClusterRuleSet implements RuleSet { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterValve.java tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterValve.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/ClusterValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/ClusterValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,12 +21,10 @@ /** * Cluster valves are a simple extension to the Tomcat valve architecture with a small addition of being able to * reference the cluster component in the container it sits in. - * - * @author Peter Rossbach */ public interface ClusterValve extends Valve { /** - * Returns the cluster the cluster deployer is associated with + * Returns the cluster the valve is associated with * * @return CatalinaCluster */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java tomcat10-10.1.52/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/authenticator/ClusterSingleSignOn.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,7 +34,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * A Valve that supports a "single sign on" user experience on each nodes of a cluster, where the + * A Valve that supports a "single sign on" user experience on each node of a cluster, where the * security identity of a user who successfully authenticates to one web application is propagated to other web * applications and to other nodes cluster in the same security domain. For successful use, the following requirements * must be met: @@ -46,8 +46,6 @@ *
      • The web applications themselves must use one of the standard Authenticators found in the * org.apache.catalina.authenticator package.
      • *
      - * - * @author Fabien Carrion */ public class ClusterSingleSignOn extends SingleSignOn implements ClusterValve, MapOwner { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/backend/CollectedInfo.java tomcat10-10.1.52/java/org/apache/catalina/ha/backend/CollectedInfo.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/backend/CollectedInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/backend/CollectedInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,7 +28,7 @@ import org.apache.tomcat.util.res.StringManager; /* - * Listener to provider informations to mod_heartbeat.c + * Listener to provide information to mod_heartbeat.c * *msg_format = "v=%u&ready=%u&busy=%u"; (message to send). * send the multicast message using the format... * what about the bind(IP. port) only IP makes sense (for the moment). @@ -55,7 +55,7 @@ public void init(String host, int port) throws Exception { int iport = 0; String shost = null; - mBeanServer = Registry.getRegistry(null, null).getMBeanServer(); + mBeanServer = Registry.getRegistry(null).getMBeanServer(); String onStr = "*:type=ThreadPool,*"; ObjectName objectName = new ObjectName(onStr); Set set = mBeanServer.queryMBeans(objectName, null); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/backend/HeartbeatListener.java tomcat10-10.1.52/java/org/apache/catalina/ha/backend/HeartbeatListener.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/backend/HeartbeatListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/backend/HeartbeatListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,7 @@ import org.apache.tomcat.util.res.StringManager; /* - * Listener to provider informations to mod_heartbeat.c + * Listener to provide information to mod_heartbeat.c * *msg_format = "v=%u&ready=%u&busy=%u"; (message to send). * send the multicast message using the format... * what about the bind(IP. port) only IP makes sense (for the moment). @@ -199,8 +199,8 @@ coll = new CollectedInfo(host, port); this.port = coll.port; this.host = coll.host; - } catch (Exception ex) { - log.error(sm.getString("heartbeatListener.errorCollectingInfo"), ex); + } catch (Exception e) { + log.error(sm.getString("heartbeatListener.errorCollectingInfo"), e); coll = null; return; } @@ -209,8 +209,8 @@ /* Start or restart sender */ try { sender.init(this); - } catch (Exception ex) { - log.error(sm.getString("heartbeatListener.senderInitError"), ex); + } catch (Exception e) { + log.error(sm.getString("heartbeatListener.senderInitError"), e); sender = null; return; } @@ -218,16 +218,16 @@ /* refresh the connector information and send it */ try { coll.refresh(); - } catch (Exception ex) { - log.error(sm.getString("heartbeatListener.refreshError"), ex); + } catch (Exception e) { + log.error(sm.getString("heartbeatListener.refreshError"), e); coll = null; return; } String output = "v=1&ready=" + coll.ready + "&busy=" + coll.busy + "&port=" + port; try { sender.send(output); - } catch (Exception ex) { - log.error(sm.getString("heartbeatListener.sendError"), ex); + } catch (Exception e) { + log.error(sm.getString("heartbeatListener.sendError"), e); } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/backend/MultiCastSender.java tomcat10-10.1.52/java/org/apache/catalina/ha/backend/MultiCastSender.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/backend/MultiCastSender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/backend/MultiCastSender.java 2026-01-23 19:33:36.000000000 +0000 @@ -60,8 +60,8 @@ s.setTimeToLive(config.getTtl()); s.joinGroup(new InetSocketAddress(group, 0), null); - } catch (Exception ex) { - log.error(sm.getString("multiCastSender.multiCastFailed"), ex); + } catch (Exception e) { + log.error(sm.getString("multiCastSender.multiCastFailed"), e); s = null; return -1; } @@ -72,8 +72,8 @@ DatagramPacket data = new DatagramPacket(buf, buf.length, group, config.getMultiport()); try { s.send(data); - } catch (Exception ex) { - log.error(sm.getString("multiCastSender.sendFailed"), ex); + } catch (Exception e) { + log.error(sm.getString("multiCastSender.sendFailed"), e); s.close(); s = null; return -1; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/backend/TcpSender.java tomcat10-10.1.52/java/org/apache/catalina/ha/backend/TcpSender.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/backend/TcpSender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/backend/TcpSender.java 2026-01-23 19:33:36.000000000 +0000 @@ -106,8 +106,8 @@ } connectionReaders[i] = new BufferedReader(new InputStreamReader(connections[i].getInputStream())); connectionWriters[i] = new BufferedWriter(new OutputStreamWriter(connections[i].getOutputStream())); - } catch (Exception ex) { - log.error(sm.getString("tcpSender.connectionFailed"), ex); + } catch (Exception e) { + log.error(sm.getString("tcpSender.connectionFailed"), e); close(i); } } @@ -125,8 +125,8 @@ writer.write(mess); writer.write("\r\n"); writer.flush(); - } catch (Exception ex) { - log.error(sm.getString("tcpSender.sendFailed"), ex); + } catch (Exception e) { + log.error(sm.getString("tcpSender.sendFailed"), e); close(i); } if (connections[i] == null) { @@ -164,7 +164,7 @@ if (contentLength > 0) { char[] buf = new char[512]; while (contentLength > 0) { - int thisTime = (contentLength > buf.length) ? buf.length : contentLength; + int thisTime = Math.min(contentLength, buf.length); int n = connectionReaders[i].read(buf, 0, thisTime); if (n <= 0) { log.error(sm.getString("tcpSender.readError")); @@ -192,21 +192,24 @@ if (connectionReaders[i] != null) { connectionReaders[i].close(); } - } catch (IOException e) { + } catch (IOException ignore) { + // Ignore } connectionReaders[i] = null; try { if (connectionWriters[i] != null) { connectionWriters[i].close(); } - } catch (IOException e) { + } catch (IOException ignore) { + // Ignore } connectionWriters[i] = null; try { if (connections[i] != null) { connections[i].close(); } - } catch (IOException e) { + } catch (IOException ignore) { + // Ignore } connections[i] = null; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/context/ReplicatedContext.java tomcat10-10.1.52/java/org/apache/catalina/ha/context/ReplicatedContext.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/context/ReplicatedContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/context/ReplicatedContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -62,9 +62,9 @@ map.setChannelSendOptions(mapSendOptions); ((ReplApplContext) this.context).setAttributeMap(map); } - } catch (Exception x) { - log.error(sm.getString("replicatedContext.startUnable", getName()), x); - throw new LifecycleException(sm.getString("replicatedContext.startFailed", getName()), x); + } catch (Exception e) { + log.error(sm.getString("replicatedContext.startUnable", getName()), e); + throw new LifecycleException(sm.getString("replicatedContext.startFailed", getName()), e); } } @@ -98,7 +98,7 @@ } public ClassLoader[] getClassLoaders() { - Loader loader = null; + Loader loader; ClassLoader classLoader = null; loader = this.getLoader(); if (loader != null) { @@ -192,7 +192,7 @@ public Enumeration getAttributeNames() { Set names = new HashSet<>(attributes.keySet()); - return new MultiEnumeration<>( + return new MultiEnumeration( new Enumeration[] { super.getAttributeNames(), Collections.enumeration(names) }); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/FarmWarDeployer.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,8 +51,6 @@ *
    • tempDir - a temporaryDirectory to store binary data when downloading a war from the cluster
    • * * Currently we only support deployment of WAR files since they are easier to send across the wire. - * - * @author Peter Rossbach */ public class FarmWarDeployer extends ClusterListener implements ClusterDeployer, FileChangeListener { /*--Static Variables----------------------------------------*/ @@ -146,8 +144,7 @@ return; } Engine engine = (Engine) econtainer; - String hostname = null; - hostname = host.getName(); + String hostname = host.getName(); try { oname = new ObjectName(engine.getName() + ":type=Deployer,host=" + hostname); } catch (Exception e) { @@ -164,7 +161,7 @@ configBase = host.getConfigBaseFile(); // Retrieve the MBean server - mBeanServer = Registry.getRegistry(null, null).getMBeanServer(); + mBeanServer = Registry.getRegistry(null).getMBeanServer(); started = true; count = 0; @@ -238,8 +235,8 @@ } else { log.error(sm.getString("farmWarDeployer.servicingDeploy", contextName, name)); } - } catch (Exception ex) { - log.error(sm.getString("farmWarDeployer.fileMessageError"), ex); + } catch (Exception e) { + log.error(sm.getString("farmWarDeployer.fileMessageError"), e); } finally { removeFactory(fmsg); } @@ -263,12 +260,12 @@ } else { log.error(sm.getString("farmWarDeployer.servicingUndeploy", contextName)); } - } catch (Exception ex) { - log.error(sm.getString("farmWarDeployer.undeployMessageError"), ex); + } catch (Exception e) { + log.error(sm.getString("farmWarDeployer.undeployMessageError"), e); } } - } catch (IOException x) { - log.error(sm.getString("farmWarDeployer.msgIoe"), x); + } catch (IOException ioe) { + log.error(sm.getString("farmWarDeployer.msgIoe"), ioe); } } @@ -396,8 +393,8 @@ log.error(sm.getString("farmWarDeployer.removeFailRemote", contextName)); } - } catch (Exception ex) { - log.error(sm.getString("farmWarDeployer.removeFailLocal", contextName), ex); + } catch (Exception e) { + log.error(sm.getString("farmWarDeployer.removeFailLocal", contextName), e); } } @@ -429,8 +426,8 @@ log.error(sm.getString("farmWarDeployer.servicingDeploy", cn.getName(), deployWar.getName())); } install(cn.getName(), deployWar); - } catch (Exception x) { - log.error(sm.getString("farmWarDeployer.modInstallFail"), x); + } catch (Exception e) { + log.error(sm.getString("farmWarDeployer.modInstallFail"), e); } } @@ -442,8 +439,8 @@ log.info(sm.getString("farmWarDeployer.removeLocal", cn.getName())); } remove(cn.getName(), true); - } catch (Exception x) { - log.error(sm.getString("farmWarDeployer.removeLocalFail"), x); + } catch (Exception e) { + log.error(sm.getString("farmWarDeployer.removeLocalFail"), e); } } @@ -488,7 +485,7 @@ */ protected void undeployDir(File dir) { - String files[] = dir.list(); + String[] files = dir.list(); if (files == null) { files = new String[0]; } @@ -682,8 +679,8 @@ return false; } } - } catch (IOException e) { - log.error(sm.getString("farmWarDeployer.fileCopyFail", from, to), e); + } catch (IOException ioe) { + log.error(sm.getString("farmWarDeployer.fileCopyFail", from, to), ioe); return false; } @@ -697,8 +694,8 @@ } os.write(buf, 0, len); } - } catch (IOException e) { - log.error(sm.getString("farmWarDeployer.fileCopyFail", from, to), e); + } catch (IOException ioe) { + log.error(sm.getString("farmWarDeployer.fileCopyFail", from, to), ioe); return false; } return true; @@ -721,7 +718,8 @@ } try { dir = dir.getCanonicalFile(); - } catch (IOException e) {// ignore + } catch (IOException ignore) { + // Ignore } return dir; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/FileMessage.java tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/FileMessage.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/FileMessage.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/FileMessage.java 2026-01-23 19:33:36.000000000 +0000 @@ -72,12 +72,7 @@ @Override public String getUniqueId() { - StringBuilder result = new StringBuilder(getFileName()); - result.append("#-#"); - result.append(getMessageNumber()); - result.append("#-#"); - result.append(System.currentTimeMillis()); - return result.toString(); + return getFileName() + "#-#" + getMessageNumber() + "#-#" + System.currentTimeMillis(); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/FileMessageFactory.java tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/FileMessageFactory.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/FileMessageFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/FileMessageFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -121,7 +121,7 @@ /** * The time this instance was last modified. */ - protected long lastModified = 0; + protected long lastModified; /** * The maximum time (in seconds) this instance will be allowed to exist from lastModifiedTime. @@ -130,7 +130,7 @@ /** * Private constructor, either instantiates a factory to read or write.
      - * When openForWrite==true, then a the file, f, will be created and an output stream is opened to write to it.
      + * When openForWrite==true, then the file f will be created and an output stream is opened to write to it.
      * When openForWrite==false, an input stream is opened, the file has to exist. * * @param f File - the file to be read/written @@ -244,7 +244,7 @@ // Have received a new message. Update the last modified time (even if the message is being buffered for now). lastModified = System.currentTimeMillis(); - FileMessage next = null; + FileMessage next; synchronized (this) { if (!isWriting) { next = msgBuffer.get(Long.valueOf(lastMessageProcessed.get() + 1)); @@ -286,12 +286,14 @@ try { in.close(); } catch (IOException ignore) { + // Ignore } } if (out != null) { try { out.close(); } catch (IOException ignore) { + // Ignore } } in = null; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/UndeployMessage.java tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/UndeployMessage.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/UndeployMessage.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/UndeployMessage.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,7 @@ private Member address; private long timestamp; - private String uniqueId; + private final String uniqueId; private final String contextName; public UndeployMessage(Member address, long timestamp, String uniqueId, String contextName) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/WarWatcher.java tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/WarWatcher.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/deploy/WarWatcher.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/deploy/WarWatcher.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,6 @@ /** * The WarWatcher watches the deployDir for changes made to the directory (adding new WAR files->deploy or * remove WAR files->undeploy) and notifies a listener of the changes made. - * - * @author Peter Rossbach */ public class WarWatcher { @@ -149,7 +147,7 @@ protected static class WarInfo { protected final File war; - protected long lastChecked = 0; + protected long lastChecked; protected long lastState = 0; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/BackupManager.java tomcat10-10.1.52/java/org/apache/catalina/ha/session/BackupManager.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/BackupManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/BackupManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -145,9 +145,9 @@ map.setChannelSendOptions(mapSendOptions); map.setAccessTimeout(accessTimeout); this.sessions = map; - } catch (Exception x) { - log.error(sm.getString("backupManager.startUnable", getName()), x); - throw new LifecycleException(sm.getString("backupManager.startFailed", getName()), x); + } catch (Exception e) { + log.error(sm.getString("backupManager.startUnable", getName()), e); + throw new LifecycleException(sm.getString("backupManager.startFailed", getName()), e); } setState(LifecycleState.STARTING); } @@ -265,8 +265,7 @@ @Override public Set getSessionIdsFull() { LazyReplicatedMap map = (LazyReplicatedMap) sessions; - Set sessionIds = new HashSet<>(map.keySetFull()); - return sessionIds; + return new HashSet<>(map.keySetFull()); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/ClusterManagerBase.java tomcat10-10.1.52/java/org/apache/catalina/ha/session/ClusterManagerBase.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/ClusterManagerBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/ClusterManagerBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -60,7 +60,7 @@ */ private boolean recordAllActions = false; - private SynchronizedStack deltaRequestPool = new SynchronizedStack<>(); + private final SynchronizedStack deltaRequestPool = new SynchronizedStack<>(); protected SynchronizedStack getDeltaRequestPool() { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/ClusterSessionListener.java tomcat10-10.1.52/java/org/apache/catalina/ha/session/ClusterSessionListener.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/ClusterSessionListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/ClusterSessionListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,6 @@ /** * Receive replicated SessionMessage form other cluster node. - * - * @author Peter Rossbach */ public class ClusterSessionListener extends ClusterListener { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/DeltaManager.java tomcat10-10.1.52/java/org/apache/catalina/ha/session/DeltaManager.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/DeltaManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/DeltaManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,6 +23,8 @@ import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import org.apache.catalina.Engine; import org.apache.catalina.Host; @@ -48,9 +50,6 @@ *

      * IMPLEMENTATION NOTE : Correct behavior of session storing and reloading depends upon external calls to the * start() and stop() methods of this class at the correct times. - * - * @author Craig R. McClanahan - * @author Peter Rossbach */ public class DeltaManager extends ClusterManagerBase { @@ -76,6 +75,7 @@ private int stateTransferTimeout = 60; private boolean sendAllSessions = true; private int sendAllSessionsSize = 1000; + private boolean enableStatistics = true; /** * wait time between send session block (default 2 sec) @@ -88,25 +88,25 @@ // -------------------------------------------------------- stats attributes - private volatile long sessionReplaceCounter = 0; - private volatile long counterReceive_EVT_GET_ALL_SESSIONS = 0; - private volatile long counterReceive_EVT_ALL_SESSION_DATA = 0; - private volatile long counterReceive_EVT_SESSION_CREATED = 0; - private volatile long counterReceive_EVT_SESSION_EXPIRED = 0; - private volatile long counterReceive_EVT_SESSION_ACCESSED = 0; - private volatile long counterReceive_EVT_SESSION_DELTA = 0; - private volatile int counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0; - private volatile long counterReceive_EVT_CHANGE_SESSION_ID = 0; - private volatile long counterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER = 0; - private volatile long counterSend_EVT_GET_ALL_SESSIONS = 0; - private volatile long counterSend_EVT_ALL_SESSION_DATA = 0; - private volatile long counterSend_EVT_SESSION_CREATED = 0; - private volatile long counterSend_EVT_SESSION_DELTA = 0; - private volatile long counterSend_EVT_SESSION_ACCESSED = 0; - private volatile long counterSend_EVT_SESSION_EXPIRED = 0; - private volatile int counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0; - private volatile long counterSend_EVT_CHANGE_SESSION_ID = 0; - private volatile int counterNoStateTransferred = 0; + private final AtomicLong sessionReplaceCounter = new AtomicLong(0); + private final AtomicLong counterReceive_EVT_GET_ALL_SESSIONS = new AtomicLong(0); + private final AtomicLong counterReceive_EVT_ALL_SESSION_DATA = new AtomicLong(0); + private final AtomicLong counterReceive_EVT_SESSION_CREATED = new AtomicLong(0); + private final AtomicLong counterReceive_EVT_SESSION_EXPIRED = new AtomicLong(0); + private final AtomicLong counterReceive_EVT_SESSION_ACCESSED = new AtomicLong(0); + private final AtomicLong counterReceive_EVT_SESSION_DELTA = new AtomicLong(0); + private final AtomicInteger counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = new AtomicInteger(0); + private final AtomicLong counterReceive_EVT_CHANGE_SESSION_ID = new AtomicLong(0); + private final AtomicLong counterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER = new AtomicLong(0); + private final AtomicLong counterSend_EVT_GET_ALL_SESSIONS = new AtomicLong(0); + private final AtomicLong counterSend_EVT_ALL_SESSION_DATA = new AtomicLong(0); + private final AtomicLong counterSend_EVT_SESSION_CREATED = new AtomicLong(0); + private final AtomicLong counterSend_EVT_SESSION_DELTA = new AtomicLong(0); + private final AtomicLong counterSend_EVT_SESSION_ACCESSED = new AtomicLong(0); + private final AtomicLong counterSend_EVT_SESSION_EXPIRED = new AtomicLong(0); + private final AtomicInteger counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = new AtomicInteger(0); + private final AtomicLong counterSend_EVT_CHANGE_SESSION_ID = new AtomicLong(0); + private final AtomicInteger counterNoStateTransferred = new AtomicInteger(0); // ------------------------------------------------------------- Constructor @@ -130,98 +130,98 @@ * @return Returns the counterSend_EVT_GET_ALL_SESSIONS. */ public long getCounterSend_EVT_GET_ALL_SESSIONS() { - return counterSend_EVT_GET_ALL_SESSIONS; + return counterSend_EVT_GET_ALL_SESSIONS.get(); } /** * @return Returns the counterSend_EVT_SESSION_ACCESSED. */ public long getCounterSend_EVT_SESSION_ACCESSED() { - return counterSend_EVT_SESSION_ACCESSED; + return counterSend_EVT_SESSION_ACCESSED.get(); } /** * @return Returns the counterSend_EVT_SESSION_CREATED. */ public long getCounterSend_EVT_SESSION_CREATED() { - return counterSend_EVT_SESSION_CREATED; + return counterSend_EVT_SESSION_CREATED.get(); } /** * @return Returns the counterSend_EVT_SESSION_DELTA. */ public long getCounterSend_EVT_SESSION_DELTA() { - return counterSend_EVT_SESSION_DELTA; + return counterSend_EVT_SESSION_DELTA.get(); } /** * @return Returns the counterSend_EVT_SESSION_EXPIRED. */ public long getCounterSend_EVT_SESSION_EXPIRED() { - return counterSend_EVT_SESSION_EXPIRED; + return counterSend_EVT_SESSION_EXPIRED.get(); } /** * @return Returns the counterSend_EVT_ALL_SESSION_DATA. */ public long getCounterSend_EVT_ALL_SESSION_DATA() { - return counterSend_EVT_ALL_SESSION_DATA; + return counterSend_EVT_ALL_SESSION_DATA.get(); } /** * @return Returns the counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE. */ public int getCounterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE() { - return counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE; + return counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE.get(); } /** * @return Returns the counterSend_EVT_CHANGE_SESSION_ID. */ public long getCounterSend_EVT_CHANGE_SESSION_ID() { - return counterSend_EVT_CHANGE_SESSION_ID; + return counterSend_EVT_CHANGE_SESSION_ID.get(); } /** * @return Returns the counterReceive_EVT_ALL_SESSION_DATA. */ public long getCounterReceive_EVT_ALL_SESSION_DATA() { - return counterReceive_EVT_ALL_SESSION_DATA; + return counterReceive_EVT_ALL_SESSION_DATA.get(); } /** * @return Returns the counterReceive_EVT_GET_ALL_SESSIONS. */ public long getCounterReceive_EVT_GET_ALL_SESSIONS() { - return counterReceive_EVT_GET_ALL_SESSIONS; + return counterReceive_EVT_GET_ALL_SESSIONS.get(); } /** * @return Returns the counterReceive_EVT_SESSION_ACCESSED. */ public long getCounterReceive_EVT_SESSION_ACCESSED() { - return counterReceive_EVT_SESSION_ACCESSED; + return counterReceive_EVT_SESSION_ACCESSED.get(); } /** * @return Returns the counterReceive_EVT_SESSION_CREATED. */ public long getCounterReceive_EVT_SESSION_CREATED() { - return counterReceive_EVT_SESSION_CREATED; + return counterReceive_EVT_SESSION_CREATED.get(); } /** * @return Returns the counterReceive_EVT_SESSION_DELTA. */ public long getCounterReceive_EVT_SESSION_DELTA() { - return counterReceive_EVT_SESSION_DELTA; + return counterReceive_EVT_SESSION_DELTA.get(); } /** * @return Returns the counterReceive_EVT_SESSION_EXPIRED. */ public long getCounterReceive_EVT_SESSION_EXPIRED() { - return counterReceive_EVT_SESSION_EXPIRED; + return counterReceive_EVT_SESSION_EXPIRED.get(); } @@ -229,35 +229,35 @@ * @return Returns the counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE. */ public int getCounterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE() { - return counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE; + return counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE.get(); } /** * @return Returns the counterReceive_EVT_CHANGE_SESSION_ID. */ public long getCounterReceive_EVT_CHANGE_SESSION_ID() { - return counterReceive_EVT_CHANGE_SESSION_ID; + return counterReceive_EVT_CHANGE_SESSION_ID.get(); } /** * @return Returns the counterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER. */ public long getCounterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER() { - return counterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER; + return counterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER.get(); } /** * @return Returns the sessionReplaceCounter. */ public long getSessionReplaceCounter() { - return sessionReplaceCounter; + return sessionReplaceCounter.get(); } /** * @return Returns the counterNoStateTransferred. */ public int getCounterNoStateTransferred() { - return counterNoStateTransferred; + return counterNoStateTransferred.get(); } public int getReceivedQueueSize() { @@ -391,6 +391,19 @@ this.notifyContainerListenersOnReplication = notifyContainerListenersOnReplication; } + /** + * @return the enableStatistics + */ + public boolean getEnableStatistics() { + return this.enableStatistics; + } + + /** + * @param enableStatistics the enableStatistics to set + */ + public void setEnableStatistics(boolean enableStatistics) { + this.enableStatistics = enableStatistics; + } // --------------------------------------------------------- Public Methods @@ -433,7 +446,9 @@ log.trace(sm.getString("deltaManager.sendMessage.newSession", name, sessionId)); } msg.setTimestamp(session.getCreationTime()); - counterSend_EVT_SESSION_CREATED++; + if (enableStatistics) { + counterSend_EVT_SESSION_CREATED.incrementAndGet(); + } send(msg); } } @@ -495,10 +510,12 @@ SessionMessage msg = new SessionMessageImpl(getName(), SessionMessage.EVT_CHANGE_SESSION_ID, data, orgSessionID, orgSessionID + "-" + System.currentTimeMillis()); msg.setTimestamp(System.currentTimeMillis()); - counterSend_EVT_CHANGE_SESSION_ID++; + if (enableStatistics) { + counterSend_EVT_CHANGE_SESSION_ID.incrementAndGet(); + } send(msg); - } catch (IOException e) { - log.error(sm.getString("deltaManager.unableSerializeSessionID", newSessionID), e); + } catch (IOException ioe) { + log.error(sm.getString("deltaManager.unableSerializeSessionID", newSessionID), ioe); } } } @@ -573,7 +590,9 @@ session.resetDeltaRequest(); // FIXME How inform other session id cache like SingleSignOn if (findSession(session.getIdInternal()) != null) { - sessionReplaceCounter++; + if (enableStatistics) { + sessionReplaceCounter.incrementAndGet(); + } // FIXME better is to grap this sessions again ! if (log.isWarnEnabled()) { log.warn(sm.getString("deltaManager.loading.existing.session", session.getIdInternal())); @@ -587,9 +606,9 @@ } catch (ClassNotFoundException e) { log.error(sm.getString("deltaManager.loading.cnfe", e), e); throw e; - } catch (IOException e) { - log.error(sm.getString("deltaManager.loading.ioe", e), e); - throw e; + } catch (IOException ioe) { + log.error(sm.getString("deltaManager.loading.ioe", ioe), ioe); + throw ioe; } } @@ -615,9 +634,9 @@ } // Flush and close the output stream oos.flush(); - } catch (IOException e) { - log.error(sm.getString("deltaManager.unloading.ioe", e), e); - throw e; + } catch (IOException ioe) { + log.error(sm.getString("deltaManager.unloading.ioe", ioe), ioe); + throw ioe; } // send object data as byte[] @@ -684,7 +703,9 @@ // set reference time stateTransferCreateSendTime = beforeSendTime; // request session state - counterSend_EVT_GET_ALL_SESSIONS++; + if (enableStatistics) { + counterSend_EVT_GET_ALL_SESSIONS.incrementAndGet(); + } stateTransferred = false; // FIXME This send call block the deploy thread, when sender waitForAck is enabled try { @@ -736,7 +757,7 @@ */ protected Member findSessionMasterMember() { Member mbr = null; - Member mbrs[] = cluster.getMembers(); + Member[] mbrs = cluster.getMembers(); if (mbrs.length != 0) { mbr = mbrs[0]; } @@ -764,8 +785,8 @@ do { try { Thread.sleep(100); - } catch (Exception sleep) { - // + } catch (Exception ignore) { + // Ignore } reqNow = System.currentTimeMillis(); isTimeout = ((reqNow - reqStart) > (1000L * getStateTransferTimeout())); @@ -776,14 +797,17 @@ do { try { Thread.sleep(100); - } catch (Exception sleep) { + } catch (Exception ignore) { + // Ignore } } while ((!getStateTransferred()) && (!isNoContextManagerReceived())); reqNow = System.currentTimeMillis(); } } if (isTimeout) { - counterNoStateTransferred++; + if (enableStatistics) { + counterNoStateTransferred.incrementAndGet(); + } log.error(sm.getString("deltaManager.noSessionState", getName(), new Date(beforeSendTime), Long.valueOf(reqNow - beforeSendTime))); } else if (isNoContextManagerReceived()) { @@ -819,7 +843,7 @@ if (log.isInfoEnabled()) { log.info(sm.getString("deltaManager.expireSessions", getName())); } - Session sessions[] = findSessions(); + Session[] sessions = findSessions(); for (Session value : sessions) { DeltaSession session = (DeltaSession) value; if (!session.isValid()) { @@ -883,7 +907,7 @@ * @return a SessionMessage to be sent, */ public ClusterMessage requestCompleted(String sessionId, boolean expires) { - DeltaSession session = null; + DeltaSession session; SessionMessage msg = null; try { session = (DeltaSession) findSession(sessionId); @@ -893,17 +917,21 @@ return null; } if (session.isDirty()) { - counterSend_EVT_SESSION_DELTA++; + if (enableStatistics) { + counterSend_EVT_SESSION_DELTA.incrementAndGet(); + } msg = new SessionMessageImpl(getName(), SessionMessage.EVT_SESSION_DELTA, session.getDiff(), sessionId, sessionId + "-" + System.currentTimeMillis()); } - } catch (IOException x) { - log.error(sm.getString("deltaManager.createMessage.unableCreateDeltaRequest", sessionId), x); + } catch (IOException ioe) { + log.error(sm.getString("deltaManager.createMessage.unableCreateDeltaRequest", sessionId), ioe); return null; } if (msg == null) { if (!expires && !session.isPrimarySession()) { - counterSend_EVT_SESSION_ACCESSED++; + if (enableStatistics) { + counterSend_EVT_SESSION_ACCESSED.incrementAndGet(); + } msg = new SessionMessageImpl(getName(), SessionMessage.EVT_SESSION_ACCESSED, null, sessionId, sessionId + "-" + System.currentTimeMillis()); if (log.isDebugEnabled()) { @@ -922,7 +950,9 @@ if (!expires && (msg == null)) { long replDelta = System.currentTimeMillis() - session.getLastTimeReplicated(); if (session.getMaxInactiveInterval() >= 0 && replDelta > (session.getMaxInactiveInterval() * 1000L)) { - counterSend_EVT_SESSION_ACCESSED++; + if (enableStatistics) { + counterSend_EVT_SESSION_ACCESSED.incrementAndGet(); + } msg = new SessionMessageImpl(getName(), SessionMessage.EVT_SESSION_ACCESSED, null, sessionId, sessionId + "-" + System.currentTimeMillis()); if (log.isDebugEnabled()) { @@ -958,25 +988,25 @@ } } rejectedSessions = 0; - sessionReplaceCounter = 0; - counterNoStateTransferred = 0; + sessionReplaceCounter.set(0); + counterNoStateTransferred.set(0); setMaxActive(getActiveSessions()); - counterReceive_EVT_ALL_SESSION_DATA = 0; - counterReceive_EVT_GET_ALL_SESSIONS = 0; - counterReceive_EVT_SESSION_ACCESSED = 0; - counterReceive_EVT_SESSION_CREATED = 0; - counterReceive_EVT_SESSION_DELTA = 0; - counterReceive_EVT_SESSION_EXPIRED = 0; - counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0; - counterReceive_EVT_CHANGE_SESSION_ID = 0; - counterSend_EVT_ALL_SESSION_DATA = 0; - counterSend_EVT_GET_ALL_SESSIONS = 0; - counterSend_EVT_SESSION_ACCESSED = 0; - counterSend_EVT_SESSION_CREATED = 0; - counterSend_EVT_SESSION_DELTA = 0; - counterSend_EVT_SESSION_EXPIRED = 0; - counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE = 0; - counterSend_EVT_CHANGE_SESSION_ID = 0; + counterReceive_EVT_ALL_SESSION_DATA.set(0); + counterReceive_EVT_GET_ALL_SESSIONS.set(0); + counterReceive_EVT_SESSION_ACCESSED.set(0); + counterReceive_EVT_SESSION_CREATED.set(0); + counterReceive_EVT_SESSION_DELTA.set(0); + counterReceive_EVT_SESSION_EXPIRED.set(0); + counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE.set(0); + counterReceive_EVT_CHANGE_SESSION_ID.set(0); + counterSend_EVT_ALL_SESSION_DATA.set(0); + counterSend_EVT_GET_ALL_SESSIONS.set(0); + counterSend_EVT_SESSION_ACCESSED.set(0); + counterSend_EVT_SESSION_CREATED.set(0); + counterSend_EVT_SESSION_DELTA.set(0); + counterSend_EVT_SESSION_EXPIRED.set(0); + counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE.set(0); + counterSend_EVT_CHANGE_SESSION_ID.set(0); } @@ -989,7 +1019,9 @@ */ protected void sessionExpired(String id) { if (cluster.getMembers().length > 0) { - counterSend_EVT_SESSION_EXPIRED++; + if (enableStatistics) { + counterSend_EVT_SESSION_EXPIRED.incrementAndGet(); + } SessionMessage msg = new SessionMessageImpl(getName(), SessionMessage.EVT_SESSION_EXPIRED, null, id, id + "-EXPIRED-MSG"); msg.setTimestamp(System.currentTimeMillis()); @@ -1004,7 +1036,7 @@ * Expire all find sessions. */ public void expireAllLocalSessions() { - Session sessions[] = findSessions(); + Session[] sessions = findSessions(); int expireDirect = 0; int expireIndirect = 0; @@ -1092,8 +1124,8 @@ // we didn't recognize the message type, do nothing break; } // switch - } catch (Exception x) { - log.error(sm.getString("deltaManager.receiveMessage.error", getName()), x); + } catch (Exception e) { + log.error(sm.getString("deltaManager.receiveMessage.error", getName()), e); } finally { currentThread.setContextClassLoader(contextLoader); } @@ -1109,7 +1141,9 @@ * @param sender Member which sent the message */ protected void handleALL_SESSION_TRANSFERCOMPLETE(SessionMessage msg, Member sender) { - counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE++; + if (enableStatistics) { + counterReceive_EVT_ALL_SESSION_TRANSFERCOMPLETE.incrementAndGet(); + } if (log.isDebugEnabled()) { log.debug(sm.getString("deltaManager.receiveMessage.transfercomplete", getName(), sender.getHost(), Integer.valueOf(sender.getPort()))); @@ -1128,7 +1162,9 @@ * @throws ClassNotFoundException Serialization error */ protected void handleSESSION_DELTA(SessionMessage msg, Member sender) throws IOException, ClassNotFoundException { - counterReceive_EVT_SESSION_DELTA++; + if (enableStatistics) { + counterReceive_EVT_SESSION_DELTA.incrementAndGet(); + } byte[] delta = msg.getSession(); DeltaSession session = (DeltaSession) findSession(msg.getSessionID()); if (session == null) { @@ -1153,7 +1189,9 @@ * @throws IOException Propagated IO error */ protected void handleSESSION_ACCESSED(SessionMessage msg, Member sender) throws IOException { - counterReceive_EVT_SESSION_ACCESSED++; + if (enableStatistics) { + counterReceive_EVT_SESSION_ACCESSED.incrementAndGet(); + } DeltaSession session = (DeltaSession) findSession(msg.getSessionID()); if (session != null) { if (log.isDebugEnabled()) { @@ -1166,7 +1204,7 @@ } /** - * handle receive session is expire at other node ( expire session also here) + * handle receive session is expired at other node ( expire session also here) * * @param msg Session message * @param sender Member which sent the message @@ -1174,7 +1212,9 @@ * @throws IOException Propagated IO error */ protected void handleSESSION_EXPIRED(SessionMessage msg, Member sender) throws IOException { - counterReceive_EVT_SESSION_EXPIRED++; + if (enableStatistics) { + counterReceive_EVT_SESSION_EXPIRED.incrementAndGet(); + } DeltaSession session = (DeltaSession) findSession(msg.getSessionID()); if (session != null) { if (log.isDebugEnabled()) { @@ -1191,7 +1231,9 @@ * @param sender Member which sent the message */ protected void handleSESSION_CREATED(SessionMessage msg, Member sender) { - counterReceive_EVT_SESSION_CREATED++; + if (enableStatistics) { + counterReceive_EVT_SESSION_CREATED.incrementAndGet(); + } if (log.isDebugEnabled()) { log.debug(sm.getString("deltaManager.receiveMessage.createNewSession", getName(), msg.getSessionID())); } @@ -1219,7 +1261,9 @@ */ protected void handleALL_SESSION_DATA(SessionMessage msg, Member sender) throws ClassNotFoundException, IOException { - counterReceive_EVT_ALL_SESSION_DATA++; + if (enableStatistics) { + counterReceive_EVT_ALL_SESSION_DATA.incrementAndGet(); + } if (log.isDebugEnabled()) { log.debug(sm.getString("deltaManager.receiveMessage.allSessionDataBegin", getName())); } @@ -1241,7 +1285,9 @@ * @throws IOException IO error sending messages */ protected void handleGET_ALL_SESSIONS(SessionMessage msg, Member sender) throws IOException { - counterReceive_EVT_GET_ALL_SESSIONS++; + if (enableStatistics) { + counterReceive_EVT_GET_ALL_SESSIONS.incrementAndGet(); + } // get a list of all the session from this manager if (log.isDebugEnabled()) { log.debug(sm.getString("deltaManager.receiveMessage.unloadingBegin", getName())); @@ -1265,7 +1311,8 @@ if (getSendAllSessionsWaitTime() > 0 && remain > 0) { try { Thread.sleep(getSendAllSessionsWaitTime()); - } catch (Exception sleep) { + } catch (Exception ignore) { + // Ignore } } } @@ -1277,7 +1324,9 @@ if (log.isDebugEnabled()) { log.debug(sm.getString("deltaManager.createMessage.allSessionTransferred", getName())); } - counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE++; + if (enableStatistics) { + counterSend_EVT_ALL_SESSION_TRANSFERCOMPLETE.incrementAndGet(); + } cluster.send(newmsg, sender); } @@ -1290,7 +1339,9 @@ * @throws IOException IO error with serialization */ protected void handleCHANGE_SESSION_ID(SessionMessage msg, Member sender) throws IOException { - counterReceive_EVT_CHANGE_SESSION_ID++; + if (enableStatistics) { + counterReceive_EVT_CHANGE_SESSION_ID.incrementAndGet(); + } DeltaSession session = (DeltaSession) findSession(msg.getSessionID()); if (session != null) { String newSessionID = deserializeSessionId(msg.getSession()); @@ -1308,7 +1359,9 @@ * @param sender Member which sent the message */ protected void handleALL_SESSION_NOCONTEXTMANAGER(SessionMessage msg, Member sender) { - counterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER++; + if (enableStatistics) { + counterReceive_EVT_ALL_SESSION_NOCONTEXTMANAGER.incrementAndGet(); + } if (log.isDebugEnabled()) { log.debug(sm.getString("deltaManager.receiveMessage.noContextManager", getName(), sender.getHost(), Integer.valueOf(sender.getPort()))); @@ -1336,7 +1389,9 @@ if (log.isDebugEnabled()) { log.debug(sm.getString("deltaManager.createMessage.allSessionData", getName())); } - counterSend_EVT_ALL_SESSION_DATA++; + if (enableStatistics) { + counterSend_EVT_ALL_SESSION_DATA.incrementAndGet(); + } int sendOptions = Channel.SEND_OPTIONS_SYNCHRONIZED_ACK | Channel.SEND_OPTIONS_USE_ACK; cluster.send(newmsg, sender, sendOptions); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/DeltaRequest.java tomcat10-10.1.52/java/org/apache/catalina/ha/session/DeltaRequest.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/DeltaRequest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/DeltaRequest.java 2026-01-23 19:33:36.000000000 +0000 @@ -126,8 +126,7 @@ } public void setNew(boolean n) { - int action = ACTION_SET; - addAction(TYPE_ISNEW, action, NAME_ISNEW, Boolean.valueOf(n)); + addAction(TYPE_ISNEW, ACTION_SET, NAME_ISNEW, Boolean.valueOf(n)); } public void setAuthType(String authType) { @@ -144,12 +143,12 @@ } protected void addAction(int type, int action, String name, Object value) { - AttributeInfo info = null; - if (this.actionPool.size() > 0) { + AttributeInfo info; + if (!this.actionPool.isEmpty()) { try { info = actionPool.removeFirst(); - } catch (Exception x) { - log.error(sm.getString("deltaRequest.removeUnable"), x); + } catch (Exception e) { + log.error(sm.getString("deltaRequest.removeUnable"), e); info = new AttributeInfo(type, action, name, value); } info.init(type, action, name, value); @@ -247,16 +246,15 @@ } public void reset() { - while (actions.size() > 0) { + while (!actions.isEmpty()) { try { AttributeInfo info = actions.removeFirst(); info.recycle(); actionPool.addLast(info); - } catch (Exception x) { - log.error(sm.getString("deltaRequest.removeUnable"), x); + } catch (Exception e) { + log.error(sm.getString("deltaRequest.removeUnable"), e); } } - actions.clear(); } public String getSessionId() { @@ -266,8 +264,8 @@ public void setSessionId(String sessionId) { this.sessionId = sessionId; if (sessionId == null) { - Exception e = new Exception(sm.getString("deltaRequest.ssid.null")); - log.error(sm.getString("deltaRequest.ssid.null"), e.fillInStackTrace()); + String msg = sm.getString("deltaRequest.ssid.null"); + log.error(msg, new Exception(msg)); } } @@ -291,12 +289,12 @@ recordAllActions = in.readBoolean(); int cnt = in.readInt(); for (int i = 0; i < cnt; i++) { - AttributeInfo info = null; - if (this.actionPool.size() > 0) { + AttributeInfo info; + if (!this.actionPool.isEmpty()) { try { info = actionPool.removeFirst(); - } catch (Exception x) { - log.error(sm.getString("deltaRequest.removeUnable"), x); + } catch (Exception e) { + log.error(sm.getString("deltaRequest.removeUnable"), e); info = new AttributeInfo(); } } else { @@ -433,11 +431,8 @@ @Override public String toString() { - StringBuilder buf = new StringBuilder("AttributeInfo[type="); - buf.append(getType()).append(", action=").append(getAction()); - buf.append(", name=").append(getName()).append(", value=").append(getValue()); - buf.append(", addr=").append(super.toString()).append(']'); - return buf.toString(); + return "AttributeInfo[type=" + getType() + ", action=" + getAction() + ", name=" + getName() + ", value=" + + getValue() + ", addr=" + super.toString() + ']'; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/DeltaSession.java tomcat10-10.1.52/java/org/apache/catalina/ha/session/DeltaSession.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/DeltaSession.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/DeltaSession.java 2026-01-23 19:33:36.000000000 +0000 @@ -107,7 +107,7 @@ } /* - * DeltaRequest instances are created via this protected method to enable sub-classes to over-ride the method to use + * DeltaRequest instances are created via this protected method to enable subclasses to over-ride the method to use * custom DeltaRequest implementations. */ protected DeltaRequest createRequest(String sessionId, boolean recordAllActions) { @@ -129,7 +129,7 @@ @Override public byte[] getDiff() throws IOException { SynchronizedStack deltaRequestPool = null; - DeltaRequest newDeltaRequest = null; + DeltaRequest newDeltaRequest; if (manager instanceof ClusterManagerBase) { deltaRequestPool = ((ClusterManagerBase) manager).getDeltaRequestPool(); @@ -243,10 +243,7 @@ @Override public boolean isAccessReplicate() { long replDelta = System.currentTimeMillis() - getLastTimeReplicated(); - if (maxInactiveInterval >= 0 && replDelta > (maxInactiveInterval * 1000L)) { - return true; - } - return false; + return maxInactiveInterval >= 0 && replDelta > (maxInactiveInterval * 1000L); } @Override @@ -461,11 +458,7 @@ @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("DeltaSession["); - sb.append(id); - sb.append(']'); - return sb.toString(); + return "DeltaSession[" + id + ']'; } @Override @@ -831,12 +824,11 @@ stream.writeObject(notes.get(org.apache.catalina.authenticator.Constants.FORM_REQUEST_NOTE)); // Accumulate the names of serializable and non-serializable attributes - String keys[] = keys(); + String[] keys = keys(); List saveNames = new ArrayList<>(); List saveValues = new ArrayList<>(); for (String key : keys) { - Object value = null; - value = attributes.get(key); + Object value = attributes.get(key); if (value != null && !exclude(key, value) && isAttributeDistributable(key, value)) { saveNames.add(key); saveValues.add(value); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/JvmRouteBinderValve.java tomcat10-10.1.52/java/org/apache/catalina/ha/session/JvmRouteBinderValve.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/JvmRouteBinderValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/JvmRouteBinderValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -58,8 +58,6 @@ * You can enable this mod_jk turnover mode via JMX before you drop a node to all backup nodes! Set enable true on all * JvmRouteBinderValve backups, disable worker at mod_jk and then drop node and restart it! Then enable mod_jk worker * and disable JvmRouteBinderValves again. This use case means that only requested sessions are migrated. - * - * @author Peter Rossbach */ public class JvmRouteBinderValve extends ValveBase implements ClusterValve { @@ -138,7 +136,7 @@ } /** - * Detect possible the JVMRoute change at cluster backup node.. + * Detect possible the JVMRoute change at cluster backup node. * * @param request tomcat request being processed * @param response tomcat response being processed @@ -258,8 +256,8 @@ Session catalinaSession = null; try { catalinaSession = getManager(request).findSession(sessionId); - } catch (IOException e) { - // Hups! + } catch (IOException ignore) { + // Error looking for session using old session ID. Treat it as not found. } String id = sessionId.substring(0, index); String newSessionID = id + "." + localJvmRoute; @@ -270,11 +268,11 @@ } else { try { catalinaSession = getManager(request).findSession(newSessionID); - } catch (IOException e) { - // Hups! + } catch (IOException ignore) { + // Error looking for session using new session ID. Treat it as not found. } if (catalinaSession != null) { - // session is rewrite at other request, rewrite this also + // Session was rewritten in other, concurrent request. Rewrite this request also. changeRequestSessionID(request, sessionId, newSessionID); } else { if (log.isDebugEnabled()) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/ha/session/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -31,7 +31,7 @@ deltaManager.createMessage.allSessionTransferred=マネージャ [{0}] はすべてのセッションデータ転送を送信しました deltaManager.createMessage.delta=マネージャ [{0}]: セッション [{1}] のデルタリクエストメッセージを作成します deltaManager.createMessage.expire=マネージャ [{0}]: セッション [{1}] のセッションの期限切れメッセージを作成します -deltaManager.createMessage.unableCreateDeltaRequest=セッションID [{0}]のDeltaRequestシリアライズできません。 +deltaManager.createMessage.unableCreateDeltaRequest=セッションID [{0}] の DeltaRequest がシリアライズできません deltaManager.createSession.newSession=DeltaSession (ID は [{0}]) を作成しました。総数は [{1}] です。 deltaManager.dropMessage=マネージャ [{0}]: GET_ALL_SESSIONS同期フェーズの開始日 [{2}] メッセージの日付 [{3}] 内のメッセージ [{1}] をドロップします deltaManager.expireSessions=シャットダウン時にマネージャ [{0}] はセッションを満了します @@ -82,7 +82,7 @@ jvmRoute.changeSession=セッションを [{0}] から [{1}] へ変更しました。 jvmRoute.failover=他の jvmRoute へのフェールオーバーを検出しました。元のルートは [{0}]、新しいルートは [{1}]、セッション ID は [{2}] です。 jvmRoute.foundManager=Cluster Manager [{0}] を [{1}] で発見しました -jvmRoute.missingJvmRouteAttribute=jvmRoute 属性にエンジンが指定されていません。 +jvmRoute.missingJvmRouteAttribute=Engine に jvmRoute 属性が指定されていません! jvmRoute.noCluster=JvmRouterBinderValveは設定されていますが、クラスタリングは使用されていません。 PersistentManagerが使用されている場合、フェールオーバーは引き続き機能します。 jvmRoute.notFoundManager=[{0}]でCluster Managerが見つかりません。 jvmRoute.set.originalsessionid=オリジナルSession idをリクエスト属性[{0}]の値:[{1}]で設定します。 @@ -91,5 +91,5 @@ jvmRoute.valve.stopped=JvmRouteBinderValve が停止しました。 standardSession.notSerializable=セッション[{1}]のセッション属性[{0}]をシリアライズできません。 -standardSession.removeAttribute.ise=removeAttribute: Session already invalidated +standardSession.removeAttribute.ise=removeAttribute: セッションは既に無効化されています standardSession.setAttribute.namenull=setAttribute:nameパラメータをnullにすることはできません。 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/SessionMessage.java tomcat10-10.1.52/java/org/apache/catalina/ha/session/SessionMessage.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/SessionMessage.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/SessionMessage.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,8 +47,8 @@ int EVT_SESSION_EXPIRED = 2; /** - * Event type used when a session has been accessed (ie, last access time has been updated. This is used so that the - * replicated sessions will not expire on the network + * Event type used when a session has been accessed (ie, last access time has been updated). This is used so that + * the replicated sessions will not expire on the network */ int EVT_SESSION_ACCESSED = 3; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/session/SessionMessageImpl.java tomcat10-10.1.52/java/org/apache/catalina/ha/session/SessionMessageImpl.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/session/SessionMessageImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/session/SessionMessageImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,9 +20,7 @@ import org.apache.catalina.ha.ClusterMessageBase; /** - * Session cluster message - * - * @author Peter Rossbach + * Session cluster message. */ public class SessionMessageImpl extends ClusterMessageBase implements SessionMessage { @@ -61,7 +59,7 @@ * EVT_SESSION_ACCESSED
      * The parameters: sessionID must be set.
      * EVT_GET_ALL_SESSIONS
      - * get all sessions from from one of the nodes.
      + * get all sessions from one of the nodes.
      * EVT_SESSION_DELTA
      * Send attribute delta (add,update,remove attribute or principal, ...).
      * EVT_ALL_SESSION_DATA
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/Constants.java tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,6 @@ /** * Manifest constants for the org.apache.catalina.ha.tcp package. - * - * @author Peter Rossbach */ public class Constants { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,7 @@ ReplicationValve.filter.loading=リクエストフィルタ= [{0}]のロード ReplicationValve.invoke.uri=[{0}]のレプリケーションリクエストを呼び出します。 ReplicationValve.nocluster=このリクエストに対して構成されたクラスタはありません。 -ReplicationValve.resetDeltaRequest=クラスタはスタンドアロンである:コンテキスト [{0}] でセッションのデルタリクエストをリセットします +ReplicationValve.resetDeltaRequest=クラスタはスタンドアロンです:コンテキスト [{0}] でセッションのデルタリクエストをリセットします ReplicationValve.send.failure=レプリケーションリクエストを実行できません。 ReplicationValve.send.invalid.failure=セッション[id = {0}]無効メッセージをクラスタに送信できません。 ReplicationValve.session.found=コンテキスト [{0}]: セッション [{1}] は見つかりましたが、クラスターセッションではありません diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/ReplicationValve.java tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/ReplicationValve.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/ReplicationValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/ReplicationValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,7 +18,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAdder; @@ -48,21 +47,14 @@ import org.apache.tomcat.util.res.StringManager; /** - *

      * Implementation of a Valve that logs interesting contents from the specified Request (before processing) and the * corresponding Response (after processing). It is especially useful in debugging problems related to headers and * cookies. - *

      *

      * This Valve may be attached to any Container, depending on the granularity of the logging you wish to perform. - *

      *

      * primaryIndicator=true, then the request attribute org.apache.catalina.ha.tcp.isPrimarySession. is set true, * when request processing is at sessions primary node. - *

      - * - * @author Craig R. McClanahan - * @author Peter Rossbach */ public class ReplicationValve extends ValveBase implements ClusterValve { @@ -101,7 +93,7 @@ protected LongAdder nrOfCrossContextSendRequests = new LongAdder(); /** - * must primary change indicator set + * Must set primary change indicator. */ protected boolean primaryIndicator = false; @@ -154,7 +146,7 @@ log.trace(sm.getString("ReplicationValve.filter.loading", filter)); } - if (filter == null || filter.length() == 0) { + if (filter == null || filter.isEmpty()) { this.filter = null; } else { try { @@ -295,7 +287,7 @@ createPrimaryIndicator(request); } Context context = request.getContext(); - boolean isCrossContext = context != null && context instanceof StandardContext && context.getCrossContext(); + boolean isCrossContext = context instanceof StandardContext && context.getCrossContext(); boolean isAsync = request.getAsyncContextInternal() != null; try { if (isCrossContext) { @@ -320,7 +312,7 @@ } } } finally { - // Array must be remove: Current master request send endAccess at recycle. + // Array must be removed: Current master request send endAccess at recycle. // Don't register this request session again! if (isCrossContext) { if (log.isTraceEnabled()) { @@ -378,9 +370,9 @@ if (isCrossContext) { sendCrossContextSession(); } - } catch (Exception x) { + } catch (Exception e) { // FIXME we have a lot of sends, but the trouble with one node stops the correct replication to other nodes! - log.error(sm.getString("ReplicationValve.send.failure"), x); + log.error(sm.getString("ReplicationValve.send.failure"), e); } finally { if (doStatistics()) { updateStats(totalstart, start, isAsync); @@ -393,7 +385,7 @@ */ protected void sendCrossContextSession() { List sessions = crossContextSessions.get(); - if (sessions != null && sessions.size() > 0) { + if (sessions != null && !sessions.isEmpty()) { for (DeltaSession session : sessions) { if (log.isTraceEnabled()) { log.trace(sm.getString("ReplicationValve.crossContext.sendDelta", @@ -421,15 +413,10 @@ } if (isCrossContext) { List sessions = crossContextSessions.get(); - if (sessions != null && sessions.size() > 0) { - Iterator iter = sessions.iterator(); - for (; iter.hasNext();) { - Session session = iter.next(); + if (sessions != null) { + for (DeltaSession session : sessions) { resetDeltaRequest(session); - if (session instanceof DeltaSession) { - ((DeltaSession) contextSession).setPrimarySession(true); - } - + session.setPrimarySession(true); } } } @@ -509,13 +496,11 @@ */ protected void sendInvalidSessions(ClusterManager manager) { String[] invalidIds = manager.getInvalidatedSessions(); - if (invalidIds.length > 0) { - for (String invalidId : invalidIds) { - try { - send(manager, invalidId); - } catch (Exception x) { - log.error(sm.getString("ReplicationValve.send.invalid.failure", invalidId), x); - } + for (String invalidId : invalidIds) { + try { + send(manager, invalidId); + } catch (Exception e) { + log.error(sm.getString("ReplicationValve.send.invalid.failure", invalidId), e); } } } @@ -549,13 +534,12 @@ if (log.isDebugEnabled()) { if ((nrOfRequests.longValue() % 100) == 0) { log.debug(sm.getString("ReplicationValve.stats", - new Object[] { Long.valueOf(totalRequestTime.longValue() / nrOfRequests.longValue()), - Long.valueOf(totalSendTime.longValue() / nrOfRequests.longValue()), - Long.valueOf(nrOfRequests.longValue()), Long.valueOf(nrOfSendRequests.longValue()), - Long.valueOf(nrOfCrossContextSendRequests.longValue()), - Long.valueOf(nrOfFilterRequests.longValue()), - Long.valueOf(totalRequestTime.longValue()), - Long.valueOf(totalSendTime.longValue()) })); + Long.valueOf(totalRequestTime.longValue() / nrOfRequests.longValue()), + Long.valueOf(totalSendTime.longValue() / nrOfRequests.longValue()), + Long.valueOf(nrOfRequests.longValue()), Long.valueOf(nrOfSendRequests.longValue()), + Long.valueOf(nrOfCrossContextSendRequests.longValue()), + Long.valueOf(nrOfFilterRequests.longValue()), Long.valueOf(totalRequestTime.longValue()), + Long.valueOf(totalSendTime.longValue()))); } } } @@ -571,7 +555,7 @@ */ protected void createPrimaryIndicator(Request request) throws IOException { String id = request.getRequestedSessionId(); - if ((id != null) && (id.length() > 0)) { + if ((id != null) && (!id.isEmpty())) { Manager manager = request.getContext().getManager(); Session session = manager.findSession(id); if (session instanceof ClusterSession) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/SendMessageData.java tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/SendMessageData.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/SendMessageData.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/SendMessageData.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,9 +18,6 @@ import org.apache.catalina.tribes.Member; -/** - * @author Peter Rossbach - */ public class SendMessageData { private Object message; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java --- tomcat10-10.1.34/java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ha/tcp/SimpleTcpCluster.java 2026-01-23 19:33:36.000000000 +0000 @@ -59,9 +59,6 @@ /** * A Cluster implementation using simple multicast. Responsible for setting up a cluster and provides callers * with a valid multicast receiver/sender. - * - * @author Remy Maucherat - * @author Peter Rossbach */ public class SimpleTcpCluster extends LifecycleMBeanBase implements CatalinaCluster, MembershipListener, ChannelListener { @@ -345,8 +342,8 @@ try { manager = managerTemplate.cloneFromTemplate(); manager.setName(name); - } catch (Exception x) { - log.error(sm.getString("simpleTcpCluster.clustermanager.cloneFailed"), x); + } catch (Exception e) { + log.error(sm.getString("simpleTcpCluster.clustermanager.cloneFailed"), e); manager = new DeltaManager(); } finally { if (manager != null) { @@ -470,19 +467,19 @@ clusterDeployer.start(); } registerMember(channel.getLocalMember(false)); - } catch (Exception x) { - log.error(sm.getString("simpleTcpCluster.startUnable"), x); - throw new LifecycleException(x); + } catch (Exception e) { + log.error(sm.getString("simpleTcpCluster.startUnable"), e); + throw new LifecycleException(e); } setState(LifecycleState.STARTING); } protected void checkDefaults() { - if (clusterListeners.size() == 0 && managerTemplate instanceof DeltaManager) { + if (clusterListeners.isEmpty() && managerTemplate instanceof DeltaManager) { addClusterListener(new ClusterSessionListener()); } - if (valves.size() == 0) { + if (valves.isEmpty()) { addValve(new JvmRouteBinderValve()); addValve(new ReplicationValve()); } @@ -560,8 +557,8 @@ channel.removeChannelListener(this); channel.removeMembershipListener(this); this.unregisterClusterValve(); - } catch (Exception x) { - log.error(sm.getString("simpleTcpCluster.stopUnable"), x); + } catch (Exception e) { + log.error(sm.getString("simpleTcpCluster.stopUnable"), e); } channel.setUtilityExecutor(null); @@ -612,8 +609,8 @@ log.debug(sm.getString("simpleTcpCluster.noMembers", msg)); } } - } catch (Exception x) { - log.error(sm.getString("simpleTcpCluster.sendFailed"), x); + } catch (Exception e) { + log.error(sm.getString("simpleTcpCluster.sendFailed"), e); } } @@ -631,8 +628,8 @@ // Notify our interested LifecycleListeners fireLifecycleEvent(AFTER_MEMBERREGISTER_EVENT, member); - } catch (Exception x) { - log.error(sm.getString("simpleTcpCluster.member.addFailed"), x); + } catch (Exception e) { + log.error(sm.getString("simpleTcpCluster.member.addFailed"), e); } } @@ -651,8 +648,8 @@ // Notify our interested LifecycleListeners fireLifecycleEvent(AFTER_MEMBERUNREGISTER_EVENT, member); - } catch (Exception x) { - log.error(sm.getString("simpleTcpCluster.member.removeFailed"), x); + } catch (Exception e) { + log.error(sm.getString("simpleTcpCluster.member.removeFailed"), e); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/loader/JdbcLeakPrevention.java tomcat10-10.1.52/java/org/apache/catalina/loader/JdbcLeakPrevention.java --- tomcat10-10.1.34/java/org/apache/catalina/loader/JdbcLeakPrevention.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/loader/JdbcLeakPrevention.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,8 +40,8 @@ /* * DriverManager.getDrivers() has a nasty side-effect of registering drivers that are visible to this class * loader but haven't yet been loaded. Therefore, the first call to this method a) gets the list of originally - * loaded drivers and b) triggers the unwanted side-effect. The second call gets the complete list of drivers - * ensuring that both original drivers and any loaded as a result of the side-effects are all de-registered. + * loaded drivers and b) triggers the unwanted side effect. The second call gets the complete list of drivers + * ensuring that both original drivers and any loaded as a result of the side effects are all de-registered. */ Set originalDrivers = new HashSet<>(); Enumeration drivers = DriverManager.getDrivers(); @@ -56,7 +56,7 @@ continue; } // Only report drivers that were originally registered. Skip any - // that were registered as a side-effect of this code. + // that were registered as a side effect of this code. if (originalDrivers.contains(driver)) { driverNames.add(driver.getClass().getCanonicalName()); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -50,7 +50,7 @@ webappClassLoader.restrictedPackage=Security violation, attempt to use restricted class [{0}] webappClassLoader.securityException=Security exception trying to find class [{0}] in findClassInternal [{1}] webappClassLoader.stackTrace=The web application [{0}] appears to have started a thread named [{1}] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:{2} -webappClassLoader.stackTraceRequestThread=The web application [{0}] is still processing a request that has yet to finish. This is very likely to create a memory leak. You can control the time allowed for requests to finish by using the unloadDelay attribute of the standard Context implementation. Stack trace of request processing thread:[{2}] +webappClassLoader.stackTraceRequestThread=The thread [{1}] of web application [{0}] is still processing a request that has yet to finish. This is very likely to create a memory leak. You can control the time allowed for requests to finish by using the unloadDelay attribute of the standard Context implementation. Stack trace of request processing thread:[{2}] webappClassLoader.stopThreadFail=Failed to terminate thread named [{0}] for web application [{1}] webappClassLoader.stopTimerThreadFail=Failed to terminate TimerThread named [{0}] for web application [{1}] webappClassLoader.stopped=Illegal access: this web application instance has been stopped already. Could not load [{0}]. The following stack trace is thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -50,7 +50,7 @@ webappClassLoader.restrictedPackage=Violation de sécurité en essayant d''utiliser à une classe à accès restreint [{0}] webappClassLoader.securityException=Exception de sécurité en essayant de trouver la classe [{0}] dans findClassInternal [{1}] webappClassLoader.stackTrace=L''application web [{0}] semble avoir démarré un thread nommé [{1}] mais ne l''a pas arrêté, ce qui va probablement créer une fuite de mémoire ; la trace du thread est : {2} -webappClassLoader.stackTraceRequestThread=Une requête de l''application web [{0}] est toujours en cours, ce qui causera certainement une fuite de mémoire, vous pouvez contrôler le temps alloué en utilisant l''attribut unloadDelay de l''implémentation standard de Context ; trace du fil d’exécution de la requête : [{2}] +webappClassLoader.stackTraceRequestThread=Le thread [{1}] de l''application web [{0}] est toujours en cours, ce qui risque de causer une fuite de mémoire. Vous pouvez contrôler le temps alloué en utilisant l''attribut unloadDelay de l''implémentation standard de Context ; trace de la pile d''exécution de la requête : [{2}] webappClassLoader.stopThreadFail=Impossible de terminer le thread nommé [{0}] pour l''application [{1}] webappClassLoader.stopTimerThreadFail=Echec de l''arrêt du TimerThread nommé [{0}] pour l''application web [{1}] webappClassLoader.stopped=Impossible de charger [{0}], ce chargeur de classes a déjà été arrêté diff -Nru tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -50,7 +50,7 @@ webappClassLoader.restrictedPackage=セキュリティー違反。制限されたクラス [{0}] を使おうとしました。 webappClassLoader.securityException=indClassInternal [{1}] でクラス [{0}] を検索中のセキュリティ例外です webappClassLoader.stackTrace=Webアプリケーション [{0}] は [{1}] という名前のスレッドを開始したようですが、停止に失敗しました。これはメモリリークを引き起こす可能性が非常に高いです。スレッドのスタックトレース: {2} -webappClassLoader.stackTraceRequestThread=Webアプリケーション[{0}]はまだ完了していないリクエストを処理しています。 これはメモリリークを引き起こす可能性が非常に高いです。 リクエストの終了時間は、StandardContext実装のunloadDelay属性を使用して制御できます。 リクエスト処理スレッドのスタックトレース:[{2}] +webappClassLoader.stackTraceRequestThread=Webアプリケーション[{0}]のスレッド[{1}]はまだ完了していないリクエストを処理しています。 これはメモリリークを引き起こす可能性が非常に高いです。 リクエストの終了時間は、StandardContext実装のunloadDelay属性を使用して制御できます。 リクエスト処理スレッドのスタックトレース:[{2}] webappClassLoader.stopThreadFail=Web アプリケーション [{1}] のスレッド [{0}] は終了できません。 webappClassLoader.stopTimerThreadFail=Webアプリケーション [{1}] の [{0}] という名前のTimerThreadを終了できませんでした webappClassLoader.stopped=不正なアクセス: このWebアプリケーションのインスタンスは既に停止されています Could not load [{0}]. 不正なアクセスを引き起こしたスレッドを終了させ、投げられたエラーによりデバッグ用に次のスタックトレースが生成されましたが,機能に影響はありません diff -Nru tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings_ko.properties tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_ko.properties --- tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings_ko.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_ko.properties 2026-01-23 19:33:36.000000000 +0000 @@ -50,7 +50,7 @@ webappClassLoader.restrictedPackage=보안 위반 행위: 제한된 클래스 [{0}]을(를) 사용하려 시도했습니다. webappClassLoader.securityException=findClassInternal에서, 클래스 [{0}]을(를) 찾으려 시도 중 보안 예외 발생: [{1}] webappClassLoader.stackTrace=웹 애플리케이션 [{0}]이(가) [{1}](이)라는 이름의 쓰레드를 시작시킨 것으로 보이지만, 해당 쓰레드를 중지시키지 못했습니다. 이는 메모리 누수를 유발할 가능성이 큽니다. 해당 쓰레드의 스택 트레이스:{2} -webappClassLoader.stackTraceRequestThread=웹 애플리케이션 [{0}]이(가) 여전히 완료되지 않은 요청을 처리하고 있습니다. 이는 메모리 누수를 유발할 가능성이 높습니다. 표준 컨텍스트 구현의 unloadDelay 속성을 이용하여, 요청 완료 허용 시간을 통제할 수 있습니다. 요청 처리 쓰레드의 스택 트레이스:[{2}] +webappClassLoader.stackTraceRequestThread=웹 애플리케이션 [{0}]의 쓰레드 [{1}]가 여전히 완료되지 않은 요청을 처리하고 있습니다. 이는 메모리 누수를 유발할 가능성이 높습니다. 표준 컨텍스트 구현의 unloadDelay 속성을 이용하여, 요청 완료 허용 시간을 통제할 수 있습니다. 요청 처리 쓰레드의 스택 트레이스:[{2}] webappClassLoader.stopThreadFail=웹 애플리케이션 [{1}]을 위한, [{0}](이)라는 이름의 쓰레드를 종료시키지 못했습니다. webappClassLoader.stopTimerThreadFail=웹 애플리케이션 [{1}]을(를) 위한, [{0}](이)라는 이름의 TimerThread를 종료시키지 못했습니다. webappClassLoader.stopped=불허되는 접근: 이 웹 애플리케이션 인스턴스는 이미 중지되었습니다. [{0}]을(를) 로드할 수 없습니다. 디버그 목적 및 불허되는 접근을 발생시킨 해당 쓰레드를 종료시키기 위한 시도로서, 다음 스택 트레이스가 생성됩니다. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/loader/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/loader/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -50,7 +50,7 @@ webappClassLoader.restrictedPackage=安全冲突,尝试使用受限类[{0}] webappClassLoader.securityException=尝试在findClassInternal[{1}]中查找类[{0}]时出现安全异常 webappClassLoader.stackTrace=Web应用程序[{0}]似乎启动了一个名为[{1}]的线程,但未能停止它。这很可能会造成内存泄漏。线程的堆栈跟踪:[{2}] -webappClassLoader.stackTraceRequestThread=web应用程序[{0}]仍在处理一个尚未完成的请求。这很可能会造成内存泄漏。您可以使用标准上下文实现的unloadDelay属性来控制请求完成所允许的时间。请求处理线程的堆栈跟踪:[{2}] +webappClassLoader.stackTraceRequestThread=web应用程序[{0}]中的线程[{1}]仍在处理一个尚未完成的请求。这很可能会造成内存泄漏。您可以使用标准上下文实现的unloadDelay属性来控制请求完成所允许的时间。请求处理线程的堆栈跟踪:[{2}] webappClassLoader.stopThreadFail=为web应用程序[{1}]终止线程[{0}]失败 webappClassLoader.stopTimerThreadFail=无法终止名为[{0}]的TimerThread,web应用程序:[{1}] webappClassLoader.stopped=非法访问:此Web应用程序实例已停止。无法加载[{0}]。为了调试以及终止导致非法访问的线程,将抛出以下堆栈跟踪。 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/loader/ResourceEntry.java tomcat10-10.1.52/java/org/apache/catalina/loader/ResourceEntry.java --- tomcat10-10.1.34/java/org/apache/catalina/loader/ResourceEntry.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/loader/ResourceEntry.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,6 @@ /** * Resource entry. - * - * @author Remy Maucherat */ public class ResourceEntry { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/loader/WebappClassLoaderBase.java tomcat10-10.1.52/java/org/apache/catalina/loader/WebappClassLoaderBase.java --- tomcat10-10.1.34/java/org/apache/catalina/loader/WebappClassLoaderBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/loader/WebappClassLoaderBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,6 +19,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileNotFoundException; import java.io.FilePermission; import java.io.IOException; import java.io.InputStream; @@ -113,9 +114,6 @@ * IMPLEMENTATION NOTE - As of 8.0, this class loader implements {@link InstrumentableClassLoader}, * permitting web application classes to instrument other classes in the same web application. It does not permit * instrumentation of system or container classes or classes in other web apps. - * - * @author Remy Maucherat - * @author Craig R. McClanahan */ public abstract class WebappClassLoaderBase extends URLClassLoader implements Lifecycle, InstrumentableClassLoader, WebappProperties, PermissionCheck { @@ -387,7 +385,7 @@ /** * Repositories managed by this class rather than the super class. */ - private List localRepositories = new ArrayList<>(); + private final List localRepositories = new ArrayList<>(); private volatile LifecycleState state = LifecycleState.NEW; @@ -738,7 +736,7 @@ sb.append(this.parent.toString()); sb.append("\r\n"); } - if (this.transformers.size() > 0) { + if (!this.transformers.isEmpty()) { sb.append("----------> Class file transformers:\r\n"); for (ClassFileTransformer transformer : this.transformers) { sb.append(transformer).append("\r\n"); @@ -899,7 +897,8 @@ url = super.findResource(name); } - if (url == null) { + // Skip caching results for invalid names (it might mask lookups for valid ones) + if (url == null && !name.isEmpty() && name.charAt(0) != '/') { notFoundClassResources.add(path); } } @@ -984,7 +983,7 @@ checkStateForResourceLoading(name); - URL url = null; + URL url; boolean delegateFirst = delegate || filter(name, false); @@ -1093,7 +1092,7 @@ if (resource.exists()) { stream = resource.getInputStream(); // Filter out .class resources through the ClassFileTranformer - if (name.endsWith(CLASS_FILE_SUFFIX) && transformers.size() > 0) { + if (name.endsWith(CLASS_FILE_SUFFIX) && !transformers.isEmpty()) { // If the resource is a class, decorate it with any attached transformers ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[8192]; @@ -1102,13 +1101,14 @@ while ((numRead = stream.read(buf)) >= 0) { baos.write(buf, 0, numRead); } - } catch (IOException e) { - log.error(sm.getString("webappClassLoader.transformError", name), e); + } catch (IOException ioe) { + log.error(sm.getString("webappClassLoader.transformError", name), ioe); return null; } finally { try { stream.close(); - } catch (IOException e) { + } catch (IOException ignore) { + // Ignore } } byte[] binaryContent = baos.toByteArray(); @@ -1135,7 +1135,7 @@ stream = url.openStream(); } } - } catch (IOException e) { + } catch (IOException ioe) { // Ignore } if (stream != null) { @@ -1145,7 +1145,10 @@ return stream; } - notFoundClassResources.add(path); + // Skip caching results for invalid names (it might mask lookups for valid ones) + if (!name.isEmpty() && name.charAt(0) != '/') { + notFoundClassResources.add(path); + } } // (3) Delegate to parent unconditionally @@ -1210,7 +1213,7 @@ if (log.isTraceEnabled()) { log.trace("loadClass(" + name + ", " + resolve + ")"); } - Class clazz = null; + Class clazz; // Log access to stopped class loader checkStateForClassLoading(name); @@ -1390,9 +1393,8 @@ // It is not permitted to load resources once the web application has // been stopped. if (!state.isAvailable()) { - String msg = sm.getString("webappClassLoader.stopped", resource); - IllegalStateException ise = new IllegalStateException(msg); - log.info(msg, ise); + IllegalStateException ise = new IllegalStateException(sm.getString("webappClassLoader.stopped", resource)); + log.info(ise.getMessage(), ise); throw ise; } } @@ -1645,6 +1647,9 @@ byte[] classBytes = new byte[2048]; int offset = 0; try (InputStream is = getResourceAsStream("org/apache/catalina/loader/JdbcLeakPrevention.class")) { + if (is == null) { + throw new FileNotFoundException("org/apache/catalina/loader/JdbcLeakPrevention.class"); + } int read = is.read(classBytes, offset, classBytes.length - offset); while (read > -1) { offset += read; @@ -1769,7 +1774,7 @@ // when the executor was shut down or the thread was interrupted but // that depends on the thread correctly handling the interrupt. Check // each thread and if any are still running give all threads up to a - // total of 2 seconds to shutdown. + // total of 2 seconds to shut down. int count = 0; for (Thread t : threadsToStop) { while (t.isAlive() && count < 100) { @@ -1800,7 +1805,7 @@ StackTraceElement[] elements = thread.getStackTrace(); - if (elements == null || elements.length == 0) { + if (elements.length == 0) { // Must have stopped already. Too late to ignore it. Assume not a // request processing thread. return false; @@ -2040,7 +2045,7 @@ } /** - * @return the set of current threads as an array. + * @return the current threads as an array. */ private Thread[] getThreads() { // Get the current thread group @@ -2282,7 +2287,7 @@ URL codeBase = resource.getCodeBase(); Certificate[] certificates = resource.getCertificates(); - if (transformers.size() > 0) { + if (!transformers.isEmpty()) { // If the resource is a class just being loaded, decorate it // with any attached transformers @@ -2335,7 +2340,11 @@ if (pkg != null) { boolean sealCheck = true; if (pkg.isSealed()) { - sealCheck = pkg.isSealed(codeBase); + if (codeBase != null) { + sealCheck = pkg.isSealed(codeBase); + } else { + sealCheck = false; + } } else { sealCheck = manifest == null || !isPackageSealed(packageName, manifest); } @@ -2564,6 +2573,8 @@ protected void addURL(URL url) { super.addURL(url); hasExternalRepositories = true; + // Clear the not found resources as they may now be available at the added URL. + notFoundClassResources.clear(); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/loader/WebappLoader.java tomcat10-10.1.52/java/org/apache/catalina/loader/WebappLoader.java --- tomcat10-10.1.34/java/org/apache/catalina/loader/WebappLoader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/loader/WebappLoader.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,9 @@ import java.io.FilePermission; import java.io.IOException; import java.lang.reflect.Constructor; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; -import java.nio.charset.StandardCharsets; import javax.management.ObjectName; @@ -43,7 +43,6 @@ import org.apache.tomcat.jakartaee.EESpecProfile; import org.apache.tomcat.jakartaee.EESpecProfiles; import org.apache.tomcat.util.ExceptionUtils; -import org.apache.tomcat.util.buf.UDecoder; import org.apache.tomcat.util.compat.JreCompat; import org.apache.tomcat.util.modeler.Registry; import org.apache.tomcat.util.res.StringManager; @@ -56,9 +55,6 @@ * This class loader is configured via the Resources children of its Context prior to calling start(). When * a new class is required, these Resources will be consulted first to locate the class. If it is not present, the * system class loader will be used instead. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class WebappLoader extends LifecycleMBeanBase implements Loader { @@ -163,7 +159,7 @@ /** - * @return a non null String if the loader will attempt to use the Jakarta converter. The String is the name of the + * @return a non-null String if the loader will attempt to use the Jakarta converter. The String is the name of the * profile used for conversion. */ public String getJakartaConverter() { @@ -253,7 +249,7 @@ } public String getLoaderRepositoriesString() { - String repositories[] = getLoaderRepositories(); + String[] repositories = getLoaderRepositories(); StringBuilder sb = new StringBuilder(); for (String repository : repositories) { sb.append(repository).append(':'); @@ -274,7 +270,7 @@ @Override public boolean modified() { - return classLoader != null ? classLoader.modified() : false; + return classLoader != null && classLoader.modified(); } @@ -322,13 +318,13 @@ MigrationUtil.addJakartaEETransformer(classLoader, getJakartaConverter()); } - // Configure our repositories - setClassPath(); - setPermissions(); classLoader.start(); + // Configure our repositories + setClassPath(); + String contextName = context.getName(); if (!contextName.startsWith("/")) { contextName = "/" + contextName; @@ -336,12 +332,12 @@ ObjectName cloname = new ObjectName(context.getDomain() + ":type=" + classLoader.getClass().getSimpleName() + ",host=" + context.getParent().getName() + ",context=" + contextName); - Registry.getRegistry(null, null).registerComponent(classLoader, cloname, null); + Registry.getRegistry(null).registerComponent(classLoader, cloname, null); } catch (Throwable t) { - t = ExceptionUtils.unwrapInvocationTargetException(t); - ExceptionUtils.handleThrowable(t); - throw new LifecycleException(sm.getString("webappLoader.startError"), t); + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + throw new LifecycleException(sm.getString("webappLoader.startError"), throwable); } setState(LifecycleState.STARTING); @@ -385,7 +381,7 @@ ObjectName cloname = new ObjectName(context.getDomain() + ":type=" + classLoader.getClass().getSimpleName() + ",host=" + context.getParent().getName() + ",context=" + contextName); - Registry.getRegistry(null, null).unregisterComponent(cloname); + Registry.getRegistry(null).unregisterComponent(cloname); } catch (Exception e) { log.warn(sm.getString("webappLoader.stopError"), e); } @@ -412,16 +408,14 @@ } Class clazz = Class.forName(loaderClass); - WebappClassLoaderBase classLoader = null; ClassLoader parentClassLoader = context.getParentClassLoader(); Class[] argTypes = { ClassLoader.class }; Object[] args = { parentClassLoader }; Constructor constr = clazz.getConstructor(argTypes); - classLoader = (WebappClassLoaderBase) constr.newInstance(args); - return classLoader; + return (WebappClassLoaderBase) constr.newInstance(args); } @@ -506,17 +500,22 @@ private boolean buildClassPath(StringBuilder classpath, ClassLoader loader) { if (loader instanceof URLClassLoader) { - URL repositories[] = ((URLClassLoader) loader).getURLs(); + URL[] repositories = ((URLClassLoader) loader).getURLs(); for (URL url : repositories) { String repository = url.toString(); - if (repository.startsWith("file://")) { - repository = UDecoder.URLDecode(repository.substring(7), StandardCharsets.UTF_8); - } else if (repository.startsWith("file:")) { - repository = UDecoder.URLDecode(repository.substring(5), StandardCharsets.UTF_8); - } else { + if (repository == null) { continue; } - if (repository == null) { + if (repository.startsWith("file:")) { + // Let the JRE handle all the edge cases for URL to path conversion. + try { + File f = new File(url.toURI()); + repository = f.getAbsolutePath(); + } catch (URISyntaxException | IllegalArgumentException e) { + // Can't convert from URL to URI. Treat as non-file URL and skip. + continue; + } + } else { continue; } if (classpath.length() > 0) { @@ -525,10 +524,9 @@ classpath.append(repository); } } else if (loader == ClassLoader.getSystemClassLoader()) { - // From Java 9 the internal class loaders no longer extend - // URLCLassLoader + // From Java 9 the internal class loaders no longer extend URLCLassLoader String cp = System.getProperty("java.class.path"); - if (cp != null && cp.length() > 0) { + if (cp != null && !cp.isEmpty()) { if (classpath.length() > 0) { classpath.append(File.pathSeparator); } @@ -572,7 +570,7 @@ /** - * Implemented in a sub-class so EESpecProfile and EESpecProfiles are not loaded unless a profile is configured. + * Implemented in a subclass so EESpecProfile and EESpecProfiles are not loaded unless a profile is configured. * Otherwise, tomcat-embed-core.jar has a runtime dependency on the migration tool whether it is used or not. */ private static class MigrationUtil { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/Constants.java tomcat10-10.1.52/java/org/apache/catalina/manager/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,6 +36,7 @@ HTML_HEADER_SECTION = "\n" + "\n" + + "\n" + "\n"; BODY_HEADER_SECTION = @@ -130,7 +131,7 @@ HTML_TAIL_SECTION = "
      \n" + "
      \n" + - " Copyright © 1999-2024, Apache Software Foundation" + + " Copyright © 1999-2026, Apache Software Foundation" + "
      \n" + "\n" + "\n" + diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/HTMLManagerServlet.java tomcat10-10.1.52/java/org/apache/catalina/manager/HTMLManagerServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/HTMLManagerServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/HTMLManagerServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -62,13 +62,9 @@ * However if you use a software that parses the output of ManagerServlet you won't be able to upgrade to * this Servlet since the output are not in the same format ar from ManagerServlet * - * @author Bip Thelin - * @author Malcolm Edgar - * @author Glenn L. Nielsen - * * @see ManagerServlet */ -public final class HTMLManagerServlet extends ManagerServlet { +public class HTMLManagerServlet extends ManagerServlet { private static final long serialVersionUID = 1L; @@ -113,8 +109,10 @@ // List always displayed - nothing to do here } else if (command.equals("/sessions")) { try { - doSessions(cn, request, response, smClient); - return; + if (cn != null) { + doSessions(cn, request, response, smClient); + return; + } } catch (Exception e) { log(sm.getString("htmlManagerServlet.error.sessions", cn), e); message = smClient.getString("managerServlet.exception", e.toString()); @@ -156,11 +154,11 @@ String deployWar = request.getParameter("deployWar"); String deployConfig = request.getParameter("deployConfig"); ContextName deployCn = null; - if (deployPath != null && deployPath.length() > 0) { + if (deployPath != null && !deployPath.isEmpty()) { deployCn = new ContextName(deployPath, request.getParameter("deployVersion")); - } else if (deployConfig != null && deployConfig.length() > 0) { + } else if (deployConfig != null && !deployConfig.isEmpty()) { deployCn = ContextName.extractFromPath(deployConfig); - } else if (deployWar != null && deployWar.length() > 0) { + } else if (deployWar != null && !deployWar.isEmpty()) { deployCn = ContextName.extractFromPath(deployWar); } @@ -171,7 +169,7 @@ String message = ""; - if (command == null || command.length() == 0) { + if (command == null || command.isEmpty()) { // No command == list // List always displayed -> do nothing } else if (command.equals("/upload")) { @@ -317,7 +315,7 @@ // Message Section args = new Object[3]; args[0] = smClient.getString("htmlManagerServlet.messageLabel"); - if (message == null || message.length() == 0) { + if (message == null || message.isEmpty()) { args[1] = "OK"; } else { args[1] = Escape.htmlElementContent(message); @@ -353,8 +351,8 @@ // Apps Row Section // Create sorted map of deployed applications by context name. - Container children[] = host.findChildren(); - String contextNames[] = new String[children.length]; + Container[] children = host.findChildren(); + String[] contextNames = new String[children.length]; for (int i = 0; i < children.length; i++) { contextNames[i] = children[i].getName(); } @@ -369,8 +367,8 @@ String noVersion = "" + smClient.getString("htmlManagerServlet.noVersion") + ""; boolean isHighlighted = true; - boolean isDeployed = true; - String highlightColor = null; + boolean isDeployed; + String highlightColor; for (String contextName : contextNames) { Context ctxt = (Context) host.findChild(contextName); @@ -386,7 +384,7 @@ String contextPath = ctxt.getPath(); String displayPath = contextPath; - if (displayPath.equals("")) { + if (displayPath.isEmpty()) { displayPath = "/"; } @@ -394,7 +392,7 @@ tmp.append("path="); tmp.append(URLEncoder.DEFAULT.encode(displayPath, StandardCharsets.UTF_8)); final String webappVersion = ctxt.getWebappVersion(); - if (webappVersion != null && webappVersion.length() > 0) { + if (webappVersion != null && !webappVersion.isEmpty()) { tmp.append("&version="); tmp.append(URLEncoder.DEFAULT.encode(webappVersion, StandardCharsets.UTF_8)); } @@ -566,12 +564,12 @@ /** * Reload the web application at the specified context path. * - * @see ManagerServlet#reload(PrintWriter, ContextName, StringManager) - * * @param cn Name of the application to be restarted * @param smClient StringManager for the client's locale * * @return message String + * + * @see ManagerServlet#reload(PrintWriter, ContextName, StringManager) */ protected String reload(ContextName cn, StringManager smClient) { @@ -586,12 +584,12 @@ /** * Undeploy the web application at the specified context path. * - * @see ManagerServlet#undeploy(PrintWriter, ContextName, StringManager) - * * @param cn Name of the application to be undeployed * @param smClient StringManager for the client's locale * * @return message String + * + * @see ManagerServlet#undeploy(PrintWriter, ContextName, StringManager) */ protected String undeploy(ContextName cn, StringManager smClient) { @@ -606,13 +604,13 @@ /** * Display session information and invoke list. * - * @see ManagerServlet#sessions(PrintWriter, ContextName, int, StringManager) - * * @param cn Name of the application to list session information * @param idle Expire all sessions with idle time ≥ idle for this context * @param smClient StringManager for the client's locale * * @return message String + * + * @see ManagerServlet#sessions(PrintWriter, ContextName, int, StringManager) */ protected String sessions(ContextName cn, int idle, StringManager smClient) { @@ -627,12 +625,12 @@ /** * Start the web application at the specified context path. * - * @see ManagerServlet#start(PrintWriter, ContextName, StringManager) - * * @param cn Name of the application to be started * @param smClient StringManager for the client's locale * * @return message String + * + * @see ManagerServlet#start(PrintWriter, ContextName, StringManager) */ protected String start(ContextName cn, StringManager smClient) { @@ -647,12 +645,12 @@ /** * Stop the web application at the specified context path. * - * @see ManagerServlet#stop(PrintWriter, ContextName, StringManager) - * * @param cn Name of the application to be stopped * @param smClient StringManager for the client's locale * * @return message String + * + * @see ManagerServlet#stop(PrintWriter, ContextName, StringManager) */ protected String stop(ContextName cn, StringManager smClient) { @@ -667,11 +665,11 @@ /** * Find potential memory leaks caused by web application reload. * - * @see ManagerServlet#findleaks(boolean, PrintWriter, StringManager) - * * @param smClient StringManager for the client's locale * * @return message String + * + * @see ManagerServlet#findleaks(boolean, PrintWriter, StringManager) */ protected String findleaks(StringManager smClient) { @@ -684,7 +682,7 @@ String writerText = stringWriter.toString(); - if (writerText.length() > 0) { + if (!writerText.isEmpty()) { if (!writerText.startsWith("FAIL -")) { msg.append(smClient.getString("htmlManagerServlet.findleaksList")); } @@ -730,7 +728,7 @@ @Override public String getServletInfo() { - return "HTMLManagerServlet, Copyright (c) 1999-2024, The Apache Software Foundation"; + return "HTMLManagerServlet, Copyright (c) 1999-2026, The Apache Software Foundation"; } @Override @@ -738,8 +736,7 @@ super.init(); // Set our properties from the initialization parameters - String value = null; - value = getServletConfig().getInitParameter("showProxySessions"); + String value = getServletConfig().getInitParameter("showProxySessions"); showProxySessions = Boolean.parseBoolean(value); htmlSubTitle = getServletConfig().getInitParameter("htmlSubTitle"); @@ -795,7 +792,7 @@ } else if ("invalidateSessions".equals(action)) { String[] sessionIds = req.getParameterValues("sessionIds"); int i = invalidateSessions(cn, sessionIds, smClient); - req.setAttribute(APPLICATION_MESSAGE, "" + i + " sessions invalidated."); + req.setAttribute(APPLICATION_MESSAGE, Integer.toString(i) + " sessions invalidated."); } else if ("removeSessionAttribute".equals(action)) { String sessionId = req.getParameter("sessionId"); String name = req.getParameter("attributeName"); @@ -810,7 +807,7 @@ } protected List getSessionsForName(ContextName cn, StringManager smClient) { - if (cn == null || !(cn.getPath().startsWith("/") || cn.getPath().equals(""))) { + if (cn == null || !(cn.getPath().startsWith("/") || cn.getPath().isEmpty())) { String path = null; if (cn != null) { path = cn.getPath(); @@ -871,7 +868,7 @@ List sessions = getSessionsForName(cn, smClient); String sortBy = req.getParameter("sort"); String orderBy = null; - if (null != sortBy && !"".equals(sortBy.trim())) { + if (null != sortBy && !sortBy.trim().isEmpty()) { Comparator comparator = getComparator(sortBy); if (comparator != null) { orderBy = req.getParameter("order"); @@ -1000,39 +997,29 @@ } protected Comparator getComparator(String sortBy) { - Comparator comparator = null; if ("CreationTime".equalsIgnoreCase(sortBy)) { return Comparator.comparingLong(Session::getCreationTime); - } else if ("id".equalsIgnoreCase(sortBy)) { return comparingNullable(Session::getId); - } else if ("LastAccessedTime".equalsIgnoreCase(sortBy)) { return Comparator.comparingLong(Session::getLastAccessedTime); - } else if ("MaxInactiveInterval".equalsIgnoreCase(sortBy)) { return Comparator.comparingInt(Session::getMaxInactiveInterval); - } else if ("new".equalsIgnoreCase(sortBy)) { return Comparator.comparing(s -> Boolean.valueOf(s.getSession().isNew())); - } else if ("locale".equalsIgnoreCase(sortBy)) { return Comparator.comparing(JspHelper::guessDisplayLocaleFromSession); - } else if ("user".equalsIgnoreCase(sortBy)) { return comparingNullable(JspHelper::guessDisplayUserFromSession); - } else if ("UsedTime".equalsIgnoreCase(sortBy)) { return Comparator.comparingLong(SessionUtils::getUsedTimeForSession); - } else if ("InactiveTime".equalsIgnoreCase(sortBy)) { return Comparator.comparingLong(SessionUtils::getInactiveTimeForSession); - } else if ("TTL".equalsIgnoreCase(sortBy)) { return Comparator.comparingLong(SessionUtils::getTTLForSession); - + } else { + return null; } - return comparator; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/JMXProxyServlet.java tomcat10-10.1.52/java/org/apache/catalina/manager/JMXProxyServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/JMXProxyServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/JMXProxyServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,8 +43,6 @@ /** * This servlet will dump JMX attributes in a simple format and implement proxy services for modeler. - * - * @author Costin Manolache */ public class JMXProxyServlet extends HttpServlet { @@ -69,8 +67,8 @@ @Override public void init() throws ServletException { // Retrieve the MBean server - registry = Registry.getRegistry(null, null); - mBeanServer = Registry.getRegistry(null, null).getMBeanServer(); + registry = Registry.getRegistry(null); + mBeanServer = Registry.getRegistry(null).getMBeanServer(); } @@ -148,9 +146,9 @@ writer.print(" = "); writer.println(MBeanDumper.escape(valueStr)); - } catch (Exception ex) { - writer.println("Error - " + ex.toString()); - ex.printStackTrace(writer); + } catch (Exception e) { + writer.println("Error - " + e.toString()); + e.printStackTrace(writer); } } @@ -159,23 +157,23 @@ try { setAttributeInternal(onameStr, att, val); writer.println("OK - Attribute set"); - } catch (Exception ex) { - writer.println("Error - " + ex.toString()); - ex.printStackTrace(writer); + } catch (Exception e) { + writer.println("Error - " + e.toString()); + e.printStackTrace(writer); } } public void listBeans(PrintWriter writer, String qry) { - Set names = null; + Set names; try { names = mBeanServer.queryNames(new ObjectName(qry), null); writer.println("OK - Number of results: " + names.size()); writer.println(); - } catch (Exception ex) { - writer.println("Error - " + ex.toString()); - ex.printStackTrace(writer); + } catch (Exception e) { + writer.println("Error - " + e.toString()); + e.printStackTrace(writer); return; } @@ -205,9 +203,9 @@ } else { writer.println("OK - Operation " + op + " without return value"); } - } catch (Exception ex) { - writer.println("Error - " + ex.toString()); - ex.printStackTrace(writer); + } catch (Exception e) { + writer.println("Error - " + e.toString()); + e.printStackTrace(writer); } } @@ -259,7 +257,7 @@ MBeanOperationInfo methodInfo = registry.getMethodInfo(oname, operation, paramCount); if (null == methodInfo) { // getMethodInfo returns null for both "object not found" and "operation not found" - MBeanInfo info = null; + MBeanInfo info; try { info = registry.getMBeanServer().getMBeanInfo(oname); } catch (InstanceNotFoundException infe) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/JspHelper.java tomcat10-10.1.52/java/org/apache/catalina/manager/JspHelper.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/JspHelper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/JspHelper.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,10 +28,8 @@ /** - * Helper JavaBean for JSPs, because JSTL 1.1/EL 2.0 is too dumb to to what I need (call methods with parameters), or I + * Helper JavaBean for JSPs, because JSTL 1.1/EL 2.0 is too dumb to do what I need (call methods with parameters), or I * am too dumb to use it correctly. :) - * - * @author Cédrik LIME */ public class JspHelper { @@ -66,11 +64,11 @@ } /** - * Try to get user name from the session, if possible. + * Try to get username from the session, if possible. * * @param in_session The Servlet session * - * @return the user name + * @return the username */ public static String guessDisplayUserFromSession(Session in_session) { Object user = SessionUtils.guessUserFromSession(in_session); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -125,7 +125,8 @@ managerServlet.alreadyContext=FAIL - Application already exists at path [{0}] managerServlet.certsNotAvailable=Certificate information cannot be obtained from this connector at runtime -managerServlet.copyError=Could not copy configuration file from path [{0}] +managerServlet.certsNotLoaded=Certificates were not loaded for this connector +managerServlet.copyFail=FAIL - Unable to copy [{0}] to [{1}], details of the error may be in the server logs managerServlet.deleteFail=FAIL - Unable to delete [{0}]. The continued presence of this file may cause problems. managerServlet.deployFailed=FAIL - Failed to deploy application at context path [{0}] managerServlet.deployed=OK - Deployed application at context path [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -123,7 +123,8 @@ managerServlet.alreadyContext=FAIL - l''application existe déjà dans le chemin [{0}] managerServlet.certsNotAvailable=L'information sur les certificats ne peut pas être obtenu de ce connecteur au cours de son exécution -managerServlet.copyError=Impossible de copier le fichier de configuration à partir du chemin [{0}] +managerServlet.certsNotLoaded=Les certificats n'ont pas été chargés pour ce connecteur +managerServlet.copyFail=FAIL - Impossible de copier [{0}] vers [{1}], les détails de l''erreur peuvent se trouver dans les logs du serveur managerServlet.deleteFail=FAIL - Impossible de supprimer [{0}], ce qui pourrait causer des problèmes managerServlet.deployFailed=FAIL - Echec au déploiement de l''application pour le chemin de contexte [{0}] managerServlet.deployed=OK - Application déployée pour le chemin de contexte [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -125,7 +125,8 @@ managerServlet.alreadyContext=FAIL - アプリケーションは、既にパス [{0}] に存在します managerServlet.certsNotAvailable=実行時にこのコネクタから証明書情報を取得できません。 -managerServlet.copyError=パス [{0}] から設定ファイルをコピーできません +managerServlet.certsNotLoaded=このコネクタの証明書は読み込まれませんでした +managerServlet.copyFail=FAIL - [{0}] を [{1}] にコピーできません。エラーの詳細はサーバーログに記載されている可能性があります managerServlet.deleteFail=FAIL - [{0}]を削除できません。 このファイルが継続して存在すると、問題が発生する可能性があります。 managerServlet.deployFailed=FAIL - コンテキストパス [{0}] にアプリケーションを配備できません。 managerServlet.deployed=OK - コンテキストパス [{0}] でアプリケーションを配備しました @@ -180,7 +181,7 @@ managerServlet.sessions=OK - コンテキストパス [{0}] のアプリケーションのセッション情報です managerServlet.sessiontimeout=[{0}]分: [{1}]セッション managerServlet.sessiontimeout.expired=[{0}]分: expired [{1}]セッション -managerServlet.sessiontimeout.unlimited=unlimited 分: [{0}]セッション +managerServlet.sessiontimeout.unlimited=無制限の時間: [{0}] セッション managerServlet.sslConnectorCerts=OK - コネクタ/証明書チェーンの情報 managerServlet.sslConnectorCiphers=OK - Connector/ SSL暗号情報 managerServlet.sslConnectorTrustedCerts=OK - コネクタ/信頼された証明書情報 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_ko.properties tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_ko.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_ko.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_ko.properties 2026-01-23 19:33:36.000000000 +0000 @@ -124,7 +124,6 @@ managerServlet.alreadyContext=실패 - 애플리케이션이 이미 경로 [{0}]에 존재합니다. managerServlet.certsNotAvailable=이 Connector로부터, 인증서 정보를 런타임에 구할 수 없습니다. -managerServlet.copyError=경로 [{0}](으)로부터 설정 파일을 복사할 수 없었습니다. managerServlet.deleteFail=실패 - [{0}]을(를) 삭제할 수 없습니다. 이 파일이 계속해서 존재하면 문제들을 일으킬 수 있습니다. managerServlet.deployFailed=실패 - 컨텍스트 경로 [{0}]에, 애플리케이션을 배치하지 못했습니다. managerServlet.deployed=OK - 컨텍스트 경로 [{0}]에 애플리케이션을 배치했습니다. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_pt_BR.properties tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_pt_BR.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_pt_BR.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_pt_BR.properties 2026-01-23 19:33:36.000000000 +0000 @@ -20,7 +20,6 @@ htmlManagerServlet.appsSessions=Sessões htmlManagerServlet.serverIPAddress=Endereço IP -managerServlet.copyError=Não foi possível copiar o arquivo de configuração do caminho [{0}] managerServlet.deployed=OK - Instalada aplicação no path de contexto [{0}] managerServlet.mkdirFail=FALHA - Incapaz de criar o diretório [{0}] -managerServlet.resourcesAll=Recursos de todos os tipos, listados. +managerServlet.resourcesAll=OK - Recursos de todos os tipos, listados. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -53,7 +53,7 @@ htmlManagerServlet.connectorStateThreadCount=Текущее число потоков: htmlManagerServlet.deployButton=Развернуть htmlManagerServlet.deployConfig=Путь XML файла конфигурации контекста: -htmlManagerServlet.deployPath=Путь: +htmlManagerServlet.deployPath=Путь к контексту: htmlManagerServlet.deployServer=Развернуть серверный WAR файл htmlManagerServlet.deployTitle=Развернуть htmlManagerServlet.deployUpload=WAR файл для развёртывания diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -123,7 +123,6 @@ managerServlet.alreadyContext=失败 - 应用程序已存在于路径 [{0}] managerServlet.certsNotAvailable=在运行期,无法从连接器中获取认证信息 -managerServlet.copyError=无法从路径[{0}]拷贝配置文件 managerServlet.deleteFail=失败 - 不能删除[{0}]。这个文件一直存在会出现问题。 managerServlet.deployFailed=FAIL - 在上下文路径[{0}]下部署应用失败 managerServlet.deployed=OK - 以应用path [{0}] 部署应用 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/ManagerServlet.java tomcat10-10.1.52/java/org/apache/catalina/manager/ManagerServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/ManagerServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/ManagerServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -145,9 +145,6 @@ *
    • debug - The debugging detail level that controls the amount of information that is logged by this servlet. * Default is zero. * - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class ManagerServlet extends HttpServlet implements ContainerServlet { @@ -246,7 +243,7 @@ } // Retrieve the MBean server - mBeanServer = Registry.getRegistry(null, null).getMBeanServer(); + mBeanServer = Registry.getRegistry(null).getMBeanServer(); } @@ -287,16 +284,10 @@ String type = request.getParameter("type"); String tag = request.getParameter("tag"); - boolean update = false; - if (request.getParameter("update") != null && request.getParameter("update").equals("true")) { - update = true; - } + boolean update = request.getParameter("update") != null && request.getParameter("update").equals("true"); String tlsHostName = request.getParameter("tlsHostName"); - boolean statusLine = false; - if ("true".equals(request.getParameter("statusLine"))) { - statusLine = true; - } + boolean statusLine = "true".equals(request.getParameter("statusLine")); // Prepare our output writer to generate the response message response.setContentType("text/plain; charset=" + Constants.CHARSET); @@ -379,10 +370,7 @@ } String config = request.getParameter("config"); String tag = request.getParameter("tag"); - boolean update = false; - if (request.getParameter("update") != null && request.getParameter("update").equals("true")) { - update = true; - } + boolean update = request.getParameter("update") != null && request.getParameter("update").equals("true"); // Prepare our output writer to generate the response message response.setContentType("text/plain;charset=" + Constants.CHARSET); @@ -417,7 +405,7 @@ } // Set our properties from the initialization parameters - String value = null; + String value; try { value = getServletConfig().getInitParameter("debug"); debug = Integer.parseInt(value); @@ -502,21 +490,21 @@ protected void sslReload(PrintWriter writer, String tlsHostName, StringManager smClient) { - Connector connectors[] = getConnectors(); + Connector[] connectors = getConnectors(); boolean found = false; for (Connector connector : connectors) { if (Boolean.TRUE.equals(connector.getProperty("SSLEnabled"))) { ProtocolHandler protocol = connector.getProtocolHandler(); if (protocol instanceof AbstractHttp11Protocol) { AbstractHttp11Protocol http11Protoocol = (AbstractHttp11Protocol) protocol; - if (tlsHostName == null || tlsHostName.length() == 0) { + if (tlsHostName == null || tlsHostName.isEmpty()) { found = true; http11Protoocol.reloadSslHostConfigs(); } else { SSLHostConfig[] sslHostConfigs = http11Protoocol.findSslHostConfigs(); for (SSLHostConfig sslHostConfig : sslHostConfigs) { - // tlsHostName is as provided by the user so use a case insensitive - // comparison as host names are case insensitive. + // tlsHostName is as provided by the user so use a case-insensitive + // comparison as host names are case-insensitive. if (sslHostConfig.getHostName().equalsIgnoreCase(tlsHostName)) { found = true; http11Protoocol.reloadSslHostConfig(tlsHostName); @@ -527,7 +515,7 @@ } } if (found) { - if (tlsHostName == null || tlsHostName.length() == 0) { + if (tlsHostName == null || tlsHostName.isEmpty()) { writer.println(smClient.getString("managerServlet.sslReloadAll")); } else { writer.println(smClient.getString("managerServlet.sslReload", tlsHostName)); @@ -625,7 +613,7 @@ return; } - if (path == null || path.length() == 0 || !path.startsWith("/")) { + if (path == null || !path.startsWith("/")) { try { mBeanServer.invoke(storeConfigOname, "storeConfig", null, null); writer.println(smClient.getString("managerServlet.saved")); @@ -673,7 +661,7 @@ protected void deploy(PrintWriter writer, String config, ContextName cn, String tag, boolean update, HttpServletRequest request, StringManager smClient) { - if (config != null && config.length() == 0) { + if (config != null && config.isEmpty()) { config = null; } @@ -742,8 +730,10 @@ writer.println(smClient.getString("managerServlet.mkdirFail", configBase)); return; } - if (ExpandWar.copy(new File(config), new File(configBase, baseName + ".xml")) == false) { - throw new Exception(sm.getString("managerServlet.copyError", config)); + if (!ExpandWar.copy(new File(config), new File(configBase, baseName + ".xml"))) { + writer.println(smClient.getString("managerServlet.copyFail", new File(config), + new File(configBase, baseName + ".xml"))); + return; } } // Upload WAR @@ -761,7 +751,10 @@ } if (tag != null) { // Copy WAR to the host's appBase - ExpandWar.copy(uploadedWar, deployedWar); + if (!ExpandWar.copy(uploadedWar, deployedWar)) { + writer.println(smClient.getString("managerServlet.copyFail", uploadedWar, deployedWar)); + return; + } } } finally { removeServiced(name); @@ -815,7 +808,10 @@ writer.println(smClient.getString("managerServlet.deleteFail", deployedWar)); return; } - ExpandWar.copy(localWar, deployedWar); + if (!ExpandWar.copy(localWar, deployedWar)) { + writer.println(smClient.getString("managerServlet.copyFail", localWar, deployedWar)); + return; + } } finally { removeServiced(name); } @@ -847,10 +843,10 @@ protected void deploy(PrintWriter writer, String config, ContextName cn, String war, boolean update, StringManager smClient) { - if (config != null && config.length() == 0) { + if (config != null && config.isEmpty()) { config = null; } - if (war != null && war.length() == 0) { + if (war != null && war.isEmpty()) { war = null; } @@ -909,7 +905,11 @@ writer.println(smClient.getString("managerServlet.deleteFail", localConfigFile)); return; } - ExpandWar.copy(configFile, localConfigFile); + if (!ExpandWar.copy(configFile, localConfigFile)) { + writer.println( + smClient.getString("managerServlet.copyFail", configFile, localConfigFile)); + return; + } } } if (war != null) { @@ -929,7 +929,10 @@ writer.println(smClient.getString("managerServlet.deleteFail", localWarFile)); return; } - ExpandWar.copy(warFile, localWarFile); + if (!ExpandWar.copy(warFile, localWarFile)) { + writer.println(smClient.getString("managerServlet.copyFail", warFile, localWarFile)); + return; + } } } } finally { @@ -981,10 +984,10 @@ Context context = (Context) container; if (context != null) { String displayPath = context.getPath(); - if (displayPath.equals("")) { + if (displayPath.isEmpty()) { displayPath = "/"; } - List parts = null; + List parts; if (context.getState().isAvailable()) { parts = Arrays.asList(displayPath, "running", "" + context.getManager().findSessions().length, context.getDocBase()); @@ -1498,11 +1501,11 @@ try (ServletInputStream istream = request.getInputStream(); OutputStream ostream = new FileOutputStream(war)) { IOTools.flow(istream, ostream); - } catch (IOException e) { + } catch (IOException ioe) { if (war.exists() && !war.delete()) { writer.println(smClient.getString("managerServlet.deleteFail", war)); } - throw e; + throw ioe; } } @@ -1511,7 +1514,7 @@ // ContextName should be non-null with a path that is empty or starts // with / - if (cn != null && (cn.getPath().startsWith("/") || cn.getPath().equals(""))) { + if (cn != null && (cn.getPath().startsWith("/") || cn.getPath().isEmpty())) { return true; } @@ -1526,7 +1529,7 @@ protected Map> getConnectorCiphers(StringManager smClient) { Map> result = new HashMap<>(); - Connector connectors[] = getConnectors(); + Connector[] connectors = getConnectors(); for (Connector connector : connectors) { if (Boolean.TRUE.equals(connector.getProperty("SSLEnabled"))) { SSLHostConfig[] sslHostConfigs = connector.getProtocolHandler().findSslHostConfigs(); @@ -1549,7 +1552,7 @@ protected Map> getConnectorCerts(StringManager smClient) { Map> result = new HashMap<>(); - Connector connectors[] = getConnectors(); + Connector[] connectors = getConnectors(); for (Connector connector : connectors) { if (Boolean.TRUE.equals(connector.getProperty("SSLEnabled"))) { SSLHostConfig[] sslHostConfigs = connector.getProtocolHandler().findSslHostConfigs(); @@ -1564,12 +1567,16 @@ if (alias == null) { alias = SSLUtilBase.DEFAULT_KEY_ALIAS; } - X509Certificate[] certs = sslContext.getCertificateChain(alias); - if (certs == null) { - certList.add(smClient.getString("managerServlet.certsNotAvailable")); + if (sslContext == null) { + certList.add(smClient.getString("managerServlet.certsNotLoaded")); } else { - for (Certificate cert : certs) { - certList.add(cert.toString()); + X509Certificate[] certs = sslContext.getCertificateChain(alias); + if (certs == null) { + certList.add(smClient.getString("managerServlet.certsNotAvailable")); + } else { + for (Certificate cert : certs) { + certList.add(cert.toString()); + } } } result.put(name, certList); @@ -1589,7 +1596,7 @@ protected Map> getConnectorTrustedCerts(StringManager smClient) { Map> result = new HashMap<>(); - Connector connectors[] = getConnectors(); + Connector[] connectors = getConnectors(); for (Connector connector : connectors) { if (Boolean.TRUE.equals(connector.getProperty("SSLEnabled"))) { SSLHostConfig[] sslHostConfigs = connector.getProtocolHandler().findSslHostConfigs(); @@ -1597,14 +1604,18 @@ String name = connector.toString() + "-" + sslHostConfig.getHostName(); List certList = new ArrayList<>(); SSLContext sslContext = sslHostConfig.getCertificates().iterator().next().getSslContext(); - X509Certificate[] certs = sslContext.getAcceptedIssuers(); - if (certs == null) { - certList.add(smClient.getString("managerServlet.certsNotAvailable")); - } else if (certs.length == 0) { - certList.add(smClient.getString("managerServlet.trustedCertsNotConfigured")); + if (sslContext == null) { + certList.add(smClient.getString("managerServlet.certsNotLoaded")); } else { - for (Certificate cert : certs) { - certList.add(cert.toString()); + X509Certificate[] certs = sslContext.getAcceptedIssuers(); + if (certs == null) { + certList.add(smClient.getString("managerServlet.certsNotAvailable")); + } else if (certs.length == 0) { + certList.add(smClient.getString("managerServlet.trustedCertsNotConfigured")); + } else { + for (Certificate cert : certs) { + certList.add(cert.toString()); + } } } result.put(name, certList); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/StatusManagerServlet.java tomcat10-10.1.52/java/org/apache/catalina/manager/StatusManagerServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/StatusManagerServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/StatusManagerServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,8 +44,6 @@ /** * This servlet will display a complete status of the HTTP/1.1 connector. - * - * @author Remy Maucherat */ public class StatusManagerServlet extends HttpServlet implements NotificationListener { @@ -88,7 +86,7 @@ public void init() throws ServletException { // Retrieve the MBean server - mBeanServer = Registry.getRegistry(null, null).getMBeanServer(); + mBeanServer = Registry.getRegistry(null).getMBeanServer(); try { @@ -169,10 +167,8 @@ PrintWriter writer = response.getWriter(); - boolean completeStatus = false; - if (request.getPathInfo() != null && request.getPathInfo().equals("/all")) { - completeStatus = true; - } + boolean completeStatus = request.getPathInfo() != null && request.getPathInfo().equals("/all"); + // use StatusTransformer to output status Object[] args = new Object[1]; args[0] = getServletContext().getContextPath(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/StatusTransformer.java tomcat10-10.1.52/java/org/apache/catalina/manager/StatusTransformer.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/StatusTransformer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/StatusTransformer.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,8 +40,6 @@ /** * This is a refactoring of the servlet to externalize the output into a simple class. Although we could use XSLT, that * is unnecessarily complex. - * - * @author Peter Lin */ public class StatusTransformer { @@ -517,7 +515,7 @@ int stage = stageValue.intValue(); boolean fullStatus = true; boolean showRequest = true; - String stageStr = null; + String stageStr; switch (stage) { @@ -595,7 +593,7 @@ writer.write(' '); writer.write(Escape.htmlElementContent(mBeanServer.getAttribute(pName, "currentUri"))); String queryString = (String) mBeanServer.getAttribute(pName, "currentQueryString"); - if (queryString != null && !queryString.equals("")) { + if (queryString != null && !queryString.isEmpty()) { writer.write("?"); writer.print(Escape.htmlElementContent(queryString)); } @@ -641,7 +639,7 @@ Escape.htmlElementContent(mBeanServer.getAttribute(pName, "currentUri")) + "\""); String queryString = (String) mBeanServer.getAttribute(pName, "currentQueryString"); - if (queryString != null && !queryString.equals("")) { + if (queryString != null && !queryString.isEmpty()) { writer.write(" currentQueryString=\"" + Escape.htmlElementContent(queryString) + "\""); } else { writer.write(" currentQueryString=\"?\""); @@ -767,8 +765,8 @@ return; } - String hostName = null; - String contextName = null; + String hostName; + String contextName; if (name.startsWith("//")) { name = name.substring(2); } @@ -805,6 +803,8 @@ writer.print(""); writer.print("

      "); + writer.print("State: "); + writer.print(mBeanServer.getAttribute(objectName, "stateName")); Object startTime = mBeanServer.getAttribute(objectName, "startTime"); writer.print(" Start time: " + new Date(((Long) startTime).longValue())); writer.print(" Startup time: "); @@ -831,6 +831,8 @@ } else if (mode == 2) { indent(writer, 2).append('{').println(); appendJSonValue(indent(writer, 3), "name", JSONFilter.escape(JSONFilter.escape(name))).append(','); + appendJSonValue(writer, "state", mBeanServer.getAttribute(objectName, "stateName")); + writer.append(','); appendJSonValue(writer, "startTime", new Date(((Long) mBeanServer.getAttribute(objectName, "startTime")).longValue()).toString()) .append(','); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/host/Constants.java tomcat10-10.1.52/java/org/apache/catalina/manager/host/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/host/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/host/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -80,7 +80,7 @@ public static final String HTML_TAIL_SECTION = "


      \n" + "
      \n" + - " Copyright © 1999-2024, Apache Software Foundation" + + " Copyright © 1999-2026, Apache Software Foundation" + "
      \n" + "\n" + "\n" + diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java tomcat10-10.1.52/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/host/HTMLHostManagerServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,6 +20,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.text.MessageFormat; import java.util.Arrays; import java.util.SortedSet; @@ -46,14 +47,9 @@ * However if you use a software that parses the output of HostManagerServlet you won't be able to upgrade * to this Servlet since the output are not in the same format as from HostManagerServlet * - * @author Bip Thelin - * @author Malcolm Edgar - * @author Glenn L. Nielsen - * @author Peter Rossbach - * * @see org.apache.catalina.manager.ManagerServlet */ -public final class HTMLHostManagerServlet extends HostManagerServlet { +public class HTMLHostManagerServlet extends HostManagerServlet { private static final long serialVersionUID = 1L; @@ -254,7 +250,7 @@ // Message Section args = new Object[3]; args[0] = smClient.getString("htmlHostManagerServlet.messageLabel"); - if (message == null || message.length() == 0) { + if (message == null || message.isEmpty()) { args[1] = "OK"; } else { args[1] = Escape.htmlElementContent(message); @@ -288,13 +284,12 @@ // Hosts Row Section // Create sorted set of host names. Container[] children = engine.findChildren(); - String hostNames[] = new String[children.length]; + String[] hostNames = new String[children.length]; for (int i = 0; i < children.length; i++) { hostNames[i] = children[i].getName(); } - SortedSet sortedHostNames = new TreeSet<>(); - sortedHostNames.addAll(Arrays.asList(hostNames)); + SortedSet sortedHostNames = new TreeSet<>(Arrays.asList(hostNames)); String hostsStart = smClient.getString("htmlHostManagerServlet.hostsStart"); String hostsStop = smClient.getString("htmlHostManagerServlet.hostsStop"); @@ -329,15 +324,15 @@ args = new Object[5]; if (host.getState().isAvailable()) { args[0] = response.encodeURL(getServletContext().getContextPath() + "/html/stop?name=" + - URLEncoder.encode(hostName, "UTF-8")); + URLEncoder.encode(hostName, StandardCharsets.UTF_8)); args[1] = hostsStop; } else { args[0] = response.encodeURL(getServletContext().getContextPath() + "/html/start?name=" + - URLEncoder.encode(hostName, "UTF-8")); + URLEncoder.encode(hostName, StandardCharsets.UTF_8)); args[1] = hostsStart; } args[2] = response.encodeURL(getServletContext().getContextPath() + "/html/remove?name=" + - URLEncoder.encode(hostName, "UTF-8")); + URLEncoder.encode(hostName, StandardCharsets.UTF_8)); args[3] = hostsRemove; args[4] = hostThis; if (host == this.installedHost) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/host/HostManagerServlet.java tomcat10-10.1.52/java/org/apache/catalina/manager/host/HostManagerServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/host/HostManagerServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/host/HostManagerServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -76,9 +76,6 @@ *
    • debug - The debugging detail level that controls the amount of information that is logged by this servlet. * Default is zero. * - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class HostManagerServlet extends HttpServlet implements ContainerServlet { @@ -270,7 +267,7 @@ } // Set our properties from the initialization parameters - String value = null; + String value; try { value = getServletConfig().getInitParameter("debug"); debug = Integer.parseInt(value); @@ -307,7 +304,7 @@ } // Validate the requested host name - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { writer.println(smClient.getString("hostManagerServlet.invalidHostName", name)); return; } @@ -319,10 +316,10 @@ } // Validate and create appBase - File appBaseFile = null; - File file = null; + File appBaseFile; + File file; String applicationBase = appBase; - if (applicationBase == null || applicationBase.length() == 0) { + if (applicationBase == null || applicationBase.isEmpty()) { applicationBase = name; } file = new File(applicationBase); @@ -331,7 +328,7 @@ } try { appBaseFile = file.getCanonicalFile(); - } catch (IOException e) { + } catch (IOException ioe) { appBaseFile = file; } if (!appBaseFile.mkdirs() && !appBaseFile.isDirectory()) { @@ -355,7 +352,7 @@ } Path dest = new File(configBaseFile, "manager.xml").toPath(); Files.copy(is, dest); - } catch (IOException e) { + } catch (IOException ioe) { writer.println(smClient.getString("hostManagerServlet.managerXml")); return; } @@ -413,7 +410,7 @@ } // Validate the requested host name - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { writer.println(smClient.getString("hostManagerServlet.invalidHostName", name)); return; } @@ -491,7 +488,7 @@ } // Validate the requested host name - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { writer.println(smClient.getString("hostManagerServlet.invalidHostName", name)); return; } @@ -542,7 +539,7 @@ } // Validate the requested host name - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { writer.println(smClient.getString("hostManagerServlet.invalidHostName", name)); return; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/host/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/manager/host/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/host/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/host/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,7 +18,7 @@ hostManagerServlet.add=add: ホスト [{0}] を追加 hostManagerServlet.addFailed=FAIL - ホスト [{0}] を追加できません。 -hostManagerServlet.addSuccess=OK - ホスト [{0}] を追加しました +hostManagerServlet.addSuccess=OK - ホスト [{0}] を追加しました hostManagerServlet.alreadyHost=FAIL - ホスト名[{0}]のホストが既に存在します hostManagerServlet.alreadyStarted=FAIL - ホスト [{0}] はすでに開始しています。 hostManagerServlet.alreadyStopped=FAIL - Host [{0}] はすでに停止しています。 @@ -42,7 +42,7 @@ hostManagerServlet.postCommand=FAIL - コマンド [{0}] をGETリクエストで使用しようとしましたが、POSTが必要です hostManagerServlet.remove=remove: ホスト [{0}] を削除します。 hostManagerServlet.removeFailed=FAIL - Host [{0}] を削除できません。 -hostManagerServlet.removeSuccess=OK - ホスト [{0}] を削除しました +hostManagerServlet.removeSuccess=OK - ホスト [{0}] を削除しました hostManagerServlet.start=開始:名前[{0}]のホストを起動しています hostManagerServlet.startFailed=FAIL - ホスト [{0}] の起動に失敗しました hostManagerServlet.started=OK - ホスト [{0}] を開始しました diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/host/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/manager/host/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/host/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/host/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -39,13 +39,13 @@ hostManagerServlet.persistFailed=Ошибка - Не удалось сохранить конфигурацию hostManagerServlet.persisted=OK - Конфигурация сохранена hostManagerServlet.postCommand=Ошибка - Команда [{0}] была подана при помощи запроса GET, но требуется POST -hostManagerServlet.remove=remove: Удаление сервера [{0}] +hostManagerServlet.remove=remove: Удаление хоста [{0}] hostManagerServlet.removeFailed=Ошибка - Не удалось удалить сервер [{0}] hostManagerServlet.removeSuccess=OK - Сервер удалён [{0}] -hostManagerServlet.start=start: Запуск сервера с именем [{0}] +hostManagerServlet.start=start: Запуск хоста с именем [{0}] hostManagerServlet.startFailed=Ошибка - Не удалось запустить сервер [{0}] hostManagerServlet.started=OK - Сервер [{0}] запущен -hostManagerServlet.stop=stop: Остановка сервера с именем [{0}] +hostManagerServlet.stop=stop: Остановка хоста с именем [{0}] hostManagerServlet.stopFailed=Ошибка - Не удалось остановить сервер [{0}] hostManagerServlet.stopped=OK - Сервер [{0}] остановлен hostManagerServlet.unknownCommand=Ошибка - Неизвестная команда [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/host/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/manager/host/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/manager/host/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/host/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,7 +18,7 @@ hostManagerServlet.add=添加:添加主机[{0}] hostManagerServlet.addFailed=失败 - 添加主机 [{0}] 失败 -hostManagerServlet.addSuccess=确定-添加主机[{0}] +hostManagerServlet.addSuccess=OK - 确定-添加主机[{0}] hostManagerServlet.alreadyHost=失败 - 主机名称[{0}]已经存在 hostManagerServlet.alreadyStarted=失败 - Host[{0}]已经启动。 hostManagerServlet.alreadyStopped=失败 - 主机[{0}]已经停止 @@ -34,6 +34,7 @@ hostManagerServlet.managerXml=FAIL - 无法安装manager.xml hostManagerServlet.noCommand=失败 - 未指定命令 hostManagerServlet.noHost=失败 - 主机名称[{0}]不存在 +hostManagerServlet.noStoreConfig=失败 - 请开启StoreConfig后使用这个特性 hostManagerServlet.noWrapper=容器未给当前servlet设置setWrapper() hostManagerServlet.persist=持久化: 正在持久化当前配置 hostManagerServlet.persistFailed=失败 - 无法持久化配置 @@ -41,12 +42,12 @@ hostManagerServlet.postCommand=失败 - 尝试通过GET请求使用命令[{0}],但是需要使用POST请求 hostManagerServlet.remove=移除:正在移除主机 [{0}] hostManagerServlet.removeFailed=失败 - 无法移除主机 [{0}] -hostManagerServlet.removeSuccess=确定-已删除主机[{0}] +hostManagerServlet.removeSuccess=OK - 已删除主机[{0}] hostManagerServlet.start=启动:启动主机[{0}] hostManagerServlet.startFailed=失败 - 无法启动主机 [{0}] hostManagerServlet.started=OK - 主机 [{0}] 已启动 hostManagerServlet.stop=停止:停止主机[{0}] -hostManagerServlet.stopFailed=失败 - 无法停止主机 [{0}] +hostManagerServlet.stopFailed=OK - 无法停止主机 [{0}] hostManagerServlet.stopped=OK - 主机 [{0}] 已停止 hostManagerServlet.unknownCommand=失败 - 未知命令 [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/manager/util/SessionUtils.java tomcat10-10.1.52/java/org/apache/catalina/manager/util/SessionUtils.java --- tomcat10-10.1.34/java/org/apache/catalina/manager/util/SessionUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/manager/util/SessionUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,8 +32,6 @@ /** * Utility methods on HttpSessions. - * - * @author Cédrik LIME */ public class SessionUtils { @@ -216,10 +214,6 @@ user = principalArray.get(0); } - if (null != user) { - return user; - } - return user; } catch (IllegalStateException ise) { // ignore: invalidated session @@ -230,8 +224,7 @@ public static long getUsedTimeForSession(Session in_session) { try { - long diffMilliSeconds = in_session.getThisAccessedTime() - in_session.getCreationTime(); - return diffMilliSeconds; + return in_session.getThisAccessedTime() - in_session.getCreationTime(); } catch (IllegalStateException ise) { // ignore: invalidated session return -1; @@ -240,9 +233,8 @@ public static long getTTLForSession(Session in_session) { try { - long diffMilliSeconds = 1000 * in_session.getMaxInactiveInterval() - + return 1000L * in_session.getMaxInactiveInterval() - (System.currentTimeMillis() - in_session.getThisAccessedTime()); - return diffMilliSeconds; } catch (IllegalStateException ise) { // ignore: invalidated session return -1; @@ -251,8 +243,7 @@ public static long getInactiveTimeForSession(Session in_session) { try { - long diffMilliSeconds = System.currentTimeMillis() - in_session.getThisAccessedTime(); - return diffMilliSeconds; + return System.currentTimeMillis() - in_session.getThisAccessedTime(); } catch (IllegalStateException ise) { // ignore: invalidated session return -1; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mapper/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/mapper/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/mapper/LocalStrings_ru.properties 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mapper/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,19 @@ +# 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. + +# Do not edit this file directly. +# To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations + +mapper.addHostAlias.success=Зарегистрирован псевдоним [{0}] для хоста [{1}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mapper/Mapper.java tomcat10-10.1.52/java/org/apache/catalina/mapper/Mapper.java --- tomcat10-10.1.34/java/org/apache/catalina/mapper/Mapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mapper/Mapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,8 +41,6 @@ /** * Mapper, which implements the servlet API mapping rules (which are derived from the HTTP rules). - * - * @author Remy Maucherat */ public final class Mapper { @@ -438,7 +436,7 @@ * false otherwise * @param resourceOnly true if this wrapper always expects a physical resource to be present (such as a JSP) */ - protected void addWrapper(ContextVersion context, String path, Wrapper wrapper, boolean jspWildCard, + private void addWrapper(ContextVersion context, String path, Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) { synchronized (context) { @@ -466,12 +464,11 @@ } } else if (path.equals("/")) { // Default wrapper - MappedWrapper newWrapper = new MappedWrapper("", wrapper, jspWildCard, resourceOnly); - context.defaultWrapper = newWrapper; + context.defaultWrapper = new MappedWrapper("", wrapper, jspWildCard, resourceOnly); } else { // Exact wrapper final String name; - if (path.length() == 0) { + if (path.isEmpty()) { // Special case for the Context Root mapping which is // treated as an exact match name = "/"; @@ -506,7 +503,7 @@ removeWrapper(contextVersion, path); } - protected void removeWrapper(ContextVersion context, String path) { + private void removeWrapper(ContextVersion context, String path) { if (log.isTraceEnabled()) { log.trace(sm.getString("mapper.removeWrapper", context.name, path)); @@ -549,7 +546,7 @@ } else { // Exact wrapper String name; - if (path.length() == 0) { + if (path.isEmpty()) { // Special case for the Context Root mapping which is // treated as an exact match name = "/"; @@ -747,13 +744,12 @@ int lastSlash = -1; int uriEnd = uri.getEnd(); - int length = -1; boolean found = false; MappedContext context = null; while (pos >= 0) { context = contexts[pos]; if (uri.startsWith(context.name)) { - length = context.name.length(); + int length = context.name.length(); if (uri.getLength() == length) { found = true; break; @@ -773,7 +769,7 @@ uri.setEnd(uriEnd); if (!found) { - if (contexts[0].name.equals("")) { + if (contexts[0].name.isEmpty()) { context = contexts[0]; } else { context = null; @@ -845,7 +841,7 @@ if (buf[pathEnd - 1] == '/') { /* * Path ending in '/' was mapped to JSP servlet based on wildcard match (e.g., as specified in - * url-pattern of a jsp-property-group. Force the context's welcome files, which are interpreted as + * url-pattern of a jsp-property-group). Force the context's welcome files, which are interpreted as * JSP files (since they match the url-pattern), to be considered. See Bugzilla 27664. */ mappingData.wrapper = null; @@ -921,7 +917,7 @@ } /* - * welcome file processing - take 2 Now that we have looked for welcome files with a physical backing, now look + * Welcome file processing - take 2. Now that we have looked for welcome files with a physical backing, now look * for an extension mapping listed but may not have a physical backing to it. This is for the case of index.jsf, * index.do, etc. A watered down version of rule 4 */ @@ -963,7 +959,7 @@ if (contextVersion.object.getMapperDirectoryRedirectEnabled()) { WebResource file; // Handle context root - if (pathStr.length() == 0) { + if (pathStr.isEmpty()) { file = contextVersion.resources.getResource("/"); } else { file = contextVersion.resources.getResource(pathStr); @@ -1135,7 +1131,7 @@ return 0; } - int i = 0; + int i; while (true) { i = (b + a) >>> 1; int result = compare(name, start, end, map[i].name); @@ -1187,7 +1183,7 @@ return 0; } - int i = 0; + int i; while (true) { i = (b + a) >>> 1; int result = compareIgnoreCase(name, start, end, map[i].name); @@ -1234,7 +1230,7 @@ return 0; } - int i = 0; + int i; while (true) { i = (b + a) >>> 1; int result = name.compareTo(map[i].name); @@ -1396,8 +1392,7 @@ private static int nthSlash(CharChunk name, int n) { char[] c = name.getBuffer(); int end = name.getEnd(); - int start = name.getStart(); - int pos = start; + int pos = name.getStart(); int count = 0; while (pos < end) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mapper/MapperListener.java tomcat10-10.1.52/java/org/apache/catalina/mapper/MapperListener.java --- tomcat10-10.1.34/java/org/apache/catalina/mapper/MapperListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mapper/MapperListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,9 +40,6 @@ /** * Mapper listener. - * - * @author Remy Maucherat - * @author Costin Manolache */ public class MapperListener extends LifecycleMBeanBase implements ContainerListener, LifecycleListener { @@ -258,7 +255,7 @@ boolean found = false; - if (defaultHost != null && defaultHost.length() > 0) { + if (defaultHost != null && !defaultHost.isEmpty()) { Container[] containers = engine.findChildren(); for (Container container : containers) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mapper/MappingData.java tomcat10-10.1.52/java/org/apache/catalina/mapper/MappingData.java --- tomcat10-10.1.34/java/org/apache/catalina/mapper/MappingData.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mapper/MappingData.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Mapping data. - * - * @author Remy Maucherat */ public class MappingData { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/ClassNameMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/ClassNameMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/ClassNameMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/ClassNameMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ *

      * * @param The type that this bean represents. - * - * @author Craig R. McClanahan */ public class ClassNameMBean extends BaseCatalinaMBean { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/ConnectorMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/ConnectorMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/ConnectorMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/ConnectorMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,12 +27,8 @@ import org.apache.tomcat.util.res.StringManager; /** - *

      * A ModelMBean implementation for the org.apache.coyote.tomcat5.CoyoteConnector * component. - *

      - * - * @author Amy Roh */ public class ConnectorMBean extends ClassNameMBean { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/ContextEnvironmentMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/ContextEnvironmentMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/ContextEnvironmentMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/ContextEnvironmentMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,12 +25,8 @@ import org.apache.tomcat.util.descriptor.web.NamingResources; /** - *

      * A ModelMBean implementation for the * org.apache.tomcat.util.descriptor.web.ContextEnvironment component. - *

      - * - * @author Amy Roh */ public class ContextEnvironmentMBean extends BaseCatalinaMBean { @@ -42,7 +38,7 @@ ContextEnvironment ce = doGetManagedResource(); - // cannot use side-effects. It's removed and added back each time + // cannot use side effects. It's removed and added back each time // there is a modification in a resource. NamingResources nr = ce.getNamingResources(); nr.removeEnvironment(ce.getName()); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/ContextResourceLinkMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/ContextResourceLinkMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/ContextResourceLinkMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/ContextResourceLinkMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,12 +27,8 @@ import org.apache.tomcat.util.res.StringManager; /** - *

      * A ModelMBean implementation for the * org.apache.tomcat.util.descriptor.web.ContextResourceLink component. - *

      - * - * @author Amy Roh */ public class ContextResourceLinkMBean extends BaseCatalinaMBean { @@ -100,7 +96,7 @@ crl.setProperty(name, "" + value); } - // cannot use side-effects. It's removed and added back each time + // cannot use side effects. It's removed and added back each time // there is a modification in a resource. NamingResources nr = crl.getNamingResources(); nr.removeResourceLink(crl.getName()); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/ContextResourceMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/ContextResourceMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/ContextResourceMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/ContextResourceMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,12 +27,8 @@ import org.apache.tomcat.util.res.StringManager; /** - *

      * A ModelMBean implementation for the * org.apache.tomcat.util.descriptor.web.ContextResource component. - *

      - * - * @author Amy Roh */ public class ContextResourceMBean extends BaseCatalinaMBean { @@ -103,7 +99,7 @@ cr.setProperty(name, "" + value); } - // cannot use side-effects. It's removed and added back each time + // cannot use side effects. It's removed and added back each time // there is a modification in a resource. NamingResources nr = cr.getNamingResources(); nr.removeResource(cr.getName()); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/DataSourceUserDatabaseMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/DataSourceUserDatabaseMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/DataSourceUserDatabaseMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/DataSourceUserDatabaseMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,8 +33,6 @@ * A ModelMBean implementation for the org.apache.catalina.users.DataSourceUserDatabase * component. *

      - * - * @author Craig R. McClanahan */ public class DataSourceUserDatabaseMBean extends BaseModelMBean { @@ -134,11 +132,11 @@ /** * Create a new User and return the corresponding name. * - * @param username User name of the new user + * @param username Username of the new user * @param password Password for the new user * @param fullName Full name for the new user * - * @return the new user name + * @return the new username */ public String createUser(String username, String password, String fullName) { UserDatabase database = (UserDatabase) this.resource; @@ -180,7 +178,7 @@ /** * Remove an existing user. * - * @param username User name to remove + * @param username Username to remove */ public void removeUser(String username) { UserDatabase database = (UserDatabase) this.resource; @@ -195,7 +193,7 @@ /** * Change user credentials. * - * @param username The user name + * @param username The username * @param password The new credentials */ public void changeUserPassword(String username, String password) { @@ -210,7 +208,7 @@ /** * Add specified role to the user. * - * @param username The user name + * @param username The username * @param rolename The role name */ public void addUserRole(String username, String rolename) { @@ -226,7 +224,7 @@ /** * Remove specified role from the user. * - * @param username The user name + * @param username The username * @param rolename The role name */ public void removeUserRole(String username, String rolename) { @@ -242,7 +240,7 @@ /** * Get roles for a user. * - * @param username The user name + * @param username The username * * @return Array of role names */ @@ -266,7 +264,7 @@ /** * Add group to user. * - * @param username The user name + * @param username The username * @param groupname The group name */ public void addUserGroup(String username, String groupname) { @@ -282,7 +280,7 @@ /** * Remove group from user. * - * @param username The user name + * @param username The username * @param groupname The group name */ public void removeUserGroup(String username, String groupname) { @@ -298,7 +296,7 @@ /** * Get groups for a user. * - * @param username The user name + * @param username The username * * @return Array of group names */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/GlobalResourcesLifecycleListener.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/GlobalResourcesLifecycleListener.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/GlobalResourcesLifecycleListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/GlobalResourcesLifecycleListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,8 +45,6 @@ *

      * This listener must only be nested within {@link Server} elements. * - * @author Craig R. McClanahan - * * @since 4.1 */ public class GlobalResourcesLifecycleListener implements LifecycleListener { @@ -93,7 +91,7 @@ */ protected void createMBeans() { // Look up our global naming context - Context context = null; + Context context; try { context = (Context) (new InitialContext()).lookup("java:/"); } catch (NamingException e) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/GroupMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/GroupMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/GroupMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/GroupMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,8 +35,6 @@ *

      * A ModelMBean implementation for the org.apache.catalina.Group component. *

      - * - * @author Craig R. McClanahan */ public class GroupMBean extends BaseModelMBean { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/MBeanDumper.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/MBeanDumper.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/MBeanDumper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/MBeanDumper.java 2026-01-23 19:33:36.000000000 +0000 @@ -70,8 +70,7 @@ buf.append(code); buf.append(CRLF); - MBeanAttributeInfo attrs[] = minfo.getAttributes(); - Object value = null; + MBeanAttributeInfo[] attrs = minfo.getAttributes(); for (MBeanAttributeInfo attr : attrs) { if (!attr.isReadable()) { @@ -85,6 +84,7 @@ continue; } + Object value; try { value = mbeanServer.getAttribute(oname, attName); } catch (JMRuntimeException rme) { @@ -190,11 +190,11 @@ int pos = start; while (end - pos > 78) { - sb.append(value.substring(pos, pos + 78)); + sb.append(value, pos, pos + 78); sb.append("\n "); pos = pos + 78; } - sb.append(value.substring(pos, end)); + sb.append(value, pos, end); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/MBeanFactory.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/MBeanFactory.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/MBeanFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/MBeanFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,10 +48,6 @@ import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.res.StringManager; - -/** - * @author Amy Roh - */ public class MBeanFactory { private static final Log log = LogFactory.getLog(MBeanFactory.class); @@ -115,15 +111,13 @@ String path = name.substring(i); Container host = engine.findChild(hostName); String pathStr = getPathStr(path); - Container context = host.findChild(pathStr); - return context; + return host.findChild(pathStr); } else if (type != null) { if (type.equals("Engine")) { return engine; } else if (type.equals("Host")) { String hostName = pname.getKeyProperty("host"); - Container host = engine.findChild(hostName); - return host; + return engine.findChild(hostName); } } return null; @@ -145,14 +139,12 @@ return engine; } else if (path == null) { // child's container is Host - Container host = engine.findChild(hostName); - return host; + return engine.findChild(hostName); } else { // child's container is Context Container host = engine.findChild(hostName); path = getPathStr(path); - Container context = host.findChild(path); - return context; + return host.findChild(path); } } @@ -207,7 +199,7 @@ * @param dataSourceName the datasource name * @param roleNameCol the column name for the role names * @param userCredCol the column name for the user credentials - * @param userNameCol the column name for the user names + * @param userNameCol the column name for the usernames * @param userRoleTable the table name for the roles table * @param userTable the table name for the users * @@ -285,7 +277,7 @@ // Set the protocol in the constructor String protocol = isAjp ? "AJP/1.3" : "HTTP/1.1"; Connector retobj = new Connector(protocol); - if ((address != null) && (address.length() > 0)) { + if ((address != null) && (!address.isEmpty())) { retobj.setProperty("address", address); } // Set port number @@ -646,7 +638,7 @@ address = ObjectName.unquote(address); } - Connector conns[] = service.findConnectors(); + Connector[] conns = service.findConnectors(); for (Connector conn : conns) { String connAddress = null; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/MBeanUtils.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/MBeanUtils.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/MBeanUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/MBeanUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,12 +39,8 @@ import org.apache.tomcat.util.modeler.Registry; import org.apache.tomcat.util.res.StringManager; - /** * Public utility methods in support of the server side MBeans implementation. - * - * @author Craig R. McClanahan - * @author Amy Roh */ public class MBeanUtils { @@ -56,7 +52,7 @@ * The set of exceptions to the normal rules used by createManagedBean(). The first element of each * pair is a class name, and the second element is the managed bean name. */ - private static final String exceptions[][] = { { "org.apache.catalina.users.MemoryGroup", "Group" }, + private static final String[][] exceptions = { { "org.apache.catalina.users.MemoryGroup", "Group" }, { "org.apache.catalina.users.MemoryRole", "Role" }, { "org.apache.catalina.users.MemoryUser", "User" }, { "org.apache.catalina.users.GenericGroup", "Group" }, { "org.apache.catalina.users.GenericRole", "Role" }, { "org.apache.catalina.users.GenericUser", "User" } }; @@ -449,10 +445,8 @@ */ static ObjectName createObjectName(String domain, Group group) throws MalformedObjectNameException { - ObjectName name = null; - name = new ObjectName(domain + ":type=Group,groupname=" + ObjectName.quote(group.getGroupname()) + + return new ObjectName(domain + ":type=Group,groupname=" + ObjectName.quote(group.getGroupname()) + ",database=" + group.getUserDatabase().getId()); - return name; } @@ -469,9 +463,9 @@ */ static ObjectName createObjectName(String domain, Role role) throws MalformedObjectNameException { - ObjectName name = new ObjectName(domain + ":type=Role,rolename=" + ObjectName.quote(role.getRolename()) + - ",database=" + role.getUserDatabase().getId()); - return name; + return new ObjectName(domain + ":type=Role,rolename=" + ObjectName.quote(role.getRolename()) + ",database=" + + role.getUserDatabase().getId()); + } @@ -487,9 +481,8 @@ */ static ObjectName createObjectName(String domain, User user) throws MalformedObjectNameException { - ObjectName name = new ObjectName(domain + ":type=User,username=" + ObjectName.quote(user.getUsername()) + - ",database=" + user.getUserDatabase().getId()); - return name; + return new ObjectName(domain + ":type=User,username=" + ObjectName.quote(user.getUsername()) + ",database=" + + user.getUserDatabase().getId()); } @@ -505,9 +498,7 @@ */ static ObjectName createObjectName(String domain, UserDatabase userDatabase) throws MalformedObjectNameException { - ObjectName name = null; - name = new ObjectName(domain + ":type=UserDatabase,database=" + userDatabase.getId()); - return name; + return new ObjectName(domain + ":type=UserDatabase,database=" + userDatabase.getId()); } @@ -518,7 +509,7 @@ */ public static synchronized Registry createRegistry() { if (registry == null) { - registry = Registry.getRegistry(null, null); + registry = Registry.getRegistry(null); ClassLoader cl = MBeanUtils.class.getClassLoader(); registry.loadDescriptors("org.apache.catalina.mbeans", cl); @@ -549,7 +540,7 @@ */ public static synchronized MBeanServer createServer() { if (mserver == null) { - mserver = Registry.getRegistry(null, null).getMBeanServer(); + mserver = Registry.getRegistry(null).getMBeanServer(); } return mserver; } @@ -725,8 +716,8 @@ */ static void destroyMBeanUserDatabase(String userDatabase) throws Exception { - ObjectName query = null; - Set results = null; + ObjectName query; + Set results; // Groups query = new ObjectName("Users:type=Group,database=" + userDatabase + ",*"); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/MemoryUserDatabaseMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ * A ModelMBean implementation for the org.apache.catalina.users.MemoryUserDatabase * component. *

      - * - * @author Craig R. McClanahan */ public class MemoryUserDatabaseMBean extends SparseUserDatabaseMBean { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/NamingResourcesMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/NamingResourcesMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/NamingResourcesMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/NamingResourcesMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,12 +32,8 @@ import org.apache.tomcat.util.res.StringManager; /** - *

      * A ModelMBean implementation for the org.apache.catalina.deploy.NamingResourcesImpl * component. - *

      - * - * @author Amy Roh */ public class NamingResourcesMBean extends BaseModelMBean { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/RoleMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/RoleMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/RoleMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/RoleMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,6 @@ *

      * A ModelMBean implementation for the org.apache.catalina.Role component. *

      - * - * @author Craig R. McClanahan */ public class RoleMBean extends BaseModelMBean { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/ServiceMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/ServiceMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/ServiceMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/ServiceMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,7 +39,7 @@ Service service = doGetManagedResource(); String protocol = isAjp ? "AJP/1.3" : "HTTP/1.1"; Connector connector = new Connector(protocol); - if ((address != null) && (address.length() > 0)) { + if ((address != null) && (!address.isEmpty())) { connector.setProperty("address", address); } connector.setPort(port); @@ -53,7 +53,7 @@ /** * Adds a named executor to the service * - * @param type Classname of the Executor to be added + * @param type Class name of the Executor to be added * * @throws MBeanException error creating the executor */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/SparseUserDatabaseMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/SparseUserDatabaseMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/SparseUserDatabaseMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/SparseUserDatabaseMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,11 +37,9 @@ *

      * A ModelMBean implementation for the org.apache.catalina.users.SparseUserDatabase * component. The main difference is that the MBeans are created on demand (for example, the findUser method would - * register the corresponding user and make it available for management. All the MBeans created for users, groups and + * register the corresponding user and make it available for management). All the MBeans created for users, groups and * roles are then discarded when save is invoked. *

      - * - * @author Craig R. McClanahan */ public class SparseUserDatabaseMBean extends BaseModelMBean { @@ -177,7 +175,7 @@ /** * Create a new User and return the corresponding MBean Name. * - * @param username User name of the new user + * @param username Username of the new user * @param password Password for the new user * @param fullName Full name for the new user * @@ -247,9 +245,9 @@ /** - * Return the MBean Name for the specified user name (if any); otherwise return null. + * Return the MBean Name for the specified username (if any); otherwise return null. * - * @param username User name to look up + * @param username Username to look up * * @return the user object name */ @@ -314,7 +312,7 @@ /** * Remove an existing user and destroy the corresponding MBean. * - * @param username User name to remove + * @param username Username to remove */ public void removeUser(String username) { UserDatabase database = (UserDatabase) this.resource; @@ -338,8 +336,8 @@ try { UserDatabase database = (UserDatabase) this.resource; if (database.isSparse()) { - ObjectName query = null; - Set results = null; + ObjectName query; + Set results; // Groups query = new ObjectName("Users:type=Group,database=" + database.getId() + ",*"); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/mbeans/UserMBean.java tomcat10-10.1.52/java/org/apache/catalina/mbeans/UserMBean.java --- tomcat10-10.1.34/java/org/apache/catalina/mbeans/UserMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/mbeans/UserMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,8 +35,6 @@ *

      * A ModelMBean implementation for the org.apache.catalina.User component. *

      - * - * @author Craig R. McClanahan */ public class UserMBean extends BaseModelMBean { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/AuthenticatedUserRealm.java tomcat10-10.1.52/java/org/apache/catalina/realm/AuthenticatedUserRealm.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/AuthenticatedUserRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/AuthenticatedUserRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,7 +22,7 @@ * This Realm is intended for use with Authenticator implementations * ({@link org.apache.catalina.authenticator.SSLAuthenticator}, * {@link org.apache.catalina.authenticator.SpnegoAuthenticator}) that authenticate the user as well as obtain the user - * credentials. An authenticated Principal is always created from the user name presented to without further validation. + * credentials. An authenticated Principal is always created from the username presented to without further validation. *

      * Note: It is unsafe to use this Realm with Authenticator implementations that do not validate the * provided credentials. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/CombinedRealm.java tomcat10-10.1.52/java/org/apache/catalina/realm/CombinedRealm.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/CombinedRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/CombinedRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -67,7 +67,7 @@ /** - * @return the set of Realms that this Realm is wrapping + * @return the array of Realms that this Realm is wrapping */ public ObjectName[] getRealms() { ObjectName[] result = new ObjectName[realms.size()]; @@ -81,7 +81,7 @@ /** - * @return the list of Realms contained by this Realm. + * @return the array of Realms contained by this Realm. */ public Realm[] getNestedRealms() { return realms.toArray(new Realm[0]); @@ -283,7 +283,7 @@ public Principal authenticate(GSSContext gssContext, boolean storeCred) { if (gssContext.isEstablished()) { Principal authenticatedUser = null; - GSSName gssName = null; + GSSName gssName; try { gssName = gssContext.getSrcName(); } catch (GSSException e) { @@ -359,7 +359,7 @@ // Stack trace will show where this was called from UnsupportedOperationException uoe = new UnsupportedOperationException(sm.getString("combinedRealm.getPassword")); - log.error(sm.getString("combinedRealm.unexpectedMethod"), uoe); + log.error(uoe.getMessage(), uoe); throw uoe; } @@ -369,7 +369,7 @@ // Stack trace will show where this was called from UnsupportedOperationException uoe = new UnsupportedOperationException(sm.getString("combinedRealm.getPrincipal")); - log.error(sm.getString("combinedRealm.unexpectedMethod"), uoe); + log.error(uoe.getMessage(), uoe); throw uoe; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/DataSourceRealm.java tomcat10-10.1.52/java/org/apache/catalina/realm/DataSourceRealm.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/DataSourceRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/DataSourceRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,11 +34,6 @@ /** * Implementation of Realm that works with any JDBC JNDI DataSource. See the Realm How-To for more details on how * to set up the database and for configuration options. - * - * @author Glenn L. Nielsen - * @author Craig R. McClanahan - * @author Carson McDonald - * @author Ignacio Ortega */ public class DataSourceRealm extends RealmBase { @@ -206,7 +201,7 @@ } /** - * @return the table that holds user data.. + * @return the table that holds user data */ public String getUserTable() { return userTable; @@ -240,10 +235,8 @@ return null; } - Connection dbConnection = null; - // Ensure that we have an open database connection - dbConnection = open(); + Connection dbConnection = open(); if (dbConnection == null) { // If the db connection open fails, return "not authenticated" return null; @@ -362,7 +355,7 @@ protected Connection open() { try { - Context context = null; + Context context; if (localDataSource) { context = ContextBindings.getClassLoader(); context = (Context) context.lookup("comp/env"); @@ -390,10 +383,8 @@ @Override protected String getPassword(String username) { - Connection dbConnection = null; - // Ensure that we have an open database connection - dbConnection = open(); + Connection dbConnection = open(); if (dbConnection == null) { return null; } @@ -407,7 +398,7 @@ /** - * Return the password associated with the given principal's user name. + * Return the password associated with the given principal's username. * * @param dbConnection The database connection to be used * @param username Username for which password should be retrieved @@ -451,18 +442,16 @@ } /** - * Return the roles associated with the given user name. + * Return the roles associated with the given username. * - * @param username User name for which roles should be retrieved + * @param username Username for which roles should be retrieved * * @return an array list of the role names */ protected ArrayList getRoles(String username) { - Connection dbConnection = null; - // Ensure that we have an open database connection - dbConnection = open(); + Connection dbConnection = open(); if (dbConnection == null) { return null; } @@ -476,10 +465,10 @@ /** - * Return the roles associated with the given user name. + * Return the roles associated with the given username. * * @param dbConnection The database connection to be used - * @param username User name for which roles should be retrieved + * @param username Username for which roles should be retrieved * * @return an array list of the role names */ @@ -491,14 +480,11 @@ return null; } - ArrayList list = null; - try (PreparedStatement stmt = dbConnection.prepareStatement(preparedRoles)) { stmt.setString(1, username); try (ResultSet rs = stmt.executeQuery()) { - list = new ArrayList<>(); - + ArrayList list = new ArrayList<>(); while (rs.next()) { String role = rs.getString(1); if (role != null) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/DigestCredentialHandlerBase.java tomcat10-10.1.52/java/org/apache/catalina/realm/DigestCredentialHandlerBase.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/DigestCredentialHandlerBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/DigestCredentialHandlerBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -110,7 +110,7 @@ if (saltLength == 0) { salt = new byte[0]; } else if (saltLength > 0) { - // Double checked locking. OK since random is volatile. + // Double-checked locking. OK since random is volatile. if (random == null) { synchronized (randomLock) { if (random == null) { @@ -137,14 +137,7 @@ // Output the simple/old format for backwards compatibility return serverCredential; } else { - StringBuilder result = new StringBuilder((saltLength << 1) + 10 + serverCredential.length() + 2); - result.append(HexUtils.toHexString(salt)); - result.append('$'); - result.append(iterations); - result.append('$'); - result.append(serverCredential); - - return result.toString(); + return HexUtils.toHexString(salt) + '$' + iterations + '$' + serverCredential; } } @@ -236,7 +229,7 @@ /** * Generates the equivalent stored credentials for the given input credentials, salt, iterations and key length. The - * default implementation calls ignores the key length and calls {@link #mutate(String, byte[], int)}. Sub-classes + * default implementation calls ignores the key length and calls {@link #mutate(String, byte[], int)}. Subclasses * that use the key length should override this method. * * @param inputCredentials User provided credentials diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/GenericPrincipal.java tomcat10-10.1.52/java/org/apache/catalina/realm/GenericPrincipal.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/GenericPrincipal.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/GenericPrincipal.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,6 +23,7 @@ import java.util.Enumeration; import java.util.List; import java.util.Map; +import java.util.Objects; import javax.security.auth.login.LoginContext; @@ -32,8 +33,6 @@ /** * Generic implementation of java.security.Principal that is available for use by Realm * implementations. - * - * @author Craig R. McClanahan */ public class GenericPrincipal implements TomcatPrincipal, Serializable { @@ -221,11 +220,7 @@ @Override public Principal getUserPrincipal() { - if (userPrincipal != null) { - return userPrincipal; - } else { - return this; - } + return Objects.requireNonNullElse(userPrincipal, this); } @@ -238,7 +233,7 @@ /** * The user's delegated credentials. */ - protected transient GSSCredential gssCredential = null; + protected transient GSSCredential gssCredential; @Override public GSSCredential getGssCredential() { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/JAASCallbackHandler.java tomcat10-10.1.52/java/org/apache/catalina/realm/JAASCallbackHandler.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/JAASCallbackHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/JAASCallbackHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,22 +29,15 @@ import org.apache.tomcat.util.res.StringManager; /** - *

      * Implementation of the JAAS CallbackHandler interface, used to negotiate delivery of the username and * credentials that were specified to our constructor. No interaction with the user is required (or possible). - *

      *

      * This CallbackHandler will pre-digest the supplied password, if required by the * <Realm> element in server.xml. - *

      *

      * At present, JAASCallbackHandler knows how to handle callbacks of type * javax.security.auth.callback.NameCallback and * javax.security.auth.callback.PasswordCallback. - *

      - * - * @author Craig R. McClanahan - * @author Andrew R. Jaquith */ public class JAASCallbackHandler implements CallbackHandler { @@ -177,7 +170,7 @@ * @exception UnsupportedCallbackException if the login method requests an unsupported callback type */ @Override - public void handle(Callback callbacks[]) throws IOException, UnsupportedCallbackException { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/JAASMemoryLoginModule.java tomcat10-10.1.52/java/org/apache/catalina/realm/JAASMemoryLoginModule.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/JAASMemoryLoginModule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/JAASMemoryLoginModule.java 2026-01-23 19:33:36.000000000 +0000 @@ -66,8 +66,6 @@ * requirements of the GenericPrincipal constructor. It does not actually perform the functionality * required of a Realm implementation. *

      - * - * @author Craig R. McClanahan */ public class JAASMemoryLoginModule extends MemoryRealm implements LoginModule { // We need to extend MemoryRealm to avoid class cast @@ -140,7 +138,6 @@ if (committed) { logout(); } else { - committed = false; principal = null; } if (log.isTraceEnabled()) { @@ -167,7 +164,7 @@ // Add the roles as additional subjects as per the contract with the // JAASRealm if (principal instanceof GenericPrincipal) { - String roles[] = ((GenericPrincipal) principal).getRoles(); + String[] roles = ((GenericPrincipal) principal).getRoles(); for (String role : roles) { subject.getPrincipals().add(new GenericPrincipal(role)); } @@ -239,7 +236,7 @@ if (callbackHandler == null) { throw new LoginException(sm.getString("jaasMemoryLoginModule.noCallbackHandler")); } - Callback callbacks[] = new Callback[10]; + Callback[] callbacks = new Callback[10]; callbacks[0] = new NameCallback("Username: "); callbacks[1] = new PasswordCallback("Password: ", false); callbacks[2] = new TextInputCallback("nonce"); @@ -252,16 +249,16 @@ callbacks[9] = new TextInputCallback("authMethod"); // Interact with the user to retrieve the username and password - String username = null; - String password = null; - String nonce = null; - String nc = null; - String cnonce = null; - String qop = null; - String realmName = null; - String digestA2 = null; - String algorithm = null; - String authMethod = null; + String username; + String password; + String nonce; + String nc; + String cnonce; + String qop; + String realmName; + String digestA2; + String algorithm; + String authMethod; try { callbackHandler.handle(callbacks); @@ -360,10 +357,10 @@ return null; } - Callback callbacks[] = new Callback[1]; + Callback[] callbacks = new Callback[1]; callbacks[0] = new TextInputCallback("catalinaBase"); - String result = null; + String result; try { callbackHandler.handle(callbacks); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/JAASRealm.java tomcat10-10.1.52/java/org/apache/catalina/realm/JAASRealm.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/JAASRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/JAASRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -108,16 +108,13 @@ *
      CATALINA_OPTS="-Djava.security.auth.login.config=$CATALINA_HOME/conf/jaas.config"
      *
    • *
    • As part of the login process, JAASRealm registers its own CallbackHandler, called (unsurprisingly) - * JAASCallbackHandler. This handler supplies the HTTP requests's username and credentials to the + * JAASCallbackHandler. This handler supplies the HTTP requests' username and credentials to the * user-supplied LoginModule
    • *
    • As with other Realm implementations, digested passwords are supported if the * <Realm> element in server.xml contains a digest attribute; * JAASCallbackHandler will digest the password prior to passing it back to the * LoginModule
    • * - * - * @author Craig R. McClanahan - * @author Yoav Shapira */ public class JAASRealm extends RealmBase { @@ -161,7 +158,7 @@ protected volatile boolean jaasConfigurationLoaded = false; /** - * Keeps track if JAAS invocation of login modules was successful or not. By default it is true unless we detect + * Keeps track if JAAS invocation of login modules was successful or not. By default, it is true unless we detect * JAAS login module can't perform the login. This will be used for realm's {@link #isAvailable()} status so that * {@link LockOutRealm} will not lock the user out if JAAS login modules are unavailable to perform the actual * login. @@ -271,7 +268,7 @@ String[] classNames = classNamesString.split("[ ]*,[ ]*"); for (String className : classNames) { - if (className.length() == 0) { + if (className.isEmpty()) { continue; } try { @@ -329,7 +326,7 @@ /** * Perform the actual JAAS authentication. * - * @param username The user name + * @param username The username * @param callbackHandler The callback handler * * @return the associated principal, or null if there is none. @@ -338,7 +335,6 @@ // Establish a LoginContext to use for authentication try { - LoginContext loginContext = null; if (appName == null) { appName = "Tomcat"; } @@ -357,12 +353,13 @@ currentThread.setContextClassLoader(this.getClass().getClassLoader()); } + LoginContext loginContext; try { Configuration config = getConfig(); loginContext = new LoginContext(appName, null, callbackHandler, config); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - log.error(sm.getString("jaasRealm.unexpectedError"), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("jaasRealm.unexpectedError"), t); // There is configuration issue with JAAS so mark the realm as // unavailable invocationSuccess = false; @@ -378,7 +375,7 @@ } // Negotiate a login via this LoginContext - Subject subject = null; + Subject subject; try { loginContext.login(); subject = loginContext.getSubject(); @@ -395,7 +392,7 @@ } } catch (AccountExpiredException e) { if (log.isDebugEnabled()) { - log.debug(sm.getString("jaasRealm.accountExpired", username)); + log.debug(sm.getString("jaasRealm.accountExpired", username), e); } // JAAS checked LoginExceptions are successful authentication // invocations so mark JAAS realm as available @@ -403,7 +400,7 @@ return null; } catch (CredentialExpiredException e) { if (log.isDebugEnabled()) { - log.debug(sm.getString("jaasRealm.credentialExpired", username)); + log.debug(sm.getString("jaasRealm.credentialExpired", username), e); } // JAAS checked LoginExceptions are successful authentication // invocations so mark JAAS realm as available @@ -411,7 +408,7 @@ return null; } catch (FailedLoginException e) { if (log.isDebugEnabled()) { - log.debug(sm.getString("jaasRealm.failedLogin", username)); + log.debug(sm.getString("jaasRealm.failedLogin", username), e); } // JAAS checked LoginExceptions are successful authentication // invocations so mark JAAS realm as available @@ -423,10 +420,10 @@ // invocations so mark JAAS realm as available invocationSuccess = true; return null; - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - log.error(sm.getString("jaasRealm.unexpectedError"), e); - // JAAS throws exception different than LoginException so mark the + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("jaasRealm.unexpectedError"), t); + // JAAS throws exception different from LoginException so mark the // realm as unavailable invocationSuccess = false; return null; @@ -451,7 +448,7 @@ return principal; } catch (Throwable t) { log.error(sm.getString("jaasRealm.unexpectedError"), t); - // JAAS throws exception different than LoginException so mark the realm as unavailable + // JAAS throws exception different from LoginException so mark the realm as unavailable invocationSuccess = false; return null; } @@ -459,7 +456,7 @@ /** - * @return the password associated with the given principal's user name. This always returns null as the JAASRealm + * @return the password associated with the given principal's username. This always returns null as the JAASRealm * has no way of obtaining this information. */ @Override @@ -485,7 +482,7 @@ * the LoginModules are mapped to roles, but only if their respective classes match one of the "role class" classes. * If a user Principal cannot be constructed, return null. * - * @param username The associated user name + * @param username The associated username * @param subject The Subject representing the logged-in user * @param loginContext Associated with the Principal so {@link LoginContext#logout()} can be called later * @@ -528,7 +525,7 @@ } return null; } else { - if (roles.size() == 0) { + if (roles.isEmpty()) { if (log.isDebugEnabled()) { log.debug(sm.getString("jaasRealm.rolePrincipalFailure")); } @@ -601,7 +598,7 @@ (Class) Class.forName("com.sun.security.auth.login.ConfigFile"); Constructor constructor = sunConfigFile.getConstructor(URI.class); URL resource = Thread.currentThread().getContextClassLoader().getResource(configFile); - Configuration config = null; + Configuration config; if (resource == null) { try (ConfigurationSource.Resource configFileResource = ConfigFileLoader.getSource().getResource(configFile)) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/JNDIRealm.java tomcat10-10.1.52/java/org/apache/catalina/realm/JNDIRealm.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/JNDIRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/JNDIRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -133,19 +133,17 @@ * descriptor allows applications to refer to roles programmatically by names other than those used in the directory * server itself. * - *

      - * WARNING - There is a reported bug against the Netscape provider code - * (com.netscape.jndi.ldap.LdapContextFactory) with respect to successfully authenticated a non-existing user. The - * report is here: https://bz.apache.org/bugzilla/show_bug.cgi?id=11210 . With luck, Netscape has updated their provider - * code and this is not an issue. - *

      - * - * @author John Holman - * @author Craig R. McClanahan */ public class JNDIRealm extends RealmBase { - // ----------------------------------------------------- Instance Variables + /** + * Constant that holds the name of the environment property for specifying the manner in which aliases should be + * dereferenced. + */ + public static final String DEREF_ALIASES = "java.naming.ldap.derefAliases"; + + private static final String AUTHENTICATION_NAME_GSSAPI = "GSSAPI"; + /** * The type of authentication to use @@ -179,12 +177,6 @@ protected String derefAliases = null; /** - * Constant that holds the name of the environment property for specifying the manner in which aliases should be - * dereferenced. - */ - public static final String DEREF_ALIASES = "java.naming.ldap.derefAliases"; - - /** * The protocol that will be used in the communication with the directory server. */ protected String protocol = null; @@ -955,7 +947,7 @@ /** - * @return list of the allowed cipher suites when connections are made using StartTLS + * @return array of the allowed cipher suites when connections are made using StartTLS */ private String[] getCipherSuitesArray() { if (cipherSuites == null || cipherSuitesArray != null) { @@ -1039,7 +1031,7 @@ if (this.hostnameVerifier != null) { return this.hostnameVerifier; } - if (this.hostNameVerifierClassName == null || hostNameVerifierClassName.equals("")) { + if (this.hostNameVerifierClassName == null || hostNameVerifierClassName.isEmpty()) { return null; } try { @@ -1081,7 +1073,7 @@ /** - * @return the list of supported ssl protocols by the default {@link SSLContext} + * @return the array of supported ssl protocols by the default {@link SSLContext} */ private String[] getSupportedSslProtocols() { try { @@ -1134,7 +1126,7 @@ ClassLoader ocl = null; Thread currentThread = null; JNDIConnection connection = null; - Principal principal = null; + Principal principal; try { // https://bz.apache.org/bugzilla/show_bug.cgi?id=65553 @@ -1152,7 +1144,7 @@ try { - // Occasionally the directory context will timeout. Try one more + // Occasionally the directory context will time out. Try one more // time before giving up. // Authenticate the specified username if possible @@ -1161,9 +1153,9 @@ } catch (NullPointerException | NamingException e) { /* * BZ 61313 NamingException may or may not indicate an error that is recoverable via fail over. - * Therefore a decision needs to be made whether to fail over or not. Generally, attempting to fail over - * when it is not appropriate is better than not failing over when it is appropriate so the code always - * attempts to fail over for NamingExceptions. + * Therefore, a decision needs to be made whether to fail over or not. Generally, attempting to fail + * over when it is not appropriate is better than not failing over when it is appropriate so the code + * always attempts to fail over for NamingExceptions. */ /* @@ -1225,10 +1217,10 @@ * * @exception NamingException if a directory server error occurs */ - public Principal authenticate(JNDIConnection connection, String username, String credentials) + protected Principal authenticate(JNDIConnection connection, String username, String credentials) throws NamingException { - if (username == null || username.equals("") || credentials == null || credentials.equals("")) { + if (username == null || username.isEmpty() || credentials == null || credentials.isEmpty()) { if (containerLog.isTraceEnabled()) { containerLog.trace("username null or empty: returning null principal."); } @@ -1267,9 +1259,9 @@ // Log the problem for posterity containerLog.warn(sm.getString("jndiRealm.exception"), ine); // ignore; this is probably due to a name not fitting - // the search path format exactly, as in a fully- - // qualified name being munged into a search path - // that already contains cn= or vice-versa + // the search path format exactly, as in a fully-qualified + // name being munged into a search path + // that already contains cn= or vice versa } } } @@ -1482,7 +1474,7 @@ protected User getUser(JNDIConnection connection, String username, String credentials, int curUserPattern) throws NamingException { - User user = null; + User user; // Get attributes to retrieve from user entry List list = new ArrayList<>(); @@ -1551,7 +1543,7 @@ } // Get required attributes from user entry - Attributes attrs = null; + Attributes attrs; try { attrs = context.getAttributes(dn, attrIds); } catch (NameNotFoundException e) { @@ -1575,7 +1567,7 @@ // Retrieve values of userRoleName attribute ArrayList roles = null; if (userRoleName != null) { - roles = addAttributeValues(userRoleName, attrs, roles); + roles = addAttributeValues(userRoleName, attrs, null); } return new User(username, dn, password, roles, userRoleAttrValue); @@ -1601,7 +1593,7 @@ protected User getUserByPattern(JNDIConnection connection, String username, String credentials, String[] attrIds, int curUserPattern) throws NamingException { - User user = null; + User user; if (username == null || userPatternArray[curUserPattern] == null) { return null; @@ -1733,7 +1725,7 @@ // Retrieve values of userRoleName attribute ArrayList roles = null; if (userRoleName != null) { - roles = addAttributeValues(userRoleName, attrs, roles); + roles = addAttributeValues(userRoleName, attrs, null); } return new User(username, dn, password, roles, userRoleAttrValue); @@ -1748,7 +1740,7 @@ /** * Check whether the given User can be authenticated with the given credentials. If the userPassword * configuration attribute is specified, the credentials previously retrieved from the directory are compared - * explicitly with those presented by the user. Otherwise the presented credentials are checked by binding to the + * explicitly with those presented by the user. Otherwise, the presented credentials are checked by binding to the * directory as the user. * * @param context The directory context @@ -1761,7 +1753,7 @@ */ protected boolean checkCredentials(DirContext context, User user, String credentials) throws NamingException { - boolean validated = false; + boolean validated; if (userPassword == null) { validated = bindAsUser(context, user, credentials); @@ -1836,11 +1828,16 @@ containerLog.trace(" validating credentials by binding as the user"); } - userCredentialsAdd(context, dn, credentials); - - // Elicit an LDAP bind operation boolean validated = false; + Hashtable preservedEnvironment = context.getEnvironment(); + + // Elicit an LDAP bind operation using the provided user credentials try { + userCredentialsAdd(context, dn, credentials); + // Need to make sure GSSAPI SASL authentication is not used if configured + if (AUTHENTICATION_NAME_GSSAPI.equals(preservedEnvironment.get(Context.SECURITY_AUTHENTICATION))) { + context.removeFromEnvironment(Context.SECURITY_AUTHENTICATION); + } if (containerLog.isTraceEnabled()) { containerLog.trace(" binding as " + dn); } @@ -1848,12 +1845,14 @@ validated = true; } catch (AuthenticationException e) { if (containerLog.isTraceEnabled()) { - containerLog.trace(" bind attempt failed"); + containerLog.trace(" bind attempt failed", e); } + } finally { + // Restore GSSAPI SASL if previously configured + restoreEnvironmentParameter(context, Context.SECURITY_AUTHENTICATION, preservedEnvironment); + userCredentialsRemove(context); } - userCredentialsRemove(context); - return validated; } @@ -1965,11 +1964,11 @@ } controls.setReturningAttributes(new String[] { roleName }); - String base = null; + String base; if (connection.roleBaseFormat != null) { NameParser np = connection.context.getNameParser(""); Name name = np.parse(dn); - String nameParts[] = new String[name.size()]; + String[] nameParts = new String[name.size()]; for (int i = 0; i < name.size(); i++) { // May have been returned with \ escaping rather than // \. Make sure it is \. @@ -2053,7 +2052,7 @@ } String dname = getDistinguishedName(connection.context, roleBase, result); String name = getAttributeValue(roleName, attrs); - if (name != null && dname != null && !groupMap.keySet().contains(dname)) { + if (name != null && dname != null && !groupMap.containsKey(dname)) { groupMap.put(dname, name); newThisRound.put(dname, name); @@ -2141,7 +2140,7 @@ if (value == null) { return null; } - String valueString = null; + String valueString; if (value instanceof byte[]) { valueString = new String((byte[]) value); } else { @@ -2215,8 +2214,8 @@ if (tls != null) { try { tls.close(); - } catch (IOException e) { - containerLog.error(sm.getString("jndiRealm.tlsClose"), e); + } catch (IOException ioe) { + containerLog.error(sm.getString("jndiRealm.tlsClose"), ioe); } } // Close our opened connection @@ -2243,7 +2242,7 @@ if (connectionPool != null) { // Close any pooled connections as they might be bad as well synchronized (connectionPool) { - JNDIConnection connection = null; + JNDIConnection connection; while ((connection = connectionPool.pop()) != null) { close(connection); } @@ -2260,12 +2259,12 @@ } JNDIConnection connection = null; - User user = null; + User user; try { // Ensure that we have a directory context available connection = get(); - // Occasionally the directory context will timeout. Try one more + // Occasionally the directory context will time out. Try one more // time before giving up. try { user = getUser(connection, username, null); @@ -2330,13 +2329,13 @@ protected Principal getPrincipal(String username, GSSCredential gssCredential) { JNDIConnection connection = null; - Principal principal = null; + Principal principal; try { // Ensure that we have a directory context available connection = get(); - // Occasionally the directory context will timeout. Try one more + // Occasionally the directory context will time out. Try one more // time before giving up. try { @@ -2384,10 +2383,10 @@ /** - * Get the principal associated with the specified certificate. + * Get the principal associated with the specified username. * * @param connection The directory context - * @param username The user name + * @param username The username * @param gssCredential The credentials * * @return the Principal associated with the given certificate. @@ -2397,7 +2396,7 @@ protected Principal getPrincipal(JNDIConnection connection, String username, GSSCredential gssCredential) throws NamingException { - User user = null; + User user; List roles = null; Hashtable preservedEnvironment = null; DirContext context = connection.context; @@ -2407,7 +2406,7 @@ // Preserve the current context environment parameters preservedEnvironment = context.getEnvironment(); // Set up context - context.addToEnvironment(Context.SECURITY_AUTHENTICATION, "GSSAPI"); + context.addToEnvironment(Context.SECURITY_AUTHENTICATION, AUTHENTICATION_NAME_GSSAPI); context.addToEnvironment("javax.security.sasl.server.authentication", "true"); context.addToEnvironment("javax.security.sasl.qop", spnegoDelegationQop); // Note: Subject already set in SPNEGO authenticator so no need @@ -2454,7 +2453,7 @@ * @exception NamingException if a directory server error occurs */ protected JNDIConnection get() throws NamingException { - JNDIConnection connection = null; + JNDIConnection connection; // Use the pool if available, otherwise use the single connection if (connectionPool != null) { connection = connectionPool.pop(); @@ -2516,7 +2515,7 @@ // Ensure that we have a directory context available connection.context = createDirContext(getDirectoryContextEnvironment()); } catch (Exception e) { - if (alternateURL == null || alternateURL.length() == 0) { + if (alternateURL == null || alternateURL.isEmpty()) { // No alternate URL. Re-throw the exception. throw e; } @@ -2554,7 +2553,7 @@ return sslSocketFactory; } final SSLSocketFactory result; - if (this.sslSocketFactoryClassName != null && !sslSocketFactoryClassName.trim().equals("")) { + if (this.sslSocketFactoryClassName != null && !sslSocketFactoryClassName.trim().isEmpty()) { result = createSSLSocketFactoryFromClassName(this.sslSocketFactoryClassName); } else { result = createSSLContextFactoryFromProtocol(sslProtocol); @@ -2568,7 +2567,7 @@ try { Object o = constructInstance(className); if (o instanceof SSLSocketFactory) { - return sslSocketFactory; + return (SSLSocketFactory) o; } else { throw new IllegalArgumentException(sm.getString("jndiRealm.invalidSslSocketFactory", className)); } @@ -2627,8 +2626,10 @@ try { SSLSession negotiate = tls.negotiate(getSSLSocketFactory()); containerLog.debug(sm.getString("jndiRealm.negotiatedTls", negotiate.getProtocol())); - } catch (IOException e) { - throw new NamingException(e.getMessage()); + } catch (IOException ioe) { + NamingException ne = new NamingException(ioe.getMessage()); + ne.initCause(ioe); + throw ne; } } finally { if (result != null) { @@ -2761,8 +2762,8 @@ /** * Given a string containing LDAP patterns for user locations (separated by parentheses in a pseudo-LDAP search - * string format - "(location1)(location2)", returns an array of those paths. Real LDAP search strings are supported - * as well (though only the "|" "OR" type). + * string format - "(location1)(location2)"), returns an array of those paths. Real LDAP search strings are + * supported as well (though only the "|" "OR" type). * * @param userPatternString - a string LDAP search paths surrounded by parentheses * @@ -2777,9 +2778,7 @@ // no parens here; return whole thing return new String[] { userPatternString }; } - int startingPoint = 0; while (startParenLoc > -1) { - int endParenLoc = 0; // weed out escaped open parens and parens enclosing the // whole statement (in the case of valid LDAP search // strings: (|(something)(somethingelse)) @@ -2787,15 +2786,14 @@ (startParenLoc != 0 && userPatternString.charAt(startParenLoc - 1) == '\\')) { startParenLoc = userPatternString.indexOf('(', startParenLoc + 1); } - endParenLoc = userPatternString.indexOf(')', startParenLoc + 1); + int endParenLoc = userPatternString.indexOf(')', startParenLoc + 1); // weed out escaped end-parens while (userPatternString.charAt(endParenLoc - 1) == '\\') { endParenLoc = userPatternString.indexOf(')', endParenLoc + 1); } String nextPathPart = userPatternString.substring(startParenLoc + 1, endParenLoc); pathList.add(nextPathPart); - startingPoint = endParenLoc + 1; - startParenLoc = userPatternString.indexOf('(', startingPoint); + startParenLoc = userPatternString.indexOf('(', endParenLoc + 1); } return pathList.toArray(new String[0]); } @@ -2884,7 +2882,7 @@ URI userNameUri = new URI(resultName); String pathComponent = userNameUri.getPath(); // Should not ever have an empty path component, since that is /{DN} - if (pathComponent.length() < 1) { + if (pathComponent.isEmpty()) { throw new InvalidNameException(sm.getString("jndiRealm.invalidName", resultName)); } name = parser.parse(pathComponent.substring(1)); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/realm/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/realm/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations dataSourceRealm.getPassword.exception=Исключение при получении пароля для [{0}] +dataSourceRealm.getRoles.exception=Ошибка получения ролей для [{0}] lockOutRealm.authLockedUser=Заблокированный пользователь [{0}] совершил попытку авторизоваться diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/LockOutRealm.java tomcat10-10.1.52/java/org/apache/catalina/realm/LockOutRealm.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/LockOutRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/LockOutRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -134,8 +134,8 @@ @Override public Principal authenticate(GSSContext gssContext, boolean storeCreds) { if (gssContext.isEstablished()) { - String username = null; - GSSName name = null; + String username; + GSSName name; try { name = gssContext.getSrcName(); } catch (GSSException e) { @@ -202,10 +202,10 @@ /* * Checks to see if the current user is locked. If this is associated with a login attempt, then the last access - * time will be recorded and any attempt to authenticated a locked user will log a warning. + * time will be recorded and any attempt to authenticate a locked user will log a warning. */ public boolean isLocked(String username) { - LockRecord lockRecord = null; + LockRecord lockRecord; synchronized (this) { lockRecord = failedUsers.get(username); } @@ -216,13 +216,10 @@ } // Check to see if user is locked - if (lockRecord.getFailures() >= failureCount && - (System.currentTimeMillis() - lockRecord.getLastFailureTime()) / 1000 < lockOutTime) { - return true; - } + // Otherwise, user has not, yet, exceeded lock thresholds + return lockRecord.getFailures() >= failureCount && + (System.currentTimeMillis() - lockRecord.getLastFailureTime()) / 1000 < lockOutTime; - // User has not, yet, exceeded lock thresholds - return false; } @@ -239,7 +236,7 @@ * After a failed authentication, add the record of the failed authentication. */ private void registerAuthFailure(String username) { - LockRecord lockRecord = null; + LockRecord lockRecord; synchronized (this) { if (!failedUsers.containsKey(username)) { lockRecord = new LockRecord(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/MemoryRealm.java tomcat10-10.1.52/java/org/apache/catalina/realm/MemoryRealm.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/MemoryRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/MemoryRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,8 +38,6 @@ * IMPLEMENTATION NOTE: It is assumed that the in-memory collection representing our defined users (and * their roles) is initialized at application startup and never modified again. Therefore, no thread synchronization is * performed around accesses to the principals collection. - * - * @author Craig R. McClanahan */ public class MemoryRealm extends RealmBase { @@ -63,13 +61,13 @@ /** - * The set of valid Principals for this Realm, keyed by user name. + * The set of valid Principals for this Realm, keyed by username. */ private final Map principals = new HashMap<>(); /** - * The set of credentials for this Realm, keyed by user name. + * The set of credentials for this Realm, keyed by username. */ private final Map credentials = new HashMap<>(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/MemoryRuleSet.java tomcat10-10.1.52/java/org/apache/catalina/realm/MemoryRuleSet.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/MemoryRuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/MemoryRuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ *

      * RuleSet for recognizing the users defined in the XML file processed by MemoryRealm. *

      - * - * @author Craig R. McClanahan */ public class MemoryRuleSet implements RuleSet { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/MessageDigestCredentialHandler.java tomcat10-10.1.52/java/org/apache/catalina/realm/MessageDigestCredentialHandler.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/MessageDigestCredentialHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/MessageDigestCredentialHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -58,6 +58,7 @@ private Charset encoding = StandardCharsets.UTF_8; private String algorithm = null; + private boolean digestInRfc3112Order = false; public String getEncoding() { @@ -91,6 +92,16 @@ } + public boolean getDigestInRfc3112Order() { + return digestInRfc3112Order; + } + + + public void setDigestInRfc3112Order(boolean digestInRfc3112Order) { + this.digestInRfc3112Order = digestInRfc3112Order; + } + + @Override public boolean matches(String inputCredentials, String storedCredentials) { if (inputCredentials == null || storedCredentials == null) { @@ -162,7 +173,12 @@ if (salt == null) { userDigest = ConcurrentMessageDigest.digest(algorithm, iterations, inputCredentialbytes); } else { - userDigest = ConcurrentMessageDigest.digest(algorithm, iterations, salt, inputCredentialbytes); + if (digestInRfc3112Order) { + // RFC 3112 states that the input order for the digest is credentials then salt + userDigest = ConcurrentMessageDigest.digest(algorithm, iterations, inputCredentialbytes, salt); + } else { + userDigest = ConcurrentMessageDigest.digest(algorithm, iterations, salt, inputCredentialbytes); + } } return HexUtils.toHexString(userDigest); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/NullRealm.java tomcat10-10.1.52/java/org/apache/catalina/realm/NullRealm.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/NullRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/NullRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,8 +19,8 @@ import java.security.Principal; /** - * Minimal Realm implementation that always returns null when an attempt is made to validate a user name and password. - * It is intended to be used as a default Realm implementation when no other Realm is specified. + * Minimal Realm implementation that always returns null when an attempt is made to validate a username and password. It + * is intended to be used as a default Realm implementation when no other Realm is specified. */ public class NullRealm extends RealmBase { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/RealmBase.java tomcat10-10.1.52/java/org/apache/catalina/realm/RealmBase.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/RealmBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/RealmBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -69,8 +69,6 @@ /** * Simple implementation of Realm that reads an XML file to configure the valid users, passwords, and roles. The * file format (and default file location) are identical to those currently supported by Tomcat 3.X. - * - * @author Craig R. McClanahan */ public abstract class RealmBase extends LifecycleMBeanBase implements Realm { @@ -138,12 +136,12 @@ protected boolean validate = true; /** - * The name of the class to use for retrieving user names from X509 certificates. + * The name of the class to use for retrieving usernames from X509 certificates. */ protected String x509UsernameRetrieverClassName; /** - * The object that will extract user names from X509 client certificates. + * The object that will extract usernames from X509 client certificates. */ protected X509UsernameRetriever x509UsernameRetriever; @@ -155,7 +153,7 @@ /** * When processing users authenticated via the GSS-API, should any "@..." be stripped from the end of the - * user name? + * username? */ protected boolean stripRealmForGss = true; @@ -268,19 +266,19 @@ } /** - * Gets the name of the class that will be used to extract user names from X509 client certificates. + * Gets the name of the class that will be used to extract usernames from X509 client certificates. * - * @return The name of the class that will be used to extract user names from X509 client certificates. + * @return The name of the class that will be used to extract usernames from X509 client certificates. */ public String getX509UsernameRetrieverClassName() { return x509UsernameRetrieverClassName; } /** - * Sets the name of the class that will be used to extract user names from X509 client certificates. The class must + * Sets the name of the class that will be used to extract usernames from X509 client certificates. The class must * implement X509UsernameRetriever. * - * @param className The name of the class that will be used to extract user names from X509 client certificates. + * @param className The name of the class that will be used to extract usernames from X509 client certificates. * * @see X509UsernameRetriever */ @@ -410,7 +408,7 @@ serverDigestValue = digestA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + digestA2; } - byte[] valueBytes = null; + byte[] valueBytes; try { valueBytes = serverDigestValue.getBytes(getDigestCharset()); } catch (UnsupportedEncodingException uee) { @@ -435,7 +433,7 @@ @Override - public Principal authenticate(X509Certificate certs[]) { + public Principal authenticate(X509Certificate[] certs) { if ((certs == null) || (certs.length < 1)) { return null; @@ -529,7 +527,7 @@ ArrayList results = null; // Are there any defined security constraints? - SecurityConstraint constraints[] = context.findConstraints(); + SecurityConstraint[] constraints = context.findConstraints(); if (constraints == null || constraints.length == 0) { if (log.isTraceEnabled()) { log.trace(" No applicable constraints defined"); @@ -541,7 +539,7 @@ String uri = request.getRequestPathMB().toString(); // Bug47080 - in rare cases this may be null or "" // Mapper treats as '/' do the same to prevent NPE - if (uri == null || uri.length() == 0) { + if (uri == null || uri.isEmpty()) { uri = "/"; } @@ -573,7 +571,7 @@ for (String pattern : patterns) { // Exact match including special case for the context root. - if (uri.equals(pattern) || pattern.length() == 0 && uri.equals("/")) { + if (uri.equals(pattern) || pattern.isEmpty() && uri.equals("/")) { found = true; if (securityCollection.findMethod(method)) { if (results == null) { @@ -761,7 +759,7 @@ * Convert an ArrayList to a SecurityConstraint []. */ private SecurityConstraint[] resultsToArray(ArrayList results) { - if (results == null || results.size() == 0) { + if (results == null || results.isEmpty()) { return null; } return results.toArray(new SecurityConstraint[0]); @@ -781,7 +779,7 @@ boolean status = false; boolean denyfromall = false; for (SecurityConstraint constraint : constraints) { - String roles[]; + String[] roles; if (constraint.getAllRoles()) { // * means all roles defined in web.xml roles = request.getContext().findSecurityRoles(); @@ -840,7 +838,6 @@ } // Check for an all roles(role-name="*") for (SecurityConstraint constraint : constraints) { - String roles[]; // If the all roles mode exists, sets if (constraint.getAllRoles()) { if (allRolesMode == AllRolesMode.AUTH_ONLY_MODE) { @@ -852,7 +849,7 @@ } // For AllRolesMode.STRICT_AUTH_ONLY_MODE there must be zero roles - roles = request.getContext().findSecurityRoles(); + String[] roles = request.getContext().findSecurityRoles(); if (roles == null) { roles = new String[0]; } @@ -930,7 +927,7 @@ List attrs = new ArrayList<>(); for (String name : userAttributes.split(USER_ATTRIBUTES_DELIMITER)) { name = name.trim(); - if (name.length() == 0) { + if (name.isEmpty()) { continue; } if (name.equals(USER_ATTRIBUTES_WILDCARD)) { @@ -942,7 +939,7 @@ } attrs.add(name); } - return attrs.size() > 0 ? attrs : null; + return !attrs.isEmpty() ? attrs : null; } @@ -1124,9 +1121,9 @@ /** - * Return the digest associated with given principal's user name. + * Return the digest associated with given principal's username. * - * @param username The user name + * @param username The username * @param realmName The realm name * * @return the digest for the specified user @@ -1156,7 +1153,7 @@ String digestValue = username + ":" + realmName + ":" + getPassword(username); - byte[] valueBytes = null; + byte[] valueBytes; try { valueBytes = digestValue.getBytes(getDigestCharset()); } catch (UnsupportedEncodingException uee) { @@ -1190,9 +1187,9 @@ /** * Get the password for the specified user. * - * @param username The user name + * @param username The username * - * @return the password associated with the given principal's user name. + * @return the password associated with the given principal's username. */ protected abstract String getPassword(String username); @@ -1218,9 +1215,9 @@ /** * Get the principal associated with the specified user. * - * @param username The user name + * @param username The username * - * @return the Principal associated with the given user name. + * @return the Principal associated with the given username. */ protected abstract Principal getPrincipal(String username); @@ -1232,7 +1229,7 @@ * @param gssCredential the GSS credential of the principal * @param gssContext the established GSS context * - * @return the principal associated with the given user name. + * @return the principal associated with the given username. */ protected Principal getPrincipal(GSSName gssName, GSSCredential gssCredential, GSSContext gssContext) { return getPrincipal(gssName, gssCredential); @@ -1245,7 +1242,7 @@ * @param gssName The GSS name * @param gssCredential the GSS credential of the principal * - * @return the principal associated with the given user name. + * @return the principal associated with the given username. */ protected Principal getPrincipal(GSSName gssName, GSSCredential gssCredential) { String name = gssName.toString(); @@ -1270,7 +1267,7 @@ /** * Return the Server object that is the ultimate parent for the container with which this Realm is associated. If - * the server cannot be found (eg because the container hierarchy is not complete), null is returned. + * the server cannot be found (e.g. because the container hierarchy is not complete), null is returned. * * @return the Server associated with the realm */ @@ -1328,7 +1325,7 @@ * * @throws IOException If an error occurs reading the password file */ - public static void main(String args[]) throws IOException { + public static void main(String[] args) throws IOException { // Use negative values since null is not an option to indicate 'not set' int saltLength = -1; @@ -1498,12 +1495,7 @@ @Override public String getObjectNameKeyProperties() { - - StringBuilder keyProperties = new StringBuilder("type=Realm"); - keyProperties.append(getRealmSuffix()); - keyProperties.append(container.getMBeanKeyProperties()); - - return keyProperties.toString(); + return "type=Realm" + getRealmSuffix() + container.getMBeanKeyProperties(); } @Override diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/SecretKeyCredentialHandler.java tomcat10-10.1.52/java/org/apache/catalina/realm/SecretKeyCredentialHandler.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/SecretKeyCredentialHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/SecretKeyCredentialHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,8 +53,7 @@ @Override public void setAlgorithm(String algorithm) throws NoSuchAlgorithmException { - SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(algorithm); - this.secretKeyFactory = secretKeyFactory; + this.secretKeyFactory = SecretKeyFactory.getInstance(algorithm); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/UserDatabaseRealm.java tomcat10-10.1.52/java/org/apache/catalina/realm/UserDatabaseRealm.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/UserDatabaseRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/UserDatabaseRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,8 +39,6 @@ * available through the JNDI resources configured for this instance of Catalina. Set the resourceName * parameter to the JNDI resources name for the configured instance of UserDatabase that we should consult. * - * @author Craig R. McClanahan - * * @since 4.1 */ public class UserDatabaseRealm extends RealmBase { @@ -112,8 +110,7 @@ * Determines whether this Realm is configured to obtain the associated {@link UserDatabase} from the global JNDI * context or a local (web application) JNDI context. * - * @return {@code true} if a local JNDI context will be used, {@code false} if the the global JNDI context will be - * used + * @return {@code true} if a local JNDI context will be used, {@code false} if the global JNDI context will be used */ public boolean getLocalJndiResource() { return localJndiResource; @@ -210,7 +207,7 @@ synchronized (databaseLock) { if (database == null) { try { - Context context = null; + Context context; if (localJndiResource) { context = ContextBindings.getClassLoader(); context = (Context) context.lookup("comp/env"); @@ -220,13 +217,13 @@ containerLog.error(sm.getString("userDatabaseRealm.noNamingContext")); return null; } - context = getServer().getGlobalNamingContext(); + context = server.getGlobalNamingContext(); } database = (UserDatabase) context.lookup(resourceName); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); if (containerLog != null) { - containerLog.error(sm.getString("userDatabaseRealm.lookup", resourceName), e); + containerLog.error(sm.getString("userDatabaseRealm.lookup", resourceName), t); } database = null; } @@ -268,7 +265,7 @@ @Override public boolean isAvailable() { - return (database == null) ? false : database.isAvailable(); + return database != null && database.isAvailable(); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/realm/X509UsernameRetriever.java tomcat10-10.1.52/java/org/apache/catalina/realm/X509UsernameRetriever.java --- tomcat10-10.1.34/java/org/apache/catalina/realm/X509UsernameRetriever.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/realm/X509UsernameRetriever.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,15 +19,15 @@ import java.security.cert.X509Certificate; /** - * Provides an interface for retrieving a user name from an X509Certificate. + * Provides an interface for retrieving a username from an X509Certificate. */ public interface X509UsernameRetriever { /** - * Gets a user name from an X509Certificate. + * Gets a username from an X509Certificate. * - * @param cert The certificate containing the user name. + * @param cert The certificate containing the username. * - * @return An appropriate user name obtained from one or more fields in the certificate. + * @return An appropriate username obtained from one or more fields in the certificate. */ String getUsername(X509Certificate cert); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/security/SecurityClassLoad.java tomcat10-10.1.52/java/org/apache/catalina/security/SecurityClassLoad.java --- tomcat10-10.1.34/java/org/apache/catalina/security/SecurityClassLoad.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/security/SecurityClassLoad.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,8 +19,6 @@ /** * Static class used to preload java classes when using the Java SecurityManager so that the defineClassInPackage * RuntimePermission does not trigger an AccessControlException. - * - * @author Glenn L. Nielsen */ public final class SecurityClassLoad { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/security/SecurityConfig.java tomcat10-10.1.52/java/org/apache/catalina/security/SecurityConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/security/SecurityConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/security/SecurityConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,6 @@ /** * Util class to protect Catalina against package access and insertion. The code are been moved from Catalina.java - * - * @author the Catalina.java authors */ public final class SecurityConfig { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/security/SecurityListener.java tomcat10-10.1.52/java/org/apache/catalina/security/SecurityListener.java --- tomcat10-10.1.34/java/org/apache/catalina/security/SecurityListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/security/SecurityListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -86,18 +86,18 @@ /** * Set the list of operating system users not permitted to run Tomcat. By default, only root is prevented from * running Tomcat. Calling this method with null or the empty string will clear the list of users and effectively - * disables this check. User names will always be checked in a case insensitive manner using the system default + * disables this check. Usernames will always be checked in a case-insensitive manner using the system default * Locale. * * @param userNameList A comma separated list of operating system users not permitted to run Tomcat */ public void setCheckedOsUsers(String userNameList) { - if (userNameList == null || userNameList.length() == 0) { + if (userNameList == null || userNameList.isEmpty()) { checkedOsUsers.clear(); } else { String[] userNames = userNameList.split(","); for (String userName : userNames) { - if (userName.length() > 0) { + if (!userName.isEmpty()) { checkedOsUsers.add(userName.toLowerCase(Locale.getDefault())); } } @@ -108,7 +108,7 @@ /** * Returns the current list of operating system users not permitted to run Tomcat. * - * @return A comma separated list of operating system user names. + * @return A comma separated list of operating system usernames. */ public String getCheckedOsUsers() { return StringUtils.join(checkedOsUsers); @@ -121,7 +121,7 @@ * @param umask The 4-digit umask as returned by the OS command umask */ public void setMinimumUmask(String umask) { - if (umask == null || umask.length() == 0) { + if (umask == null || umask.isEmpty()) { minimumUmask = Integer.valueOf(0); } else { minimumUmask = Integer.valueOf(umask, 8); @@ -201,13 +201,12 @@ if (log.isDebugEnabled()) { log.debug(sm.getString("SecurityListener.checkUmaskSkip")); } - return; } else { if (minimumUmask.intValue() > 0) { log.warn(sm.getString("SecurityListener.checkUmaskNone", UMASK_PROPERTY_NAME, getMinimumUmask())); } - return; } + return; } if ((umask.intValue() & minimumUmask.intValue()) != minimumUmask.intValue()) { @@ -222,8 +221,7 @@ if (allowedAgeDays >= 0) { String buildDateString = ServerInfo.getServerBuiltISO(); - if (null == buildDateString || buildDateString.length() < 1 || - !Character.isDigit(buildDateString.charAt(0))) { + if (null == buildDateString || buildDateString.isEmpty() || !Character.isDigit(buildDateString.charAt(0))) { log.warn(sm.getString("SecurityListener.buildDateUnreadable", buildDateString)); } else { try { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/security/SecurityUtil.java tomcat10-10.1.52/java/org/apache/catalina/security/SecurityUtil.java --- tomcat10-10.1.34/java/org/apache/catalina/security/SecurityUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/security/SecurityUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -214,6 +214,7 @@ * * @throws Exception an execution error occurred */ + @SuppressWarnings("deprecation") private static void execute(final Method method, final Object targetObject, final Object[] targetArguments, Principal principal) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/security/TLSCertificateReloadListener.java tomcat10-10.1.52/java/org/apache/catalina/security/TLSCertificateReloadListener.java --- tomcat10-10.1.34/java/org/apache/catalina/security/TLSCertificateReloadListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/security/TLSCertificateReloadListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,7 +53,7 @@ private int daysBefore = 14; // State - private Calendar nextCheck = Calendar.getInstance(); + private final Calendar nextCheck = Calendar.getInstance(); /** @@ -140,7 +140,7 @@ */ calendar.add(Calendar.DAY_OF_MONTH, getDaysBefore()); - // Check all of the certificates + // Check all the certificates Service[] services = server.findServices(); for (Service service : services) { Connector[] connectors = service.findConnectors(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/CGIServlet.java tomcat10-10.1.52/java/org/apache/catalina/servlets/CGIServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/servlets/CGIServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/CGIServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,16 +43,21 @@ import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; +import jakarta.servlet.UnavailableException; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; +import org.apache.catalina.Globals; +import org.apache.catalina.WebResource; +import org.apache.catalina.WebResourceRoot; import org.apache.catalina.util.IOTools; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.compat.JrePlatform; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.res.StringManager; @@ -192,17 +197,12 @@ *
    • Confirm use of ServletInputStream.available() in CGIRunner.run() is not needed *
    • [add more to this TODO list] * - * - * @author Martin T Dengler [root@martindengler.com] - * @author Amy Roh */ public final class CGIServlet extends HttpServlet { private static final Log log = LogFactory.getLog(CGIServlet.class); private static final StringManager sm = StringManager.getManager(CGIServlet.class); - /* some vars below copied from Craig R. McClanahan's InvokerServlet */ - private static final long serialVersionUID = 1L; private static final Set DEFAULT_SUPER_METHODS = new HashSet<>(); @@ -210,9 +210,9 @@ private static final String ALLOW_ANY_PATTERN = ".*"; static { - DEFAULT_SUPER_METHODS.add("HEAD"); - DEFAULT_SUPER_METHODS.add("OPTIONS"); - DEFAULT_SUPER_METHODS.add("TRACE"); + DEFAULT_SUPER_METHODS.add(Method.HEAD); + DEFAULT_SUPER_METHODS.add(Method.OPTIONS); + DEFAULT_SUPER_METHODS.add(Method.TRACE); if (JrePlatform.IS_WINDOWS) { DEFAULT_CMD_LINE_ARGUMENTS_DECODED_PATTERN = Pattern.compile("[\\w\\Q-.\\/:\\E]+"); @@ -240,9 +240,11 @@ private String parameterEncoding = System.getProperty("file.encoding", "UTF-8"); /* The HTTP methods this Servlet will pass to the CGI script */ - private Set cgiMethods = new HashSet<>(); + private final Set cgiMethods = new HashSet<>(); private boolean cgiMethodsAll = false; + private transient WebResourceRoot resources = null; + /** * The time (in milliseconds) to wait for the reading of stderr to complete before terminating the CGI process. @@ -270,7 +272,7 @@ private boolean enableCmdLineArguments = false; /** - * Limits the encoded form of individual command line arguments. By default values are limited to those allowed by + * Limits the encoded form of individual command line arguments. By default, values are limited to those allowed by * the RFC. See https://tools.ietf.org/html/rfc3875#section-4.4 Uses \Q...\E to avoid individual quoting. */ private Pattern cmdLineArgumentsEncodedPattern = Pattern.compile("[\\w\\Q%;/?:@&,$-.!~*'()\\E]+"); @@ -283,9 +285,6 @@ /** * Sets instance variables. - *

      - * Modified from Craig R. McClanahan's InvokerServlet - *

      * * @param config a ServletConfig object containing the servlet's configuration and initialization * parameters @@ -362,8 +361,8 @@ } } } else { - cgiMethods.add("GET"); - cgiMethods.add("POST"); + cgiMethods.add(Method.GET); + cgiMethods.add(Method.POST); } if (getServletConfig().getInitParameter("cmdLineArgumentsEncoded") != null) { @@ -378,20 +377,22 @@ } else if (value != null) { cmdLineArgumentsDecodedPattern = Pattern.compile(value); } + + // Load the web resources + resources = (WebResourceRoot) getServletContext().getAttribute(Globals.RESOURCES_ATTR); + + if (resources == null) { + throw new UnavailableException(sm.getString("cgiServlet.noResources")); + } } /** * Logs important Servlet API and container information. - *

      - * Based on SnoopAllServlet by Craig R. McClanahan - *

      * * @param req HttpServletRequest object used as source of information - * - * @exception IOException if a write operation exception occurs */ - private void printServletEnvironment(HttpServletRequest req) throws IOException { + private void printServletEnvironment(HttpServletRequest req) { // Document the properties from ServletRequest log.trace("ServletRequest Properties"); @@ -427,7 +428,7 @@ log.trace("HttpServletRequest Properties"); log.trace("Auth Type: [" + req.getAuthType() + "]"); log.trace("Context Path: [" + req.getContextPath() + "]"); - Cookie cookies[] = req.getCookies(); + Cookie[] cookies = req.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { log.trace("Cookie: " + cookie.getName() + ": [" + cookie.getValue() + "]"); @@ -543,7 +544,7 @@ CGIRunner cgi = new CGIRunner(cgiEnv.getCommand(), cgiEnv.getEnvironment(), cgiEnv.getWorkingDirectory(), cgiEnv.getParameters()); - if ("POST".equals(req.getMethod())) { + if (Method.POST.equals(req.getMethod())) { cgi.setInput(req.getInputStream()); } cgi.setResponse(res); @@ -566,7 +567,7 @@ @Override protected void doOptions(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // Note: This method will never be called if cgiMethods is "*" so that - // case does nto need to be handled here. + // case does not need to be handled here. Set allowedMethods = new HashSet<>(); allowedMethods.addAll(cgiMethods); allowedMethods.addAll(DEFAULT_SUPER_METHODS); @@ -623,9 +624,6 @@ /** pathInfo for the current request */ private String pathInfo = null; - /** real file system directory of the enclosing servlet's web app */ - private String webAppRootDir = null; - /** tempdir for context - used to expand scripts in unexpanded wars */ private File tmpDir = null; @@ -679,7 +677,6 @@ */ protected void setupFromContext(ServletContext context) { this.context = context; - this.webAppRootDir = context.getRealPath("/"); this.tmpDir = (File) context.getAttribute(ServletContext.TEMPDIR); } @@ -695,12 +692,9 @@ */ protected boolean setupFromRequest(HttpServletRequest req) throws UnsupportedEncodingException { - boolean isIncluded = false; + boolean isIncluded = req.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null; // Look to see if this request is an include - if (req.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI) != null) { - isIncluded = true; - } if (isIncluded) { this.contextPath = (String) req.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH); this.servletPath = (String) req.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); @@ -720,8 +714,8 @@ // does not contain an unencoded "=" this is an indexed query. // The parsed query string becomes the command line parameters // for the cgi command. - if (enableCmdLineArguments && (req.getMethod().equals("GET") || req.getMethod().equals("POST") || - req.getMethod().equals("HEAD"))) { + if (enableCmdLineArguments && (Method.GET.equals(req.getMethod()) || Method.POST.equals(req.getMethod()) || + Method.HEAD.equals(req.getMethod()))) { String qs; if (isIncluded) { qs = (String) req.getAttribute(RequestDispatcher.INCLUDE_QUERY_STRING); @@ -786,10 +780,9 @@ * cgiPathPrefix is defined by setting this servlet's cgiPathPrefix init parameter *

      * - * @param pathInfo String from HttpServletRequest.getPathInfo() - * @param webAppRootDir String from context.getRealPath("/") * @param contextPath String as from HttpServletRequest.getContextPath() * @param servletPath String as from HttpServletRequest.getServletPath() + * @param pathInfo String from HttpServletRequest.getPathInfo() * @param cgiPathPrefix subdirectory of webAppRootDir below which the web app's CGIs may be stored; can be null. * The CGI search path will start at webAppRootDir + File.separator + cgiPathPrefix (or * webAppRootDir alone if cgiPathPrefix is null). cgiPathPrefix is defined by setting @@ -806,59 +799,104 @@ * found * */ - protected String[] findCGI(String pathInfo, String webAppRootDir, String contextPath, String servletPath, - String cgiPathPrefix) { - String path = null; - String name = null; - String scriptname = null; + protected String[] findCGI(String contextPath, String servletPath, String pathInfo, String cgiPathPrefix) { - if (webAppRootDir.lastIndexOf(File.separator) == (webAppRootDir.length() - 1)) { - // strip the trailing "/" from the webAppRootDir - webAppRootDir = webAppRootDir.substring(0, (webAppRootDir.length() - 1)); - } + StringBuilder cgiPath = new StringBuilder(); + StringBuilder urlPath = new StringBuilder(); - if (cgiPathPrefix != null) { - webAppRootDir = webAppRootDir + File.separator + cgiPathPrefix; - } + WebResource cgiScript = null; - if (log.isTraceEnabled()) { - log.trace(sm.getString("cgiServlet.find.path", pathInfo, webAppRootDir)); + if (cgiPathPrefix == null || cgiPathPrefix.isEmpty()) { + cgiPath.append(servletPath); + } else { + cgiPath.append('/'); + cgiPath.append(cgiPathPrefix); } + urlPath.append(servletPath); - File currentLocation = new File(webAppRootDir); - StringTokenizer dirWalker = new StringTokenizer(pathInfo, "/"); - if (log.isTraceEnabled()) { - log.trace(sm.getString("cgiServlet.find.location", currentLocation.getAbsolutePath())); - } - StringBuilder cginameBuilder = new StringBuilder(); - while (!currentLocation.isFile() && dirWalker.hasMoreElements()) { - String nextElement = (String) dirWalker.nextElement(); - currentLocation = new File(currentLocation, nextElement); - cginameBuilder.append('/').append(nextElement); + StringTokenizer pathWalker = new StringTokenizer(pathInfo, "/"); + + while (pathWalker.hasMoreElements() && (cgiScript == null || !cgiScript.isFile())) { + String urlSegment = pathWalker.nextToken(); + cgiPath.append('/'); + cgiPath.append(urlSegment); + urlPath.append('/'); + urlPath.append(urlSegment); if (log.isTraceEnabled()) { - log.trace(sm.getString("cgiServlet.find.location", currentLocation.getAbsolutePath())); + log.trace(sm.getString("cgiServlet.find.location", cgiPath.toString())); } + cgiScript = resources.getResource(cgiPath.toString()); } - String cginame = cginameBuilder.toString(); - if (!currentLocation.isFile()) { + + // No script was found + if (cgiScript == null || !cgiScript.isFile()) { return new String[] { null, null, null, null }; } - path = currentLocation.getAbsolutePath(); - name = currentLocation.getName(); + // Set-up return values + String path = null; + String scriptName = null; + String cgiName = null; + String name = null; + + path = cgiScript.getCanonicalPath(); + if (path == null) { + /* + * The script doesn't exist directly on the file system. It might be located in an archive or similar. + * Such scripts are extracted to the web application's temporary file location. + */ + File tmpCgiFile = new File(tmpDir + cgiPath.toString()); + if (!tmpCgiFile.exists()) { - if (servletPath.startsWith(cginame)) { - scriptname = contextPath + cginame; - } else { - scriptname = contextPath + servletPath + cginame; + // Create directories + File parent = tmpCgiFile.getParentFile(); + if (!parent.mkdirs() && !parent.isDirectory()) { + log.warn(sm.getString("cgiServlet.expandCreateDirFail", parent.getAbsolutePath())); + return new String[] { null, null, null, null }; + } + + try (InputStream is = cgiScript.getInputStream()) { + synchronized (expandFileLock) { + // Check if file was created by concurrent request + if (!tmpCgiFile.exists()) { + try { + Files.copy(is, tmpCgiFile.toPath()); + } catch (IOException ioe) { + log.warn(sm.getString("cgiServlet.expandFail", cgiScript.getURL(), + tmpCgiFile.getAbsolutePath()), ioe); + if (tmpCgiFile.exists()) { + if (!tmpCgiFile.delete()) { + log.warn(sm.getString("cgiServlet.expandDeleteFail", + tmpCgiFile.getAbsolutePath())); + } + } + return new String[] { null, null, null, null }; + } + if (log.isDebugEnabled()) { + log.debug(sm.getString("cgiServlet.expandOk", cgiScript.getURL(), + tmpCgiFile.getAbsolutePath())); + } + } + } + } catch (IOException ioe) { + log.warn(sm.getString("cgiServlet.expandCloseFail", cgiScript.getURL()), ioe); + } + } + path = tmpCgiFile.getAbsolutePath(); } + scriptName = urlPath.toString(); + cgiName = scriptName.substring(servletPath.length()); + name = scriptName.substring(scriptName.lastIndexOf('/') + 1); + if (log.isTraceEnabled()) { - log.trace(sm.getString("cgiServlet.find.found", name, path, scriptname, cginame)); + log.trace(sm.getString("cgiServlet.find.found", name, path, scriptName, cgiName)); } - return new String[] { path, scriptname, cginame, name }; + + return new String[] { path, scriptName, cgiName, name }; } + /** * Constructs the CGI environment to be supplied to the invoked CGI script; relies heavily on Servlet API * methods and findCGI @@ -880,26 +918,20 @@ Map envp = new HashMap<>(shellEnv); // Add the CGI environment variables - String sPathInfoOrig = null; - String sPathInfoCGI = null; + String sPathInfoOrig; + String sPathInfoCGI; String sPathTranslatedCGI = null; - String sCGIFullPath = null; - String sCGIScriptName = null; - String sCGIFullName = null; - String sCGIName = null; + String sCGIFullPath; + String sCGIScriptName; + String sCGIFullName; + String sCGIName; String[] sCGINames; sPathInfoOrig = this.pathInfo; sPathInfoOrig = sPathInfoOrig == null ? "" : sPathInfoOrig; - if (webAppRootDir == null) { - // The app has not been deployed in exploded form - webAppRootDir = tmpDir.toString(); - expandCGIScript(); - } - - sCGINames = findCGI(sPathInfoOrig, webAppRootDir, contextPath, servletPath, cgiPathPrefix); + sCGINames = findCGI(contextPath, servletPath, sPathInfoOrig, cgiPathPrefix); sCGIFullPath = sCGINames[0]; sCGIScriptName = sCGINames[1]; @@ -938,7 +970,7 @@ * (see method findCGI, where the real work is done) * */ - if (pathInfo == null || (pathInfo.substring(sCGIFullName.length()).length() <= 0)) { + if (pathInfo == null || (pathInfo.substring(sCGIFullName.length()).isEmpty())) { sPathInfoCGI = ""; } else { sPathInfoCGI = pathInfo.substring(sCGIFullName.length()); @@ -960,7 +992,7 @@ * path_translated = servletContext.getRealPath("/trans1/trans2") * * That is, PATH_TRANSLATED = webAppRootDir + sPathInfoCGI - * (unless sPathInfoCGI is null or blank, then the CGI + * (unless sPathInfoCGI is null or blank), then the CGI * specification dictates that the PATH_TRANSLATED metavariable * SHOULD NOT be defined. * @@ -968,7 +1000,7 @@ if (!sPathInfoCGI.isEmpty()) { sPathTranslatedCGI = context.getRealPath(sPathInfoCGI); } - if (sPathTranslatedCGI == null || "".equals(sPathTranslatedCGI)) { + if (sPathTranslatedCGI == null || sPathTranslatedCGI.isEmpty()) { // NOOP } else { envp.put("PATH_TRANSLATED", nullsToBlanks(sPathTranslatedCGI)); @@ -1002,9 +1034,8 @@ Enumeration headers = req.getHeaderNames(); - String header = null; + String header; while (headers.hasMoreElements()) { - header = null; header = headers.nextElement().toUpperCase(Locale.ENGLISH); // REMIND: rewrite multiple headers as if received as single // REMIND: change character set @@ -1027,93 +1058,6 @@ } /** - * Extracts requested resource from web app archive to context work directory to enable CGI script to be - * executed. - */ - protected void expandCGIScript() { - StringBuilder srcPath = new StringBuilder(); - StringBuilder destPath = new StringBuilder(); - InputStream is = null; - - // paths depend on mapping - if (cgiPathPrefix == null) { - srcPath.append(pathInfo); - is = context.getResourceAsStream(srcPath.toString()); - destPath.append(tmpDir); - destPath.append(pathInfo); - } else { - // essentially same search algorithm as findCGI() - srcPath.append(cgiPathPrefix); - StringTokenizer pathWalker = new StringTokenizer(pathInfo, "/"); - // start with first element - while (pathWalker.hasMoreElements() && (is == null)) { - srcPath.append('/'); - srcPath.append(pathWalker.nextElement()); - is = context.getResourceAsStream(srcPath.toString()); - } - destPath.append(tmpDir); - destPath.append('/'); - destPath.append(srcPath); - } - - if (is == null) { - // didn't find anything, give up now - log.warn(sm.getString("cgiServlet.expandNotFound", srcPath)); - return; - } - - try { - File f = new File(destPath.toString()); - if (f.exists()) { - // Don't need to expand if it already exists - return; - } - - // create directories - File dir = f.getParentFile(); - if (!dir.mkdirs() && !dir.isDirectory()) { - log.warn(sm.getString("cgiServlet.expandCreateDirFail", dir.getAbsolutePath())); - return; - } - - try { - synchronized (expandFileLock) { - // make sure file doesn't exist - if (f.exists()) { - return; - } - - // create file - if (!f.createNewFile()) { - return; - } - - Files.copy(is, f.toPath()); - - if (log.isDebugEnabled()) { - log.debug(sm.getString("cgiServlet.expandOk", srcPath, destPath)); - } - } - } catch (IOException ioe) { - log.warn(sm.getString("cgiServlet.expandFail", srcPath, destPath), ioe); - // delete in case file is corrupted - if (f.exists()) { - if (!f.delete()) { - log.warn(sm.getString("cgiServlet.expandDeleteFail", f.getAbsolutePath())); - } - } - } - } finally { - try { - is.close(); - } catch (IOException e) { - log.warn(sm.getString("cgiServlet.expandCloseFail", srcPath), e); - } - } - } - - - /** * Returns important CGI environment information in a multi-line text format. * * @return CGI environment info @@ -1332,11 +1276,7 @@ * Checks and sets ready status */ protected void updateReadyStatus() { - if (command != null && env != null && wd != null && params != null && response != null) { - readyToRun = true; - } else { - readyToRun = false; - } + readyToRun = command != null && env != null && wd != null && params != null && response != null; } @@ -1410,11 +1350,11 @@ *
    • Allowed characters in pathInfo: This implementation does not allow ASCII NUL nor any character * which cannot be URL-encoded according to internet standards; *
    • Allowed characters in path segments: This implementation does not allow non-terminal NULL segments - * in the the path -- IOExceptions may be thrown; + * in the path -- IOExceptions may be thrown; *
    • "." and ".." path segments: This implementation does not allow - * "." and ".." in the the path, and such characters will result in an IOException - * being thrown (this should never happen since Tomcat normalises the requestURI before determining the - * contextPath, servletPath and pathInfo); + * "." and ".." in the path, and such characters will result in an IOException being + * thrown (this should never happen since Tomcat normalises the requestURI before determining the contextPath, + * servletPath and pathInfo); *
    • Implementation limitations: This implementation does not impose any limitations except as * documented above. This implementation may be limited by the servlet container used to house this * implementation. In particular, all the primary CGI variable values are derived either directly or indirectly @@ -1424,7 +1364,7 @@ * * @exception IOException if problems during reading/writing occur * - * @see java.lang.Runtime#exec(String command, String[] envp, File dir) + * @see java.lang.Runtime#exec(String[] command, String[] envp, File dir) */ protected void run() throws IOException { @@ -1445,21 +1385,17 @@ throw new IOException(sm.getString("cgiServlet.invalidCommand", command)); } - /* - * original content/structure of this section taken from - * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4216884 with major modifications by Martin Dengler - */ - Runtime rt = null; + Runtime rt; BufferedReader cgiHeaderReader = null; InputStream cgiOutput = null; - BufferedReader commandsStdErr = null; + BufferedReader commandsStdErr; Thread errReaderThread = null; - BufferedOutputStream commandsStdIn = null; + BufferedOutputStream commandsStdIn; Process proc = null; int bufRead = -1; List cmdAndArgs = new ArrayList<>(); - if (cgiExecutable.length() != 0) { + if (!cgiExecutable.isEmpty()) { cmdAndArgs.add(cgiExecutable); } if (cgiExecutableArgs != null) { @@ -1505,7 +1441,7 @@ while (isRunning) { try { // set headers - String line = null; + String line; while (((line = cgiHeaderReader.readLine()) != null) && !line.isEmpty()) { if (log.isTraceEnabled()) { log.trace("addHeader(\"" + line + "\")"); @@ -1562,9 +1498,9 @@ } } // replacement for Process.waitFor() - } catch (IOException e) { - log.warn(sm.getString("cgiServlet.runFail"), e); - throw e; + } catch (IOException ioe) { + log.warn(sm.getString("cgiServlet.runFail"), ioe); + throw ioe; } finally { // Close the header reader if (cgiHeaderReader != null) { @@ -1587,12 +1523,11 @@ try { errReaderThread.join(stderrTimeout); } catch (InterruptedException e) { - log.warn(sm.getString("cgiServlet.runReaderInterrupt")); + log.warn(sm.getString("cgiServlet.runReaderInterrupt"), e); } } if (proc != null) { proc.destroy(); - proc = null; } } } @@ -1659,20 +1594,20 @@ } private void sendToLog(BufferedReader rdr) { - String line = null; + String line; int lineCount = 0; try { while ((line = rdr.readLine()) != null) { log.warn(sm.getString("cgiServlet.runStdErr", line)); lineCount++; } - } catch (IOException e) { - log.warn(sm.getString("cgiServlet.runStdErrFail"), e); + } catch (IOException ioe) { + log.warn(sm.getString("cgiServlet.runStdErrFail"), ioe); } finally { try { rdr.close(); - } catch (IOException e) { - log.warn(sm.getString("cgiServlet.runStdErrFail"), e); + } catch (IOException ioe) { + log.warn(sm.getString("cgiServlet.runStdErrFail"), ioe); } } if (lineCount > 0) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/DataSourcePropertyStore.java tomcat10-10.1.52/java/org/apache/catalina/servlets/DataSourcePropertyStore.java --- tomcat10-10.1.34/java/org/apache/catalina/servlets/DataSourcePropertyStore.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/DataSourcePropertyStore.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,53 +41,82 @@ import org.w3c.dom.Node; /** - * WebDAV dead properties storage backed by a DataSource. Usually table and column names - * are configurable, but for simplicity this is not the case. - * The schema is: - * table properties ( path, namespace, name, node ) - * path: the resource path - * namespace: the node namespace - * name: the local name in the namespace - * node: the full serialized XML node including the name + * WebDAV dead properties storage using a DataSource. + *

      + * A single properties table with four columns is used: + *

        + *
      • path: the resource path
      • + *
      • namespace: the node namespace
      • + *
      • name: the local name in the namespace
      • + *
      • node: the full serialized XML node including the name
      • + *
      + * The table name used can be configured using the tableName property of the store. + *

      + * Example table schema: CREATE TABLE properties ( + * path VARCHAR(1024) NOT NULL, + * namespace VARCHAR(64) NOT NULL, + * name VARCHAR(64) NOT NULL, + * node VARCHAR(2048) NOT NULL, + * PRIMARY KEY (path, namespace, name) + * ) */ public class DataSourcePropertyStore implements WebdavServlet.PropertyStore { protected static final StringManager sm = StringManager.getManager(DataSourcePropertyStore.class); private final Log log = LogFactory.getLog(DataSourcePropertyStore.class); - private static String ADD_PROPERTY_STMT = "INSERT INTO properties (path, namespace, name, node) VALUES (?, ?, ?, ?)"; - private static String SET_PROPERTY_STMT = "UPDATE properties SET node = ? WHERE path = ? AND namespace = ? AND name = ?"; - private static String REMOVE_ALL_PROPERTIES_STMT = "DELETE FROM properties WHERE path = ?"; - private static String REMOVE_PROPERTY_STMT = "DELETE FROM properties WHERE path = ? AND namespace = ? AND name = ?"; - private static String GET_PROPERTY_STMT = "SELECT node FROM properties WHERE path = ? AND namespace = ? AND name = ?"; - private static String GET_PROPERTIES_NAMES_STMT = "SELECT namespace, name FROM properties WHERE path = ?"; - private static String GET_PROPERTIES_STMT = "SELECT namespace, name, node FROM properties WHERE path = ?"; - private static String GET_PROPERTIES_NODES_STMT = "SELECT node FROM properties WHERE path = ?"; - /** * DataSource JNDI name, will be prefixed with java:comp/env for the lookup. */ private String dataSourceName = "WebdavPropertyStore"; + /** + * Table name. + */ + private String tableName = "properties"; + + private String addPropertyStatement; + private String setPropertyStatement; + private String removeAllPropertiesStatement; + private String removePropertyStatement; + private String getPropertyStatement; + private String getPropertiesNameStatement; + private String getPropertiesStatement; + private String getPropertiesNodeStatement; + private final ReentrantReadWriteLock dbLock = new ReentrantReadWriteLock(); private final Lock dbReadLock = dbLock.readLock(); private final Lock dbWriteLock = dbLock.writeLock(); /** - * @return the dataSourceName + * @return the DataSource JNDI name, will be prefixed with java:comp/env for the lookup. */ public String getDataSourceName() { return this.dataSourceName; } /** - * @param dataSourceName the dataSourceName to set + * @param dataSourceName the DataSource JNDI name, will be prefixed with java:comp/env for the lookup. */ public void setDataSourceName(String dataSourceName) { this.dataSourceName = dataSourceName; } /** + * @return the table name that will be used in the database + */ + public String getTableName() { + return this.tableName; + } + + /** + * @param tableName the table name to use in the database + */ + public void setTableName(String tableName) { + this.tableName = tableName; + } + + /** * DataSource instance being used. */ protected DataSource dataSource = null; @@ -98,9 +127,18 @@ try { dataSource = (DataSource) ((new InitialContext()).lookup("java:comp/env/" + dataSourceName)); } catch (NamingException e) { - throw new IllegalArgumentException(sm.getString("webdavservlet.dataSourceStore.noDataSource", dataSourceName), e); + throw new IllegalArgumentException( + sm.getString("webdavservlet.dataSourceStore.noDataSource", dataSourceName), e); } } + addPropertyStatement = "INSERT INTO " + tableName + " (path, namespace, name, node) VALUES (?, ?, ?, ?)"; + setPropertyStatement = "UPDATE " + tableName + " SET node = ? WHERE path = ? AND namespace = ? AND name = ?"; + removeAllPropertiesStatement = "DELETE FROM " + tableName + " WHERE path = ?"; + removePropertyStatement = "DELETE FROM " + tableName + " WHERE path = ? AND namespace = ? AND name = ?"; + getPropertyStatement = "SELECT node FROM " + tableName + " WHERE path = ? AND namespace = ? AND name = ?"; + getPropertiesNameStatement = "SELECT namespace, name FROM " + tableName + " WHERE path = ?"; + getPropertiesStatement = "SELECT namespace, name, node FROM " + tableName + " WHERE path = ?"; + getPropertiesNodeStatement = "SELECT node FROM " + tableName + " WHERE path = ?"; } @Override @@ -118,7 +156,7 @@ } dbWriteLock.lock(); try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(GET_PROPERTIES_STMT)) { + PreparedStatement statement = connection.prepareStatement(getPropertiesStatement)) { statement.setString(1, source); if (statement.execute()) { ResultSet rs = statement.getResultSet(); @@ -127,7 +165,7 @@ String name = rs.getString(2); String node = rs.getString(3); boolean found = false; - try (PreparedStatement statement2 = connection.prepareStatement(GET_PROPERTY_STMT)) { + try (PreparedStatement statement2 = connection.prepareStatement(getPropertyStatement)) { statement2.setString(1, destination); statement2.setString(2, namespace); statement2.setString(3, name); @@ -139,7 +177,7 @@ } } if (found) { - try (PreparedStatement statement2 = connection.prepareStatement(SET_PROPERTY_STMT)) { + try (PreparedStatement statement2 = connection.prepareStatement(setPropertyStatement)) { statement2.setString(1, node); statement2.setString(2, destination); statement2.setString(3, namespace); @@ -147,7 +185,7 @@ statement2.execute(); } } else { - try (PreparedStatement statement2 = connection.prepareStatement(ADD_PROPERTY_STMT)) { + try (PreparedStatement statement2 = connection.prepareStatement(addPropertyStatement)) { statement2.setString(1, destination); statement2.setString(2, namespace); statement2.setString(3, name); @@ -171,7 +209,7 @@ } dbWriteLock.lock(); try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(REMOVE_ALL_PROPERTIES_STMT)) { + PreparedStatement statement = connection.prepareStatement(removeAllPropertiesStatement)) { statement.setString(1, resource); statement.execute(); } catch (SQLException e) { @@ -190,7 +228,7 @@ // Add the names of all properties dbReadLock.lock(); try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(GET_PROPERTIES_NAMES_STMT)) { + PreparedStatement statement = connection.prepareStatement(getPropertiesNameStatement)) { statement.setString(1, resource); if (statement.execute()) { ResultSet rs = statement.getResultSet(); @@ -209,7 +247,7 @@ // Add a single property dbReadLock.lock(); try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(GET_PROPERTY_STMT)) { + PreparedStatement statement = connection.prepareStatement(getPropertyStatement)) { statement.setString(1, resource); statement.setString(2, property.getNamespaceURI()); statement.setString(3, property.getLocalName()); @@ -230,7 +268,7 @@ // Add all properties dbReadLock.lock(); try (Connection connection = dataSource.getConnection(); - PreparedStatement statement = connection.prepareStatement(GET_PROPERTIES_NODES_STMT)) { + PreparedStatement statement = connection.prepareStatement(getPropertiesNodeStatement)) { statement.setString(1, resource); if (statement.execute()) { ResultSet rs = statement.getResultSet(); @@ -284,7 +322,7 @@ String serializedNode = strWriter.toString(); boolean found = false; try { - try (PreparedStatement statement = connection.prepareStatement(GET_PROPERTY_STMT)) { + try (PreparedStatement statement = connection.prepareStatement(getPropertyStatement)) { statement.setString(1, resource); statement.setString(2, node.getNamespaceURI()); statement.setString(3, node.getLocalName()); @@ -296,7 +334,7 @@ } } if (found) { - try (PreparedStatement statement = connection.prepareStatement(SET_PROPERTY_STMT)) { + try (PreparedStatement statement = connection.prepareStatement(setPropertyStatement)) { statement.setString(1, serializedNode); statement.setString(2, resource); statement.setString(3, node.getNamespaceURI()); @@ -304,7 +342,7 @@ statement.execute(); } } else { - try (PreparedStatement statement = connection.prepareStatement(ADD_PROPERTY_STMT)) { + try (PreparedStatement statement = connection.prepareStatement(addPropertyStatement)) { statement.setString(1, resource); statement.setString(2, node.getNamespaceURI()); statement.setString(3, node.getLocalName()); @@ -320,7 +358,7 @@ } if (operation.getUpdateType() == PropertyUpdateType.REMOVE) { Node node = operation.getPropertyNode(); - try (PreparedStatement statement = connection.prepareStatement(REMOVE_PROPERTY_STMT)) { + try (PreparedStatement statement = connection.prepareStatement(removePropertyStatement)) { statement.setString(1, resource); statement.setString(2, node.getNamespaceURI()); statement.setString(3, node.getLocalName()); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/DefaultServlet.java tomcat10-10.1.52/java/org/apache/catalina/servlets/DefaultServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/servlets/DefaultServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/DefaultServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -79,6 +79,7 @@ import org.apache.catalina.webresources.CachedResource; import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.ResponseUtil; import org.apache.tomcat.util.http.parser.ContentRange; import org.apache.tomcat.util.http.parser.EntityTag; @@ -136,9 +137,6 @@ * Then a request to /context/static/images/tomcat.jpg will succeed while a request to * /context/images/tomcat2.jpg will fail. *

      - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class DefaultServlet extends HttpServlet { @@ -294,6 +292,13 @@ */ private boolean useStrongETags = false; + /** + * Will direct ({@link DispatcherType#REQUEST} or {@link DispatcherType#ASYNC}) requests using the POST method be + * processed as GET requests. If not allowed, direct requests using the POST method will be rejected with a 405 + * (method not allowed). + */ + private boolean allowPostAsGet = true; + // --------------------------------------------------------- Public Methods @@ -418,6 +423,9 @@ useStrongETags = Boolean.parseBoolean(getServletConfig().getInitParameter("useStrongETags")); } + if (getServletConfig().getInitParameter("allowPostAsGet") != null) { + allowPostAsGet = Boolean.parseBoolean(getServletConfig().getInitParameter("allowPostAsGet")); + } } private CompressionFormat[] parseCompressionFormats(String precompressed, String gzip) { @@ -476,7 +484,7 @@ } StringBuilder result = new StringBuilder(); - if (servletPath.length() > 0) { + if (!servletPath.isEmpty()) { result.append(servletPath); } if (pathInfo != null) { @@ -503,6 +511,16 @@ } + protected boolean isListings() { + return listings; + } + + + protected boolean isReadOnly() { + return readOnly || resources.isReadOnly(); + } + + @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { @@ -562,10 +580,14 @@ StringBuilder allow = new StringBuilder(); // Start with methods that are always allowed - allow.append("OPTIONS, GET, HEAD, POST"); + allow.append("OPTIONS, GET, HEAD"); + + if (allowPostAsGet) { + allow.append(", POST"); + } // PUT and DELETE depend on readonly - if (!readOnly) { + if (!isReadOnly()) { allow.append(", PUT, DELETE"); } @@ -587,14 +609,39 @@ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - doGet(request, response); + if (allowPostAsGet) { + doGet(request, response); + } else { + // Use a switch without a default to ensure all possibilities are explicitly handled + switch (request.getDispatcherType()) { + case ASYNC: + case REQUEST: { + // Direct POST requests may not be processed as GET + sendNotAllowed(request, response); + break; + } + case ERROR: + case FORWARD: + case INCLUDE: { + /* + * Forward and Include are processed as GET as it is possible that a POST to a servlet may use a + * forward or an include as part of generating the response. + * + * Error should have already been converted to GET but convert here anyway as that is better than + * failing the request. + */ + doGet(request, response); + break; + } + } + } } @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (readOnly) { + if (isReadOnly()) { sendNotAllowed(req, resp); return; } @@ -610,8 +657,12 @@ return; } - InputStream resourceInputStream = null; + if (!checkIfHeaders(req, resp, resource)) { + return; + } + InputStream resourceInputStream = null; + File tempContentFile = null; try { // Append data specified in ranges to existing content for this // resource - create a temp. file on the local filesystem to @@ -620,11 +671,13 @@ if (range == IGNORE) { resourceInputStream = req.getInputStream(); } else { - File contentFile = executePartialPut(req, range, path); - resourceInputStream = new FileInputStream(contentFile); + tempContentFile = executePartialPut(req, range, path); + if (tempContentFile != null) { + resourceInputStream = new FileInputStream(tempContentFile); + } } - if (resources.write(path, resourceInputStream, true)) { + if (resourceInputStream != null && resources.write(path, resourceInputStream, true)) { if (resource.exists()) { resp.setStatus(HttpServletResponse.SC_NO_CONTENT); } else { @@ -632,7 +685,8 @@ } } else { try { - resp.sendError(HttpServletResponse.SC_CONFLICT); + resp.sendError(resourceInputStream != null ? HttpServletResponse.SC_CONFLICT : + HttpServletResponse.SC_BAD_REQUEST); } catch (IllegalStateException e) { // Already committed, ignore } @@ -641,10 +695,15 @@ if (resourceInputStream != null) { try { resourceInputStream.close(); - } catch (IOException ioe) { + } catch (IOException ignore) { // Ignore } } + if (tempContentFile != null) { + if (!tempContentFile.delete()) { + log(sm.getString("defaultServlet.deleteTempFileFailed", tempContentFile.getAbsolutePath())); + } + } } } @@ -667,13 +726,7 @@ // resource - create a temp. file on the local filesystem to // perform this operation File tempDir = (File) getServletContext().getAttribute(ServletContext.TEMPDIR); - // Convert all '/' characters to '.' in resourcePath - String convertedResourcePath = path.replace('/', '.'); - File contentFile = new File(tempDir, convertedResourcePath); - if (contentFile.createNewFile()) { - // Clean up contentFile when Tomcat is terminated - contentFile.deleteOnExit(); - } + File contentFile = File.createTempFile("put-part-", null, tempDir); try (RandomAccessFile randAccessContentFile = new RandomAccessFile(contentFile, "rw")) { @@ -697,13 +750,31 @@ // Append data in request input stream to contentFile randAccessContentFile.seek(range.getStart()); + long received = 0; int numBytesRead; byte[] transferBuffer = new byte[BUFFER_SIZE]; try (BufferedInputStream requestBufInStream = new BufferedInputStream(req.getInputStream(), BUFFER_SIZE)) { + long rangeBytes = range.getEnd() - range.getStart() + 1L; while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) { + received += numBytesRead; + if (received > rangeBytes) { + throw new IllegalStateException(sm.getString("defaultServlet.wrongByteCountForRange", + String.valueOf(received), String.valueOf(rangeBytes))); + } randAccessContentFile.write(transferBuffer, 0, numBytesRead); } + if (received < rangeBytes) { + throw new IllegalStateException(sm.getString("defaultServlet.wrongByteCountForRange", + String.valueOf(received), String.valueOf(rangeBytes))); + } } + + } catch (IOException | RuntimeException | Error e) { + // This has to be done this way to be able to close the file without changing the method signature + if (!contentFile.delete()) { + log(sm.getString("defaultServlet.deleteTempFileFailed", contentFile.getAbsolutePath())); + } + return null; } return contentFile; @@ -713,7 +784,7 @@ @Override protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - if (readOnly) { + if (isReadOnly()) { sendNotAllowed(req, resp); return; } @@ -722,16 +793,19 @@ WebResource resource = resources.getResource(path); + if (!checkIfHeaders(req, resp, resource)) { + return; + } + if (resource.exists()) { if (resource.delete()) { resp.setStatus(HttpServletResponse.SC_NO_CONTENT); } else { - resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + sendNotAllowed(req, resp); } } else { resp.sendError(HttpServletResponse.SC_NOT_FOUND); } - } @@ -749,10 +823,21 @@ */ protected boolean checkIfHeaders(HttpServletRequest request, HttpServletResponse response, WebResource resource) throws IOException { - - return checkIfMatch(request, response, resource) && checkIfModifiedSince(request, response, resource) && - checkIfNoneMatch(request, response, resource) && checkIfUnmodifiedSince(request, response, resource); - + if (request.getHeader("If-Match") != null) { + if (!checkIfMatch(request, response, resource)) { + return false; + } + } else if (request.getHeader("If-Unmodified-Since") != null) { + if (!checkIfUnmodifiedSince(request, response, resource)) { + return false; + } + } + if (request.getHeader("If-None-Match") != null) { + return checkIfNoneMatch(request, response, resource); + } else if (request.getHeader("If-Modified-Since") != null) { + return checkIfModifiedSince(request, response, resource); + } + return true; } @@ -796,7 +881,7 @@ } } - if (path.length() == 0) { + if (path.isEmpty()) { // Context root redirect doDirectoryRedirect(request, response); return; @@ -820,6 +905,10 @@ if (isError) { response.sendError(((Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE)).intValue()); } else { + // Need to check If headers before we return a 404 + if (!checkIfHeaders(request, response, resource)) { + return; + } response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("defaultServlet.missingResource", requestUri)); } @@ -848,15 +937,6 @@ } boolean included = false; - // Check if the conditions specified in the optional If headers are - // satisfied. - if (resource.isFile()) { - // Checking If headers - included = (request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH) != null); - if (!included && !isError && !checkIfHeaders(request, response, resource)) { - return; - } - } // Find content type. String contentType = resource.getMimeType(); @@ -870,11 +950,21 @@ // be needed later String eTag = null; String lastModifiedHttp = null; + if (resource.isFile() && !isError) { eTag = generateETag(resource); lastModifiedHttp = resource.getLastModifiedHttp(); } + // Check if the conditions specified in the optional If headers are + // satisfied. + if (resource.isFile()) { + // Checking If headers + included = (request.getAttribute(RequestDispatcher.INCLUDE_CONTEXT_PATH) != null); + if (!included && !isError && !checkIfHeaders(request, response, resource)) { + return; + } + } // Serve a precompressed version of the file if present boolean usingPrecompressedVersion = false; @@ -902,7 +992,7 @@ // Skip directory listings if we have been configured to // suppress them - if (!listings) { + if (!isListings()) { response.sendError(HttpServletResponse.SC_NOT_FOUND, sm.getString("defaultServlet.missingResource", request.getRequestURI())); return; @@ -1005,7 +1095,7 @@ response.setContentType(contentType); } } - if (resource.isFile() && contentLength >= 0 && (!serveContent || ostream != null)) { + if (resource.isFile() && contentLength >= 0 && (!serveContent || ostream != null || writer != null)) { if (debug > 0) { log("DefaultServlet.serveFile: contentLength=" + contentLength); } @@ -1019,8 +1109,8 @@ if (serveContent) { try { response.setBufferSize(output); - } catch (IllegalStateException e) { - // Silent catch + } catch (IllegalStateException ignore) { + // Content has already been written - this must be an include. Ignore the error and continue. } InputStream renderResult = null; if (ostream == null) { @@ -1106,7 +1196,7 @@ } else { - if ((ranges == null) || (ranges.getEntries().isEmpty())) { + if (ranges.getEntries().isEmpty()) { return; } @@ -1133,8 +1223,8 @@ if (serveContent) { try { response.setBufferSize(output); - } catch (IllegalStateException e) { - // Silent catch + } catch (IllegalStateException ignore) { + // Content has already been written - this must be an include. Ignore the error and continue. } if (ostream != null) { if (!checkSendfile(request, response, resource, contentLength, range)) { @@ -1151,7 +1241,7 @@ try { response.setBufferSize(output); } catch (IllegalStateException e) { - // Silent catch + // Content has already been written - this must be an include. Ignore the error and continue. } if (ostream != null) { copy(resource, contentLength, ostream, ranges, contentType); @@ -1188,7 +1278,7 @@ skip(is, 2, stripBom); return StandardCharsets.UTF_16BE; } - // Delay the UTF_16LE check if there are more that 2 bytes since it + // Delay the UTF_16LE check if there are more than 2 bytes since it // overlaps with UTF-32LE. if (count == 2 && b0 == 0xFF && b1 == 0xFE) { skip(is, 2, stripBom); @@ -1457,6 +1547,9 @@ /** * Parse the range header. + *

      + * The caller is required to have confirmed that the requested resource exists and is a file before calling this + * method. * * @param request The servlet request we are processing * @param response The servlet response we are creating @@ -1470,41 +1563,35 @@ protected Ranges parseRange(HttpServletRequest request, HttpServletResponse response, WebResource resource) throws IOException { - if (!"GET".equals(request.getMethod())) { + // Retrieving the range header (if any is specified) + String rangeHeader = request.getHeader("Range"); + + if (rangeHeader == null) { + // No Range header is the same as ignoring any Range header + return FULL; + } + + if (!Method.GET.equals(request.getMethod()) || !isRangeRequestsSupported()) { // RFC 9110 - Section 14.2: GET is the only method for which range handling is defined. // Otherwise MUST ignore a Range header field return FULL; } - // Checking If-Range - String headerValue = request.getHeader("If-Range"); - - if (headerValue != null) { - - long headerValueTime = (-1L); - try { - headerValueTime = request.getDateHeader("If-Range"); - } catch (IllegalArgumentException e) { - // Ignore - } - - String eTag = generateETag(resource); - long lastModified = resource.getLastModified(); - - if (headerValueTime == (-1L)) { - // If the ETag the client gave does not match the entity - // etag, then the entire entity is returned. - if (!eTag.equals(headerValue.trim())) { - return FULL; - } - } else { - // If the timestamp of the entity the client got differs from - // the last modification date of the entity, the entire entity - // is returned. - if (Math.abs(lastModified - headerValueTime) > 1000) { - return FULL; - } + // Evaluate If-Range + if (!checkIfRange(request, response, resource)) { + if (response.isCommitted()) { + /* + * Ideally, checkIfRange() would be changed to return Boolean so the three states (satisfied, + * unsatisfied and error) could each be communicated via the return value. There isn't a backwards + * compatible way to do that that doesn't involve changing the method name and there are benefits to + * retaining the consistency of the existing method name pattern. Hence, this 'trick'. For the error + * state, checkIfRange() will call response.sendError() which will commit the response which this method + * can then detect. + */ + return null; } + // No error but If-Range not satisfied + return FULL; } long fileLength = resource.getContentLength(); @@ -1515,13 +1602,6 @@ return FULL; } - // Retrieving the range header (if any is specified) - String rangeHeader = request.getHeader("Range"); - - if (rangeHeader == null) { - // No Range header is the same as ignoring any Range header - return FULL; - } Ranges ranges = Ranges.parse(new StringReader(rangeHeader)); @@ -1758,7 +1838,7 @@ String parent = directoryWebappPath.substring(0, slash); sb.append(" \u2013 \r\n"); sb.append("

    • \r\n"); sb.append("\r\n"); sb.append("\r\n"); sb.append(" * * - * + * * * * @@ -397,13 +397,13 @@ /** * @see #setInternalProxies(String) */ - private Pattern internalProxies = - Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" + - "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + - "100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + - "100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" + - "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + - "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "0:0:0:0:0:0:0:1|::1"); + private Pattern internalProxies = Pattern.compile("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" + + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + + "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + "100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" + + "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" + + "0:0:0:0:0:0:0:1|::1|" + "fe[89ab]\\p{XDigit}:.*|" + "f[cd]\\p{XDigit}{2}+:.*"); /** * @see #setProtocolHeader(String) @@ -641,21 +641,23 @@ // We know we need a DNS look up so use getCanonicalHostName() request.setRemoteHost(inetAddress.getCanonicalHostName()); } catch (UnknownHostException e) { - log.debug(sm.getString("remoteIpValve.invalidRemoteAddress", remoteIp), e); + if (log.isDebugEnabled()) { + log.debug(sm.getString("remoteIpValve.invalidRemoteAddress", remoteIp), e); + } request.setRemoteHost(remoteIp); } } else { request.setRemoteHost(remoteIp); } - if (proxiesHeaderValue.size() == 0) { + if (proxiesHeaderValue.isEmpty()) { request.getCoyoteRequest().getMimeHeaders().removeHeader(proxiesHeader); } else { String commaDelimitedListOfProxies = StringUtils.join(proxiesHeaderValue); request.getCoyoteRequest().getMimeHeaders().setValue(proxiesHeader) .setString(commaDelimitedListOfProxies); } - if (newRemoteIpHeaderValue.size() == 0) { + if (newRemoteIpHeaderValue.isEmpty()) { request.getCoyoteRequest().getMimeHeaders().removeHeader(remoteIpHeader); } else { String commaDelimitedRemoteIpHeaderValue = StringUtils.join(newRemoteIpHeaderValue); @@ -696,7 +698,7 @@ } } catch (IllegalArgumentException iae) { - log.debug(sm.getString("remoteIpValve.invalidHostHeader", hostHeaderValue, hostHeader)); + log.debug(sm.getString("remoteIpValve.invalidHostHeader", hostHeaderValue, hostHeader), iae); } } } @@ -743,13 +745,13 @@ request.setLocalPort(originalLocalPort); MimeHeaders headers = request.getCoyoteRequest().getMimeHeaders(); - if (originalProxiesHeader == null || originalProxiesHeader.length() == 0) { + if (originalProxiesHeader == null || originalProxiesHeader.isEmpty()) { headers.removeHeader(proxiesHeader); } else { headers.setValue(proxiesHeader).setString(originalProxiesHeader); } - if (originalRemoteIpHeader == null || originalRemoteIpHeader.length() == 0) { + if (originalRemoteIpHeader == null || originalRemoteIpHeader.isEmpty()) { headers.removeHeader(remoteIpHeader); } else { headers.setValue(remoteIpHeader).setString(originalRemoteIpHeader); @@ -837,7 +839,7 @@ * @param internalProxies The proxy regular expression */ public void setInternalProxies(String internalProxies) { - if (internalProxies == null || internalProxies.length() == 0) { + if (internalProxies == null || internalProxies.isEmpty()) { this.internalProxies = null; } else { this.internalProxies = Pattern.compile(internalProxies); @@ -861,7 +863,7 @@ /** *

      - * Case insensitive value of the protocol header to indicate that the incoming http request uses SSL. + * Case-insensitive value of the protocol header to indicate that the incoming http request uses SSL. *

      *

      * Default value : https @@ -884,7 +886,7 @@ * Name of the http header that holds the list of trusted proxies that has been traversed by the http request. *

      *

      - * The value of this header can be comma delimited. + * The value of this header can be comma-delimited. *

      *

      * Default value : X-Forwarded-By @@ -901,7 +903,7 @@ * Name of the http header from which the remote ip is extracted. *

      *

      - * The value of this header can be comma delimited. + * The value of this header can be comma-delimited. *

      *

      * Default value : X-Forwarded-For @@ -914,7 +916,7 @@ } /** - * Should this valve set request attributes for IP address, Hostname, protocol and port used for the request? This + * Should this valve set request attributes for IP address, Hostname, protocol and port used for the request? These * are typically used in conjunction with the {@link AccessLog} which will otherwise log the original values. * Default is true. The attributes set are: *

        @@ -943,7 +945,7 @@ * @param trustedProxies The regular expression */ public void setTrustedProxies(String trustedProxies) { - if (trustedProxies == null || trustedProxies.length() == 0) { + if (trustedProxies == null || trustedProxies.isEmpty()) { this.trustedProxies = null; } else { this.trustedProxies = Pattern.compile(trustedProxies); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/RequestFilterValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/RequestFilterValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/RequestFilterValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/RequestFilterValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,7 +41,7 @@ *
      • The subclass extracts the request property to be filtered, and calls the common process() method. *
      • If there is a deny expression configured, the property will be compared to the expression. If a match is found, * this request will be rejected with a "Forbidden" HTTP response.
      • - *
      • If there is a allow expression configured, the property will be compared to each such expression. If a match is + *
      • If there is an allow expression configured, the property will be compared to each such expression. If a match is * found, this request will be allowed to pass through to the next Valve in the current pipeline.
      • *
      • If a deny expression was specified but no allow expression, allow this request to pass through (because none of * the deny expressions matched it). @@ -53,8 +53,6 @@ * authentication instead of denial. *

        * This Valve may be attached to any Container, depending on the granularity of the filtering you wish to perform. - * - * @author Craig R. McClanahan */ public abstract class RequestFilterValve extends ValveBase { @@ -111,9 +109,8 @@ protected int denyStatus = HttpServletResponse.SC_FORBIDDEN; /** - *

        * If invalidAuthenticationWhenDeny is true and the context has preemptiveAuthentication - * set, set an invalid authorization header to trigger basic auth instead of denying the request.. + * set, set an invalid authorization header to trigger basic auth instead of denying the request. */ private boolean invalidAuthenticationWhenDeny = false; @@ -149,7 +146,7 @@ * @param allow The new allow expression */ public void setAllow(String allow) { - if (allow == null || allow.length() == 0) { + if (allow == null || allow.isEmpty()) { this.allow = null; allowValue = null; allowValid = true; @@ -183,7 +180,7 @@ * @param deny The new deny expression */ public void setDeny(String deny) { - if (deny == null || deny.length() == 0) { + if (deny == null || deny.isEmpty()) { this.deny = null; denyValue = null; denyValid = true; @@ -420,11 +417,7 @@ } // Allow if denies specified but not allows - if (deny != null && allow == null) { - return true; - } - - // Deny this request - return false; + // Otherwise deny this request + return deny != null && allow == null; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/SSLValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/SSLValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/SSLValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/SSLValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -158,7 +158,7 @@ String header = "-----BEGIN CERTIFICATE-----\n"; String strcerts = header.concat(body); ByteArrayInputStream bais = new ByteArrayInputStream(strcerts.getBytes(StandardCharsets.ISO_8859_1)); - X509Certificate jsseCerts[] = null; + X509Certificate[] jsseCerts = null; String providerName = (String) request.getConnector().getProperty("clientCertProvider"); try { CertificateFactory cf; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/SemaphoreValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/SemaphoreValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/SemaphoreValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/SemaphoreValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,8 +35,6 @@ * perform. Note that internally, some async requests may require multiple serial requests to complete what - to the * user - appears as a single request. *

        - * - * @author Remy Maucherat */ public class SemaphoreValve extends ValveBase { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/StuckThreadDetectionValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/StuckThreadDetectionValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/StuckThreadDetectionValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/StuckThreadDetectionValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -61,7 +61,7 @@ /** * Keeps count of the number of stuck threads that have been interrupted */ - private AtomicLong interruptedThreadsCount = new AtomicLong(); + private final AtomicLong interruptedThreadsCount = new AtomicLong(); /** * In seconds. Default 600 (10 minutes). @@ -322,7 +322,9 @@ // going out from here, maybe already serving a new request this.interruptionSemaphore.acquire(); } catch (InterruptedException e) { - log.debug(sm.getString("stuckThreadDetectionValve.interrupted"), e); + if (log.isDebugEnabled()) { + log.debug(sm.getString("stuckThreadDetectionValve.interrupted"), e); + } } // no need to release the semaphore, it will be GCed } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/ValveBase.java tomcat10-10.1.52/java/org/apache/catalina/valves/ValveBase.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/ValveBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/ValveBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,6 @@ * Convenience base class for implementations of the Valve interface. A subclass MUST implement * an invoke() method to provide the required functionality, and MAY implement the * Lifecycle interface to provide configuration management and lifecycle support. - * - * @author Craig R. McClanahan */ public abstract class ValveBase extends LifecycleMBeanBase implements Contained, Valve { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,8 @@ quotedStringTokenizer.tokenizeError=Error tokenizing text [{0}] after position [{1}] from mode [{2}] +resolverImpl.tlsError=Unable to obtain TLS information + rewriteMap.tooManyParameters=Too many parameters for this map rewriteMap.txtInvalidLine=Invalid line [{0}] in text file [{1}] rewriteMap.txtReadError=Error reading text file [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,8 @@ quotedStringTokenizer.tokenizeError=Erreur de découpage du texte [{0}] après la position [{1}] en utilisant le mode [{2}] +resolverImpl.tlsError=Impossible d'obtenir l'information TLS + rewriteMap.tooManyParameters=Cette map ne supporte pas plusieurs paramètres rewriteMap.txtInvalidLine=Ligne invalide [{0}] dans le fichier texte [{1}] rewriteMap.txtReadError=Erreur en lisant le fichier texte [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,8 @@ quotedStringTokenizer.tokenizeError=モード [{2}] の位置 [{1}] の後のテキスト [{0}] のトークン化中にエラーが発生しました +resolverImpl.tlsError=TLS情報を取得できません + rewriteMap.tooManyParameters=このマップのパラメータが多すぎます rewriteMap.txtInvalidLine=テキスト ファイル [{1}] の行 [{0}] が無効です rewriteMap.txtReadError=テキスト ファイル [{0}] の読み取り中のエラー diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/QuotedStringTokenizer.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,8 @@ protected static final StringManager sm = StringManager.getManager(QuotedStringTokenizer.class); - private Iterator tokenIterator; - private int tokenCount; + private final Iterator tokenIterator; + private final int tokenCount; private int returnedTokens = 0; enum WordMode { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RandomizedTextRewriteMap.java 2026-01-23 19:33:36.000000000 +0000 @@ -50,14 +50,17 @@ BufferedReader reader = new BufferedReader(new InputStreamReader(txtResource.getInputStream()))) { while ((line = reader.readLine()) != null) { if (line.startsWith("#") || line.isEmpty()) { - // Ignore comment or empty lines + // Ignore comment line or empty lines continue; + } else if (line.indexOf('#') > 0) { + // Ignore comment characters after '#' + line = line.substring(0, line.indexOf('#')).trim(); } String[] keyValuePair = line.split(" ", 2); if (keyValuePair.length > 1) { String key = keyValuePair[0]; String value = keyValuePair[1]; - String[] possibleValues = null; + String[] possibleValues; if (useRandom && value.contains("|")) { possibleValues = value.split("\\|"); } else { @@ -69,8 +72,8 @@ throw new IllegalArgumentException(sm.getString("rewriteMap.txtInvalidLine", line, txtFilePath)); } } - } catch (IOException e) { - throw new IllegalArgumentException(sm.getString("rewriteMap.txtReadError", txtFilePath), e); + } catch (IOException ioe) { + throw new IllegalArgumentException(sm.getString("rewriteMap.txtReadError", txtFilePath), ioe); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/ResolverImpl.java tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/ResolverImpl.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/ResolverImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/ResolverImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,6 +26,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; @@ -33,21 +34,42 @@ import org.apache.catalina.WebResource; import org.apache.catalina.WebResourceRoot; import org.apache.catalina.connector.Request; +import org.apache.juli.logging.Log; import org.apache.tomcat.util.http.FastHttpDateFormat; import org.apache.tomcat.util.net.SSLSupport; import org.apache.tomcat.util.net.jsse.PEMFile; import org.apache.tomcat.util.net.openssl.ciphers.Cipher; import org.apache.tomcat.util.net.openssl.ciphers.EncryptionLevel; import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser; +import org.apache.tomcat.util.res.StringManager; public class ResolverImpl extends Resolver { - protected Request request = null; + private static final StringManager sm = StringManager.getManager(ResolverImpl.class); + protected final Request request; + private final Log containerLog; + + + /** + * Create a resolver for the given request. + * + * @param request The request + * + * @deprecated Will be removed in Tomcat 12 onwards. Use {@link #ResolverImpl(Request, Log)} + */ + @Deprecated public ResolverImpl(Request request) { + this(request, request.getContext().getLogger()); + } + + + public ResolverImpl(Request request, Log containerLog) { this.request = request; + this.containerLog = containerLog; } + /** * The following are not implemented: *
          @@ -227,8 +249,11 @@ } } } - } catch (IOException e) { + } catch (IOException ioe) { // TLS access error + if (containerLog.isDebugEnabled()) { + containerLog.debug(sm.getString("resolverImpl.tlsError"), ioe); + } } return null; } @@ -274,14 +299,14 @@ } else if (key.equals("CERT")) { try { return PEMFile.toPEM(certificates[0]); - } catch (CertificateEncodingException e) { + } catch (CertificateEncodingException ignore) { // Ignore } } else if (key.startsWith("CERT_CHAIN_")) { key = key.substring("CERT_CHAIN_".length()); try { return PEMFile.toPEM(certificates[Integer.parseInt(key)]); - } catch (NumberFormatException | ArrayIndexOutOfBoundsException | CertificateEncodingException e) { + } catch (NumberFormatException | ArrayIndexOutOfBoundsException | CertificateEncodingException ignore) { // Ignore } } @@ -316,7 +341,7 @@ return elements.get(n); } } - } catch (NumberFormatException | ArrayIndexOutOfBoundsException | CertificateParsingException e) { + } catch (NumberFormatException | ArrayIndexOutOfBoundsException | CertificateParsingException ignore) { // Ignore } return null; @@ -324,12 +349,7 @@ @Override public String resolveHttp(String key) { - String header = request.getHeader(key); - if (header == null) { - return ""; - } else { - return header; - } + return Objects.requireNonNullElse(request.getHeader(key), ""); } @Override @@ -353,11 +373,7 @@ } private static String emptyStringIfNull(String value) { - if (value == null) { - return ""; - } else { - return value; - } + return Objects.requireNonNullElse(value, ""); } @Override diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RewriteCond.java tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteCond.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RewriteCond.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteCond.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,7 +28,7 @@ public static class PatternCondition extends Condition { public Pattern pattern; - private ThreadLocal matcher = new ThreadLocal<>(); + private final ThreadLocal matcher = new ThreadLocal<>(); @Override public boolean evaluate(String value, Resolver resolver) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RewriteMap.java tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteMap.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RewriteMap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteMap.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,7 +49,7 @@ * Optional parameters that can be defined through the {@code RewriteMap} directive in the {@code rewrite.config} * file. *

          - * This method will be called, if there are more than one parameters defined. + * This method will be called, if there is more than one parameter defined. * * @param params the optional parameters */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RewriteRule.java tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteRule.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RewriteRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -215,7 +215,7 @@ protected boolean host = false; /** - * Stop the rewriting process here and don't apply any more rewriting rules. This corresponds to the Perl last + * Stop the rewriting process here and don't apply any additional rewriting rules. This corresponds to the Perl last * command or the break command from the C language. Use this flag to prevent the currently rewritten URL from being * rewritten further by following rules. For example, use it to rewrite the root-path URL ('/') to a real one, e.g., * '/e/www/'. @@ -299,7 +299,7 @@ protected int skip = 0; /** - * Force the MIME-type of the target file to be MIME-type. For instance, this can be used to setup the content-type + * Force the MIME-type of the target file to be MIME-type. For instance, this can be used to set up the content-type * based on some conditions. For example, the following snippet allows .php files to be displayed by mod_php if they * are called with the .phps extension: RewriteRule ^(.+\.php)s$ $1 [T=application/x-httpd-php-source] */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RewriteValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/RewriteValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/RewriteValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,11 +21,13 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringReader; +import java.net.URLDecoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; @@ -64,6 +66,24 @@ */ public class RewriteValve extends ValveBase { + private static final URLEncoder REWRITE_DEFAULT_ENCODER; + private static final URLEncoder REWRITE_QUERY_ENCODER; + + static { + /* + * See the detailed explanation of encoding/decoding during URL re-writing in the invoke() method. + * + * These encoders perform the second stage of encoding, after re-writing has completed. These rewrite specific + * encoders treat '%' as a safe character so that URLs and query strings already processed by encodeForRewrite() + * do not end up with double encoding of '%' characters. + */ + REWRITE_DEFAULT_ENCODER = (URLEncoder) URLEncoder.DEFAULT.clone(); + REWRITE_DEFAULT_ENCODER.addSafeCharacter('%'); + + REWRITE_QUERY_ENCODER = (URLEncoder) URLEncoder.QUERY.clone(); + REWRITE_QUERY_ENCODER.addSafeCharacter('%'); + } + /** * The rewrite rules that the valve will use. */ @@ -135,14 +155,17 @@ InputStream is = null; // Process configuration file for this valve + // Process configuration file for this valve if (getContainer() instanceof Context) { context = true; String webInfResourcePath = "/WEB-INF/" + resourcePath; is = ((Context) getContainer()).getServletContext().getResourceAsStream(webInfResourcePath); - if (containerLog.isDebugEnabled()) { - if (is == null) { - containerLog.debug(sm.getString("rewriteValve.noConfiguration", webInfResourcePath)); - } else { + if (is == null) { + if (containerLog.isInfoEnabled()) { + containerLog.info(sm.getString("rewriteValve.noConfiguration", webInfResourcePath)); + } + } else { + if (containerLog.isDebugEnabled()) { containerLog.debug(sm.getString("rewriteValve.readConfiguration", webInfResourcePath)); } } @@ -151,9 +174,9 @@ try { ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getResource(resourceName); is = resource.getInputStream(); - } catch (IOException e) { - if (containerLog.isDebugEnabled()) { - containerLog.debug(sm.getString("rewriteValve.noConfiguration", resourceName), e); + } catch (IOException ioe) { + if (containerLog.isInfoEnabled()) { + containerLog.info(sm.getString("rewriteValve.noConfiguration", resourceName), ioe); } } } @@ -171,8 +194,8 @@ } finally { try { is.close(); - } catch (IOException e) { - containerLog.error(sm.getString("rewriteValve.closeError"), e); + } catch (IOException ioe) { + containerLog.error(sm.getString("rewriteValve.closeError"), ioe); } } @@ -182,6 +205,11 @@ if (containerLog == null) { containerLog = LogFactory.getLog(getContainer().getLogName() + ".rewrite"); } + for (RewriteMap map : maps.values()) { + if (map instanceof Lifecycle) { + ((Lifecycle) map).stop(); + } + } maps.clear(); parse(new BufferedReader(new StringReader(configuration))); } @@ -191,7 +219,7 @@ for (String mapConfiguration : mapsConfiguration) { buffer.append(mapConfiguration).append("\r\n"); } - if (mapsConfiguration.size() > 0) { + if (!mapsConfiguration.isEmpty()) { buffer.append("\r\n"); } for (RewriteRule rule : rules) { @@ -206,6 +234,7 @@ protected void parse(BufferedReader reader) throws LifecycleException { List rules = new ArrayList<>(); List conditions = new ArrayList<>(); + ArrayList mapsConfiguration = new ArrayList<>(); while (true) { try { String line = reader.readLine(); @@ -226,11 +255,10 @@ } for (RewriteCond condition : conditions) { if (containerLog.isTraceEnabled()) { - RewriteCond cond = condition; - containerLog.trace("Add condition " + cond.getCondPattern() + " test " + - cond.getTestString() + " to rule with pattern " + rule.getPatternString() + + containerLog.trace("Add condition " + condition.getCondPattern() + " test " + + condition.getTestString() + " to rule with pattern " + rule.getPatternString() + " and substitution " + rule.getSubstitutionString() + - (cond.isOrnext() ? " [OR]" : "") + (cond.isNocase() ? " [NC]" : "")); + (condition.isOrnext() ? " [OR]" : "") + (condition.isNocase() ? " [NC]" : "")); } rule.addCondition(condition); } @@ -249,16 +277,18 @@ ((Lifecycle) map).start(); } } - } catch (IOException e) { - containerLog.error(sm.getString("rewriteValve.readError"), e); + } catch (IOException ioe) { + containerLog.error(sm.getString("rewriteValve.readError"), ioe); } } - this.rules = rules.toArray(new RewriteRule[0]); + this.mapsConfiguration = mapsConfiguration; // Finish parsing the rules - for (RewriteRule rule : this.rules) { + for (RewriteRule rule : rules) { rule.parse(maps); } + + this.rules = rules.toArray(new RewriteRule[0]); } @Override @@ -293,28 +323,55 @@ try { - Resolver resolver = new ResolverImpl(request); + Resolver resolver = new ResolverImpl(request, containerLog); invoked.set(Boolean.TRUE); - // As long as MB isn't a char sequence or affiliated, this has to be - // converted to a string + // As long as MB isn't a char sequence or affiliated, this has to be converted to a string Charset uriCharset = request.getConnector().getURICharset(); - String originalQueryStringEncoded = request.getQueryString(); + String queryStringOriginalEncoded = request.getQueryString(); MessageBytes urlMB = context ? request.getRequestPathMB() : request.getDecodedRequestURIMB(); urlMB.toChars(); CharSequence urlDecoded = urlMB.getCharChunk(); + + /* + * The URL presented to the rewrite valve is the URL that is used for request mapping. That URL has been + * processed to: remove path parameters; remove the query string; decode; and normalize the URL. It may + * contain literal '%', '?' and/or ';' characters at this point. + * + * The re-write rules need to be able to process URLs with literal '?' characters and add query strings + * without the two becoming confused. The re-write rules also need to be able to insert literal '%' + * characters without them being confused with %nn encoding. + * + * To meet these requirement, the URL is processed as follows. + * + * Step 1. The URL is partially re-encoded by encodeForRewrite(). This method encodes any literal '%', ';' + * and/or '?' characters in the URL using the standard %nn form. + * + * Step 2. The re-write processing runs with the provided re-write rules against the partially encoded URL. + * If a re-write rule needs to insert a literal '%', ';' or '?', it must do so in %nn encoded form. + * + * Step 3. The URL (and query string if present) is re-encoded using the re-write specific encoders + * (REWRITE_DEFAULT_ENCODER and REWRITE_QUERY_ENCODER) that behave the same was as the standard encoders + * apart from '%' being treated as a safe character. This prevents double encoding of any '%' characters + * present in the URL from steps 1 or 2. + */ + + // Step 1. Encode URL for processing by the re-write rules. + CharSequence urlRewriteEncoded = encodeForRewrite(urlDecoded); CharSequence host = request.getServerName(); boolean rewritten = false; boolean done = false; boolean qsa = false; boolean qsd = false; boolean valveSkip = false; + + // Step 2. Process the URL using the re-write rules. for (int i = 0; i < rules.length; i++) { RewriteRule rule = rules[i]; - CharSequence test = (rule.isHost()) ? host : urlDecoded; + CharSequence test = (rule.isHost()) ? host : urlRewriteEncoded; CharSequence newtest = rule.evaluate(test, resolver); - if (newtest != null && !test.toString().equals(newtest.toString())) { + if (newtest != null && !Objects.equals(test.toString(), newtest.toString())) { if (containerLog.isTraceEnabled()) { containerLog.trace( "Rewrote " + test + " as " + newtest + " with rule pattern " + rule.getPatternString()); @@ -322,7 +379,7 @@ if (rule.isHost()) { host = newtest; } else { - urlDecoded = newtest; + urlRewriteEncoded = newtest; } rewritten = true; } @@ -359,43 +416,46 @@ if (rule.isRedirect() && newtest != null) { // Append the query string to the url if there is one and it // hasn't been rewritten - String urlStringDecoded = urlDecoded.toString(); - int index = urlStringDecoded.indexOf('?'); - String rewrittenQueryStringDecoded; + String urlStringRewriteEncoded = urlRewriteEncoded.toString(); + int index = urlStringRewriteEncoded.indexOf('?'); + String rewrittenQueryStringRewriteEncoded; if (index == -1) { - rewrittenQueryStringDecoded = null; + rewrittenQueryStringRewriteEncoded = null; } else { - rewrittenQueryStringDecoded = urlStringDecoded.substring(index + 1); - urlStringDecoded = urlStringDecoded.substring(0, index); + rewrittenQueryStringRewriteEncoded = urlStringRewriteEncoded.substring(index + 1); + urlStringRewriteEncoded = urlStringRewriteEncoded.substring(0, index); } + // Step 3. Complete the 2nd stage to encoding. StringBuilder urlStringEncoded = - new StringBuilder(URLEncoder.DEFAULT.encode(urlStringDecoded, uriCharset)); - if (!qsd && originalQueryStringEncoded != null && originalQueryStringEncoded.length() > 0) { - if (rewrittenQueryStringDecoded == null) { + new StringBuilder(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset)); + + if (!qsd && queryStringOriginalEncoded != null && !queryStringOriginalEncoded.isEmpty()) { + if (rewrittenQueryStringRewriteEncoded == null) { urlStringEncoded.append('?'); - urlStringEncoded.append(originalQueryStringEncoded); + urlStringEncoded.append(queryStringOriginalEncoded); } else { if (qsa) { // if qsa is specified append the query urlStringEncoded.append('?'); - urlStringEncoded - .append(URLEncoder.QUERY.encode(rewrittenQueryStringDecoded, uriCharset)); + urlStringEncoded.append( + REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset)); urlStringEncoded.append('&'); - urlStringEncoded.append(originalQueryStringEncoded); + urlStringEncoded.append(queryStringOriginalEncoded); } else if (index == urlStringEncoded.length() - 1) { // if the ? is the last character delete it, its only purpose was to // prevent the rewrite module from appending the query string urlStringEncoded.deleteCharAt(index); } else { urlStringEncoded.append('?'); - urlStringEncoded - .append(URLEncoder.QUERY.encode(rewrittenQueryStringDecoded, uriCharset)); + urlStringEncoded.append( + REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset)); } } - } else if (rewrittenQueryStringDecoded != null) { + } else if (rewrittenQueryStringRewriteEncoded != null) { urlStringEncoded.append('?'); - urlStringEncoded.append(URLEncoder.QUERY.encode(rewrittenQueryStringDecoded, uriCharset)); + urlStringEncoded + .append(REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset)); } // Insert the context if @@ -405,11 +465,13 @@ if (context && urlStringEncoded.charAt(0) == '/' && !UriUtil.hasScheme(urlStringEncoded)) { urlStringEncoded.insert(0, request.getContext().getEncodedPath()); } + String redirectPath; if (rule.isNoescape()) { - response.sendRedirect(UDecoder.URLDecode(urlStringEncoded.toString(), uriCharset)); + redirectPath = UDecoder.URLDecode(urlStringEncoded.toString(), uriCharset); } else { - response.sendRedirect(urlStringEncoded.toString()); + redirectPath = urlStringEncoded.toString(); } + response.sendRedirect(response.encodeRedirectURL(redirectPath)); response.setStatus(rule.getRedirectCode()); done = true; break; @@ -470,13 +532,16 @@ if (rewritten) { if (!done) { // See if we need to replace the query string - String urlStringDecoded = urlDecoded.toString(); - String queryStringDecoded = null; - int queryIndex = urlStringDecoded.indexOf('?'); + String urlStringRewriteEncoded = urlRewriteEncoded.toString(); + String queryStringRewriteEncoded = null; + int queryIndex = urlStringRewriteEncoded.indexOf('?'); if (queryIndex != -1) { - queryStringDecoded = urlStringDecoded.substring(queryIndex + 1); - urlStringDecoded = urlStringDecoded.substring(0, queryIndex); + queryStringRewriteEncoded = urlStringRewriteEncoded.substring(queryIndex + 1); + urlStringRewriteEncoded = urlStringRewriteEncoded.substring(0, queryIndex); } + // Parse path parameters from rewrite production and populate request path parameters + urlStringRewriteEncoded = + org.apache.catalina.util.RequestUtil.stripPathParams(urlStringRewriteEncoded, request); // Save the current context path before re-writing starts String contextPath = null; if (context) { @@ -489,25 +554,34 @@ // This is neither decoded nor normalized chunk.append(contextPath); } - chunk.append(URLEncoder.DEFAULT.encode(urlStringDecoded, uriCharset)); - // Decoded and normalized URI - // Rewriting may have denormalized the URL - urlStringDecoded = RequestUtil.normalize(urlStringDecoded); + + // Step 3. Complete the 2nd stage to encoding. + chunk.append(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset)); + // Rewriting may have denormalized the URL and added encoded characters + // Decode then normalize + String urlStringRewriteDecoded = URLDecoder.decode(urlStringRewriteEncoded, uriCharset); + urlStringRewriteDecoded = RequestUtil.normalize(urlStringRewriteDecoded); request.getCoyoteRequest().decodedURI().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0); chunk = request.getCoyoteRequest().decodedURI().getCharChunk(); if (context) { // This is decoded and normalized chunk.append(request.getServletContext().getContextPath()); } - chunk.append(urlStringDecoded); - // Set the new Query if there is one - if (queryStringDecoded != null) { + chunk.append(urlStringRewriteDecoded); + // Set the new Query String + if (queryStringRewriteEncoded == null) { + // No new query string. Therefore the original is retained unless QSD is defined. + if (qsd) { + request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0); + } + } else { + // New query string. Therefore the original is dropped unless QSA is defined (and QSD is not). request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0); chunk = request.getCoyoteRequest().queryString().getCharChunk(); - chunk.append(URLEncoder.QUERY.encode(queryStringDecoded, uriCharset)); - if (qsa && originalQueryStringEncoded != null && originalQueryStringEncoded.length() > 0) { + chunk.append(REWRITE_QUERY_ENCODER.encode(queryStringRewriteEncoded, uriCharset)); + if (qsa && queryStringOriginalEncoded != null && !queryStringOriginalEncoded.isEmpty()) { chunk.append('&'); - chunk.append(originalQueryStringEncoded); + chunk.append(queryStringOriginalEncoded); } } // Set the new host if it changed @@ -517,6 +591,7 @@ chunk.append(host.toString()); } request.getMappingData().recycle(); + request.recycleSessionInfo(); // Reinvoke the whole request recursively Connector connector = request.getConnector(); try { @@ -601,6 +676,10 @@ while (flagsTokenizer.hasMoreElements()) { parseRuleFlag(line, rule, flagsTokenizer.nextToken()); } + // If QSD and QSA are present, QSD always takes precedence + if (rule.isQsdiscard()) { + rule.setQsappend(false); + } } return rule; } else if (token.equals("RewriteMap")) { @@ -799,4 +878,33 @@ throw new IllegalArgumentException(sm.getString("rewriteValve.invalidFlags", line, flag)); } } + + + private CharSequence encodeForRewrite(CharSequence input) { + StringBuilder result = null; + int pos = 0; + int mark = 0; + while (pos < input.length()) { + char c = input.charAt(pos); + if (c == '%' || c == ';' || c == '?') { + if (result == null) { + result = new StringBuilder((int) (input.length() * 1.1)); + } + result.append(input.subSequence(mark, pos)); + result.append('%'); + result.append(Character.forDigit((c >> 4) & 0xF, 16)); + result.append(Character.forDigit(c & 0xF, 16)); + mark = pos + 1; + } + pos++; + } + if (result != null) { + result.append(input.subSequence(mark, input.length())); + return result; + } else { + return input; + } + } + + } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/Substitution.java tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/Substitution.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/rewrite/Substitution.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/rewrite/Substitution.java 2026-01-23 19:33:36.000000000 +0000 @@ -149,9 +149,9 @@ List elements = new ArrayList<>(); int pos = 0; - int percentPos = 0; - int dollarPos = 0; - int backslashPos = 0; + int percentPos; + int dollarPos; + int backslashPos; while (pos < sub.length()) { percentPos = sub.indexOf('%', pos); @@ -203,7 +203,7 @@ throw new IllegalArgumentException( sm.getString("substitution.noMap", sub.substring(open + 1, colon), sub)); } - String key = null; + String key; String defaultValue = null; if (def > -1) { if (!(colon < def && def < close)) { @@ -242,7 +242,7 @@ elements.add(newElement); } else if (sub.charAt(percentPos + 1) == '{') { // %: server variable as %{variable} - SubstitutionElement newElement = null; + SubstitutionElement newElement; int open = sub.indexOf('{', percentPos); int colon = findMatchingColonOrBar(true, sub, open); int close = findMatchingBrace(sub, open); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractArchiveResource.java tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractArchiveResource.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractArchiveResource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractArchiveResource.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,7 +53,7 @@ resourceName = resourceName.substring(0, resourceName.length() - 1); } String internalPath = archiveResourceSet.getInternalPath(); - if (internalPath.length() > 0 && resourceName.equals(internalPath.subSequence(1, internalPath.length()))) { + if (!internalPath.isEmpty() && resourceName.contentEquals(internalPath.subSequence(1, internalPath.length()))) { name = ""; } else { int index = resourceName.lastIndexOf('/'); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractArchiveResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,17 +31,21 @@ import org.apache.catalina.WebResource; import org.apache.catalina.WebResourceRoot; import org.apache.catalina.util.ResourceSet; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; public abstract class AbstractArchiveResourceSet extends AbstractResourceSet { + private static final Log log = LogFactory.getLog(AbstractArchiveResourceSet.class); + private URL baseUrl; private String baseUrlString; - private JarFile archive = null; + protected JarFile archive = null; protected Map archiveEntries = null; protected final Object archiveLock = new Object(); - private long archiveUseCount = 0; - private JarContents jarContents; - private boolean retainBloomFilterForArchives = false; + protected long archiveUseCount = 0; + protected JarContents jarContents; + protected boolean retainBloomFilterForArchives = false; protected final void setBaseUrl(URL baseUrl) { this.baseUrl = baseUrl; @@ -68,10 +72,10 @@ String webAppMount = getWebAppMount(); ArrayList result = new ArrayList<>(); - if (path.startsWith(webAppMount)) { + if (isPathMounted(path, webAppMount)) { String pathInJar = getInternalPath() + path.substring(webAppMount.length()); // Always strip off the leading '/' to get the JAR path - if (pathInJar.length() > 0 && pathInJar.charAt(0) == '/') { + if (!pathInJar.isEmpty() && pathInJar.charAt(0) == '/') { pathInJar = pathInJar.substring(1); } for (String name : getArchiveEntries(false).keySet()) { @@ -81,13 +85,13 @@ } else { name = name.substring(pathInJar.length()); } - if (name.length() == 0) { + if (name.isEmpty()) { continue; } if (name.charAt(0) == '/') { name = name.substring(1); } - if (name.length() > 0 && name.lastIndexOf('/') == -1) { + if (!name.isEmpty() && name.lastIndexOf('/') == -1) { result.add(name); } } @@ -108,17 +112,18 @@ return result.toArray(new String[0]); } + @Override public final Set listWebAppPaths(String path) { checkPath(path); String webAppMount = getWebAppMount(); ResourceSet result = new ResourceSet<>(); - if (path.startsWith(webAppMount)) { + if (isPathMounted(path, webAppMount)) { String pathInJar = getInternalPath() + path.substring(webAppMount.length()); // Always strip off the leading '/' to get the JAR path and make // sure it ends in '/' - if (pathInJar.length() > 0) { + if (!pathInJar.isEmpty()) { if (pathInJar.charAt(pathInJar.length() - 1) != '/') { pathInJar = pathInJar.substring(1) + '/'; } @@ -225,13 +230,13 @@ // If the JAR has been mounted below the web application root, return // an empty resource for requests outside of the mount point. - if (path.startsWith(webAppMount)) { + if (isPathMounted(path, webAppMount)) { String pathInJar = getInternalPath() + path.substring(webAppMount.length()); // Always strip off the leading '/' to get the JAR path - if (pathInJar.length() > 0 && pathInJar.charAt(0) == '/') { + if (!pathInJar.isEmpty() && pathInJar.charAt(0) == '/') { pathInJar = pathInJar.substring(1); } - if (pathInJar.equals("")) { + if (pathInJar.isEmpty()) { // Special case // This is a directory resource so the path must end with / if (!path.endsWith("/")) { @@ -293,6 +298,25 @@ throw new IllegalArgumentException(sm.getString("abstractArchiveResourceSet.setReadOnlyFalse")); } + /** + * {@inheritDoc} + *

          + * Calls to this method will be ignored as archives do not allow linking. + */ + @Override + public void setAllowLinking(boolean allowLinking) { + } + + /** + * {@inheritDoc} + *

          + * Calls to this method always return {@code false} as archives do not allow linking. + */ + @Override + public boolean getAllowLinking() { + return false; + } + @SuppressWarnings("deprecation") protected JarFile openJarFile() throws IOException { synchronized (archiveLock) { @@ -322,8 +346,8 @@ if (archive != null && archiveUseCount == 0) { try { archive.close(); - } catch (IOException e) { - // Log at least WARN + } catch (IOException ioe) { + log.warn(sm.getString("abstractArchiveResourceSet.archiveCloseFailed"), ioe); } archive = null; archiveEntries = null; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractFileResourceSet.java tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractFileResourceSet.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractFileResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractFileResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,6 +37,7 @@ private String absoluteBase; private String canonicalBase; private boolean readOnly = false; + private Boolean allowLinking; protected AbstractFileResourceSet(String internalPath) { setInternalPath(internalPath); @@ -56,6 +57,19 @@ return readOnly; } + @Override + public void setAllowLinking(boolean allowLinking) { + this.allowLinking = Boolean.valueOf(allowLinking); + } + + @Override + public boolean getAllowLinking() { + if (allowLinking == null) { + return getRoot().getAllowLinking(); + } + return allowLinking.booleanValue(); + } + protected final File file(String name, boolean mustExist) { if (name.equals("/")) { @@ -78,12 +92,12 @@ // If allow linking is enabled, files are not limited to being located // under the fileBase so all further checks are disabled. - if (getRoot().getAllowLinking()) { + if (getAllowLinking()) { return file; } // Additional Windows specific checks to handle known problems with - // File.getCanonicalPath() + // File.getCanonicalPath() and other issues if (JrePlatform.IS_WINDOWS && isInvalidWindowsFilename(name)) { return null; } @@ -92,7 +106,7 @@ String canPath = null; try { canPath = file.getCanonicalPath(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } if (canPath == null || !canPath.startsWith(canonicalBase)) { @@ -117,14 +131,14 @@ canPath = canPath.substring(canonicalBase.length()); // The remaining request path must start with '/' if it has non-zero length - if (canPath.length() > 0 && canPath.charAt(0) != File.separatorChar) { + if (!canPath.isEmpty() && canPath.charAt(0) != File.separatorChar) { return null; } // Case sensitivity check // The normalized requested path should be an exact match the equivalent // canonical path. If it is not, possible reasons include: - // - case differences on case insensitive file systems + // - case differences on case-insensitive file systems // - Windows removing a trailing ' ' or '.' from the file name // // In all cases, a mismatch here results in the resource not being @@ -132,7 +146,7 @@ // // absPath is normalized so canPath needs to be normalized as well // Can't normalize canPath earlier as canonicalBase is not normalized - if (canPath.length() > 0) { + if (!canPath.isEmpty()) { canPath = normalize(canPath); } if (!canPath.equals(absPath)) { @@ -150,42 +164,48 @@ protected void logIgnoredSymlink(String contextPath, String absPath, String canPath) { - String msg = sm.getString("abstractFileResourceSet.canonicalfileCheckFailed", contextPath, absPath, canPath); // Log issues with configuration files at a higher level if (absPath.startsWith("/META-INF/") || absPath.startsWith("/WEB-INF/")) { - log.error(msg); + log.error(sm.getString("abstractFileResourceSet.canonicalfileCheckFailed", contextPath, absPath, canPath)); } else { - log.warn(msg); + log.warn(sm.getString("abstractFileResourceSet.canonicalfileCheckFailed", contextPath, absPath, canPath)); } } + private boolean isInvalidWindowsFilename(String name) { final int len = name.length(); if (len == 0) { return false; } - // This consistently ~10 times faster than the equivalent regular - // expression irrespective of input length. + // This is consistently ~10 times faster than the equivalent regular expression irrespective of input length. for (int i = 0; i < len; i++) { char c = name.charAt(i); - if (c == '\"' || c == '<' || c == '>' || c == ':') { - // These characters are disallowed in Windows file names and - // there are known problems for file names with these characters - // when using File#getCanonicalPath(). - // Note: There are additional characters that are disallowed in - // Windows file names but these are not known to cause - // problems when using File#getCanonicalPath(). + /* + * '\"', ':', '<' and '>' are disallowed in Windows file names and there are known problems with these + * characters when using File#getCanonicalPath(). + * + * Control characters (0x00-0x31) are not permitted and tend to be display strangely in log messages and + * similar. + * + * '*', '?' and '|' are also not allowed and, while they are not currently known to cause other + * difficulties, they are checked here rather than wasting cycles trying to find an invalid file later. + * + * The file separators ('/' and '\\') are not allowed in file names but are not excluded here as paths are + * passed to this method. + * + * Note: Characters are listed in ASCII order. + */ + if (c < 32 || c == '\"' || c == '*' || c == ':' || c == '<' || c == '>' || c == '?' || c == '|') { return true; } } - // Windows does not allow file names to end in ' ' unless specific low - // level APIs are used to create the files that bypass various checks. - // File names that end in ' ' are known to cause problems when using - // File#getCanonicalPath(). - if (name.charAt(len - 1) == ' ') { - return true; - } - return false; + /* + * Windows does not allow file names to end in ' ' unless specific low-level APIs are used to create the files + * that bypass various checks. File names that end in ' ' are known to cause problems when using + * File#getCanonicalPath(). + */ + return name.charAt(len - 1) == ' '; } @@ -231,8 +251,8 @@ try { this.canonicalBase = fileBase.getCanonicalPath(); - } catch (IOException e) { - throw new IllegalArgumentException(e); + } catch (IOException ioe) { + throw new IllegalArgumentException(ioe); } // Need to handle mapping of the file system root as a special case @@ -246,4 +266,4 @@ protected abstract void checkType(File file); -} +} \ No newline at end of file diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractResource.java tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractResource.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractResource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractResource.java 2026-01-23 19:33:36.000000000 +0000 @@ -90,7 +90,7 @@ if (contentLength <= 16 * 1024) { byte[] buf = getContent(); if (buf != null) { - buf = ConcurrentMessageDigest.digest("SHA-1", buf); + buf = ConcurrentMessageDigest.digestSHA256(buf); strongETag = "\"" + HexUtils.toHexString(buf) + "\""; } else { strongETag = getETag(); @@ -98,7 +98,7 @@ } else { byte[] buf = new byte[4096]; try (InputStream is = getInputStream()) { - MessageDigest digest = MessageDigest.getInstance("SHA-1"); + MessageDigest digest = MessageDigest.getInstance("SHA-256"); while (true) { int n = is.read(buf); if (n <= 0) { @@ -128,6 +128,14 @@ @Override public final String getMimeType() { + if (mimeType == null) { + String name = getName(); + int extensionStart = name.lastIndexOf('.'); + if (extensionStart > -1) { + String extension = name.substring(extensionStart + 1); + mimeType = root.getContext().findMimeMapping(extension); + } + } return mimeType; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractResourceSet.java tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractResourceSet.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,7 +40,7 @@ protected final void checkPath(String path) { - if (path == null || path.length() == 0 || path.charAt(0) != '/') { + if (path == null || path.isEmpty() || path.charAt(0) != '/') { throw new IllegalArgumentException(sm.getString("abstractResourceSet.checkPath", path)); } } @@ -71,9 +71,13 @@ public final void setWebAppMount(String webAppMount) { checkPath(webAppMount); - // Optimise internal processing - if (webAppMount.equals("/")) { - this.webAppMount = ""; + /* + * Originally, only "/" was changed to "" to allow some optimisations. The fix for CVE-2025-49125 means that + * mounted WebResourceSets will break if webAppMount ends in '/'. So now the trailing "/" is removed in all + * cases. + */ + if (webAppMount.endsWith("/")) { + this.webAppMount = webAppMount.substring(0, webAppMount.length() - 1); } else { this.webAppMount = webAppMount; } @@ -83,6 +87,18 @@ return webAppMount; } + protected boolean isPathMounted(String path, String webAppMount) { + // Doesn't call getWebAppMount() as value might have changed + if (path.startsWith(webAppMount)) { + if (path.length() != webAppMount.length() && path.charAt(webAppMount.length()) != '/') { + return false; + } + return true; + } + return false; + } + + public final void setBase(String base) { this.base = base; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractSingleArchiveResource.java tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractSingleArchiveResource.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/AbstractSingleArchiveResource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/AbstractSingleArchiveResource.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,10 +38,10 @@ JarEntry jarEntry = jarFile.getJarEntry(getResource().getName()); InputStream is = jarFile.getInputStream(jarEntry); return new JarInputStreamWrapper(jarEntry, is); - } catch (IOException e) { + } catch (IOException ioe) { if (getLog().isDebugEnabled()) { getLog().debug(sm.getString("jarResource.getInputStreamFail", getResource().getName(), getBaseUrl()), - e); + ioe); } if (jarFile != null) { getArchiveResourceSet().closeJarFile(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/Cache.java tomcat10-10.1.52/java/org/apache/catalina/webresources/Cache.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/Cache.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/Cache.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,8 +49,8 @@ private int objectMaxSize = (int) maxSize / OBJECT_MAX_SIZE_FACTOR; private CacheStrategy cacheStrategy; - private LongAdder lookupCount = new LongAdder(); - private LongAdder hitCount = new LongAdder(); + private final LongAdder lookupCount = new LongAdder(); + private final LongAdder hitCount = new LongAdder(); private final ConcurrentMap resourceCache = new ConcurrentHashMap<>(); @@ -233,11 +233,9 @@ private boolean noCache(String path) { // Don't cache classes. The class loader handles this. // Don't cache JARs. The ResourceSet handles this. - if ((path.endsWith(".class") && (path.startsWith("/WEB-INF/classes/") || path.startsWith("/WEB-INF/lib/"))) || - (path.startsWith("/WEB-INF/lib/") && path.endsWith(".jar"))) { - return true; - } - return false; + return (path.endsWith(".class") && + (path.startsWith("/WEB-INF/classes/") || path.startsWith("/WEB-INF/lib/"))) || + (path.startsWith("/WEB-INF/lib/") && path.endsWith(".jar")); } private long evict(long targetSize, Iterator iter) { @@ -315,9 +313,10 @@ if (objectMaxSize * 1024L > Integer.MAX_VALUE) { log.warn(sm.getString("cache.objectMaxSizeTooBigBytes", Integer.valueOf(objectMaxSize))); this.objectMaxSize = Integer.MAX_VALUE; + } else { + // Internally bytes, externally kilobytes + this.objectMaxSize = objectMaxSize * 1024; } - // Internally bytes, externally kilobytes - this.objectMaxSize = objectMaxSize * 1024; } public int getObjectMaxSize() { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/CachedResource.java tomcat10-10.1.52/java/org/apache/catalina/webresources/CachedResource.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/CachedResource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/CachedResource.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,6 +32,7 @@ import java.text.Collator; import java.util.Arrays; import java.util.Locale; +import java.util.Objects; import java.util.jar.JarFile; import java.util.jar.Manifest; @@ -75,7 +76,6 @@ private volatile Boolean cachedExists = null; private volatile Boolean cachedIsVirtual = null; private volatile Long cachedContentLength = null; - private final Object cachedContentLengthLock = new Object(); private volatile String cachedStrongETag = null; @@ -85,6 +85,7 @@ this.root = root; this.webAppPath = path; this.ttl = ttl; + nextCheck = ttl + System.currentTimeMillis(); this.objectMaxSizeBytes = objectMaxSizeBytes; this.usesClassLoaderResources = usesClassLoaderResources; } @@ -101,15 +102,12 @@ return false; } - long now = System.currentTimeMillis(); - if (webResource == null) { synchronized (this) { if (webResource == null) { webResource = root.getResourceInternal(webAppPath, useClassLoaderResources); getLastModified(); getContentLength(); - nextCheck = ttl + now; // exists() is a relatively expensive check for a file so // use the fact that we know if it exists at this point if (webResource instanceof EmptyResource) { @@ -122,6 +120,8 @@ } } + long now = System.currentTimeMillis(); + if (now < nextCheck) { return true; } @@ -253,23 +253,20 @@ * 1. It is relatively expensive to calculate, it shouldn't change and this method is called multiple times * during cache validation. Caching, therefore, offers a performance benefit. * - * 2. There is a race condition if concurrent threads are trying to PUT and DELETE the same resource. If the - * DELETE thread removes the cache entry after the PUT thread has created it but before validation is complete - * the DELETE thread sees a content length for a non-existant resource but the PUT thread sees the actual - * content length. While that isn't an issue for the individual requests it, does corrupt the cache size - * tracking as the size used for the resource is different between when it is added to the cache and when it is - * removed. Caching combined with locking ensures that a consistent content length is reported for the resource. + * 2. There is a race condition if concurrent threads are trying to PUT and DELETE the same resource. See BZ + * 69527 (https://bz.apache.org/bugzilla/show_bug.cgi?id=69527#c14) for full details. The short version is that + * getContentLength() must always return the same value for any one CachedResource instance else the cache size + * will be corrupted. */ if (cachedContentLength == null) { - synchronized (cachedContentLengthLock) { - if (cachedContentLength == null) { + if (webResource == null) { + synchronized (this) { if (webResource == null) { - cachedContentLength = Long.valueOf(0); - } else { - cachedContentLength = Long.valueOf(webResource.getContentLength()); + webResource = root.getResourceInternal(webAppPath, usesClassLoaderResources); } } } + cachedContentLength = Long.valueOf(webResource.getContentLength()); } return cachedContentLength.longValue(); } @@ -299,7 +296,7 @@ if (cachedStrongETag == null) { byte[] buf = getContent(); if (buf != null) { - buf = ConcurrentMessageDigest.digest("SHA-1", buf); + buf = ConcurrentMessageDigest.digestSHA256(buf); cachedStrongETag = "\"" + HexUtils.toHexString(buf) + "\""; } else { cachedStrongETag = webResource.getStrongETag(); @@ -425,7 +422,7 @@ // Longer paths use a noticeable amount of memory so account for this in // the cache size. The fixed component of a String instance's memory // usage is accounted for in the 500 bytes above. - result += getWebappPath().length() * 2; + result += getWebappPath().length() * 2L; if (getContentLength() <= objectMaxSizeBytes) { result += getContentLength(); } @@ -595,6 +592,12 @@ private WebResource getResource() { return root.getResource(webAppPath, false, usesClassLoaderResources); } + + @Override + public String getContentType() { + // "content/unknown" is the value used by sun.net.www.URLConnection. It is used here for consistency. + return Objects.requireNonNullElse(getResource().getMimeType(), "content/unknown"); + } } @@ -657,5 +660,10 @@ return ((JarURLConnection) resourceURL.openConnection()).getJarFile(); } + @Override + public String getContentType() { + // "content/unknown" is the value used by sun.net.www.URLConnection. It is used here for consistency. + return Objects.requireNonNullElse(getResource().getMimeType(), "content/unknown"); + } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/DirResourceSet.java tomcat10-10.1.52/java/org/apache/catalina/webresources/DirResourceSet.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/DirResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/DirResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.jar.Manifest; @@ -36,6 +37,7 @@ import org.apache.catalina.util.ResourceSet; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.compat.JreCompat; import org.apache.tomcat.util.http.RequestUtil; /** @@ -45,10 +47,8 @@ private static final Log log = LogFactory.getLog(DirResourceSet.class); - private boolean caseSensitive = true; - - private Map resourceLocksByPath = new HashMap<>(); - private Object resourceLocksByPathLock = new Object(); + private final Map resourceLocksByPath = new HashMap<>(); + private final Object resourceLocksByPathLock = new Object(); /** @@ -96,18 +96,20 @@ } + @SuppressWarnings("null") // lock can never be null when lock.key is read @Override public WebResource getResource(String path) { checkPath(path); String webAppMount = getWebAppMount(); WebResourceRoot root = getRoot(); - if (path.startsWith(webAppMount)) { + boolean readOnly = isReadOnly(); + if (isPathMounted(path, webAppMount)) { /* * Lock the path for reading until the WebResource has been constructed. The lock prevents concurrent reads * and writes (e.g. HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource * where some of the fields are set as if the file exists and some as set as if it does not. */ - ResourceLock lock = lockForRead(path); + ResourceLock lock = readOnly ? null : lockForRead(path); try { File f = file(path.substring(webAppMount.length()), false); if (f == null) { @@ -119,9 +121,11 @@ if (f.isDirectory() && path.charAt(path.length() - 1) != '/') { path = path + '/'; } - return new FileResource(root, path, f, isReadOnly(), getManifest(), this, lock.key); + return new FileResource(root, path, f, readOnly, getManifest(), this, readOnly ? null : lock.key); } finally { - unlockForRead(lock); + if (!readOnly) { + unlockForRead(lock); + } } } else { return new EmptyResource(root, path); @@ -133,17 +137,13 @@ public String[] list(String path) { checkPath(path); String webAppMount = getWebAppMount(); - if (path.startsWith(webAppMount)) { + if (isPathMounted(path, webAppMount)) { File f = file(path.substring(webAppMount.length()), true); if (f == null) { return EMPTY_STRING_ARRAY; } String[] result = f.list(); - if (result == null) { - return EMPTY_STRING_ARRAY; - } else { - return result; - } + return Objects.requireNonNullElse(result, EMPTY_STRING_ARRAY); } else { if (!path.endsWith("/")) { path = path + "/"; @@ -165,15 +165,16 @@ checkPath(path); String webAppMount = getWebAppMount(); ResourceSet result = new ResourceSet<>(); - if (path.startsWith(webAppMount)) { + if (isPathMounted(path, webAppMount)) { File f = file(path.substring(webAppMount.length()), true); if (f != null) { File[] list = f.listFiles(); if (list != null) { + String fCanPath = null; for (File entry : list) { // f has already been validated so the following checks // can be much simpler than those in file() - if (!getRoot().getAllowLinking()) { + if (!getAllowLinking()) { // allow linking is disabled so need to check for // symlinks boolean symlink = true; @@ -187,7 +188,9 @@ // that what is left does not contain a symlink. absPath = entry.getAbsolutePath().substring(f.getAbsolutePath().length()); String entryCanPath = entry.getCanonicalPath(); - String fCanPath = f.getCanonicalPath(); + if (fCanPath == null) { + fCanPath = f.getCanonicalPath(); + } if (entryCanPath.length() >= fCanPath.length()) { canPath = entryCanPath.substring(fCanPath.length()); if (absPath.equals(canPath)) { @@ -239,7 +242,7 @@ return false; } String webAppMount = getWebAppMount(); - if (path.startsWith(webAppMount)) { + if (isPathMounted(path, webAppMount)) { File f = file(path.substring(webAppMount.length()), false); if (f == null) { return false; @@ -269,11 +272,11 @@ } String webAppMount = getWebAppMount(); - if (!path.startsWith(webAppMount)) { + if (!isPathMounted(path, webAppMount)) { return false; } - File dest = null; + File dest; /* * Lock the path for writing until the write is complete. The lock prevents concurrent reads and writes (e.g. * HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource where some of the fields @@ -308,7 +311,7 @@ @Override protected void checkType(File file) { - if (file.isDirectory() == false) { + if (!file.isDirectory()) { throw new IllegalArgumentException( sm.getString("dirResourceSet.notDirectory", getBase(), File.separator, getInternalPath())); } @@ -318,62 +321,62 @@ @Override protected void initInternal() throws LifecycleException { super.initInternal(); - caseSensitive = isCaseSensitive(); // Is this an exploded web application? - if (getWebAppMount().equals("")) { + if (getWebAppMount().isEmpty()) { // Look for a manifest File mf = file("META-INF/MANIFEST.MF", true); if (mf != null && mf.isFile()) { try (FileInputStream fis = new FileInputStream(mf)) { setManifest(new Manifest(fis)); - } catch (IOException e) { - log.warn(sm.getString("dirResourceSet.manifestFail", mf.getAbsolutePath()), e); + } catch (IOException ioe) { + log.warn(sm.getString("dirResourceSet.manifestFail", mf.getAbsolutePath()), ioe); } } } - } - - - /* - * Determines if this ResourceSet is based on a case sensitive file system or not. - */ - private boolean isCaseSensitive() { - try { - String canonicalPath = getFileBase().getCanonicalPath(); - File upper = new File(canonicalPath.toUpperCase(Locale.ENGLISH)); - if (!canonicalPath.equals(upper.getCanonicalPath())) { - return true; - } - File lower = new File(canonicalPath.toLowerCase(Locale.ENGLISH)); - if (!canonicalPath.equals(lower.getCanonicalPath())) { - return true; - } + // Check for exposure to CVE-2024-56337 + if (isReadOnly()) { + // CVE-2024-56337 (nor CVE-2024-50379) is not exploitable on a read-only ResourceSet + return; + } + if (JreCompat.getInstance().isCanonCachesDisabled()) { + // CVE-2024-56337 (nor CVE-2024-50379) is not exploitable if the canonical file name cache is disabled + return; + } + // This ResourceSet may be exposed to CVE-2024-56337. + if (JreCompat.getInstance().disableCanonCaches()) { + /* + * The canonical file name cache was enabled and is now disabled. + */ + log.warn(sm.getString("dirResourceSet.canonCaches.disabled", getFileBase(), + getRoot().getContext().getName())); + } else { /* - * Both upper and lower case versions of the current fileBase have the same canonical path so the file - * system must be case insensitive. + * The canonical file name cache could not be disabled (or Tomcat cannot confirm it has been disabled). This + * ResourceSet may be exposed to CVE-2024-56337. */ - } catch (IOException ioe) { - log.warn(sm.getString("dirResourceSet.isCaseSensitive.fail", getFileBase().getAbsolutePath()), ioe); + throw new IllegalStateException(sm.getString("dirResourceSet.canonCaches.enabled", getFileBase(), + getRoot().getContext().getName())); } - - return false; } private String getLockKey(String path) { - // Normalize path to ensure that the same key is used for the same path. - String normalisedPath = RequestUtil.normalize(path); - if (caseSensitive) { - return normalisedPath; - } - return normalisedPath.toLowerCase(Locale.ENGLISH); + /* + * Normalize path to ensure that the same key is used for the same path. Always convert path to lower case as + * the file system may be case insensitive. A minor performance improvement is possible by removing the + * conversion to lower case for case sensitive file systems but confirming that all the directories within a + * DirResourceSet are case sensitive is much harder than it might first appear due to various edge cases. In + * particular, Windows can make individual directories case sensitive and File.getCanonicalPath() doesn't return + * the canonical file name on Linux for some case insensitive file systems (such as mounted Windows shares). + */ + return RequestUtil.normalize(path).toLowerCase(Locale.ENGLISH); } @Override public ResourceLock lockForRead(String path) { String key = getLockKey(path); - ResourceLock resourceLock = null; + ResourceLock resourceLock; synchronized (resourceLocksByPathLock) { /* * Obtain the ResourceLock and increment the usage count inside the sync to ensure that that map always has @@ -411,7 +414,7 @@ @Override public ResourceLock lockForWrite(String path) { String key = getLockKey(path); - ResourceLock resourceLock = null; + ResourceLock resourceLock; synchronized (resourceLocksByPathLock) { /* * Obtain the ResourceLock and increment the usage count inside the sync to ensure that that map always has diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/EmptyResource.java tomcat10-10.1.52/java/org/apache/catalina/webresources/EmptyResource.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/EmptyResource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/EmptyResource.java 2026-01-23 19:33:36.000000000 +0000 @@ -99,7 +99,7 @@ } else { try { return file.getCanonicalPath(); - } catch (IOException e) { + } catch (IOException ioe) { return null; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/EmptyResourceSet.java tomcat10-10.1.52/java/org/apache/catalina/webresources/EmptyResourceSet.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/EmptyResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/EmptyResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -143,6 +143,25 @@ /** * {@inheritDoc} *

          + * Calls to this method will be ignored as this implementation does not allow linking. + */ + @Override + public void setAllowLinking(boolean allowLinking) { + } + + /** + * {@inheritDoc} + *

          + * Calls to this method always return {@code false} as this implementation does not allow linking. + */ + @Override + public boolean getAllowLinking() { + return false; + } + + /** + * {@inheritDoc} + *

          * This implementation always returns true. */ @Override diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/ExtractingRoot.java tomcat10-10.1.52/java/org/apache/catalina/webresources/ExtractingRoot.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/ExtractingRoot.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/ExtractingRoot.java 2026-01-23 19:33:36.000000000 +0000 @@ -79,8 +79,7 @@ private File getExpansionTarget() { File tmpDir = (File) getContext().getServletContext().getAttribute(ServletContext.TEMPDIR); - File expansionTarget = new File(tmpDir, APPLICATION_JARS_DIR); - return expansionTarget; + return new File(tmpDir, APPLICATION_JARS_DIR); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/FileResource.java tomcat10-10.1.52/java/org/apache/catalina/webresources/FileResource.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/FileResource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/FileResource.java 2026-01-23 19:33:36.000000000 +0000 @@ -267,9 +267,9 @@ try { BasicFileAttributes attrs = Files.readAttributes(resource.toPath(), BasicFileAttributes.class); return attrs.creationTime().toMillis(); - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(sm.getString("fileResource.getCreationFail", resource.getPath()), e); + log.debug(sm.getString("fileResource.getCreationFail", resource.getPath()), ioe); } return 0; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/FileResourceSet.java tomcat10-10.1.52/java/org/apache/catalina/webresources/FileResourceSet.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/FileResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/FileResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -86,7 +86,7 @@ if (webAppMount.startsWith(path)) { String name = path.substring(0, path.length() - 1); name = name.substring(name.lastIndexOf('/') + 1); - if (name.length() > 0) { + if (!name.isEmpty()) { return new VirtualResource(root, path, name); } } @@ -160,7 +160,7 @@ @Override protected void checkType(File file) { - if (file.isFile() == false) { + if (!file.isFile()) { throw new IllegalArgumentException( sm.getString("fileResourceSet.notFile", getBase(), File.separator, getInternalPath())); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/JarContents.java tomcat10-10.1.52/java/org/apache/catalina/webresources/JarContents.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/JarContents.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/JarContents.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ package org.apache.catalina.webresources; import java.util.BitSet; +import java.util.Collection; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -27,8 +28,7 @@ * from the beginning of the key. The hash methods are simple but good enough for this purpose. */ public final class JarContents { - private final BitSet bits1; - private final BitSet bits2; + /** * Constant used by a typical hashing method. */ @@ -44,6 +44,10 @@ */ private static final int TABLE_SIZE = 2048; + private final BitSet bits1 = new BitSet(TABLE_SIZE); + private final BitSet bits2 = new BitSet(TABLE_SIZE); + + /** * Parses the passed-in jar and populates the bit array. * @@ -51,39 +55,67 @@ */ public JarContents(JarFile jar) { Enumeration entries = jar.entries(); - bits1 = new BitSet(TABLE_SIZE); - bits2 = new BitSet(TABLE_SIZE); - while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); - String name = entry.getName(); - int startPos = 0; + processEntry(entry); + } + } - // If the path starts with a slash, that's not useful information. - // Skipping it increases the significance of our key by - // removing an insignificant character. - boolean precedingSlash = name.charAt(0) == '/'; - if (precedingSlash) { - startPos = 1; - } - // Find the correct table slot - int pathHash1 = hashcode(name, startPos, HASH_PRIME_1); - int pathHash2 = hashcode(name, startPos, HASH_PRIME_2); + /** + * Populates the bit array from the provided set of JAR entries. + * + * @param entries The set of entries for the JAR file being processed + */ + public JarContents(Collection entries) { + for (JarEntry entry : entries) { + processEntry(entry); + } + } - bits1.set(pathHash1 % TABLE_SIZE); - bits2.set(pathHash2 % TABLE_SIZE); - // While directory entry names always end in "/", application code - // may look them up without the trailing "/". Add this second form. - if (entry.isDirectory()) { - pathHash1 = hashcode(name, startPos, name.length() - 1, HASH_PRIME_1); - pathHash2 = hashcode(name, startPos, name.length() - 1, HASH_PRIME_2); + private void processEntry(JarEntry entry) { + String name = entry.getName(); + int startPos = 0; + + // If the path starts with a slash, that's not useful information. + // Skipping it increases the significance of our key by + // removing an insignificant character. + boolean precedingSlash = name.charAt(0) == '/'; + if (precedingSlash) { + startPos = 1; + } - bits1.set(pathHash1 % TABLE_SIZE); - bits2.set(pathHash2 % TABLE_SIZE); + // Versioned entries should be added to the table according to their real name + if (name.startsWith("META-INF/versions/", startPos)) { + int i = name.indexOf('/', 18 + startPos); + if (i > 0) { + int version = Integer.parseInt(name.substring(18 + startPos, i)); + if (version <= Runtime.version().feature()) { + startPos = i + 1; + } + } + if (startPos == name.length()) { + return; } } + + // Find the correct table slot + int pathHash1 = hashcode(name, startPos, HASH_PRIME_1); + int pathHash2 = hashcode(name, startPos, HASH_PRIME_2); + + bits1.set(pathHash1 % TABLE_SIZE); + bits2.set(pathHash2 % TABLE_SIZE); + + // While directory entry names always end in "/", application code + // may look them up without the trailing "/". Add this second form. + if (entry.isDirectory()) { + pathHash1 = hashcode(name, startPos, name.length() - 1, HASH_PRIME_1); + pathHash2 = hashcode(name, startPos, name.length() - 1, HASH_PRIME_2); + + bits1.set(pathHash1 % TABLE_SIZE); + bits2.set(pathHash2 % TABLE_SIZE); + } } /** @@ -116,7 +148,7 @@ * Method that identifies whether a given path MIGHT be in this jar. Uses the Bloom filter mechanism. * * @param path Requested path. Sometimes starts with "/WEB-INF/classes". - * @param webappRoot The value of the webapp location, which can be stripped from the path. Typically is + * @param webappRoot The value of the webapp location, which can be stripped from the path. Typically it is * "/WEB-INF/classes". * * @return Whether the prefix of the path is known to be in this jar. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/JarWarResource.java tomcat10-10.1.52/java/org/apache/catalina/webresources/JarWarResource.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/JarWarResource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/JarWarResource.java 2026-01-23 19:33:36.000000000 +0000 @@ -54,19 +54,18 @@ InputStream isInWar = warFile.getInputStream(jarFileInWar); jarIs = new JarInputStream(isInWar); - entry = jarIs.getNextJarEntry(); - while (entry != null && !entry.getName().equals(getResource().getName())) { + do { entry = jarIs.getNextJarEntry(); - } + } while (entry != null && !entry.getName().equals(getResource().getName())); if (entry == null) { return null; } return new JarInputStreamWrapper(entry, jarIs); - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(sm.getString("jarResource.getInputStreamFail", getResource().getName(), getBaseUrl()), e); + log.debug(sm.getString("jarResource.getInputStreamFail", getResource().getName(), getBaseUrl()), ioe); } // Ensure jarIs is closed if there is an exception entry = null; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/JarWarResourceSet.java tomcat10-10.1.52/java/org/apache/catalina/webresources/JarWarResourceSet.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/JarWarResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/JarWarResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,6 +28,7 @@ import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.jar.Manifest; +import java.util.zip.ZipFile; import org.apache.catalina.LifecycleException; import org.apache.catalina.WebResource; @@ -141,16 +142,39 @@ if (jarFileIs != null) { try { jarFileIs.close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } } } + WebResourceRoot root = getRoot(); + if (root.getArchiveIndexStrategyEnum().getUsesBloom()) { + jarContents = new JarContents(archiveEntries.values()); + retainBloomFilterForArchives = root.getArchiveIndexStrategyEnum().getRetain(); + } return archiveEntries; } } + + /** + * {@inheritDoc} + *

          + * JarWar needs to generate jarContents for the inner JAR, not the outer WAR. + */ + @Override + protected JarFile openJarFile() throws IOException { + synchronized (archiveLock) { + if (archive == null) { + archive = new JarFile(new File(getBase()), true, ZipFile.OPEN_READ, Runtime.version()); + // Don't populate JarContents here. Populate at the end of getArchiveEntries() + } + archiveUseCount++; + return archive; + } + } + protected void processArchivesEntriesForMultiRelease() { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/webresources/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,7 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +abstractArchiveResourceSet.archiveCloseFailed=Error closing archive. Archive may still be open. abstractArchiveResourceSet.setReadOnlyFalse=Archive based WebResourceSets such as those based on JARs are hard-coded to be read-only and may not be configured to be read-write abstractFileResourceSet.canonicalfileCheckFailed=Resource for web application [{0}] at path [{1}] was not loaded as the canonical path [{2}] did not match. Use of symlinks is one possible cause. @@ -30,12 +31,14 @@ cache.objectMaxSizeTooBig=The value of [{0}] KiB for objectMaxSize is larger than the limit of maxSize/20 so has been reduced to [{1}] KiB cache.objectMaxSizeTooBigBytes=The value specified for the maximum object size to cache [{0}] KiB is greater than Integer.MAX_VALUE bytes which is the maximum size that can be cached. The limit will be set to Integer.MAX_VALUE bytes. cache.sizeTracking.add=Increased cache size by [{0}] for item [{1}] at [{2}] making total cache size [{3}] -cache.sizeTracking.removed=Decreased cache size by [{0}] for item [{1}] at [{2}] making total cache size [{3}] +cache.sizeTracking.remove=Decreased cache size by [{0}] for item [{1}] at [{2}] making total cache size [{3}] cachedResource.invalidURL=Unable to create an instance of CachedResourceURLStreamHandler because the URL [{0}] is malformed classpathUrlStreamHandler.notFound=Unable to load the resource [{0}] using the thread context class loader or the current class''s class loader +dirResourceSet.canonCaches.disabled=Disabled the global canonical file name cache to protect against CVE-2024-56337 when starting the WebResourceSet at [{0}] which is part of the web application [{1}] +dirResourceSet.canonCaches.enabled=Unable to disable the global canonical file name cache or confirm that it is disabled when starting the WebResourceSet at [{0}] which is part of the web application [{1}]. The WebResourceSet may be exposed to CVE-2024-56337. dirResourceSet.isCaseSensitive.fail=Error trying to determine if file system at [{0}] is case sensitive so assuming it is not case sensitive dirResourceSet.manifestFail=Failed to read manifest from [{0}] dirResourceSet.notDirectory=The directory specified by base and internal path [{0}]{1}[{2}] does not exist. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/webresources/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,7 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +abstractArchiveResourceSet.archiveCloseFailed=Erreur lors de la fermeture de l'archive, elle pourrait toujours être ouverte abstractArchiveResourceSet.setReadOnlyFalse=Les archives basées sur WebResourceSets telles que celles des JARs sont fixées comme étant en lecture seule et ne peuvent être configurées en lecture écriture abstractFileResourceSet.canonicalfileCheckFailed=La ressource de l''application web [{0}] du chemin [{1}] n''a pas été chargée car le chemin canonique [{2}] ne correspond pas; l''utilisation de liens symboliques peut être une cause possible @@ -30,7 +31,7 @@ cache.objectMaxSizeTooBig=La valeur [{0}] KiB pour l''objectMaxSize est plus grade que la limite de maxSize/20 son elle a été réduite à [{1}] KiB\n cache.objectMaxSizeTooBigBytes=La valeur de taille d''objet maximale pouvant être mis en cache de [{0}] KiB est supérieure à Integer.MAX_VALUE qui est le maximum, la limite a donc été fixée à Integer.MAX_VALUE octets cache.sizeTracking.add=Augmentation de la taille du cache de [{0}] pour l''entrée [{1}] à [{2}] portant la taille totale du cache à [{3}] -cache.sizeTracking.removed=Diminution de la taille du cache de [{0}] pour l''entrée [{1}] à [{2}] portant la taille totale du cache à [{3}] +cache.sizeTracking.remove=Diminution de la taille du cache de [{0}] pour l''entrée [{1}] à [{2}] portant la taille totale du cache à [{3}] cachedResource.invalidURL=La création d''une instance de CachedResourceURLStreamHandler a échouée car l''URL [{0}] est malformée diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/webresources/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,7 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +abstractArchiveResourceSet.archiveCloseFailed=アーカイブを閉じる際にエラーが発生しました。アーカイブがまだ開いている可能性があります。 abstractArchiveResourceSet.setReadOnlyFalse=JAR に基づく WebResourceSet などのアーカイブ ベースの WebResourceSet は、読み取り専用にハードコードされており、読み取り/書き込み可能に構成されていない可能性があります abstractFileResourceSet.canonicalfileCheckFailed=正規パス [{2}] が一致しなかったため、パス [{1}] のWebアプリケーション [{0}] のリソースが読み込まれませんでした。 シンボリックリンクの使用は、考えられる原因の1つです。 @@ -30,7 +31,7 @@ cache.objectMaxSizeTooBig=objectMaxSizeの [{0}] KiBの値がmaxSize / 20の制限より大きいため、[{1}] KiBに減少しました cache.objectMaxSizeTooBigBytes=キャッシュ可能なオブジェクトサイズの最大値に指定された [{0}] KiB は Integer.MAX_VALUE バイトを越えています。最大値に Integer.MAX_VALUE を設定します。 cache.sizeTracking.add=[{2}] のキャッシュエントリ[{1}] のキャッシュサイズが [{0}] に増えたため、合計キャッシュサイズは [{3}] になりました -cache.sizeTracking.removed=[{2}] のキャッシュエントリ [{1}] のキャッシュサイズが [{0}] に減ったため、合計キャッシュサイズは [{3}] になりました +cache.sizeTracking.remove=[{2}] のキャッシュエントリ [{1}] のキャッシュサイズが [{0}] に減ったため、合計キャッシュサイズは [{3}] になりました cachedResource.invalidURL=URL [{0}] は不正です。CachedResourceURLStreamHandler インスタンスを生成できません diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/webresources/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,8 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +dirResourceSet.notDirectory=Директория указанная по основному и внутреннему пути [{0}]{1}[{2}] не существует. + extractingRoot.targetFailed=Ошибка создания директории [{0}] для распакованных JAR файлов standardRoot.createUnknownType=Невозможно создать WebResourceSet неизвестного типа [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/StandardRoot.java tomcat10-10.1.52/java/org/apache/catalina/webresources/StandardRoot.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/StandardRoot.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/StandardRoot.java 2026-01-23 19:33:36.000000000 +0000 @@ -67,6 +67,7 @@ private Context context; private boolean allowLinking = false; + private boolean readOnly = false; private final List preResources = new ArrayList<>(); private WebResourceSet main; private final List classResources = new ArrayList<>(); @@ -118,7 +119,7 @@ // Set because we don't want duplicates // LinkedHashSet to retain the order. It is the order of the - // WebResourceSet that matters but it is simpler to retain the order + // WebResourceSet that matters, but it is simpler to retain the order // over all of the JARs. HashSet result = new LinkedHashSet<>(); for (List list : allResources) { @@ -146,7 +147,7 @@ } } } - if (result.size() == 0) { + if (result.isEmpty()) { return null; } return result; @@ -240,7 +241,7 @@ throw new IllegalStateException(sm.getString("standardRoot.checkStateNotStarted")); } - if (path == null || path.length() == 0 || !path.startsWith("/")) { + if (path == null || !path.startsWith("/")) { throw new IllegalArgumentException(sm.getString("standardRoot.invalidPath", path)); } @@ -254,7 +255,7 @@ // convert it to '/' result = RequestUtil.normalize(path, false); } - if (result == null || result.length() == 0 || !result.startsWith("/")) { + if (result == null || !result.startsWith("/")) { throw new IllegalArgumentException(sm.getString("standardRoot.invalidPathNormal", path, result)); } @@ -262,7 +263,7 @@ } protected final WebResource getResourceInternal(String path, boolean useClassLoaderResources) { - WebResource result = null; + WebResource result; WebResource virtual = null; WebResource mainEmpty = null; for (List list : allResources) { @@ -321,7 +322,7 @@ } } - if (result.size() == 0) { + if (result.isEmpty()) { result.add(main.getResource(path)); } @@ -657,6 +658,16 @@ } + @Override + public boolean isReadOnly() { + return (readOnly || main == null || main.isReadOnly()); + } + + @Override + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + // ----------------------------------------------------------- JMX Lifecycle @Override protected String getDomainInternal() { @@ -665,10 +676,7 @@ @Override protected String getObjectNameKeyProperties() { - StringBuilder keyProperties = new StringBuilder("type=WebResourceRoot"); - keyProperties.append(context.getMBeanKeyProperties()); - - return keyProperties.toString(); + return "type=WebResourceRoot" + context.getMBeanKeyProperties(); } // --------------------------------------------------------------- Lifecycle @@ -743,6 +751,7 @@ } if (f.isDirectory()) { mainResourceSet = new DirResourceSet(this, "/", f.getAbsolutePath(), "/"); + mainResourceSet.setReadOnly(readOnly); } else if (f.isFile() && docBase.endsWith(".war")) { mainResourceSet = new WarResourceSet(this, "/", f.getAbsolutePath()); } else { @@ -781,7 +790,7 @@ trackedResource.getCreatedBy()); try { trackedResource.close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } @@ -811,11 +820,11 @@ private final String archivePath; BaseLocation(URL url) { - File f = null; + File f; if ("jar".equals(url.getProtocol()) || "war".equals(url.getProtocol())) { String jarUrl = url.toString(); - int endOfFileUrl = -1; + int endOfFileUrl; if ("jar".equals(url.getProtocol())) { endOfFileUrl = jarUrl.indexOf("!/"); } else { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/TomcatJarInputStream.java tomcat10-10.1.52/java/org/apache/catalina/webresources/TomcatJarInputStream.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/TomcatJarInputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/TomcatJarInputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,7 @@ import java.util.zip.ZipEntry; /** - * The purpose of this sub-class is to obtain references to the JarEntry objects for META-INF/ and META-INF/MANIFEST.MF + * The purpose of this subclass is to obtain references to the JarEntry objects for META-INF/ and META-INF/MANIFEST.MF * that are otherwise swallowed by the JarInputStream implementation. */ public class TomcatJarInputStream extends JarInputStream { @@ -42,7 +42,7 @@ ZipEntry ze = super.createZipEntry(name); if (metaInfEntry == null && "META-INF/".equals(name)) { metaInfEntry = (JarEntry) ze; - } else if (manifestEntry == null && "META-INF/MANIFESR.MF".equals(name)) { + } else if (manifestEntry == null && "META-INF/MANIFEST.MF".equals(name)) { manifestEntry = (JarEntry) ze; } return ze; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java tomcat10-10.1.52/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java --- tomcat10-10.1.34/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,7 +45,7 @@ private static TomcatURLStreamHandlerFactory getInstanceInternal(boolean register) { - // Double checked locking. OK because instance is volatile. + // Double-checked locking. OK because instance is volatile. if (instance == null) { synchronized (TomcatURLStreamHandlerFactory.class) { if (instance == null) { @@ -75,7 +75,7 @@ /** - * Prevent this this factory from registering with the JVM. May be called more than once. + * Prevent this factory from registering with the JVM. May be called more than once. * * @return true if the factory is already disabled or was successfully disabled as a result of this * call. false if the factory was already registered prior to this call. diff -Nru tomcat10-10.1.34/java/org/apache/coyote/AbstractProcessor.java tomcat10-10.1.52/java/org/apache/coyote/AbstractProcessor.java --- tomcat10-10.1.34/java/org/apache/coyote/AbstractProcessor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/AbstractProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -375,8 +375,8 @@ try { // Validate and write response headers prepareResponse(); - } catch (IOException e) { - handleIOException(e); + } catch (IOException ioe) { + handleIOException(ioe); } } break; @@ -385,8 +385,8 @@ action(ActionCode.COMMIT, null); try { finishResponse(); - } catch (IOException e) { - handleIOException(e); + } catch (IOException ioe) { + handleIOException(ioe); } break; } @@ -397,8 +397,8 @@ case EARLY_HINTS: { try { earlyHints(); - } catch (IOException e) { - handleIOException(e); + } catch (IOException ioe) { + handleIOException(ioe); } break; } @@ -406,9 +406,9 @@ action(ActionCode.COMMIT, null); try { flush(); - } catch (IOException e) { - handleIOException(e); - response.setErrorException(e); + } catch (IOException ioe) { + handleIOException(ioe); + response.setErrorException(ioe); } break; } @@ -679,7 +679,7 @@ /** * {@inheritDoc} *

          - * Sub-classes of this base class represent a single request/response pair. The timeout to be processed is, + * Subclasses of this base class represent a single request/response pair. The timeout to be processed is, * therefore, the Servlet asynchronous processing timeout. */ @Override @@ -734,7 +734,7 @@ /** - * When committing the response, we have to validate the set of headers, as well as setup the response filters. + * When committing the response, we have to validate the set of headers, as well as set up the response filters. * * @throws IOException IO exception during commit */ @@ -1074,9 +1074,9 @@ // Set the socket wrapper so the access log can read the socket related // information (e.g. client IP) setSocketWrapper(socketWrapper); - // Setup the minimal request information - request.setStartTimeNanos(System.nanoTime()); - // Setup the minimal response information + // Set up the minimal request information + request.markStartTime(); + // Set up the minimal response information response.setStatus(400); response.setError(); getAdapter().log(request, response, 0); diff -Nru tomcat10-10.1.34/java/org/apache/coyote/AbstractProcessorLight.java tomcat10-10.1.52/java/org/apache/coyote/AbstractProcessorLight.java --- tomcat10-10.1.34/java/org/apache/coyote/AbstractProcessorLight.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/AbstractProcessorLight.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,7 +33,7 @@ */ public abstract class AbstractProcessorLight implements Processor { - private Set dispatches = new CopyOnWriteArraySet<>(); + private final Set dispatches = new CopyOnWriteArraySet<>(); @Override @@ -75,7 +75,7 @@ } /* - * If state is already CLOSED don't call asyncPostProcess() as that will likely change the the state to some + * If state is already CLOSED don't call asyncPostProcess() as that will likely change the state to some * other value causing processing to continue when it should cease. The AsyncStateMachine will be recycled * as part of the Processor clean-up on CLOSED so it doesn't matter what state it is left in at this point. */ @@ -173,7 +173,7 @@ protected abstract SocketState service(SocketWrapperBase socketWrapper) throws IOException; /** - * Process an in-progress request that is not longer in standard HTTP mode. Uses currently include Servlet 3.0 Async + * Process an in-progress request that is no longer in standard HTTP mode. Uses currently include Servlet 3.0 Async * and HTTP upgrade connections. Further uses may be added in the future. These will typically start as HTTP * requests. * diff -Nru tomcat10-10.1.34/java/org/apache/coyote/AbstractProtocol.java tomcat10-10.1.52/java/org/apache/coyote/AbstractProtocol.java --- tomcat10-10.1.34/java/org/apache/coyote/AbstractProtocol.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/AbstractProtocol.java 2026-01-23 19:33:36.000000000 +0000 @@ -616,14 +616,14 @@ // Component not pre-registered so register it oname = createObjectName(); if (oname != null) { - Registry.getRegistry(null, null).registerComponent(this, oname, null); + Registry.getRegistry(null).registerComponent(this, oname, null); } } if (this.domain != null) { ObjectName rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName()); this.rgOname = rgOname; - Registry.getRegistry(null, null).registerComponent(getHandler().getGlobal(), rgOname, null); + Registry.getRegistry(null).registerComponent(getHandler().getGlobal(), rgOname, null); } String endpointName = getName(); @@ -642,9 +642,7 @@ } endpoint.start(); - monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(() -> { - startAsyncTimeout(); - }, 0, 60, TimeUnit.SECONDS); + monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(this::startAsyncTimeout, 0, 60, TimeUnit.SECONDS); } @@ -736,7 +734,7 @@ } finally { if (oname != null) { if (mserver == null) { - Registry.getRegistry(null, null).unregisterComponent(oname); + Registry.getRegistry(null).unregisterComponent(oname); } else { // Possibly registered with a different MBeanServer try { @@ -749,7 +747,7 @@ ObjectName rgOname = getGlobalRequestProcessorMBeanName(); if (rgOname != null) { - Registry.getRegistry(null, null).unregisterComponent(rgOname); + Registry.getRegistry(null).unregisterComponent(rgOname); } } } @@ -853,7 +851,7 @@ String negotiatedProtocol = wrapper.getNegotiatedProtocol(); // OpenSSL typically returns null whereas JSSE typically // returns "" when no protocol is negotiated - if (negotiatedProtocol != null && negotiatedProtocol.length() > 0) { + if (negotiatedProtocol != null && !negotiatedProtocol.isEmpty()) { UpgradeProtocol upgradeProtocol = getProtocol().getNegotiatedProtocol(negotiatedProtocol); if (upgradeProtocol != null) { processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter()); @@ -900,7 +898,7 @@ processor.setSslSupport(wrapper.getSslSupport()); - SocketState state = SocketState.CLOSED; + SocketState state; do { state = processor.process(wrapper, status); @@ -1006,7 +1004,7 @@ // Connection closed. OK to recycle the processor. // Processors handling upgrades require additional clean-up // before release. - if (processor != null && processor.isUpgrade()) { + if (processor.isUpgrade()) { UpgradeToken upgradeToken = processor.getUpgradeToken(); HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler(); InstanceManager instanceManager = upgradeToken.getInstanceManager(); @@ -1019,9 +1017,9 @@ } finally { try { instanceManager.destroyInstance(httpUpgradeHandler); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - getLog().error(sm.getString("abstractConnectionHandler.error"), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + getLog().error(sm.getString("abstractConnectionHandler.error"), t); } upgradeToken.getContextBind().unbind(false, oldCL); } @@ -1038,14 +1036,19 @@ return state; } catch (SocketException e) { // SocketExceptions are normal - getLog().debug(sm.getString("abstractConnectionHandler.socketexception.debug"), e); - } catch (IOException e) { + if (getLog().isDebugEnabled()) { + getLog().debug(sm.getString("abstractConnectionHandler.socketexception.debug"), e); + } + } catch (IOException ioe) { // IOExceptions are normal - getLog().debug(sm.getString("abstractConnectionHandler.ioexception.debug"), e); + if (getLog().isDebugEnabled()) { + getLog().debug(sm.getString("abstractConnectionHandler.ioexception.debug"), ioe); + } } catch (ProtocolException e) { - // Protocol exceptions normally mean the client sent invalid or - // incomplete data. - getLog().debug(sm.getString("abstractConnectionHandler.protocolexception.debug"), e); + // Protocol exceptions normally mean the client sent invalid or incomplete data. + if (getLog().isDebugEnabled()) { + getLog().debug(sm.getString("abstractConnectionHandler.protocolexception.debug"), e); + } } // Future developers: if you discover any other // rare-but-nonfatal exceptions, catch them here, and log as @@ -1056,12 +1059,12 @@ // Worst case, it isn't recoverable and the attempt at logging // will trigger another OOME. getLog().error(sm.getString("abstractConnectionHandler.oome"), oome); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); // any other exception or error is odd. Here we log it // with "ERROR" level, so it will show up even on // less-than-verbose logs. - getLog().error(sm.getString("abstractConnectionHandler.error"), e); + getLog().error(sm.getString("abstractConnectionHandler.error"), t); } // Make sure socket/processor is removed from the list of current @@ -1136,7 +1139,7 @@ if (getLog().isTraceEnabled()) { getLog().trace("Register [" + processor + "] as [" + rpName + "]"); } - Registry.getRegistry(null, null).registerComponent(rp, rpName, null); + Registry.getRegistry(null).registerComponent(rp, rpName, null); rp.setRpName(rpName); } catch (Exception e) { getLog().warn(sm.getString("abstractProtocol.processorRegisterError"), e); @@ -1160,7 +1163,7 @@ if (getLog().isTraceEnabled()) { getLog().trace("Unregister [" + rpName + "]"); } - Registry.getRegistry(null, null).unregisterComponent(rpName); + Registry.getRegistry(null).unregisterComponent(rpName); rp.setRpName(null); } catch (Exception e) { getLog().warn(sm.getString("abstractProtocol.processorUnregisterError"), e); @@ -1200,7 +1203,7 @@ @Override public boolean push(Processor processor) { int cacheSize = handler.getProtocol().getProcessorCache(); - boolean offer = cacheSize == -1 ? true : size.get() < cacheSize; + boolean offer = cacheSize == -1 || size.get() < cacheSize; // avoid over growing our cache or add after we have stopped boolean result = false; if (offer) { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/ActionCode.java tomcat10-10.1.52/java/org/apache/coyote/ActionCode.java --- tomcat10-10.1.34/java/org/apache/coyote/ActionCode.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/ActionCode.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,6 @@ * * @see ProtocolHandler * @see ActionHook - * - * @author Remy Maucherat */ public enum ActionCode { @@ -138,7 +136,7 @@ ASYNC_DISPATCH, /** - * Callback to indicate the the actual dispatch has started and that the async state needs change. + * Callback to indicate the actual dispatch has started and that the async state needs change. */ ASYNC_DISPATCHED, diff -Nru tomcat10-10.1.34/java/org/apache/coyote/ActionHook.java tomcat10-10.1.52/java/org/apache/coyote/ActionHook.java --- tomcat10-10.1.34/java/org/apache/coyote/ActionHook.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/ActionHook.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,6 @@ * coyote connectors. Some standard actions are defined in ActionCode, however custom actions are permitted. The param * object can be used to pass and return information related with the action. This interface is typically implemented by * ProtocolHandlers, and the param is usually a Request or Response object. - * - * @author Remy Maucherat */ public interface ActionHook { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/Adapter.java tomcat10-10.1.52/java/org/apache/coyote/Adapter.java --- tomcat10-10.1.34/java/org/apache/coyote/Adapter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/Adapter.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,6 @@ /** * Adapter. This represents the entry point in a coyote-based servlet container. * - * @author Remy Maucherat - * * @see ProtocolHandler */ public interface Adapter { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/AsyncStateMachine.java tomcat10-10.1.52/java/org/apache/coyote/AsyncStateMachine.java --- tomcat10-10.1.34/java/org/apache/coyote/AsyncStateMachine.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/AsyncStateMachine.java 2026-01-23 19:33:36.000000000 +0000 @@ -353,7 +353,7 @@ return true; } else if (state == AsyncState.COMPLETING || state == AsyncState.DISPATCHING || state == AsyncState.DISPATCHED) { - // NOOP - App called complete() or dispatch() between the the + // NOOP - App called complete() or dispatch() between the // timeout firing and execution reaching this point return false; } else { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/CompressionConfig.java tomcat10-10.1.52/java/org/apache/coyote/CompressionConfig.java --- tomcat10-10.1.34/java/org/apache/coyote/CompressionConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/CompressionConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,6 +32,7 @@ import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.http.ResponseUtil; import org.apache.tomcat.util.http.parser.AcceptEncoding; +import org.apache.tomcat.util.http.parser.TE; import org.apache.tomcat.util.http.parser.TokenList; import org.apache.tomcat.util.res.StringManager; @@ -124,7 +125,7 @@ * applied */ public void setNoCompressionUserAgents(String noCompressionUserAgents) { - if (noCompressionUserAgents == null || noCompressionUserAgents.length() == 0) { + if (noCompressionUserAgents == null || noCompressionUserAgents.isEmpty()) { this.noCompressionUserAgents = null; } else { this.noCompressionUserAgents = Pattern.compile(noCompressionUserAgents); @@ -152,7 +153,7 @@ StringTokenizer tokens = new StringTokenizer(compressibleMimeType, ","); while (tokens.hasMoreTokens()) { String token = tokens.nextToken().trim(); - if (token.length() > 0) { + if (!token.isEmpty()) { values.add(token); } } @@ -192,6 +193,9 @@ return false; } + boolean useTransferEncoding = false; + boolean useContentEncoding = true; + MimeHeaders responseHeaders = response.getMimeHeaders(); // Check if content is not already compressed @@ -202,13 +206,19 @@ Set tokens = new HashSet<>(); try { TokenList.parseTokenList(responseHeaders.values("Content-Encoding"), tokens); - } catch (IOException e) { + } catch (IOException ioe) { // Because we are using StringReader, any exception here is a // Tomcat bug. - log.warn(sm.getString("compressionConfig.ContentEncodingParseFail"), e); + log.warn(sm.getString("compressionConfig.ContentEncodingParseFail"), ioe); return false; } - if (tokens.contains("gzip") || tokens.contains("br")) { + if (tokens.contains("identity")) { + // If identity, do not do content modifications + useContentEncoding = false; + } else if (tokens.contains("br") || tokens.contains("compress") || tokens.contains("dcb") || + tokens.contains("dcz") || tokens.contains("deflate") || tokens.contains("gzip") || + tokens.contains("pack200-gzip") || tokens.contains("zstd")) { + // Content should not be compressed twice return false; } } @@ -229,40 +239,62 @@ } } - // Check if the resource has a strong ETag - String eTag = responseHeaders.getHeader("ETag"); - if (eTag != null && !eTag.trim().startsWith("W/")) { - // Has an ETag that doesn't start with "W/..." so it must be a - // strong ETag - return false; - } - - // If processing reaches this far, the response might be compressed. - // Therefore, set the Vary header to keep proxies happy - ResponseUtil.addVaryFieldName(responseHeaders, "accept-encoding"); - - // Check if user-agent supports gzip encoding - // Only interested in whether gzip encoding is supported. Other - // encodings and weights can be ignored. - Enumeration headerValues = request.getMimeHeaders().values("accept-encoding"); + Enumeration headerValues = request.getMimeHeaders().values("TE"); boolean foundGzip = false; + // TE and accept-encoding seem to have equivalent syntax while (!foundGzip && headerValues.hasMoreElements()) { - List acceptEncodings = null; + List tes; try { - acceptEncodings = AcceptEncoding.parse(new StringReader(headerValues.nextElement())); + tes = TE.parse(new StringReader(headerValues.nextElement())); } catch (IOException ioe) { // If there is a problem reading the header, disable compression return false; } - for (AcceptEncoding acceptEncoding : acceptEncodings) { - if ("gzip".equalsIgnoreCase(acceptEncoding.getEncoding())) { + for (TE te : tes) { + if ("gzip".equalsIgnoreCase(te.getEncoding())) { + useTransferEncoding = true; foundGzip = true; break; } } } + // Check if the resource has a strong ETag + String eTag = responseHeaders.getHeader("ETag"); + if (!useTransferEncoding && eTag != null && !eTag.trim().startsWith("W/")) { + // Has an ETag that doesn't start with "W/..." so it must be a + // strong ETag + return false; + } + + if (useContentEncoding && !useTransferEncoding) { + // If processing reaches this far, the response might be compressed. + // Therefore, set the Vary header to keep proxies happy + ResponseUtil.addVaryFieldName(responseHeaders, "accept-encoding"); + + // Check if user-agent supports gzip encoding + // Only interested in whether gzip encoding is supported. Other + // encodings and weights can be ignored. + headerValues = request.getMimeHeaders().values("accept-encoding"); + while (!foundGzip && headerValues.hasMoreElements()) { + List acceptEncodings; + try { + acceptEncodings = AcceptEncoding.parse(new StringReader(headerValues.nextElement())); + } catch (IOException ioe) { + // If there is a problem reading the header, disable compression + return false; + } + + for (AcceptEncoding acceptEncoding : acceptEncodings) { + if ("gzip".equalsIgnoreCase(acceptEncoding.getEncoding())) { + foundGzip = true; + break; + } + } + } + } + if (!foundGzip) { return false; } @@ -286,8 +318,13 @@ // Compressed content length is unknown so mark it as such. response.setContentLength(-1); - // Configure the content encoding for compressed content - responseHeaders.setValue("Content-Encoding").setString("gzip"); + if (useTransferEncoding) { + // Configure the transfer encoding for compressed content + responseHeaders.addValue("Transfer-Encoding").setString("gzip"); + } else { + // Configure the content encoding for compressed content + responseHeaders.addValue("Content-Encoding").setString("gzip"); + } return true; } @@ -299,7 +336,7 @@ * @param sArray the StringArray * @param value string */ - private static boolean startsWithStringArray(String sArray[], String value) { + private static boolean startsWithStringArray(String[] sArray, String value) { if (value == null) { return false; } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/Constants.java tomcat10-10.1.52/java/org/apache/coyote/Constants.java --- tomcat10-10.1.34/java/org/apache/coyote/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,6 @@ /** * Constants. - * - * @author Remy Maucherat */ public final class Constants { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/ErrorState.java tomcat10-10.1.52/java/org/apache/coyote/ErrorState.java --- tomcat10-10.1.34/java/org/apache/coyote/ErrorState.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/ErrorState.java 2026-01-23 19:33:36.000000000 +0000 @@ -66,7 +66,7 @@ * * @param input The error state to compare to this one * - * @return The most severe error state from the the provided error state and this one + * @return The most severe error state from the provided error state and this one */ public ErrorState getMostSevere(ErrorState input) { if (input.severity > this.severity) { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/NonPipeliningProcessor.java tomcat10-10.1.52/java/org/apache/coyote/NonPipeliningProcessor.java --- tomcat10-10.1.34/java/org/apache/coyote/NonPipeliningProcessor.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/NonPipeliningProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.coyote; + +/** + * Marker interface used to indicate that the {@link Processor} does not implement pipe-lining of requests (e.g. + * HTTP/1.1 supports pipe-lining whereas HTTP/2 does not) which may enable some components to clear references sooner to + * aid GC. + */ +public interface NonPipeliningProcessor extends Processor { + +} diff -Nru tomcat10-10.1.34/java/org/apache/coyote/OutputBuffer.java tomcat10-10.1.52/java/org/apache/coyote/OutputBuffer.java --- tomcat10-10.1.34/java/org/apache/coyote/OutputBuffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/OutputBuffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,6 @@ /** * Output buffer. This class is used internally by the protocol implementation. All writes from higher level code should * happen via Response.doWrite(). - * - * @author Remy Maucherat */ public interface OutputBuffer { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/Processor.java tomcat10-10.1.52/java/org/apache/coyote/Processor.java --- tomcat10-10.1.34/java/org/apache/coyote/Processor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/Processor.java 2026-01-23 19:33:36.000000000 +0000 @@ -67,7 +67,7 @@ * Note: The name of this method originated with the Servlet 3.0 asynchronous processing but evolved over time to * represent a timeout that is triggered independently of the socket read/write timeouts. * - * @param now The time (as returned by {@link System#currentTimeMillis()} to use as the current time to determine + * @param now The time (as returned by {@link System#currentTimeMillis()}) to use as the current time to determine * whether the timeout has expired. If negative, the timeout will always be treated as if it has * expired. */ diff -Nru tomcat10-10.1.34/java/org/apache/coyote/ProtocolHandler.java tomcat10-10.1.52/java/org/apache/coyote/ProtocolHandler.java --- tomcat10-10.1.34/java/org/apache/coyote/ProtocolHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/ProtocolHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,9 +26,6 @@ * Abstract the protocol implementation, including threading, etc. This is the main interface to be implemented by a * coyote protocol. Adapter is the main interface to be implemented by a coyote servlet container. * - * @author Remy Maucherat - * @author Costin Manolache - * * @see Adapter */ public interface ProtocolHandler { @@ -131,7 +128,7 @@ /** * Close the server socket (to prevent further connections) if the server socket was bound on {@link #start()} - * (rather than on {@link #init()} but do not perform any further shutdown. + * (rather than on {@link #init()}) but do not perform any further shutdown. */ void closeServerSocketGraceful(); diff -Nru tomcat10-10.1.34/java/org/apache/coyote/Request.java tomcat10-10.1.52/java/org/apache/coyote/Request.java --- tomcat10-10.1.34/java/org/apache/coyote/Request.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/Request.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,10 @@ import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; +import java.time.Instant; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -33,6 +35,7 @@ import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.buf.UDecoder; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.http.Parameters; import org.apache.tomcat.util.http.ServerCookies; @@ -49,15 +52,6 @@ *

            *
          • "org.apache.tomcat.request" - allows access to the low-level request object in trusted applications *
          - * - * @author James Duncan Davidson [duncan@eng.sun.com] - * @author James Todd [gonzo@eng.sun.com] - * @author Jason Hunter [jch@eng.sun.com] - * @author Harish Prabandham - * @author Alex Cruikshank [alex@epitonic.com] - * @author Hans Bergsten [hans@gefionsoftware.com] - * @author Costin Manolache - * @author Remy Maucherat */ public final class Request { @@ -71,8 +65,8 @@ * another 3,000,000 years before it gets back to zero). * * Local testing shows that 5, 10, 50, 500 or 1000 threads can obtain 60,000,000+ IDs a second from a single - * AtomicLong. That is about about 17ns per request. It does not appear that the introduction of this counter will - * cause a bottleneck for request processing. + * AtomicLong. That is about 17ns per request. It does not appear that the introduction of this counter will cause a + * bottleneck for request processing. */ private static final AtomicLong requestIdGenerator = new AtomicLong(0); @@ -120,7 +114,7 @@ /** * Notes. */ - private final Object notes[] = new Object[Constants.MAX_NOTES]; + private final Object[] notes = new Object[Constants.MAX_NOTES]; /** @@ -164,6 +158,7 @@ private long bytesRead = 0; // Time of the request - useful to avoid repeated calls to System.currentTime private long startTimeNanos = -1; + private Instant startInstant = null; private long threadId = 0; private int available = 0; @@ -236,7 +231,7 @@ public boolean isReady() { // Assume read is not possible - boolean ready = false; + boolean ready; synchronized (nonBlockingStateLock) { if (registeredForRead) { fireListener = true; @@ -316,10 +311,36 @@ return schemeMB; } + /** + * Get a MessageBytes instance that holds the current request's HTTP method. + * + * @return a MessageBytes instance that holds the current request's HTTP method. + * + * @deprecated Use {@link #getMethod()}, {@link Request#setMethod(String)} and {@link #setMethod(byte[], int, int)} + */ + @Deprecated public MessageBytes method() { return methodMB; } + public void setMethod(String method) { + methodMB.setString(method); + } + + public void setMethod(byte[] buf, int start, int len) { + String method = Method.bytesToString(buf, start, len); + if (method == null) { + methodMB.setBytes(buf, start, len); + method = methodMB.toStringType(); + } else { + methodMB.setString(method); + } + } + + public String getMethod() { + return methodMB.toStringType(); + } + public MessageBytes requestURI() { return uriMB; } @@ -511,17 +532,13 @@ response.setRequest(this); } - protected void setHook(ActionHook hook) { + void setHook(ActionHook hook) { this.hook = hook; } public void action(ActionCode actionCode, Object param) { if (hook != null) { - if (param == null) { - hook.action(actionCode, this); - } else { - hook.action(actionCode, param); - } + hook.action(actionCode, Objects.requireNonNullElse(param, this)); } } @@ -603,10 +620,7 @@ } public boolean getSupportsRelativeRedirects() { - if (protocol().equals("") || protocol().equals("HTTP/1.0")) { - return false; - } - return true; + return !protocol().equals("") && !protocol().equals("HTTP/1.0"); } @@ -704,21 +718,41 @@ return System.currentTimeMillis() - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTimeNanos); } + public long getStartTimeNanos() { + return startTimeNanos; + } + /** * @param startTime time * - * @deprecated This setter will be removed in Tomcat 11 + * @deprecated This setter will be removed in Tomcat 11. Use {@link #markStartTime()}. */ @Deprecated public void setStartTime(long startTime) { + startTimeNanos = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(startTime - System.currentTimeMillis()); + startInstant = Instant.now(); } - public long getStartTimeNanos() { - return startTimeNanos; - } - + /** + * Set the start time using the value provided by {@code System.nanoTime()}. + * + * @param startTimeNanos The value returned from {@code System.nanoTime()} at the point the requests started. + * + * @deprecated Unused. Will be removed in Tomcat 12 onwards. Use {@link #markStartTime()}. + */ + @Deprecated public void setStartTimeNanos(long startTimeNanos) { this.startTimeNanos = startTimeNanos; + startInstant = Instant.now(); + } + + public void markStartTime() { + startTimeNanos = System.nanoTime(); + startInstant = Instant.now(); + } + + public Instant getStartInstant() { + return startInstant; } public long getThreadId() { @@ -828,7 +862,17 @@ allDataReadEventSent.set(false); startTimeNanos = -1; + startInstant = null; threadId = 0; + + if (hook instanceof NonPipeliningProcessor) { + /* + * No requirement to maintain state between requests so clear the hook (a.k.a. Processor) and the input + * buffer to aid GC. + */ + setHook(null); + setInputBuffer(null); + } } // -------------------- Info -------------------- @@ -863,7 +907,7 @@ MediaType mediaType = null; try { mediaType = MediaType.parseMediaType(new StringReader(contentType)); - } catch (IOException e) { + } catch (IOException ioe) { // Ignore - null test below handles this } if (mediaType != null) { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/RequestInfo.java tomcat10-10.1.52/java/org/apache/coyote/RequestInfo.java --- tomcat10-10.1.34/java/org/apache/coyote/RequestInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/RequestInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,9 +25,7 @@ * Structure holding the Request and Response objects. It also holds statistical information about request processing * and provide management information about the requests being processed. Each thread uses a Request/Response pair that * is recycled on each request. This object provides a place to collect global low-level statistics - without having to - * deal with synchronization ( since each thread will have it's own RequestProcessorMX ). - * - * @author Costin Manolache + * deal with synchronization (since each thread will have its own RequestProcessorMX). */ public class RequestInfo { private RequestGroupInfo global = null; @@ -65,7 +63,7 @@ // This is useful for long-running requests only public String getMethod() { - return req.method().toString(); + return req.getMethod(); } public String getCurrentUri() { @@ -101,7 +99,7 @@ /** * Obtain the remote address for this connection as reported by an intermediate proxy (if any). * - * @return The remote address for the this connection + * @return The remote address for this connection */ public String getRemoteAddrForwarded() { String remoteAddrProxy = (String) req.getAttribute(Constants.REMOTE_ADDR_ATTRIBUTE); @@ -261,4 +259,18 @@ public void setLastRequestProcessingTime(long lastRequestProcessingTime) { this.lastRequestProcessingTime = lastRequestProcessingTime; } + + public void recycleStatistcs() { + this.bytesSent = 0; + this.bytesReceived = 0; + + this.processingTime = 0; + this.maxTime = 0; + this.maxRequestUri = null; + + this.requestCount = 0; + this.errorCount = 0; + + this.lastRequestProcessingTime = 0; + } } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/Response.java tomcat10-10.1.52/java/org/apache/coyote/Response.java --- tomcat10-10.1.34/java/org/apache/coyote/Response.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/Response.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,6 +23,7 @@ import java.nio.charset.Charset; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -40,13 +41,6 @@ /** * Response object. - * - * @author James Duncan Davidson [duncan@eng.sun.com] - * @author Jason Hunter [jch@eng.sun.com] - * @author James Todd [gonzo@eng.sun.com] - * @author Harish Prabandham - * @author Hans Bergsten [hans@gefionsoftware.com] - * @author Remy Maucherat */ public final class Response { @@ -93,7 +87,7 @@ /** * Notes. */ - final Object notes[] = new Object[Constants.MAX_NOTES]; + final Object[] notes = new Object[Constants.MAX_NOTES]; /** @@ -183,7 +177,7 @@ } - protected void setHook(ActionHook hook) { + void setHook(ActionHook hook) { this.hook = hook; } @@ -204,11 +198,7 @@ public void action(ActionCode actionCode, Object param) { if (hook != null) { - if (param == null) { - hook.action(actionCode, this); - } else { - hook.action(actionCode, param); - } + hook.action(actionCode, Objects.requireNonNullElse(param, this)); } } @@ -355,7 +345,7 @@ throw new IllegalStateException(); } - recycle(); + recycle(false); } @@ -555,7 +545,7 @@ MediaType m = null; try { m = MediaType.parseMediaType(new StringReader(type)); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore - null test below handles this } if (m == null) { @@ -576,7 +566,7 @@ // There is a charset so have to rebuild content-type without it this.contentType = m.toStringNoCharset(); charsetValue = charsetValue.trim(); - if (charsetValue.length() > 0) { + if (!charsetValue.isEmpty()) { try { charset = B2CConverter.getCharset(charsetValue); } catch (UnsupportedEncodingException e) { @@ -632,10 +622,13 @@ contentWritten += len - chunk.remaining(); } - // -------------------- public void recycle() { + recycle(true); + } + + private void recycle(boolean responseComplete) { contentType = null; contentLanguage = null; locale = DEFAULT_LOCALE; @@ -659,8 +652,20 @@ // update counters contentWritten = 0; + + if (responseComplete && hook instanceof NonPipeliningProcessor) { + /* + * No requirement to maintain state between responses so clear the hook (a.k.a. Processor) and the output + * buffer to aid GC. + * + * Only clear these between responses. They need to be retained when the response is reset. + */ + setHook(null); + setOutputBuffer(null); + } } + /** * Bytes written by application - i.e. before compression, chunking, etc. * @@ -752,7 +757,7 @@ return false; } // Assume write is not possible - boolean ready = false; + boolean ready; synchronized (nonBlockingStateLock) { if (registeredForWrite) { fireListener = true; diff -Nru tomcat10-10.1.34/java/org/apache/coyote/ajp/AbstractAjpProtocol.java tomcat10-10.1.52/java/org/apache/coyote/ajp/AbstractAjpProtocol.java --- tomcat10-10.1.34/java/org/apache/coyote/ajp/AbstractAjpProtocol.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/ajp/AbstractAjpProtocol.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,7 +29,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * The is the base implementation for the AJP protocol handlers. Implementations typically extend this base class rather + * This the base implementation for the AJP protocol handlers. Implementations typically extend this base class rather * than implement {@link org.apache.coyote.ProtocolHandler}. All of the implementations that ship with Tomcat are * implemented this way. * @@ -97,7 +97,7 @@ /** * Configure whether to aend an AJP flush packet when flushing. A flush packet is a zero byte AJP13 SEND_BODY_CHUNK * packet. mod_jk and mod_proxy_ajp interpret this as a request to flush data to the client. AJP always does flush - * at the and of the response, so if it is not important, that the packets get streamed up to the client, do not use + * at the end of the response, so if it is not important, that the packets get streamed up to the client, do not use * extra flush packets. For compatibility and to stay on the safe side, flush packets are enabled by default. * * @param ajpFlush The new flush setting @@ -257,8 +257,7 @@ @Override protected Processor createProcessor() { - AjpProcessor processor = new AjpProcessor(this, getAdapter()); - return processor; + return new AjpProcessor(this, getAdapter()); } @@ -273,7 +272,7 @@ public void start() throws Exception { if (getSecretRequired()) { String secret = getSecret(); - if (secret == null || secret.length() == 0) { + if (secret == null || secret.isEmpty()) { throw new IllegalArgumentException(sm.getString("ajpprotocol.noSecret")); } } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/ajp/AjpMessage.java tomcat10-10.1.52/java/org/apache/coyote/ajp/AjpMessage.java --- tomcat10-10.1.34/java/org/apache/coyote/ajp/AjpMessage.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/ajp/AjpMessage.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,16 +29,9 @@ * A single packet for communication between the web server and the container. Designed to be reused many times with no * creation of garbage. Understands the format of data types for these packets. Can be used (somewhat confusingly) for * both incoming and outgoing packets. - * - * @author Henri Gomez - * @author Dan Milstein - * @author Keith Wannamaker - * @author Kevin Seguin - * @author Costin Manolache */ public class AjpMessage { - private static final Log log = LogFactory.getLog(AjpMessage.class); /** @@ -47,21 +40,15 @@ protected static final StringManager sm = StringManager.getManager(AjpMessage.class); - // ------------------------------------------------------------ Constructor - - public AjpMessage(int packetSize) { buf = new byte[packetSize]; } - // ----------------------------------------------------- Instance Variables - - /** * Fixed size buffer. */ - protected final byte buf[]; + protected final byte[] buf; /** @@ -78,9 +65,6 @@ protected int len; - // --------------------------------------------------------- Public Methods - - /** * Prepare this packet for accumulating a message from the container to the web server. Set the write position to * just after the header (but leave the length unwritten, because it is as yet unknown). @@ -373,7 +357,7 @@ // ------------------------------------------------------ Protected Methods - protected static String hexLine(byte buf[], int start, int len) { + protected static String hexLine(byte[] buf, int start, int len) { StringBuilder sb = new StringBuilder(); for (int i = start; i < start + 16; i++) { if (i < len + 4) { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/ajp/AjpProcessor.java tomcat10-10.1.52/java/org/apache/coyote/ajp/AjpProcessor.java --- tomcat10-10.1.34/java/org/apache/coyote/ajp/AjpProcessor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/ajp/AjpProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,9 +27,6 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -51,6 +48,7 @@ import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState; import org.apache.tomcat.util.net.ApplicationBufferHandler; @@ -131,28 +129,17 @@ System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray, 0, pongMessage.getLen()); // Build Map of Java Servlet to Jakarta Servlet attribute names - Map m = new HashMap<>(); - m.put("jakarta.servlet.request.cipher_suite", "jakarta.servlet.request.cipher_suite"); - m.put("jakarta.servlet.request.key_size", "jakarta.servlet.request.key_size"); - m.put("jakarta.servlet.request.ssl_session", "jakarta.servlet.request.ssl_session"); - m.put("jakarta.servlet.request.X509Certificate", "jakarta.servlet.request.X509Certificate"); - m.put("javax.servlet.request.cipher_suite", "jakarta.servlet.request.cipher_suite"); - m.put("javax.servlet.request.key_size", "jakarta.servlet.request.key_size"); - m.put("javax.servlet.request.ssl_session", "jakarta.servlet.request.ssl_session"); - m.put("javax.servlet.request.X509Certificate", "jakarta.servlet.request.X509Certificate"); - jakartaAttributeMapping = Collections.unmodifiableMap(m); - - Set s = new HashSet<>(); - s.add("CERT_ISSUER"); - s.add("CERT_SUBJECT"); - s.add("CERT_COOKIE"); - s.add("HTTPS_SERVER_SUBJECT"); - s.add("CERT_FLAGS"); - s.add("HTTPS_SECRETKEYSIZE"); - s.add("CERT_SERIALNUMBER"); - s.add("HTTPS_SERVER_ISSUER"); - s.add("HTTPS_KEYSIZE"); - iisTlsAttributes = Collections.unmodifiableSet(s); + jakartaAttributeMapping = Map.of("jakarta.servlet.request.cipher_suite", "jakarta.servlet.request.cipher_suite", + "jakarta.servlet.request.key_size", "jakarta.servlet.request.key_size", + "jakarta.servlet.request.ssl_session", "jakarta.servlet.request.ssl_session", + "jakarta.servlet.request.X509Certificate", "jakarta.servlet.request.X509Certificate", + "javax.servlet.request.cipher_suite", "jakarta.servlet.request.cipher_suite", + "javax.servlet.request.key_size", "jakarta.servlet.request.key_size", + "javax.servlet.request.ssl_session", "jakarta.servlet.request.ssl_session", + "javax.servlet.request.X509Certificate", "jakarta.servlet.request.X509Certificate"); + + iisTlsAttributes = Set.of("CERT_ISSUER", "CERT_SUBJECT", "CERT_COOKIE", "HTTPS_SERVER_SUBJECT", "CERT_FLAGS", + "HTTPS_SECRETKEYSIZE", "CERT_SERIALNUMBER", "HTTPS_SERVER_ISSUER", "HTTPS_KEYSIZE"); } @@ -374,11 +361,11 @@ try { socketWrapper.write(true, pongMessageArray, 0, pongMessageArray.length); socketWrapper.flush(true); - } catch (IOException e) { + } catch (IOException ioe) { if (getLog().isDebugEnabled()) { - getLog().debug(sm.getString("ajpprocessor.pongFail"), e); + getLog().debug(sm.getString("ajpprocessor.pongFail"), ioe); } - setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); + setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe); } recycle(); continue; @@ -391,13 +378,15 @@ setErrorState(ErrorState.CLOSE_CONNECTION_NOW, null); break; } - request.setStartTimeNanos(System.nanoTime()); - } catch (IOException e) { - setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); + request.markStartTime(); + } catch (IOException ioe) { + setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe); break; } catch (Throwable t) { ExceptionUtils.handleThrowable(t); - getLog().debug(sm.getString("ajpprocessor.header.error"), t); + if (getLog().isDebugEnabled()) { + getLog().debug(sm.getString("ajpprocessor.header.error"), t); + } // 400 - Bad Request response.setStatus(400); setErrorState(ErrorState.CLOSE_CLEAN, t); @@ -410,7 +399,9 @@ prepareRequest(); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); - getLog().debug(sm.getString("ajpprocessor.request.prepare"), t); + if (getLog().isDebugEnabled()) { + getLog().debug(sm.getString("ajpprocessor.request.prepare"), t); + } // 500 - Internal Server Error response.setStatus(500); setErrorState(ErrorState.CLOSE_CLEAN, t); @@ -576,7 +567,7 @@ // Zero length message. return true; } else { - if (messageLength > message.getBuffer().length) { + if (messageLength > (buf.length - Constants.H_SIZE)) { // Message too long for the buffer // Need to trigger a 400 response String msg = sm.getString("ajpprocessor.header.tooLong", Integer.valueOf(messageLength), @@ -649,7 +640,7 @@ byte methodCode = requestHeaderMessage.getByte(); if (methodCode != Constants.SC_M_JK_STORED) { String methodName = Constants.getMethodForCode(methodCode - 1); - request.method().setString(methodName); + request.setMethod(methodName); } requestHeaderMessage.getBytes(request.protocol()); @@ -678,7 +669,7 @@ boolean contentLengthSet = false; int hCount = requestHeaderMessage.getInt(); for (int i = 0; i < hCount; i++) { - String hName = null; + String hName; // Header names are encoded as either an integer code starting // with 0xA0, or as a normal string (in which case the first @@ -686,7 +677,7 @@ int isc = requestHeaderMessage.peekInt(); int hId = isc & 0xFF; - MessageBytes vMB = null; + MessageBytes vMB; isc &= 0xFF00; if (0xA000 == isc) { requestHeaderMessage.getInt(); // To advance the read position @@ -836,12 +827,14 @@ break; case Constants.SC_A_STORED_METHOD: - requestHeaderMessage.getBytes(request.method()); + requestHeaderMessage.getBytes(tmpMB); + ByteChunk tmpBC = tmpMB.getByteChunk(); + request.setMethod(tmpBC.getBytes(), tmpBC.getStart(), tmpBC.getLength()); break; case Constants.SC_A_SECRET: requestHeaderMessage.getBytes(tmpMB); - if (secret != null && secret.length() > 0) { + if (secret != null && !secret.isEmpty()) { secretPresentInRequest = true; if (!tmpMB.equals(secret)) { response.setStatus(403); @@ -859,7 +852,7 @@ } // Check if secret was submitted if required - if (secret != null && secret.length() > 0 && !secretPresentInRequest) { + if (secret != null && !secret.isEmpty() && !secretPresentInRequest) { response.setStatus(403); setErrorState(ErrorState.CLOSE_CLEAN, null); } @@ -870,10 +863,9 @@ int pos = uriBC.indexOf("://", 0, 3, 4); int uriBCStart = uriBC.getStart(); - int slashPos = -1; if (pos != -1) { byte[] uriB = uriBC.getBytes(); - slashPos = uriBC.indexOf('/', pos + 3); + int slashPos = uriBC.indexOf('/', pos + 3); if (slashPos == -1) { slashPos = uriBC.getLength(); // Set URI as "/" @@ -905,9 +897,9 @@ protected void populateHost() { try { request.serverName().duplicate(request.localName()); - } catch (IOException e) { + } catch (IOException ioe) { response.setStatus(400); - setErrorState(ErrorState.CLOSE_CLEAN, e); + setErrorState(ErrorState.CLOSE_CLEAN, ioe); } } @@ -935,7 +927,7 @@ // Responses with certain status codes and/or methods are not permitted to include a response body. int statusCode = response.getStatus(); if (statusCode < 200 || statusCode == 204 || statusCode == 205 || statusCode == 304 || - request.method().equals("HEAD")) { + Method.HEAD.equals(request.getMethod())) { // No entity body swallowResponse = true; } @@ -1068,10 +1060,12 @@ if (empty && doRead) { try { refillReadBuffer(false); - } catch (IOException timeout) { - // Not ideal. This will indicate that data is available - // which should trigger a read which in turn will trigger - // another IOException and that one can be thrown. + } catch (IOException ioe) { + /* + * Probably a timeout. This approach isn't ideal but it works. Returning 1 will indicate that data is + * available which should trigger a read which in turn will trigger another IOException and that one can + * be thrown. + */ return 1; } } @@ -1313,8 +1307,8 @@ // Validate and write response headers try { prepareResponse(); - } catch (IOException e) { - setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); + } catch (IOException ioe) { + setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe); } } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/ajp/Constants.java tomcat10-10.1.52/java/org/apache/coyote/ajp/Constants.java --- tomcat10-10.1.34/java/org/apache/coyote/ajp/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/ajp/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,10 +19,10 @@ import java.util.HashMap; import java.util.Map; +import org.apache.tomcat.util.http.Method; + /** * Constants. - * - * @author Remy Maucherat */ public final class Constants { @@ -104,10 +104,10 @@ public static final int MAX_SEND_SIZE = MAX_PACKET_SIZE - SEND_HEAD_LEN; // Translates integer codes to names of HTTP methods - private static final String[] methodTransArray = - { "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "PROPFIND", "PROPPATCH", "MKCOL", "COPY", - "MOVE", "LOCK", "UNLOCK", "ACL", "REPORT", "VERSION-CONTROL", "CHECKIN", "CHECKOUT", "UNCHECKOUT", - "SEARCH", "MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY" }; + private static final String[] methodTransArray = { Method.OPTIONS, Method.GET, Method.HEAD, Method.POST, Method.PUT, + Method.DELETE, Method.TRACE, Method.PROPFIND, Method.PROPPATCH, Method.MKCOL, Method.COPY, Method.MOVE, + Method.LOCK, Method.UNLOCK, "ACL", "REPORT", "VERSION-CONTROL", "CHECKIN", "CHECKOUT", "UNCHECKOUT", + "SEARCH", "MKWORKSPACE", "UPDATE", "LABEL", "MERGE", "BASELINE-CONTROL", "MKACTIVITY" }; /** * Converts an AJP coded HTTP method to the method name. @@ -172,12 +172,8 @@ private static final Map responseTransMap = new HashMap<>(20); static { - try { - for (int i = 0; i < SC_RESP_AJP13_MAX; i++) { - responseTransMap.put(getResponseHeaderForCode(i), Integer.valueOf(0xA001 + i)); - } - } catch (Exception e) { - // Do nothing + for (int i = 0; i < SC_RESP_AJP13_MAX; i++) { + responseTransMap.put(getResponseHeaderForCode(i), Integer.valueOf(0xA001 + i)); } } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/AbstractHttp11Protocol.java tomcat10-10.1.52/java/org/apache/coyote/http11/AbstractHttp11Protocol.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/AbstractHttp11Protocol.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/AbstractHttp11Protocol.java 2026-01-23 19:33:36.000000000 +0000 @@ -97,7 +97,7 @@ // be de-registered. ObjectName rgOname = getGlobalRequestProcessorMBeanName(); if (rgOname != null) { - Registry registry = Registry.getRegistry(null, null); + Registry registry = Registry.getRegistry(null); ObjectName query = new ObjectName(rgOname.getCanonicalName() + ",Upgrade=*"); Set upgrades = registry.getMBeanServer().queryMBeans(query, null); for (ObjectInstance upgrade : upgrades) { @@ -435,7 +435,7 @@ * "gorilla|desesplorer|tigrus" */ public void setRestrictedUserAgents(String restrictedUserAgents) { - if (restrictedUserAgents == null || restrictedUserAgents.length() == 0) { + if (restrictedUserAgents == null || restrictedUserAgents.isEmpty()) { this.restrictedUserAgents = null; } else { this.restrictedUserAgents = Pattern.compile(restrictedUserAgents); @@ -537,7 +537,7 @@ * The names of headers that are allowed to be sent via a trailer when using chunked encoding. They are stored in * lower case. */ - private Set allowedTrailerHeaders = ConcurrentHashMap.newKeySet(); + private final Set allowedTrailerHeaders = ConcurrentHashMap.newKeySet(); public void setAllowedTrailerHeaders(String commaSeparatedHeaders) { // Jump through some hoops so we don't end up with an empty set while @@ -562,7 +562,7 @@ } public boolean isTrailerHeaderAllowed(String headerName) { - return allowedTrailerHeaders.contains(headerName); + return allowedTrailerHeaders.contains(headerName.trim().toLowerCase(Locale.ENGLISH)); } public String getAllowedTrailerHeaders() { @@ -614,7 +614,7 @@ // HTTP Upgrade String httpUpgradeName = upgradeProtocol.getHttpUpgradeName(getEndpoint().isSSLEnabled()); boolean httpUpgradeConfigured = false; - if (httpUpgradeName != null && httpUpgradeName.length() > 0) { + if (httpUpgradeName != null && !httpUpgradeName.isEmpty()) { httpUpgradeProtocols.put(httpUpgradeName, upgradeProtocol); httpUpgradeConfigured = true; getLog().info(sm.getString("abstractHttp11Protocol.httpUpgradeConfigured", getName(), httpUpgradeName)); @@ -623,7 +623,7 @@ // ALPN String alpnName = upgradeProtocol.getAlpnName(); - if (alpnName != null && alpnName.length() > 0) { + if (alpnName != null && !alpnName.isEmpty()) { // ALPN is only available with TLS if (getEndpoint().isSSLEnabled()) { negotiatedProtocols.put(alpnName, upgradeProtocol); @@ -678,7 +678,7 @@ ObjectName oname = getONameForUpgrade(upgradeProtocol); if (oname != null) { try { - Registry.getRegistry(null, null).registerComponent(result, oname, null); + Registry.getRegistry(null).registerComponent(result, oname, null); } catch (Exception e) { getLog().warn(sm.getString("abstractHttp11Protocol.upgradeJmxRegistrationFail"), e); result = null; @@ -792,12 +792,15 @@ } + public boolean checkSni(String sniHostName, String protocolHostName) { + return getEndpoint().checkSni(sniHostName, protocolHostName); + } + // ------------------------------------------------------------- Common code @Override protected Processor createProcessor() { - Http11Processor processor = new Http11Processor(this, adapter); - return processor; + return new Http11Processor(this, adapter); } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/Constants.java tomcat10-10.1.52/java/org/apache/coyote/http11/Constants.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Constants. - * - * @author Remy Maucherat */ public final class Constants { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/Http11InputBuffer.java tomcat10-10.1.52/java/org/apache/coyote/http11/Http11InputBuffer.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/Http11InputBuffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/Http11InputBuffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -100,7 +100,7 @@ /** * Underlying input buffer. */ - private InputBuffer inputStreamInputBuffer; + private final InputBuffer inputStreamInputBuffer; /** @@ -122,15 +122,15 @@ /** - * Parsing state - used for non blocking parsing so that when more data arrives, we can pick up where we left off. + * Parsing state - used for non-blocking parsing so that when more data arrives, we can pick up where we left off. */ private byte prevChr = 0; private byte chr = 0; private volatile boolean parsingRequestLine; - private int parsingRequestLinePhase = 0; - private boolean parsingRequestLineEol = false; - private int parsingRequestLineStart = 0; - private int parsingRequestLineQPos = -1; + private int parsingRequestLinePhase; + private boolean parsingRequestLineEol; + private int parsingRequestLineStart; + private int parsingRequestLineQPos; private HeaderParsePosition headerParsePos; private final HeaderParseData headerData = new HeaderParseData(); private final HttpParser httpParser; @@ -140,12 +140,6 @@ */ private final int headerBufferSize; - /** - * Known size of the NioChannel read buffer. - */ - private int socketReadBufferSize; - - // ----------------------------------------------------------- Constructors public Http11InputBuffer(Request request, int headerBufferSize, boolean rejectIllegalHeader, @@ -259,7 +253,10 @@ activeFilters[i].recycle(); } - byteBuffer.limit(0).position(0); + // Avoid rare NPE reported on users@ list + if (byteBuffer != null) { + byteBuffer.limit(0).position(0); + } lastActiveFilter = -1; swallowInput = true; @@ -369,7 +366,7 @@ // just skipping blank lines) if (parsingRequestLinePhase == 0) { parsingRequestLinePhase = 1; - request.setStartTimeNanos(System.nanoTime()); + request.markStartTime(); } chr = byteBuffer.get(); } while (chr == Constants.CR || chr == Constants.LF); @@ -397,8 +394,7 @@ chr = byteBuffer.get(); if (chr == Constants.SP || chr == Constants.HT) { space = true; - request.method().setBytes(byteBuffer.array(), parsingRequestLineStart, - pos - parsingRequestLineStart); + request.setMethod(byteBuffer.array(), parsingRequestLineStart, pos - parsingRequestLineStart); } else if (!HttpParser.isToken(chr)) { // Avoid unknown protocol triggering an additional error request.protocol().setString(Constants.HTTP_11); @@ -586,20 +582,12 @@ throw new IllegalStateException(sm.getString("iib.parseheaders.ise.error")); } - HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS; + HeaderParseStatus status; do { status = parseHeader(); - // Checking that - // (1) Headers plus request line size does not exceed its limit - // (2) There are enough bytes to avoid expanding the buffer when - // reading body - // Technically, (2) is technical limitation, (1) is logical - // limitation to enforce the meaning of headerBufferSize - // From the way how buf is allocated and how blank lines are being - // read, it should be enough to check (1) only. - if (byteBuffer.position() > headerBufferSize || - byteBuffer.capacity() - byteBuffer.position() < socketReadBufferSize) { + // Checking that headers plus request line size does not exceed its limit + if (byteBuffer.position() > headerBufferSize) { throw new IllegalArgumentException(sm.getString("iib.requestheadertoolarge.error")); } } while (status == HeaderParseStatus.HAVE_MORE_HEADERS); @@ -780,7 +768,7 @@ byteBuffer.limit(end).position(end); } - int nRead = -1; + int nRead; int mark = byteBuffer.position(); try { if (byteBuffer.position() < byteBuffer.limit()) { @@ -1239,6 +1227,5 @@ temp.put(byteBuffer); byteBuffer = temp; byteBuffer.mark(); - temp = null; } } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/Http11OutputBuffer.java tomcat10-10.1.52/java/org/apache/coyote/http11/Http11OutputBuffer.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/Http11OutputBuffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/Http11OutputBuffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -281,7 +281,7 @@ public void sendAck() throws IOException { // It possible that the protocol configuration is changed between the - // request being received and the first read of the body. That could led + // request being received and the first read of the body. That could lead // to multiple calls to this method so ensure the ACK is only sent once. if (!response.isCommitted() && !ackSent) { ackSent = true; diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/Http11Processor.java tomcat10-10.1.52/java/org/apache/coyote/http11/Http11Processor.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/Http11Processor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/Http11Processor.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; @@ -54,6 +55,7 @@ import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.http.parser.HttpParser; import org.apache.tomcat.util.http.parser.TokenList; @@ -96,7 +98,7 @@ * Tracks how many internal filters are in the filter library so they are skipped when looking for pluggable * filters. */ - private int pluggableFilterIndex = Integer.MAX_VALUE; + private final int pluggableFilterIndex; /** @@ -301,11 +303,11 @@ socketWrapper.setReadTimeout(protocol.getConnectionUploadTimeout()); } } - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(sm.getString("http11processor.header.parse"), e); + log.debug(sm.getString("http11processor.header.parse"), ioe); } - setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); + setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe); break; } catch (Throwable t) { ExceptionUtils.handleThrowable(t); @@ -454,11 +456,7 @@ if (!protocol.getDisableUploadTimeout()) { int connectionTimeout = protocol.getConnectionTimeout(); - if (connectionTimeout > 0) { - socketWrapper.setReadTimeout(connectionTimeout); - } else { - socketWrapper.setReadTimeout(0); - } + socketWrapper.setReadTimeout(Math.max(connectionTimeout, 0)); } rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE); @@ -506,7 +504,7 @@ // Transfer the minimal information required for the copy of the Request // that is passed to the HTTP upgrade process dest.decodedURI().duplicate(source.decodedURI()); - dest.method().duplicate(source.method()); + dest.setMethod(source.getMethod()); dest.getMimeHeaders().duplicate(source.getMimeHeaders()); dest.requestURI().duplicate(source.requestURI()); dest.queryString().duplicate(source.queryString()); @@ -579,7 +577,7 @@ long contentLength = -1; try { contentLength = request.getContentLengthLong(); - } catch (Exception e) { + } catch (Exception ignore) { // Ignore, an error here is already processed in prepareRequest // but is done again since the content length is still -1 } @@ -611,6 +609,11 @@ http09 = true; http11 = false; keepAlive = false; + if (!Method.GET.equals(request.getMethod())) { + // Send 400, GET is the only allowed method for HTTP/0.9 + response.setStatus(400); + setErrorState(ErrorState.CLOSE_CLEAN, null); + } } else { // Unsupported protocol http09 = false; @@ -626,7 +629,7 @@ /** - * After reading the request headers, we have to setup the request filters. + * After reading the request headers, we have to set up the request filters. */ @SuppressWarnings("deprecation") private void prepareRequest() throws IOException { @@ -761,7 +764,7 @@ try { hostValueMB = headers.setValue("host"); hostValueMB.setBytes(uriB, uriBCStart + pos, slashPos - pos); - } catch (IllegalStateException e) { + } catch (IllegalStateException ignore) { // Edge case // If the request has too many headers it won't be // possible to create the host header. Ignore this as @@ -789,6 +792,11 @@ // Validate host name and extract port if present parseHost(hostValueMB); + // Match host name with SNI if required + if (!protocol.checkSni(socketWrapper.getSniHostName(), request.serverName().toString())) { + badRequest("http11processor.request.sni"); + } + if (!getErrorState().isIoAllowed()) { getAdapter().log(request, response, 0); } @@ -883,7 +891,7 @@ OutputFilter[] outputFilters = outputBuffer.getFilters(); - if (http09 == true) { + if (http09) { // HTTP/0.9 outputBuffer.addActiveFilter(outputFilters[Constants.IDENTITY_FILTER]); outputBuffer.commit(); @@ -905,8 +913,7 @@ } } - MessageBytes methodMB = request.method(); - boolean head = methodMB.equals("HEAD"); + boolean head = Method.HEAD.equals(request.getMethod()); if (head) { // Any entity body, if present, should not be sent outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]); @@ -1166,6 +1173,8 @@ endRequest(); inputBuffer.nextRequest(); outputBuffer.nextRequest(); + // Set keep alive timeout for next request + socketWrapper.setReadTimeout(protocol.getKeepAliveTimeout()); if (socketWrapper.isReadPending()) { return SocketState.LONG; } else { @@ -1208,8 +1217,8 @@ if (getErrorState().isIoAllowed()) { try { inputBuffer.endRequest(); - } catch (IOException e) { - setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); + } catch (IOException ioe) { + setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // 500 - Internal Server Error @@ -1224,8 +1233,8 @@ try { action(ActionCode.COMMIT, null); outputBuffer.end(); - } catch (IOException e) { - setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); + } catch (IOException ioe) { + setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); setErrorState(ErrorState.CLOSE_NOW, t); @@ -1253,8 +1262,8 @@ if (!response.isCommitted() && request.hasExpectation()) { try { outputBuffer.sendAck(); - } catch (IOException e) { - setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e); + } catch (IOException ioe) { + setErrorState(ErrorState.CLOSE_CONNECTION_NOW, ioe); } } } @@ -1424,17 +1433,14 @@ sendfileData.keepAliveState = SendfileKeepAliveState.NONE; } result = socketWrapper.processSendfile(sendfileData); - switch (result) { - case ERROR: - // Write failed - if (log.isDebugEnabled()) { - log.debug(sm.getString("http11processor.sendfile.error")); - } - setErrorState(ErrorState.CLOSE_CONNECTION_NOW, null); - //$FALL-THROUGH$ - default: - sendfileData = null; + if (Objects.requireNonNull(result) == SendfileState.ERROR) { + // Write failed + if (log.isDebugEnabled()) { + log.debug(sm.getString("http11processor.sendfile.error")); + } + setErrorState(ErrorState.CLOSE_CONNECTION_NOW, null); } + sendfileData = null; } return result; } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/InputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/InputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/InputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/InputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,6 @@ /** * Input filter interface. - * - * @author Remy Maucherat */ public interface InputFilter extends InputBuffer { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/LocalStrings.properties tomcat10-10.1.52/java/org/apache/coyote/http11/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/coyote/http11/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -39,6 +39,7 @@ http11processor.request.nonNumericContentLength=The request contained a content-length header with a non-numeric value http11processor.request.prepare=Error preparing request http11processor.request.process=Error processing request +http11processor.request.sni=The host header does not match the SNI host http11processor.request.unsupportedEncoding=Error preparing request, unsupported transfer encoding [{0}] http11processor.request.unsupportedVersion=Error preparing request, unsupported HTTP version [{0}] http11processor.response.finish=Error finishing response diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/coyote/http11/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/coyote/http11/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -25,6 +25,7 @@ http11processor.fallToDebug=\n\ \ 注意:HTTP请求解析错误的进一步发生将记录在DEBUG级别。 http11processor.header.parse=解析 HTTP 请求 header 错误 +http11processor.noParser=在HTTP协议处理程序上没有找到HttpParser,它应该在初始化时创建 http11processor.request.finish=完成请求时出错 http11processor.request.inconsistentHosts=请求行中指定的主机与主机头不一致。 http11processor.request.invalidScheme=HTTP请求包含具有无效方案的绝对URL diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/OutputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/OutputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/OutputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/OutputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Output filter. - * - * @author Remy Maucherat */ public interface OutputFilter extends HttpOutputBuffer { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/BufferedInputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/filters/BufferedInputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/BufferedInputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/BufferedInputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -88,7 +88,7 @@ if (buffered.getLimit() == 0) { // Special case - ignore (swallow) body. Do so within a limit. long swallowed = 0; - int read = 0; + int read; while ((read = buffer.doRead(this)) >= 0) { swallowed += read; if (maxSwallowSize > -1 && swallowed > maxSwallowSize) { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,6 +20,7 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; import org.apache.coyote.ActionCode; import org.apache.coyote.BadRequestException; @@ -38,8 +39,6 @@ /** * Chunked input filter. Parses chunked data according to http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
          - * - * @author Remy Maucherat */ public class ChunkedInputFilter implements InputFilter, ApplicationBufferHandler, HeaderDataSource { @@ -108,7 +107,7 @@ private volatile boolean crFound = false; private volatile int chunkSizeDigitsRead = 0; private volatile boolean parsingExtension = false; - private volatile long extensionSize; + private final AtomicLong extensionSize = new AtomicLong(0); private final HttpHeaderParser httpHeaderParser; // ----------------------------------------------------------- Constructors @@ -175,7 +174,7 @@ @Override public long end() throws IOException { long swallowed = 0; - int read = 0; + int read; // Consume extra bytes : parse the stream until the end chunk is found while ((read = doRead(this)) >= 0) { swallowed += read; @@ -199,13 +198,31 @@ available = readChunk.remaining(); } - // Handle some edge cases + if (available > 2 && (parseState == ParseState.CHUNK_BODY_CRLF || parseState == ParseState.CHUNK_HEADER)) { + if (parseState == ParseState.CHUNK_BODY_CRLF) { + if (skipCRLF()) { + parseState = ParseState.CHUNK_HEADER; + } + } + if (parseState == ParseState.CHUNK_HEADER) { + skipChunkHeader(); + } + // If ending as TRAILER_FIELDS, then the next read will be EOF and available can be > 0 + // If ending as CHUNK_HEADER then there's nothing left to read for now + // If ending as CHUNK_BODY then data is available + // If failed, will throw when trying again on the next read for CRLF or header + available = readChunk.remaining(); + } if (available == 1 && parseState == ParseState.CHUNK_BODY_CRLF) { - // Either just the CR or just the LF are left in the buffer. There is no data to read. - available = 0; + skipCRLF(); + // LF to read next, or failed + available = readChunk.remaining(); } else if (available == 2 && !crFound && parseState == ParseState.CHUNK_BODY_CRLF) { // Just CRLF is left in the buffer. There is no data to read. - available = 0; + if (skipCRLF()) { + parseState = ParseState.CHUNK_HEADER; + } + available = readChunk.remaining(); } if (available == 0) { @@ -235,7 +252,7 @@ crFound = false; chunkSizeDigitsRead = 0; parsingExtension = false; - extensionSize = 0; + extensionSize.set(0); httpHeaderParser.recycle(); } @@ -307,8 +324,8 @@ if (read < 0) { // Unexpected end of stream throwBadRequestException(sm.getString("chunkedInputFilter.invalidHeader")); - } else if (read == 0) { - return false; + } else { + return read != 0; } } return true; @@ -345,11 +362,11 @@ } eol = true; } else if (chr == Constants.SEMI_COLON && !parsingExtension) { - // First semi-colon marks the start of the extension. Further - // semi-colons may appear to separate multiple chunk-extensions. + // First semicolon marks the start of the extension. Further + // semicolons may appear to separate multiple chunk-extensions. // These need to be processed as part of parsing the extensions. parsingExtension = true; - extensionSize++; + extensionSize.incrementAndGet(); } else if (!parsingExtension) { int charValue = HexUtils.getDec(chr); if (charValue != -1 && chunkSizeDigitsRead < 8) { @@ -363,8 +380,8 @@ // Extension 'parsing' // Note that the chunk-extension is neither parsed nor // validated. Currently it is simply ignored. - extensionSize++; - if (maxExtensionSize > -1 && extensionSize > maxExtensionSize) { + long extSize = extensionSize.incrementAndGet(); + if (maxExtensionSize > -1 && extSize > maxExtensionSize) { throwBadRequestException(sm.getString("chunkedInputFilter.maxExtension")); } } @@ -391,8 +408,71 @@ } + private boolean skipChunkHeader() { + + boolean eol = false; + + while (!eol) { + if (readChunk == null || readChunk.position() >= readChunk.limit()) { + return false; + } + + byte chr = readChunk.get(readChunk.position()); + if (chr == Constants.CR || chr == Constants.LF) { + parsingExtension = false; + if (!skipCRLF()) { + return false; + } + eol = true; + } else if (chr == Constants.SEMI_COLON && !parsingExtension) { + // First semicolon marks the start of the extension. Further + // semicolons may appear to separate multiple chunk-extensions. + // These need to be processed as part of parsing the extensions. + parsingExtension = true; + extensionSize.incrementAndGet(); + } else if (!parsingExtension) { + int charValue = HexUtils.getDec(chr); + if (charValue != -1 && chunkSizeDigitsRead < 8) { + chunkSizeDigitsRead++; + remaining = (remaining << 4) | charValue; + } else { + // Isn't valid hex so this is an error condition + return false; + } + } else { + // Extension 'parsing' + // Note that the chunk-extension is neither parsed nor + // validated. Currently it is simply ignored. + long extSize = extensionSize.incrementAndGet(); + if (maxExtensionSize > -1 && extSize > maxExtensionSize) { + return false; + } + } + + // Parsing the CRLF increments pos + if (!eol) { + readChunk.position(readChunk.position() + 1); + } + } + + if (chunkSizeDigitsRead == 0 || remaining < 0) { + return false; + } else { + chunkSizeDigitsRead = 0; + } + + if (remaining == 0) { + parseState = ParseState.TRAILER_FIELDS; + } else { + parseState = ParseState.CHUNK_BODY; + } + + return true; + } + + private int parseChunkBody(ApplicationBufferHandler handler) throws IOException { - int result = 0; + int result; if (!fill()) { return 0; @@ -424,7 +504,7 @@ /** * Parse CRLF at end of chunk. * - * @return {@code true} if the read is complete or {@code false if incomplete}. In complete reads can only happen + * @return {@code true} if the read is complete or {@code false if incomplete}. Incomplete reads can only happen * with non-blocking I/O. * * @throws IOException An error occurred parsing CRLF @@ -461,6 +541,38 @@ } + private boolean skipCRLF() { + + boolean eol = false; + + while (!eol) { + if (readChunk == null || readChunk.position() >= readChunk.limit()) { + return false; + } + + byte chr = readChunk.get(readChunk.position()); + if (chr == Constants.CR) { + if (crFound) { + return false; + } + crFound = true; + } else if (chr == Constants.LF) { + if (!crFound) { + return false; + } + eol = true; + } else { + return false; + } + + readChunk.position(readChunk.position() + 1); + } + + crFound = false; + return true; + } + + /** * Parse end chunk data. * @@ -471,7 +583,7 @@ */ private boolean parseTrailerFields() throws IOException { // Handle optional trailer headers - HeaderParseStatus status = HeaderParseStatus.HAVE_MORE_HEADERS; + HeaderParseStatus status; do { try { status = httpHeaderParser.parseHeader(); diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,8 +34,6 @@ /** * Chunked output filter. - * - * @author Remy Maucherat */ public class ChunkedOutputFilter implements OutputFilter { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/GzipOutputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/filters/GzipOutputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/GzipOutputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/GzipOutputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,8 +30,6 @@ /** * Gzip output filter. - * - * @author Remy Maucherat */ public class GzipOutputFilter implements OutputFilter { @@ -98,9 +96,9 @@ log.trace("Flushing the compression stream!"); } compressionStream.flush(); - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(sm.getString("gzipOutputFilter.flushFail"), e); + log.debug(sm.getString("gzipOutputFilter.flushFail"), ioe); } } } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/IdentityInputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/filters/IdentityInputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/IdentityInputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/IdentityInputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,6 @@ /** * Identity input filter. - * - * @author Remy Maucherat */ public class IdentityInputFilter implements InputFilter, ApplicationBufferHandler { @@ -89,7 +87,7 @@ @Override public int doRead(ApplicationBufferHandler handler) throws IOException { - int result = -1; + int result; if (contentLength >= 0) { if (remaining > 0) { @@ -114,6 +112,8 @@ } result = -1; } + } else { + result = -1; } return result; diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/IdentityOutputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Identity output filter. - * - * @author Remy Maucherat */ public class IdentityOutputFilter implements OutputFilter { @@ -55,7 +53,7 @@ @Override public int doWrite(ByteBuffer chunk) throws IOException { - int result = -1; + int result; if (contentLength >= 0) { if (remaining > 0) { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/coyote/http11/filters/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,8 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +bufferedInputFilter.maxSwallowSize=忽略正文超过maxSwallowSize + chunkedInputFilter.eos=读取请求正文流异常 chunkedInputFilter.eosTrailer=读取 trailer 头时出现意外的流结束 chunkedInputFilter.error=由于先前的错误导致没有可用数据 diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/SavedRequestInputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,7 +32,7 @@ /** * The original request body. */ - protected ByteChunk input = null; + protected ByteChunk input; /** * Create a new SavedRequestInputFilter. @@ -49,11 +49,10 @@ return -1; } - ByteBuffer byteBuffer = handler.getByteBuffer(); - byteBuffer.position(byteBuffer.limit()).limit(byteBuffer.capacity()); - input.subtract(byteBuffer); - - return byteBuffer.remaining(); + int len = input.getLength(); + handler.setByteBuffer(ByteBuffer.wrap(input.getBytes(), input.getStart(), len)); + input.setStart(input.getEnd()); + return len; } /** diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/VoidInputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/filters/VoidInputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/VoidInputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/VoidInputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,6 @@ /** * Void input filter, which returns -1 when attempting a read. Used with a GET, HEAD, or a similar request. - * - * @author Remy Maucherat */ public class VoidInputFilter implements InputFilter { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/filters/VoidOutputFilter.java tomcat10-10.1.52/java/org/apache/coyote/http11/filters/VoidOutputFilter.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/filters/VoidOutputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/filters/VoidOutputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Void output filter, which silently swallows bytes written. Used with a 204 status (no content) or a HEAD request. - * - * @author Remy Maucherat */ public class VoidOutputFilter implements OutputFilter { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/upgrade/UpgradeGroupInfo.java tomcat10-10.1.52/java/org/apache/coyote/http11/upgrade/UpgradeGroupInfo.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/upgrade/UpgradeGroupInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/upgrade/UpgradeGroupInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,10 +29,10 @@ private final Set upgradeInfos = (new ConcurrentHashMap()).keySet(Boolean.TRUE); - private LongAdder deadBytesReceived = new LongAdder(); - private LongAdder deadBytesSent = new LongAdder(); - private LongAdder deadMsgsReceived = new LongAdder(); - private LongAdder deadMsgsSent = new LongAdder(); + private final LongAdder deadBytesReceived = new LongAdder(); + private final LongAdder deadBytesSent = new LongAdder(); + private final LongAdder deadMsgsReceived = new LongAdder(); + private final LongAdder deadMsgsSent = new LongAdder(); public void addUpgradeInfo(UpgradeInfo ui) { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java tomcat10-10.1.52/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/upgrade/UpgradeServletInputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -79,8 +79,8 @@ try { ready = Boolean.valueOf(socketWrapper.isReadyForRead()); - } catch (IOException e) { - onError(e); + } catch (IOException ioe) { + onError(ioe); } return ready.booleanValue(); } @@ -213,8 +213,8 @@ if (listener == null || !socketWrapper.isReadyForRead()) { return; } - } catch (IOException e) { - onError(e); + } catch (IOException ioe) { + onError(ioe); } ready = Boolean.TRUE; ClassLoader oldCL = processor.getUpgradeToken().getContextBind().bind(false, null); diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java tomcat10-10.1.52/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java --- tomcat10-10.1.34/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http11/upgrade/UpgradeServletOutputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -202,12 +202,8 @@ * Must hold writeLock to call this method. */ private void writeInternal(byte[] b, int off, int len) throws IOException { - if (listener == null) { - // Simple case - blocking IO - socketWrapper.write(true, b, off, len); - } else { - socketWrapper.write(false, b, off, len); - } + // Blocking IO if no listener + socketWrapper.write(listener == null, b, off, len); upgradeInfo.addBytesSent(len); } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/AbstractNonZeroStream.java tomcat10-10.1.52/java/org/apache/coyote/http2/AbstractNonZeroStream.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/AbstractNonZeroStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/AbstractNonZeroStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -85,9 +85,9 @@ /** * Notify that some data has been received. * - * @param payloadSize the byte count + * @param dataLength the byte count * * @throws Http2Exception if an error is detected */ - abstract void receivedData(int payloadSize) throws Http2Exception; + abstract void receivedData(int dataLength) throws Http2Exception; } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/ConnectionSettingsBase.java tomcat10-10.1.52/java/org/apache/coyote/http2/ConnectionSettingsBase.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/ConnectionSettingsBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/ConnectionSettingsBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -66,6 +66,11 @@ final void set(Setting setting, long value) throws T { + set(setting, value, false); + } + + + final void set(Setting setting, long value, boolean force) throws T { if (log.isTraceEnabled()) { log.trace(sm.getString("connectionSettings.debug", connectionId, getEndpointName(), setting, Long.toString(value))); @@ -93,16 +98,30 @@ case NO_RFC7540_PRIORITIES: validateNoRfc7540Priorities(value); break; + case ENABLE_CONNECT_PROTOCOL: + case TLS_RENEG_PERMITTED: + // Not supported. Ignore it. + return; case UNKNOWN: // Unrecognised. Ignore it. return; } - set(setting, Long.valueOf(value)); + set(setting, Long.valueOf(value), force); } - synchronized void set(Setting setting, Long value) { + /** + * Specify a new value for setting with the option to force the change to take effect immediately rather than + * waiting until an {@code ACK} is received. + * + * @param setting The setting to update + * @param value The new value for the setting + * @param force {@code false} if an {@code ACK} must be received before the setting takes effect or {@code true} + * if the setting to take effect immediately. Even if the setting takes effect immediately, it + * will still be included in the next {@code SETTINGS} frame and an {@code ACK} will be expected. + */ + synchronized void set(Setting setting, Long value, boolean force) { current.put(setting, value); } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/ConnectionSettingsLocal.java tomcat10-10.1.52/java/org/apache/coyote/http2/ConnectionSettingsLocal.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/ConnectionSettingsLocal.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/ConnectionSettingsLocal.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,12 +40,15 @@ @Override - final synchronized void set(Setting setting, Long value) { + final synchronized void set(Setting setting, Long value, boolean force) { checkSend(); if (current.get(setting).longValue() == value.longValue()) { pending.remove(setting); } else { pending.put(setting, value); + if (force) { + current.put(setting, value); + } } } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/HPackHuffman.java tomcat10-10.1.52/java/org/apache/coyote/http2/HPackHuffman.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/HPackHuffman.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/HPackHuffman.java 2026-01-23 19:33:36.000000000 +0000 @@ -324,11 +324,11 @@ int newLength = length + 1; HuffmanCode high = new HuffmanCode(code << 1 | 1, newLength); HuffmanCode low = new HuffmanCode(code << 1, newLength); - int newVal = 0; + int newVal; boolean highTerminal = allCodes.remove(high); if (highTerminal) { // bah, linear search - int i = 0; + int i; for (i = 0; i < codes.length; ++i) { if (codes[i].equals(high)) { break; @@ -344,7 +344,7 @@ boolean lowTerminal = allCodes.remove(low); if (lowTerminal) { // bah, linear search - int i = 0; + int i; for (i = 0; i < codes.length; ++i) { if (codes[i].equals(low)) { break; @@ -470,7 +470,7 @@ HuffmanCode code = HUFFMAN_CODES[c]; if (code.length + bytePos <= 8) { // it fits in the current byte - currentBufferByte |= ((code.value & 0xFF) << 8 - (code.length + bytePos)); + currentBufferByte |= (byte) ((code.value & 0xFF) << 8 - (code.length + bytePos)); bytePos += code.length; } else { // it does not fit, it may need up to 4 bytes @@ -483,9 +483,9 @@ } int remainingInByte = 8 - bytePos; if (rem > remainingInByte) { - currentBufferByte |= (val >> (rem - remainingInByte)); + currentBufferByte |= (byte) (val >> (rem - remainingInByte)); } else { - currentBufferByte |= (val << (remainingInByte - rem)); + currentBufferByte |= (byte) (val << (remainingInByte - rem)); } if (rem > remainingInByte) { buffer.put(currentBufferByte); @@ -563,11 +563,7 @@ if (length != that.length) { return false; } - if (value != that.value) { - return false; - } - - return true; + return value == that.value; } @Override diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/Hpack.java tomcat10-10.1.52/java/org/apache/coyote/http2/Hpack.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/Hpack.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/Hpack.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,7 @@ import java.nio.ByteBuffer; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.res.StringManager; final class Hpack { @@ -26,8 +27,16 @@ private static final byte LOWER_DIFF = 'a' - 'A'; static final int DEFAULT_TABLE_SIZE = 4096; - private static final int MAX_INTEGER_OCTETS = 8; // not sure what a good value for this is, but the spec says we - // need to provide an upper bound + /* + * The HPack specification says there SHOULD be an upper bound on this. + * + * Tomcat has opted to limit values to INTEGER.MAX_VALUE. Give that there is the prefix byte and then each octet + * provides up to 7-bits, a total a 5 octets plus the prefix may be required. + * + * Note: The maximum value represented by 5 octets is greater than INTEGER.MAX_VALUE. + * + */ + private static final int MAX_INTEGER_OCTETS = 5; /** * table that contains powers of two, used as both bitmask and to quickly calculate 2^n @@ -52,8 +61,8 @@ HeaderField[] fields = new HeaderField[62]; // note that zero is not used fields[1] = new HeaderField(":authority", null); - fields[2] = new HeaderField(":method", "GET"); - fields[3] = new HeaderField(":method", "POST"); + fields[2] = new HeaderField(":method", Method.GET); + fields[3] = new HeaderField(":method", Method.POST); fields[4] = new HeaderField(":path", "/"); fields[5] = new HeaderField(":path", "/index.html"); fields[6] = new HeaderField(":scheme", "http"); @@ -151,10 +160,12 @@ int sp = source.position(); int mask = PREFIX_TABLE[n]; - int i = mask & source.get(); + // Use long internally as the value may exceed Integer.MAX_VALUE + long result = mask & source.get(); int b; - if (i < PREFIX_TABLE[n]) { - return i; + if (result < PREFIX_TABLE[n]) { + // Casting is safe as result must be less than 255 at this point. + return (int) result; } else { int m = 0; do { @@ -169,11 +180,15 @@ return -1; } b = source.get(); - i = i + (b & 127) * (PREFIX_TABLE[m] + 1); + result = result + (b & 127) * (PREFIX_TABLE[m] + 1L); + if (result > Integer.MAX_VALUE) { + throw new HpackException(sm.getString("hpack.integerEncodedTooBig")); + } m += 7; } while ((b & 128) == 128); } - return i; + // Casting is safe as result must be less than Integer.MAX_VALUE at this point + return (int) result; } /** diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/HpackDecoder.java tomcat10-10.1.52/java/org/apache/coyote/http2/HpackDecoder.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/HpackDecoder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/HpackDecoder.java 2026-01-23 19:33:36.000000000 +0000 @@ -61,7 +61,7 @@ /** * The maximum allowed memory size set by the container. */ - private int maxMemorySizeHard; + private final int maxMemorySizeHard; /** * The maximum memory size currently in use. May be less than the hard limit. */ @@ -283,7 +283,7 @@ */ int getRealIndex(int index) throws HpackException { // the index is one based, but our table is zero based, hence -1 - // also because of our ring buffer setup the indexes are reversed + // also because of our ring buffer set up the indexes are reversed // index = 1 is at position firstSlotPosition + filledSlots int realIndex = (firstSlotPosition + (filledTableSlots - index)) % headerTable.length; if (realIndex < 0) { @@ -307,7 +307,7 @@ if (log.isTraceEnabled()) { log.trace(sm.getString("hpackdecoder.clearDynamic")); } - // it is to big to fit, so we just completely clear the table. + // it is too big to fit, so we just completely clear the table. while (filledTableSlots > 0) { headerTable[firstSlotPosition] = null; firstSlotPosition++; @@ -380,10 +380,9 @@ /** * Are the headers pass to the recipient so far valid? The decoder needs to process all the headers to maintain - * state even if there is a problem. In addition, it is easy for the the intended recipient to track if the - * complete set of headers is valid since to do that state needs to be maintained between the parsing of the - * initial headers and the parsing of any trailer headers. The recipient is the best place to maintain that - * state. + * state even if there is a problem. In addition, it is easy for the intended recipient to track if the complete + * set of headers is valid since to do that state needs to be maintained between the parsing of the initial + * headers and the parsing of any trailer headers. The recipient is the best place to maintain that state. * * @throws StreamException If the headers received to date are not valid */ @@ -405,6 +404,11 @@ } + void clearHeaderEmitter() { + headerEmitter = null; + } + + void setMaxHeaderCount(int maxHeaderCount) { this.maxHeaderCount = maxHeaderCount; } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java tomcat10-10.1.52/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -132,6 +132,10 @@ log.trace(sm.getString("upgradeHandler.rst.debug", connectionId, Integer.toString(se.getStreamId()), se.getError(), se.getMessage())); } + + // Treat a sent reset like a received reset and increment the overhead count + increaseOverheadCount(FrameType.RST, getProtocol().getOverheadResetFactor()); + // Write a RST frame byte[] rstFrame = new byte[13]; // Length @@ -335,7 +339,7 @@ (int) (sendfile.end - sendfile.pos); sendfile.streamReservation = sendfile.stream.reserveWindowSize(reservation, true); sendfile.connectionReservation = reserveWindowSize(sendfile.stream, sendfile.streamReservation, true); - } catch (IOException e) { + } catch (IOException ioe) { return SendfileState.ERROR; } @@ -372,7 +376,7 @@ ByteBuffer.wrap(header), sendfile.mappedBuffer); try { handleAsyncException(); - } catch (IOException e) { + } catch (IOException ioe) { return SendfileState.ERROR; } } @@ -397,13 +401,13 @@ if (sendfile.left == 0) { try { sendfile.stream.getOutputBuffer().end(); - } catch (IOException e) { - failed(e, sendfile); + } catch (IOException ioe) { + failed(ioe, sendfile); } return; } - sendfile.streamReservation -= bytesWritten; - sendfile.connectionReservation -= bytesWritten; + sendfile.streamReservation -= (int) bytesWritten; + sendfile.connectionReservation -= (int) bytesWritten; sendfile.pos += bytesWritten; try { if (sendfile.connectionReservation == 0) { @@ -415,8 +419,8 @@ sendfile.connectionReservation = reserveWindowSize(sendfile.stream, sendfile.streamReservation, true); } - } catch (IOException e) { - failed(e, sendfile); + } catch (IOException ioe) { + failed(ioe, sendfile); return; } @@ -457,8 +461,8 @@ ByteBuffer.wrap(header), sendfile.mappedBuffer); try { handleAsyncException(); - } catch (IOException e) { - failed(e, sendfile); + } catch (IOException ioe) { + failed(ioe, sendfile); return; } } @@ -497,7 +501,7 @@ @Override public void receivePing(byte[] payload, boolean ack) throws IOException { if (ack) { - super.receivePing(payload, ack); + super.receivePing(payload, true); } else { // Client originated ping. Echo it back. socketWrapper.write(BlockingMode.SEMI_BLOCK, protocol.getWriteTimeout(), TimeUnit.MILLISECONDS, null, diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/Http2Parser.java tomcat10-10.1.52/java/org/apache/coyote/http2/Http2Parser.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/Http2Parser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/Http2Parser.java 2026-01-23 19:33:36.000000000 +0000 @@ -188,7 +188,7 @@ Integer.toString(dataLength), padding)); } - ByteBuffer dest = output.startRequestBodyFrame(streamId, payloadSize, endOfStream); + ByteBuffer dest = output.startRequestBodyFrame(streamId, dataLength, endOfStream); if (dest == null) { swallowPayload(streamId, FrameType.DATA.getId(), dataLength, false, buffer); // Process padding before sending any notifications in case padding @@ -201,7 +201,7 @@ } } else { synchronized (dest) { - if (dest.remaining() < payloadSize) { + if (dest.remaining() < dataLength) { // Client has sent more data than permitted by Window size swallowPayload(streamId, FrameType.DATA.getId(), dataLength, false, buffer); if (Flags.hasPadding(flags)) { @@ -298,7 +298,7 @@ // RFC 7450 priority frames are ignored. Still need to treat as overhead. try { swallowPayload(streamId, FrameType.PRIORITY.getId(), 5, false, buffer); - } catch (ConnectionException e) { + } catch (ConnectionException ignore) { // Will never happen because swallowPayload() is called with isPadding set // to false } @@ -477,15 +477,24 @@ ByteArrayInputStream bais = new ByteArrayInputStream(payload, 4, payloadSize - 4); Reader r = new BufferedReader(new InputStreamReader(bais, StandardCharsets.US_ASCII)); - Priority p = Priority.parsePriority(r); - if (log.isTraceEnabled()) { - log.trace(sm.getString("http2Parser.processFramePriorityUpdate.debug", connectionId, - Integer.toString(prioritizedStreamID), Integer.toString(p.getUrgency()), - Boolean.valueOf(p.getIncremental()))); - } + try { + Priority p = Priority.parsePriority(r); + + if (log.isTraceEnabled()) { + log.trace(sm.getString("http2Parser.processFramePriorityUpdate.debug", connectionId, + Integer.toString(prioritizedStreamID), Integer.toString(p.getUrgency()), + Boolean.valueOf(p.getIncremental()))); + } - output.priorityUpdate(prioritizedStreamID, p); + output.priorityUpdate(prioritizedStreamID, p); + } catch (IllegalArgumentException iae) { + // Priority frames with invalid priority field values should be ignored + if (log.isTraceEnabled()) { + log.trace(sm.getString("http2Parser.processFramePriorityUpdate.invalid", connectionId, + Integer.toString(prioritizedStreamID)), iae); + } + } } @@ -643,6 +652,12 @@ Http2Error.COMPRESSION_ERROR); } + /* + * Clear the reference to the stream in the HPack decoder now that the headers have been processed so that the + * HPack decoder does not retain a reference to this stream. This aids GC. + */ + hpackDecoder.clearHeaderEmitter(); + synchronized (output) { output.headersEnd(streamId, headersEndStream); @@ -770,7 +785,7 @@ HpackDecoder getHpackDecoder(); // Data frames - ByteBuffer startRequestBodyFrame(int streamId, int payloadSize, boolean endOfStream) throws Http2Exception; + ByteBuffer startRequestBodyFrame(int streamId, int dataLength, boolean endOfStream) throws Http2Exception; void endRequestBodyFrame(int streamId, int dataLength) throws Http2Exception, IOException; diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/Http2Protocol.java tomcat10-10.1.52/java/org/apache/coyote/http2/Http2Protocol.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/Http2Protocol.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/Http2Protocol.java 2026-01-23 19:33:36.000000000 +0000 @@ -98,7 +98,7 @@ // Reference to HTTP/1.1 protocol that this instance is configured under private AbstractHttp11Protocol http11Protocol = null; - private RequestGroupInfo global = new RequestGroupInfo(); + private final RequestGroupInfo global = new RequestGroupInfo(); /* * Setting discardRequestsAndResponses can have a significant performance impact. The magnitude of the impact is @@ -133,11 +133,8 @@ @Override public Processor getProcessor(SocketWrapperBase socketWrapper, Adapter adapter) { - String upgradeProtocol = getUpgradeProtocolName(); - UpgradeProcessorInternal processor = new UpgradeProcessorInternal(socketWrapper, - new UpgradeToken(getInternalUpgradeHandler(socketWrapper, adapter, null), null, null, upgradeProtocol), - null); - return processor; + return new UpgradeProcessorInternal(socketWrapper, new UpgradeToken( + getInternalUpgradeHandler(socketWrapper, adapter, null), null, null, getUpgradeProtocolName()), null); } @@ -312,11 +309,7 @@ public void setOverheadResetFactor(int overheadResetFactor) { - if (overheadResetFactor < 0) { - this.overheadResetFactor = 0; - } else { - this.overheadResetFactor = overheadResetFactor; - } + this.overheadResetFactor = Math.max(overheadResetFactor, 0); } @@ -384,7 +377,7 @@ ObjectName oname = this.http11Protocol.getONameForUpgrade(getUpgradeProtocolName()); // This can be null when running the testsuite if (oname != null) { - Registry.getRegistry(null, null).registerComponent(global, oname, null); + Registry.getRegistry(null).registerComponent(global, oname, null); } } catch (Exception e) { log.warn(sm.getString("http2Protocol.jmxRegistration.fail"), e); @@ -431,7 +424,6 @@ void pushRequestAndResponse(Request requestAndResponse) { - requestAndResponse.recycle(); if (!discardRequestsAndResponses) { recycledRequestsAndResponses.push(requestAndResponse); } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/Http2UpgradeHandler.java tomcat10-10.1.52/java/org/apache/coyote/http2/Http2UpgradeHandler.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,6 +48,7 @@ import org.apache.coyote.http2.Http2Parser.Output; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.ExceptionUtils; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.http.parser.Priority; import org.apache.tomcat.util.log.UserDataHelper; @@ -64,7 +65,7 @@ * For reading, this implementation is blocking within frames and non-blocking between frames.
          * Note: *
            - *
          • You will need to nest an <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" /> element + *
          • You will need to nest a <UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" /> element * inside a TLS enabled Connector element in server.xml to enable HTTP/2 support.
          • *
          */ @@ -89,6 +90,8 @@ protected static final HeaderSink HEADER_SINK = new HeaderSink(); + protected static final UserDataHelper userDataHelper = new UserDataHelper(log); + protected final String connectionId; protected final Http2Protocol protocol; @@ -99,7 +102,7 @@ private volatile Http2Parser parser; // Simple state machine (sequence of states) - private AtomicReference connectionState = new AtomicReference<>(ConnectionState.NEW); + private final AtomicReference connectionState = new AtomicReference<>(ConnectionState.NEW); private volatile long pausedNanoTime = Long.MAX_VALUE; /** @@ -137,8 +140,6 @@ private volatile int lastNonFinalDataPayload; private volatile int lastWindowUpdate; - protected final UserDataHelper userDataHelper = new UserDataHelper(log); - Http2UpgradeHandler(Http2Protocol protocol, Adapter adapter, Request coyoteRequest, SocketWrapperBase socketWrapper) { @@ -153,7 +154,7 @@ // Over time the count should be a slowly decreasing negative number. // Therefore, the longer a connection is 'well-behaved', the greater // tolerance it will have for a period of 'bad' behaviour. - overheadCount = new AtomicLong(-10 * protocol.getOverheadCountFactor()); + overheadCount = new AtomicLong(-10L * protocol.getOverheadCountFactor()); lastNonFinalDataPayload = protocol.getOverheadDataThreshold() * 2; lastWindowUpdate = protocol.getOverheadWindowUpdateThreshold() * 2; @@ -163,8 +164,12 @@ remoteSettings = new ConnectionSettingsRemote(connectionId); localSettings = new ConnectionSettingsLocal(connectionId); - localSettings.set(Setting.MAX_CONCURRENT_STREAMS, protocol.getMaxConcurrentStreams()); - localSettings.set(Setting.INITIAL_WINDOW_SIZE, protocol.getInitialWindowSize()); + /* + * Force set these initial limits. A well-behaved client should ACK the settings and adhere to them before it + * reaches the limits anyway. + */ + localSettings.set(Setting.MAX_CONCURRENT_STREAMS, protocol.getMaxConcurrentStreams(), true); + localSettings.set(Setting.INITIAL_WINDOW_SIZE, protocol.getInitialWindowSize(), true); pingManager.initiateDisabled = protocol.getInitiatePingDisabled(); @@ -442,6 +447,13 @@ log.debug(sm.getString("upgradeHandler.ioerror", connectionId), ioe); } close(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + if (log.isDebugEnabled()) { + log.debug(sm.getString("upgradeHandler.throwable", connectionId), t); + } + // Unexpected errors close the connection. + close(); } if (log.isTraceEnabled()) { @@ -514,7 +526,7 @@ try { writeGoAwayFrame((1 << 31) - 1, Http2Error.NO_ERROR.getCode(), null); - } catch (IOException ioe) { + } catch (IOException ignore) { // This is fatal for the connection. Ignore it here. There will be // further attempts at I/O in upgradeDispatch() and it can better // handle the IO errors. @@ -573,6 +585,9 @@ se.getError(), se.getMessage())); } + // Treat a sent reset like a received reset and increment the overhead count + increaseOverheadCount(FrameType.RST, getProtocol().getOverheadResetFactor()); + // Write a RST frame byte[] rstFrame = new byte[13]; // Length @@ -621,7 +636,7 @@ } try { writeGoAwayFrame(maxProcessedStreamId, code, msg); - } catch (IOException ioe) { + } catch (IOException ignore) { // Ignore. GOAWAY is sent on a best efforts basis and the original // error has already been logged. } @@ -645,6 +660,7 @@ } socketWrapper.flush(true); } catch (IOException ioe) { + // Exception is logged further up stack String msg = sm.getString("upgradeHandler.sendPrefaceFail", connectionId); if (log.isDebugEnabled()) { log.debug(msg); @@ -956,8 +972,8 @@ } else if (windowSize < 1) { /* * The connection window has no capacity. If the stream has not been granted an allocation, and the - * stream was not already added to the backlog due to an partial reservation (see next else if - * block) add it to the backlog so it can obtain an allocation when capacity is available. + * stream was not already added to the backlog due to a partial reservation (see next else if block) + * add it to the backlog so it can obtain an allocation when capacity is available. */ if (stream.getConnectionAllocationMade() == 0 && stream.getConnectionAllocationRequested() == 0) { stream.setConnectionAllocationRequested(reservation); @@ -1203,12 +1219,7 @@ int leftToAllocate = allocation; if (stream.getConnectionAllocationRequested() > 0) { - int allocatedThisTime; - if (allocation >= stream.getConnectionAllocationRequested()) { - allocatedThisTime = stream.getConnectionAllocationRequested(); - } else { - allocatedThisTime = allocation; - } + int allocatedThisTime = Math.min(allocation, stream.getConnectionAllocationRequested()); stream.setConnectionAllocationRequested(stream.getConnectionAllocationRequested() - allocatedThisTime); stream.setConnectionAllocationMade(stream.getConnectionAllocationMade() + allocatedThisTime); leftToAllocate = leftToAllocate - allocatedThisTime; @@ -1407,39 +1418,59 @@ void reduceOverheadCount(FrameType frameType) { - // A non-overhead frame reduces the overhead count by - // Http2Protocol.DEFAULT_OVERHEAD_REDUCTION_FACTOR. A simple browser - // request is likely to have one non-overhead frame (HEADERS) and one - // overhead frame (REPRIORITISE). With the default settings the overhead - // count will reduce by 10 for each simple request. - // Requests and responses with bodies will create additional - // non-overhead frames, further reducing the overhead count. + /* + * A non-overhead frame reduces the overhead count by {@code Http2Protocol.DEFAULT_OVERHEAD_REDUCTION_FACTOR}. + * + * A simple browser request is likely to have one non-overhead frame (HEADERS) that results in a response with + * one further non-overhead frame (DATA). With the default settings, the overhead count will reduce by 40 for + * each simple request. + * + * Requests and responses with bodies will create additional non-overhead frames, further reducing the overhead + * count. + */ updateOverheadCount(frameType, Http2Protocol.DEFAULT_OVERHEAD_REDUCTION_FACTOR); } @Override public void increaseOverheadCount(FrameType frameType) { - // An overhead frame increases the overhead count by - // overheadCountFactor. By default, this means an overhead frame - // increases the overhead count by 10. A simple browser request is - // likely to have one non-overhead frame (HEADERS) and one overhead - // frame (REPRIORITISE). With the default settings the overhead count - // will reduce by 10 for each simple request. + /* + * An overhead frame (SETTINGS, PRIORITY, PING) increases the overhead count by overheadCountFactor. By default, + * this means an overhead frame increases the overhead count by 10. + * + * If the client ignores maxConcurrentStreams then any HEADERS frame received will also increase the overhead + * count by overheadCountFactor. + * + * A simple browser request should not trigger any overhead frames. + */ updateOverheadCount(frameType, getProtocol().getOverheadCountFactor()); } - private void increaseOverheadCount(FrameType frameType, int increment) { - // Overhead frames that indicate inefficient (and potentially malicious) - // use of small frames trigger an increase that is inversely - // proportional to size. The default threshold for all three potential - // areas for abuse (HEADERS, DATA, WINDOW_UPDATE) is 1024 bytes. Frames - // with sizes smaller than this will trigger an increase of - // threshold/size. - // DATA and WINDOW_UPDATE take an average over the last two non-final - // frames to allow for client buffering schemes that can result in some - // small DATA payloads. + /** + * Used to increase the overhead for frames that don't use the {@code overheadCountFactor} ({@code CONTINUATION}, + * {@code DATA}, {@code WINDOW_UPDATE} and {@code RESET}). + * + * @param frameType The frame type triggering the overhead increase + * @param increment The amount by which the overhead is increased + */ + protected void increaseOverheadCount(FrameType frameType, int increment) { + /* + * Three types of frame are susceptible to inefficient (and potentially malicious) use of small frames. These + * trigger an increase in overhead that is inversely proportional to size. The default threshold for all three + * potential areas for abuse (CONTINUATION, DATA, WINDOW_UPDATE) is 1024 bytes. Frames with sizes smaller than + * this will trigger an increase of threshold/size. + * + * The check for DATA and WINDOW_UPDATE frames takes an average over the last two frames to allow for client + * buffering schemes that can result in some small DATA payloads. + * + * The CONTINUATION and DATA frames checks are skipped for end of headers (CONTINUATION) and end of stream + * (DATA) as those frames may be small for legitimate reasons. + * + * RESET frames (received or sent) trigger an increase of overheadResetFactor. + * + * In all cases, the calling method determines the extent to which the overhead count is increased. + */ updateOverheadCount(frameType, increment); } @@ -1465,7 +1496,7 @@ int len = length; int pos = offset; boolean nextReadBlock = block; - int thisRead = 0; + int thisRead; while (len > 0) { // Blocking reads use the protocol level read timeout. Non-blocking @@ -1520,7 +1551,7 @@ @Override - public ByteBuffer startRequestBodyFrame(int streamId, int payloadSize, boolean endOfStream) throws Http2Exception { + public ByteBuffer startRequestBodyFrame(int streamId, int dataLength, boolean endOfStream) throws Http2Exception { // DATA frames reduce the overhead count ... reduceOverheadCount(FrameType.DATA); @@ -1534,8 +1565,8 @@ // average over two frames to avoid false positives. if (!endOfStream) { int overheadThreshold = protocol.getOverheadDataThreshold(); - int average = (lastNonFinalDataPayload >> 1) + (payloadSize >> 1); - lastNonFinalDataPayload = payloadSize; + int average = (lastNonFinalDataPayload >> 1) + (dataLength >> 1); + lastNonFinalDataPayload = dataLength; // Avoid division by zero if (average == 0) { average = 1; @@ -1547,7 +1578,7 @@ AbstractNonZeroStream abstractNonZeroStream = getAbstractNonZeroStream(streamId, true); abstractNonZeroStream.checkState(FrameType.DATA); - abstractNonZeroStream.receivedData(payloadSize); + abstractNonZeroStream.receivedData(dataLength); ByteBuffer result = abstractNonZeroStream.getInputByteBuffer(true); if (log.isTraceEnabled()) { @@ -1648,9 +1679,9 @@ if (payloadSize < overheadThreshold) { if (payloadSize == 0) { // Avoid division by zero - increaseOverheadCount(FrameType.HEADERS, overheadThreshold); + increaseOverheadCount(FrameType.CONTINUATION, overheadThreshold); } else { - increaseOverheadCount(FrameType.HEADERS, overheadThreshold / payloadSize); + increaseOverheadCount(FrameType.CONTINUATION, overheadThreshold / payloadSize); } } } @@ -1911,6 +1942,10 @@ } } + String getSniHostName() { + return socketWrapper.getSniHostName(); + } + protected class PingManager { protected boolean initiateDisabled = false; @@ -1918,7 +1953,7 @@ // 10 seconds protected final long pingIntervalNano = 10000000000L; - protected int sequence = 0; + protected volatile int sequence = 0; protected long lastPingNanoTime = Long.MIN_VALUE; protected Queue inflightPings = new ConcurrentLinkedQueue<>(); @@ -1939,18 +1974,13 @@ if (force || now - lastPingNanoTime > pingIntervalNano) { lastPingNanoTime = now; byte[] payload = new byte[8]; - socketWrapper.getLock().lock(); - try { - int sentSequence = ++sequence; - PingRecord pingRecord = new PingRecord(sentSequence, now); - inflightPings.add(pingRecord); - ByteUtil.set31Bits(payload, 4, sentSequence); - socketWrapper.write(true, PING, 0, PING.length); - socketWrapper.write(true, payload, 0, payload.length); - socketWrapper.flush(true); - } finally { - socketWrapper.getLock().unlock(); - } + int sentSequence = ++sequence; + PingRecord pingRecord = new PingRecord(sentSequence, now); + inflightPings.add(pingRecord); + ByteUtil.set31Bits(payload, 4, sentSequence); + socketWrapper.write(true, PING, 0, PING.length); + socketWrapper.write(true, payload, 0, payload.length); + socketWrapper.flush(true); } } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings.properties tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -37,6 +37,7 @@ frameType.checkStream=Invalid frame type [{0}] hpack.integerEncodedOverTooManyOctets=HPACK variable length integer encoded over too many octets, max is [{0}] +hpack.integerEncodedTooBig=The maximum permitted value of an HPACK encoded variable length integer is Integer.MAX_VALUE hpack.invalidCharacter=The Unicode character [{0}] at code point [{1}] cannot be encoded as it is outside the permitted range of 0 to 255. hpackEncoder.encodeHeader=Encoding header [{0}] with value [{1}] @@ -77,6 +78,7 @@ http2Parser.processFrameHeaders.decodingFailed=There was an error during the HPACK decoding of HTTP headers http2Parser.processFrameHeaders.payload=Connection [{0}], Stream [{1}], Processing headers payload of size [{2}] http2Parser.processFramePriorityUpdate.debug=Connection [{0}], Stream [{1}], Urgency [{2}], Incremental [{3}] +http2Parser.processFramePriorityUpdate.invalid=Connection [{0}], Stream [{1}], Priority Update frame with invalid priority field value http2Parser.processFramePriorityUpdate.streamZero=Connection [{0}], Priority update frame received to prioritize stream zero http2Parser.processFramePushPromise=Connection [{0}], Stream [{1}], Push promise frames should not be sent by the client http2Parser.processFrameSettings.ackWithNonZeroPayload=Settings frame received with the ACK flag set and payload present @@ -88,6 +90,7 @@ pingManager.roundTripTime=Connection [{0}] Round trip time measured as [{1}]ns +stream.clientResetRequest=Client reset the stream before the request was fully read stream.closed=Connection [{0}], Stream [{1}], Unable to write to stream once it has been closed stream.header.case=Connection [{0}], Stream [{1}], HTTP header name [{2}] must be in lower case stream.header.connection=Connection [{0}], Stream [{1}], HTTP header [{2}] is not permitted in an HTTP/2 request @@ -102,6 +105,7 @@ stream.header.unexpectedPseudoHeader=Connection [{0}], Stream [{1}], Pseudo header [{2}] received after a regular header stream.header.unknownPseudoHeader=Connection [{0}], Stream [{1}], Unknown pseudo header [{2}] received stream.host.inconsistent=Connection [{0}], Stream [{1}], The header host header [{2}] is inconsistent with previously provided values for host [{3}] and/or port [{4}] +stream.host.sni=Connection [{0}], Stream [{1}], The host header [{2}] does not match the SNI host [{3}] stream.inputBuffer.copy=Copying [{0}] bytes from inBuffer to outBuffer stream.inputBuffer.dispatch=Data added to inBuffer when read interest is registered. Triggering a read dispatch stream.inputBuffer.empty=The Stream input buffer is empty. Waiting for more data @@ -111,6 +115,8 @@ stream.inputBuffer.swallowUnread=Swallowing [{0}] bytes previously read into input stream buffer stream.notWritable=Connection [{0}], Stream [{1}], This stream is not writable stream.outputBuffer.flush.debug=Connection [{0}], Stream [{1}], flushing output with buffer at position [{2}], writeInProgress [{3}] and closed [{4}] +stream.recycle.duplicate=Connection [{0}], Stream [{1}] Duplicate request to recycle the associated request and response has been ignored +stream.recycle.first=Connection [{0}], Stream [{1}] The associated request and response have been recycled stream.reset.fail=Connection [{0}], Stream [{1}], Failed to reset stream stream.reset.receive=Connection [{0}], Stream [{1}], Reset received due to [{2}] stream.reset.send=Connection [{0}], Stream [{1}], Reset sent due to [{2}] @@ -130,6 +136,7 @@ upgradeHandler.allocate.debug=Connection [{0}], Stream [{1}], allocated [{2}] bytes upgradeHandler.allocate.left=Connection [{0}], Stream [{1}], [{2}] bytes unallocated - trying to allocate to children +upgradeHandler.clientCancel=Client reset the stream before the response was complete upgradeHandler.connectionError=Connection error upgradeHandler.enableRfc7450Priorities=Connection [{0}], RFC 7450 priorities may not be enabled after being disabled in the initial connection settings frame (see RFC 9218) upgradeHandler.fallToDebug=\n\ @@ -148,6 +155,8 @@ upgradeHandler.pruneStart=Connection [{0}] Starting pruning of old streams. Limit is [{1}] and there are currently [{2}] streams. upgradeHandler.pruned=Connection [{0}] Pruned completed stream [{1}] upgradeHandler.releaseBacklog=Connection [{0}], Stream [{1}] released from backlog +upgradeHandler.replace.duplicate=Connection [{0}], Stream [{1}] duplicate attempt to replace stream with lightweight implementation has been ignored +upgradeHandler.replace.first=Connection [{0}], Stream [{1}] replaced with lightweight stream implementation upgradeHandler.reset.receive=Connection [{0}], Stream [{1}], Reset received due to [{2}] upgradeHandler.rst.debug=Connection [{0}], Stream [{1}], Error [{2}], Message [{3}], RST (closing stream) upgradeHandler.sendPrefaceFail=Connection [{0}], Failed to send preface to client @@ -159,6 +168,7 @@ upgradeHandler.stream.even=A new remote stream ID of [{0}] was requested but all remote streams must use odd identifiers upgradeHandler.stream.notWritable=Connection [{0}], Stream [{1}], This stream is in state [{2}] and is not writable upgradeHandler.stream.old=A new remote stream ID of [{0}] was requested but the most recent stream was [{1}] +upgradeHandler.throwable=Connection [{0}] upgradeHandler.tooManyRemoteStreams=The client attempted to use more than [{0}] active streams upgradeHandler.tooMuchOverhead=Connection [{0}], Too much overhead so the connection will be closed upgradeHandler.unexpectedAck=Connection [{0}], Stream [{1}], A settings acknowledgement was received when not expected diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -37,6 +37,7 @@ frameType.checkStream=Type de trame invalide [{0}] hpack.integerEncodedOverTooManyOctets=Un entier de taille variable de HPACK a été encodé sur trop d''octets, le maximum est de [{0}] +hpack.integerEncodedTooBig=La valeur maximale permise pour un entier de longueur variable encodé par HPACK est Integer.MAX_VALUE hpack.invalidCharacter=Le caractère Unicode [{0}] ayant le code point [{1}] ne peut être encodé, parce qu''il est en-dehors de l''éventail permis 0-255. hpackEncoder.encodeHeader=Encodage de l''en-tête [{0}] avec la valeur [{1}] @@ -77,6 +78,7 @@ http2Parser.processFrameHeaders.decodingFailed=Une erreur de décodage HPACK des en-têtes HTTP s'est produite http2Parser.processFrameHeaders.payload=Connection [{0}], Flux [{1}], Traitement des en-têtes avec une taille de données de [{2}] http2Parser.processFramePriorityUpdate.debug=Connection [{0}], Stream [{1}], Urgency [{2}], Incremental [{3}] +http2Parser.processFramePriorityUpdate.invalid=Connection [{0}], Stream [{1}], La trame Priority Update a une valeur de champ de priorité invalide http2Parser.processFramePriorityUpdate.streamZero=Connection [{0}], La trame de mise à jour de priorité a été recue pour le flux zéro http2Parser.processFramePushPromise=Connexion [{0}], Flux (Stream) [{1}], les trames de promesse d''envoi ("Push promise frames") ne doivent pas être envoyées par le client. http2Parser.processFrameSettings.ackWithNonZeroPayload=La trame de paramètres a été reçue avec un indicateur ACK activé et des données présentes @@ -88,6 +90,7 @@ pingManager.roundTripTime=Connection [{0}] Le temps d''aller retour est de [{1}]ns +stream.clientResetRequest=Le client a réinitialisé la stream avant qu'elle ait été complètement lue stream.closed=Connection [{0}], Flux [{1}], Impossible d''écrire sur un flux après sa fermeture stream.header.case=Connection [{0}], Flux [{1}], Le nom d''en-tête HTTP [{2}] doit être en miniscules stream.header.connection=Connection [{0}], Flux [{1}], L''en-tête HTTP [{2}] n''est pas autorisé dans une requête HTTP/2 @@ -111,6 +114,8 @@ stream.inputBuffer.swallowUnread=[{0}] bytes qui ont été auparavant lu dans le tampon d''entrée ont été avalés stream.notWritable=Connection [{0}], Flux [{1}], Impossible d''écrire sur ce flux stream.outputBuffer.flush.debug=Connection [{0}], Flux [{1}], envoi des données mises en tampon depuis la position [{2}], writeInProgress [{3}] et closed [{4}] +stream.recycle.duplicate=Connection [{0}], Stream [{1}] Une tentative de double recyclage de la requête et de la réponse associées a été ignorée +stream.recycle.first=Connection [{0}], Stream [{1}] La requête et la réponse associées ont été recyclées stream.reset.fail=Connection [{0}], Flux [{1}], Echec de réinitialisation du flux stream.reset.receive=Connection [{0}], Flux [{1}], Réinitialisation reçue à cause de [{2}] stream.reset.send=Connection [{0}], Flux [{1}], Réinitialisation envoyée à cause de [{2}] @@ -130,6 +135,7 @@ upgradeHandler.allocate.debug=Connection [{0}], Flux [{1}], [{2}] octets alloués upgradeHandler.allocate.left=Connection [{0}], Flux [{1}], [{2}] octets désalloués, essai d''allocation aux enfants +upgradeHandler.clientCancel=Le client a réinitialisé la stream avant que la réponse ne soit complète upgradeHandler.connectionError=Erreur de la connection upgradeHandler.enableRfc7450Priorities=Connection [{0}], les priorités RFC 7450 ne doivent pas être activées après avoir été désactivées dans la trame initiale des paramètres de connection (voir la RFC 9218) upgradeHandler.fallToDebug=\n\ @@ -148,6 +154,8 @@ upgradeHandler.pruneStart=Connection [{0}] Début de l''élimination des anciens flux, la limite est de [{1}] et il y a actuellement [{2}] flux upgradeHandler.pruned=Connection [{0}] Elimination du flux terminé [{1}] upgradeHandler.releaseBacklog=Connection [{0}], Flux [{1}] enlevée de la file d''attente +upgradeHandler.replace.duplicate=Connection [{0}], Stream [{1}] Une tentative de double remplacement de la stream par son implémentation légère a été ignorée +upgradeHandler.replace.first=Connection [{0}], Stream [{1}] Remplacement par l''implémentation légère de stream upgradeHandler.reset.receive=Connection [{0}], Stream [{1}], Reset a été reçu à cause de [{2}] upgradeHandler.rst.debug=Connexion [{0}], Flux [{1}], Erreur [{2}], Message [{3}], RST (fermeture du flux) upgradeHandler.sendPrefaceFail=Connexion [{0}], échec d''envoi de la préface au client @@ -159,6 +167,7 @@ upgradeHandler.stream.even=Un nouvel ID de flux distant (remote stream) [{0}] a été requis, mais tous les flux distants doivent utiliser ID impairs upgradeHandler.stream.notWritable=Connection [{0}], Flux [{1}], Impossible d''écrire sur ce flux upgradeHandler.stream.old=Un nouveau flux distant avec l''ID [{0}] a été demandé mais le flux le plus récent est [{1}] +upgradeHandler.throwable=Connection [{0}] upgradeHandler.tooManyRemoteStreams=Le client a essayé d''utiliser plus de [{0}] flux actifs upgradeHandler.tooMuchOverhead=Connection [{0}], Le traitement est trop coûteux donc la connection sera fermée upgradeHandler.unexpectedAck=Connection [{0}], Flux [{1}], Une notification de réception de paramètres a été reçue alors qu''aucune n''était attendue diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -37,6 +37,7 @@ frameType.checkStream=無効なフレームタイプ [{0}] hpack.integerEncodedOverTooManyOctets=エンコードされたHPACK可変長整数は多くのオクテットを超過。最大値は [{0}] +hpack.integerEncodedTooBig=HPACKエンコードされた可変長整数の最大許容値はInteger.MAX_VALUEです hpack.invalidCharacter=コードポイント [{1}] のユニコード文字 [{0}] は有効範囲 0 から 255 の範囲外のため、エンコードできません。 hpackEncoder.encodeHeader=ヘッダー[{0}]を値[{1}]でエンコードしています @@ -77,6 +78,7 @@ http2Parser.processFrameHeaders.decodingFailed=HTTP ヘッダーの HPACK 復号化中にエラーが発生しました。 http2Parser.processFrameHeaders.payload=コネクション [{0}]、ストリーム [{1}]、サイズ [{2}] のヘッダーペイロードを処理中 http2Parser.processFramePriorityUpdate.debug=接続 [{0}]、ストリーム [{1}]、緊急度 [{2}]、増分 [{3}] +http2Parser.processFramePriorityUpdate.invalid=コネクション [{0}]、ストリーム [{1}]、優先度フィールド値が無効な優先度更新フレーム http2Parser.processFramePriorityUpdate.streamZero=接続 [{0}] は、ストリーム 0 を優先するための優先更新フレームを受信しました http2Parser.processFramePushPromise=コネクション [{0}]、ストリーム [{1}]、クライアントから PUSH_PROMISE フレームを送信するべきではありません http2Parser.processFrameSettings.ackWithNonZeroPayload=ACKフラグがセットされ、ペイロードが存在する状態で受信されたSettingsフレーム @@ -88,6 +90,7 @@ pingManager.roundTripTime=コネクション [{0}] の往復時間は [{1}] ns でした。 +stream.clientResetRequest=リクエストが完全に読み取られる前にクライアントがストリームをリセットしました stream.closed=コネクション [{0}]、ストリーム [{1}]、切断したストリームには書き込みできません stream.header.case=コネクション [{0}]、ストリーム [{1}]、HTTP ヘッダー名 [{2}] は小文字でなければなりません。 stream.header.connection=コネクション [{0}]、ストリーム [{1}]、HTTP/2 のリクエストには HTTP ヘッダー [{2}] を指定することはできません。 @@ -111,6 +114,8 @@ stream.inputBuffer.swallowUnread=以前に入力ストリームバッファに読み込まれた [{0}] バイトを飲み込ます stream.notWritable=コネクション [{0}]、ストリーム [{1}]、このストリームには書き込みできません。 stream.outputBuffer.flush.debug=コネクション [{0}]、ストリーム [{1}]、バッファポジション [{2}]で出力をフラッシュ、writeInProgress [{3}]、クローズ [{4}] +stream.recycle.duplicate=Connection [{0}]、Stream [{1}] 関連するリクエストとレスポンスをリサイクルするための重複したリクエストは無視されました +stream.recycle.first=Connection [{0}]、Stream [{1}] 関連するリクエストとレスポンスがリサイクルされました stream.reset.fail=コネクション [{0}]、ストリーム [{1}]、ストリームをリセットできません。 stream.reset.receive=コネクション [{0}]、ストリーム [{1}]、[{2}] のために受信されたリセット stream.reset.send=コネクション [{0}]、ストリーム [{1}]、[{2}] が原因で RESET を送信しました。 @@ -130,6 +135,7 @@ upgradeHandler.allocate.debug=コネクション [{0}]、ストリーム [{1}]、割り当てられた [{2}] バイト upgradeHandler.allocate.left=コネクション [{0}]、ストリーム [{1}]、[{2}] バイトが未割り当て - 子への割り当てを試みています +upgradeHandler.clientCancel=レスポンスが完了する前にクライアントがストリームをリセットしました upgradeHandler.connectionError=接続エラー upgradeHandler.enableRfc7450Priorities=接続 [{0}] は、RFC 7450 優先順位が初期接続設定フレームで無効にされた後に有効にならない場合があります (RFC 9218 を参照) upgradeHandler.fallToDebug=\n\ @@ -148,6 +154,8 @@ upgradeHandler.pruneStart=コネクション [{0}] 古いストリームのプルーニングを開始します。 上限は [{1}] で、現在 [{2}] ストリームがあります。 upgradeHandler.pruned=コネクション [{0}]、完了したストリーム [{1}] は削除します。 upgradeHandler.releaseBacklog=コネクション [{0}]、ストリーム [{1}] はバックログから解放されました +upgradeHandler.replace.duplicate=Connection [{0}]、Stream [{1}] のストリームを軽量実装に置き換える重複した試みは無視されました +upgradeHandler.replace.first=Connection [{0}]、Stream [{1}] が軽量ストリーム実装に置き換えられました upgradeHandler.reset.receive=Connection[{0}]、Stream[{1}]、[{2}]のためにリセットを受信しました upgradeHandler.rst.debug=コネクション [{0}]、ストリーム [{1}]、エラー [{2}]、メッセージ [{3}]、RST (ストリームを切断します) upgradeHandler.sendPrefaceFail=コネクション [{0}]、クライアントにプリフェイスを送信できませんでした。 @@ -159,6 +167,7 @@ upgradeHandler.stream.even=新しいリモートストリーム ID [{0}] を要求されましたがリモートストリームの ID は奇数でなければなりません。 upgradeHandler.stream.notWritable=コネクション [{0}]、ストリーム [{1}]、このストリームは書き込み可能ではありません upgradeHandler.stream.old=新しいリモートストリーム ID [{0}] を要求されましたが、最新のストリームは [{1}] です。 +upgradeHandler.throwable=コネクション[{0}] upgradeHandler.tooManyRemoteStreams=クライアントは [{0}] 以上のアクティブなストリームを使用しようとしました upgradeHandler.tooMuchOverhead=コネクション [{0}]、オーバーヘッドが多すぎるため、接続が閉じられます。 upgradeHandler.unexpectedAck=コネクション [{0}]、ストリーム [{1}]、予期しないときにsettings ackを受信しました @@ -171,7 +180,7 @@ upgradeHandler.windowUpdateConnection=コネクション [{0}]、ウィンドウの更新をクライアントに送信し、ウィンドウを[{1}]バイト増やします upgradeHandler.windowUpdateStream=コネクション [{0}]、ストリーム [{1}]、ウィンドウの更新をクライアントに送信し、ウィンドウを [{2}] バイト増やします upgradeHandler.writeBody=コネクション [{0}]、ストリーム [{1}]、データ長 [{2}] -upgradeHandler.writeHeaders=コネクション [{0}], ストリーム [{1}] +upgradeHandler.writeHeaders=コネクション [{0}], ストリーム [{1}], ヘッダの書き込み, EndOfStream [{2}] upgradeHandler.writePushHeaders=コネクション [{0}]、ストリーム [{1}]、プッシュされたストリーム [{2}]、EndOfStream [{3}] windowAllocationManager.dispatched=コネクション [{0}]、ストリーム [{1}]、ディスパッチされました diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings_ko.properties tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_ko.properties --- tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings_ko.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_ko.properties 2026-01-23 19:33:36.000000000 +0000 @@ -125,6 +125,7 @@ upgradeHandler.allocate.debug=연결 [{0}], 스트림 [{1}], [{2}] 바이트를 할당함. upgradeHandler.allocate.left=연결 [{0}], 스트림 [{1}], [{2}] 바이트들이 할당 해제되었습니다 - 자식들에 할당하려 시도합니다. +upgradeHandler.clientCancel=응답이 완료되기 전에 클라이언트가 스트림을 리셋했습니다. upgradeHandler.connectionError=연결 오류 upgradeHandler.fallToDebug=\n\ \ 주의: 추가로 발생하는 HTTP/2 스트림 오류들은 디버그 수준의 로그로 기록될 것입니다. diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/coyote/http2/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -75,6 +75,7 @@ http2Parser.processFrameHeaders.decodingDataLeft=数据在HPACK解码后依然保留 - 它本应该被消费掉 http2Parser.processFrameHeaders.decodingFailed=对HTTP头进行HPACK解码时出错 http2Parser.processFrameHeaders.payload=连接:[{0}],流:[{1}],正在处理[{1}]大小的头文件负载 +http2Parser.processFramePriorityUpdate.debug=连接 [{0}], 数据流 [{1}], 紧迫性 [{2}], 增量 [{3}] http2Parser.processFramePushPromise=连接 [{0}],流 [{1}],PUSH_PROMISE帧不应由客户端发送 http2Parser.processFrameSettings.ackWithNonZeroPayload=接收到带有ACK标志设置和有效负载的设置帧 http2Parser.processFrameWindowUpdate.debug=连接[{0}],流[{1}],窗口大小增量[{2}] @@ -125,6 +126,7 @@ upgradeHandler.allocate.debug=连接[{0}],流[{1}],已分配[{2}]字节 upgradeHandler.allocate.left=连接[{0}],流[{1}],[{2}]字节未分配 - 尝试分配给子项 +upgradeHandler.clientCancel=客户端在响应完成前重置了数据流 upgradeHandler.connectionError=连接错误 upgradeHandler.fallToDebug=注意:往后出现 HTTP/2 流的错误将以 DEBUG 日志级别输出。 upgradeHandler.goaway.debug=连接[{0}],离开,最后的流[{1}],错误码[{2}],调试数据[{3}] diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/RecycledStream.java tomcat10-10.1.52/java/org/apache/coyote/http2/RecycledStream.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/RecycledStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/RecycledStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,15 +47,15 @@ @Override - void receivedData(int payloadSize) throws ConnectionException { - remainingFlowControlWindow -= payloadSize; + void receivedData(int dataLength) throws ConnectionException { + remainingFlowControlWindow -= dataLength; } /** * {@inheritDoc} *

          - * This implementation will return an zero length ByteBuffer to trigger a flow control error if more DATA frame + * This implementation will return a zero length ByteBuffer to trigger a flow control error if more DATA frame * payload than the remaining flow control window is received for this recycled stream. */ @Override diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/Setting.java tomcat10-10.1.52/java/org/apache/coyote/http2/Setting.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/Setting.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/Setting.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,9 @@ INITIAL_WINDOW_SIZE(4), MAX_FRAME_SIZE(5), MAX_HEADER_LIST_SIZE(6), + ENABLE_CONNECT_PROTOCOL(8), NO_RFC7540_PRIORITIES(9), + TLS_RENEG_PERMITTED(10), UNKNOWN(Integer.MAX_VALUE); private final int id; @@ -61,9 +63,15 @@ case 6: { return MAX_HEADER_LIST_SIZE; } + case 8: { + return ENABLE_CONNECT_PROTOCOL; + } case 9: { return NO_RFC7540_PRIORITIES; } + case 10: { + return TLS_RENEG_PERMITTED; + } default: { return UNKNOWN; } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/Stream.java tomcat10-10.1.52/java/org/apache/coyote/http2/Stream.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/Stream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/Stream.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,6 +49,7 @@ import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.MessageBytes; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.http.parser.Host; import org.apache.tomcat.util.http.parser.Priority; @@ -103,7 +104,7 @@ private volatile StringBuilder cookieHeader = null; private volatile boolean hostHeaderSeen = false; - private Object pendingWindowUpdateForStreamLock = new Object(); + private final Object pendingWindowUpdateForStreamLock = new Object(); private int pendingWindowUpdateForStream = 0; private volatile int urgency = Priority.DEFAULT_URGENCY; @@ -134,7 +135,7 @@ // HTTP/2 Push or HTTP/1.1 upgrade /* * Implementation note. The request passed in is always newly created so it is safe to recycle it for re-use - * in the Stream.recyle() method. Need to create a matching, new response. + * in the Stream.recycle() method. Need to create a matching, new response. */ this.coyoteRequest = coyoteRequest; this.coyoteResponse = new Response(); @@ -164,7 +165,7 @@ this.coyoteResponse.setOutputBuffer(http2OutputBuffer); this.coyoteRequest.setResponse(coyoteResponse); this.coyoteRequest.protocol().setString("HTTP/2.0"); - this.coyoteRequest.setStartTimeNanos(System.nanoTime()); + this.coyoteRequest.markStartTime(); } @@ -326,7 +327,7 @@ log.trace(sm.getString("stream.header.debug", getConnectionId(), getIdAsString(), name, value)); } - // Header names must be lower case + // Header names must be lowercase if (!name.toLowerCase(Locale.US).equals(name)) { throw new HpackException(sm.getString("stream.header.case", getConnectionId(), getIdAsString(), name)); } @@ -348,7 +349,7 @@ return; } - if (name.length() == 0) { + if (name.isEmpty()) { throw new HpackException(sm.getString("stream.header.empty", getConnectionId(), getIdAsString())); } @@ -368,9 +369,9 @@ switch (name) { case ":method": { - if (coyoteRequest.method().isNull()) { - coyoteRequest.method().setString(value); - if ("HEAD".equals(value)) { + if (coyoteRequest.getMethod() == null) { + coyoteRequest.setMethod(value); + if (Method.HEAD.equals(value)) { configureVoidOutputFilter(); } } else { @@ -393,7 +394,7 @@ throw new HpackException( sm.getString("stream.header.duplicate", getConnectionId(), getIdAsString(), ":path")); } - if (value.length() == 0) { + if (value.isEmpty()) { throw new HpackException(sm.getString("stream.header.noPath", getConnectionId(), getIdAsString())); } int queryStart = value.indexOf('?'); @@ -456,6 +457,12 @@ setIncremental(p.getIncremental()); } catch (IOException ioe) { // Not possible with StringReader + } catch (IllegalArgumentException iae) { + // Invalid priority header field values should be ignored + if (log.isTraceEnabled()) { + log.trace(sm.getString("http2Parser.processFramePriorityUpdate.invalid", getConnectionId(), + getIdAsString()), iae); + } } break; } @@ -504,6 +511,12 @@ } else { coyoteRequest.serverName().setString(value); } + // Match host name with SNI if required + if (!handler.getProtocol().getHttp11Protocol().checkSni(handler.getSniHostName(), + coyoteRequest.serverName().getString())) { + throw new HpackException(sm.getString("stream.host.sni", getConnectionId(), getIdAsString(), value, + handler.getSniHostName())); + } } @@ -546,8 +559,8 @@ final boolean receivedEndOfHeaders() throws ConnectionException { - if (coyoteRequest.method().isNull() || coyoteRequest.scheme().isNull() || - !coyoteRequest.method().equals("CONNECT") && coyoteRequest.requestURI().isNull()) { + if (coyoteRequest.getMethod() == null || coyoteRequest.scheme().isNull() || + !Method.CONNECT.equals(coyoteRequest.getMethod()) && coyoteRequest.requestURI().isNull()) { throw new ConnectionException(sm.getString("stream.header.required", getConnectionId(), getIdAsString()), Http2Error.PROTOCOL_ERROR); } @@ -670,8 +683,8 @@ @Override - final void receivedData(int payloadSize) throws Http2Exception { - contentLengthReceived += payloadSize; + final void receivedData(int dataLength) throws Http2Exception { + contentLengthReceived += dataLength; long contentLengthHeader = coyoteRequest.getContentLengthLong(); if (contentLengthHeader > -1 && contentLengthReceived > contentLengthHeader) { throw new ConnectionException( @@ -696,10 +709,7 @@ final boolean isContentLengthInconsistent() { long contentLengthHeader = coyoteRequest.getContentLengthLong(); - if (contentLengthHeader > -1 && contentLengthReceived != contentLengthHeader) { - return true; - } - return false; + return contentLengthHeader > -1 && contentLengthReceived != contentLengthHeader; } @@ -859,7 +869,7 @@ return; } // Set the special HTTP/2 headers - request.getMimeHeaders().addValue(":method").duplicate(request.method()); + request.getMimeHeaders().addValue(":method").setString(request.getMethod()); request.getMimeHeaders().addValue(":scheme").duplicate(request.scheme()); StringBuilder path = new StringBuilder(request.requestURI().toString()); if (!request.queryString().isNull()) { @@ -1145,12 +1155,8 @@ // Bug 63682 // Only want to return false if the window size is zero AND we are // already waiting for an allocation. - if (getWindowSize() > 0 && allocationManager.isWaitingForStream() || - handler.getWindowSize() > 0 && allocationManager.isWaitingForConnection() || dataLeft) { - return false; - } else { - return true; - } + return (getWindowSize() <= 0 || !allocationManager.isWaitingForStream()) && + (handler.getWindowSize() <= 0 || !allocationManager.isWaitingForConnection()) && !dataLeft; } finally { writeLock.unlock(); } @@ -1185,8 +1191,8 @@ /* * This method should only be called during blocking I/O. All the Servlet API calls that end up here are * illegal during non-blocking I/O. Servlet 5.4. However, the wording Servlet specification states that the - * behaviour is undefined so we do the best we can which is to perform a flush using blocking I/O or - * non-blocking I/O based depending which is currently in use. + * behaviour is undefined so we do the best we can, which is to perform a flush using blocking I/O or + * non-blocking I/O based depending on which is currently in use. */ flush(getCoyoteResponse().getWriteListener() == null); } @@ -1281,7 +1287,7 @@ ensureBuffersExist(); - int written = -1; + int written; // It is still possible that the stream has been closed and inBuffer // set to null between the call to ensureBuffersExist() above and @@ -1511,11 +1517,22 @@ readStateLock.unlock(); } - // Trigger ReadListener.onError() - getCoyoteRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION, - new IOException(sm.getString("stream.clientResetRequest"))); - coyoteRequest.action(ActionCode.DISPATCH_ERROR, null); - coyoteRequest.action(ActionCode.DISPATCH_EXECUTE, null); + /* + * There is a potential race between a reset being received by the Http2UpgradeHandler and the + * StreamProcessor recycling the Stream. The use of recycledLock ensures that an attempt is not made to + * notify the ReadListener after the Stream has been recycled. + */ + if (!recycled) { + synchronized (recycledLock) { + if (!recycled) { + // Trigger ReadListener.onError() + getCoyoteRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION, + new IOException(sm.getString("stream.clientResetRequest"))); + coyoteRequest.action(ActionCode.DISPATCH_ERROR, null); + coyoteRequest.action(ActionCode.DISPATCH_EXECUTE, null); + } + } + } } @Override @@ -1536,7 +1553,7 @@ readStateLock.unlock(); } if (inBuffer != null) { - int unreadByteCount = 0; + int unreadByteCount; synchronized (inBuffer) { unreadByteCount = inBuffer.position(); if (log.isTraceEnabled()) { diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/StreamProcessor.java tomcat10-10.1.52/java/org/apache/coyote/http2/StreamProcessor.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/StreamProcessor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/StreamProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,6 +35,7 @@ import org.apache.coyote.Adapter; import org.apache.coyote.ContinueResponseTiming; import org.apache.coyote.ErrorState; +import org.apache.coyote.NonPipeliningProcessor; import org.apache.coyote.Request; import org.apache.coyote.RequestGroupInfo; import org.apache.coyote.Response; @@ -52,7 +53,7 @@ import org.apache.tomcat.util.net.SocketWrapperBase; import org.apache.tomcat.util.res.StringManager; -class StreamProcessor extends AbstractProcessor { +class StreamProcessor extends AbstractProcessor implements NonPipeliningProcessor { private static final Log log = LogFactory.getLog(StreamProcessor.class); private static final StringManager sm = StringManager.getManager(StreamProcessor.class); @@ -146,8 +147,14 @@ state = SocketState.CLOSED; } finally { if (state == SocketState.CLOSED) { - stream.recycle(); + /* + * Recycle this processor before the stream is recycled as recycling the stream will add the + * request and the response to the pool for re-use (if re-use is enabled) and the request + * statistics updating in StreamProcessor.recycle() needs to happen before the request and + * response are added to the pool to avoid concurrency issues corrupting the statistics. + */ recycle(); + stream.recycle(); } } } finally { @@ -307,7 +314,7 @@ stream.getInputBuffer().insertReplayedBody(body); try { stream.receivedEndOfStream(); - } catch (ConnectionException e) { + } catch (ConnectionException ignore) { // Exception will not be thrown in this case } } @@ -432,6 +439,12 @@ global.removeRequestProcessor(request.getRequestProcessor()); } + /* + * Clear the statistics ready for re-use of the request. If we don't clear the statistics, the statistics for + * the current request will be included in the statistics for all future requests. + */ + request.getRequestProcessor().recycleStatistcs(); + // Clear fields that can be cleared to aid GC and trigger NPEs if this // is reused setSocketWrapper(null); @@ -509,8 +522,7 @@ HttpParser httpParser = handler.getProtocol().getHttp11Protocol().getHttpParser(); // Method name must be a token - String method = request.method().toString(); - if (!HttpParser.isToken(method)) { + if (!HttpParser.isToken(request.getMethod())) { return false; } diff -Nru tomcat10-10.1.34/java/org/apache/coyote/http2/WindowAllocationManager.java tomcat10-10.1.52/java/org/apache/coyote/http2/WindowAllocationManager.java --- tomcat10-10.1.34/java/org/apache/coyote/http2/WindowAllocationManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/coyote/http2/WindowAllocationManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -208,7 +208,7 @@ // Reset this here so multiple notifies (possible with a // backlog containing multiple streams and small window updates) // are handled correctly (only the first should trigger a call - // to stream.notify(). Additional notify() calls may trigger + // to stream.notify()). Additional notify() calls may trigger // unexpected timeouts. waitingFor = NONE; Response response = stream.getCoyoteResponse(); diff -Nru tomcat10-10.1.34/java/org/apache/el/ExpressionFactoryImpl.java tomcat10-10.1.52/java/org/apache/el/ExpressionFactoryImpl.java --- tomcat10-10.1.34/java/org/apache/el/ExpressionFactoryImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/ExpressionFactoryImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,11 +28,8 @@ import org.apache.el.util.ExceptionUtils; import org.apache.el.util.MessageFactory; - /** * @see jakarta.el.ExpressionFactory - * - * @author Jacob Hookom [jacob@hookom.net] */ @aQute.bnd.annotation.spi.ServiceProvider(value = ExpressionFactory.class) public class ExpressionFactoryImpl extends ExpressionFactory { diff -Nru tomcat10-10.1.34/java/org/apache/el/MethodExpressionImpl.java tomcat10-10.1.52/java/org/apache/el/MethodExpressionImpl.java --- tomcat10-10.1.34/java/org/apache/el/MethodExpressionImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/MethodExpressionImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,7 +53,7 @@ * This provides the base object on which the method appears. If the base object is null, a * NullPointerException must be thrown. At the last resolution, the final property is then * coerced to a String, which provides the name of the method to be found. A method matching the name and - * expected parameters provided at parse time is found and it is either queried or invoked (depending on the method + * expected parameters provided at parse time is found, and it is either queried or invoked (depending on the method * called on this MethodExpression). *

          *

          @@ -63,8 +63,6 @@ * @see jakarta.el.Expression * @see jakarta.el.ExpressionFactory * @see jakarta.el.MethodExpression - * - * @author Jacob Hookom [jacob@hookom.net] */ public final class MethodExpressionImpl extends MethodExpression implements Externalizable { diff -Nru tomcat10-10.1.34/java/org/apache/el/ValueExpressionImpl.java tomcat10-10.1.52/java/org/apache/el/ValueExpressionImpl.java --- tomcat10-10.1.34/java/org/apache/el/ValueExpressionImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/ValueExpressionImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -70,8 +70,6 @@ * @see jakarta.el.Expression * @see jakarta.el.ExpressionFactory * @see jakarta.el.ValueExpression - * - * @author Jacob Hookom [jacob@hookom.net] */ public final class ValueExpressionImpl extends ValueExpression implements Externalizable { diff -Nru tomcat10-10.1.34/java/org/apache/el/lang/ELArithmetic.java tomcat10-10.1.52/java/org/apache/el/lang/ELArithmetic.java --- tomcat10-10.1.34/java/org/apache/el/lang/ELArithmetic.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/lang/ELArithmetic.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,9 +26,7 @@ /** - * A helper class of Arithmetic defined by the EL Specification - * - * @author Jacob Hookom [jacob@hookom.net] + * A helper class of Arithmetic defined by the EL Specification. */ public abstract class ELArithmetic { @@ -252,7 +250,7 @@ private static final Long ZERO = Long.valueOf(0); - public static final Number add(final Object obj0, final Object obj1) { + public static Number add(final Object obj0, final Object obj1) { final ELArithmetic delegate = findDelegate(obj0, obj1); if (delegate == null) { return Long.valueOf(0); @@ -264,7 +262,7 @@ return delegate.add(num0, num1); } - public static final Number mod(final Object obj0, final Object obj1) { + public static Number mod(final Object obj0, final Object obj1) { if (obj0 == null && obj1 == null) { return Long.valueOf(0); } @@ -286,7 +284,7 @@ return delegate.mod(num0, num1); } - public static final Number subtract(final Object obj0, final Object obj1) { + public static Number subtract(final Object obj0, final Object obj1) { final ELArithmetic delegate = findDelegate(obj0, obj1); if (delegate == null) { return Long.valueOf(0); @@ -298,7 +296,7 @@ return delegate.subtract(num0, num1); } - public static final Number divide(final Object obj0, final Object obj1) { + public static Number divide(final Object obj0, final Object obj1) { if (obj0 == null && obj1 == null) { return ZERO; } @@ -318,7 +316,7 @@ return delegate.divide(num0, num1); } - public static final Number multiply(final Object obj0, final Object obj1) { + public static Number multiply(final Object obj0, final Object obj1) { final ELArithmetic delegate = findDelegate(obj0, obj1); if (delegate == null) { return Long.valueOf(0); @@ -350,11 +348,11 @@ } } - public static final boolean isNumber(final Object obj) { + public static boolean isNumber(final Object obj) { return (obj != null && isNumberType(obj.getClass())); } - public static final boolean isNumberType(final Class type) { + public static boolean isNumberType(final Class type) { return type == Long.TYPE || type == Double.TYPE || type == Byte.TYPE || type == Short.TYPE || type == Integer.TYPE || type == Float.TYPE || Number.class.isAssignableFrom(type); } diff -Nru tomcat10-10.1.34/java/org/apache/el/lang/ELSupport.java tomcat10-10.1.52/java/org/apache/el/lang/ELSupport.java --- tomcat10-10.1.34/java/org/apache/el/lang/ELSupport.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/lang/ELSupport.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,9 +40,7 @@ /** - * A helper class that implements the EL Specification - * - * @author Jacob Hookom [jacob@hookom.net] + * A helper class that implements the EL Specification. */ public class ELSupport { @@ -93,7 +91,7 @@ * @throws ELException if neither object is Comparable * @throws ClassCastException if the objects are not mutually comparable */ - public static final int compare(final ELContext ctx, final Object obj0, final Object obj1) throws ELException { + public static int compare(final ELContext ctx, final Object obj0, final Object obj1) throws ELException { if (obj0 == obj1 || equals(ctx, obj0, obj1)) { return 0; } @@ -154,7 +152,7 @@ * * @throws ELException if one of the coercion fails */ - public static final boolean equals(final ELContext ctx, final Object obj0, final Object obj1) throws ELException { + public static boolean equals(final ELContext ctx, final Object obj0, final Object obj1) throws ELException { if (obj0 == obj1) { return true; } else if (obj0 == null || obj1 == null) { @@ -183,18 +181,18 @@ return obj1.equals(coerceToEnum(ctx, obj0, obj1.getClass())); } else if (obj0 instanceof String || obj1 instanceof String) { int lexCompare = coerceToString(ctx, obj0).compareTo(coerceToString(ctx, obj1)); - return (lexCompare == 0) ? true : false; + return lexCompare == 0; } else { return obj0.equals(obj1); } } /* - * Going to have to have some casts /raw types somewhere so doing it here keeps them all in one place. There might - * be a neater / better solution but I couldn't find it. + * Going to have some casts /raw types somewhere so doing it here keeps them all in one place. There might be a + * neater / better solution, but I couldn't find it. */ @SuppressWarnings("unchecked") - public static final Enum coerceToEnum(final ELContext ctx, final Object obj, + public static Enum coerceToEnum(final ELContext ctx, final Object obj, @SuppressWarnings("rawtypes") Class type) { if (ctx != null) { @@ -240,15 +238,14 @@ * * @throws ELException if object is not Boolean or String */ - public static final Boolean coerceToBoolean(final ELContext ctx, final Object obj, boolean primitive) - throws ELException { + public static Boolean coerceToBoolean(final ELContext ctx, final Object obj, boolean primitive) throws ELException { if (ctx != null) { boolean originalIsPropertyResolved = ctx.isPropertyResolved(); try { - Object result = ctx.getELResolver().convertToType(ctx, obj, Boolean.class); + Boolean result = ctx.getELResolver().convertToType(ctx, obj, Boolean.class); if (ctx.isPropertyResolved()) { - return (Boolean) result; + return result; } } finally { ctx.setPropertyResolved(originalIsPropertyResolved); @@ -279,9 +276,9 @@ if (ctx != null) { boolean originalIsPropertyResolved = ctx.isPropertyResolved(); try { - Object result = ctx.getELResolver().convertToType(ctx, obj, Character.class); + Character result = ctx.getELResolver().convertToType(ctx, obj, Character.class); if (ctx.isPropertyResolved()) { - return (Character) result; + return result; } } finally { ctx.setPropertyResolved(originalIsPropertyResolved); @@ -305,7 +302,7 @@ throw new ELException(MessageFactory.get("error.convert", obj, objType, Character.class)); } - protected static final Number coerceToNumber(final Number number, final Class type) throws ELException { + protected static Number coerceToNumber(final Number number, final Class type) throws ELException { if (Long.TYPE == type || Long.class.equals(type)) { return Long.valueOf(number.longValue()); } @@ -349,8 +346,7 @@ throw new ELException(MessageFactory.get("error.convert", number, number.getClass(), type)); } - public static final Number coerceToNumber(final ELContext ctx, final Object obj, final Class type) - throws ELException { + public static Number coerceToNumber(final ELContext ctx, final Object obj, final Class type) throws ELException { if (ctx != null) { boolean originalIsPropertyResolved = ctx.isPropertyResolved(); @@ -387,7 +383,7 @@ throw new ELException(MessageFactory.get("error.convert", obj, obj.getClass(), type)); } - protected static final Number coerceToNumber(final String val, final Class type) throws ELException { + protected static Number coerceToNumber(final String val, final Class type) throws ELException { if (Long.TYPE == type || Long.class.equals(type)) { try { return Long.valueOf(val); @@ -456,14 +452,14 @@ * * @return the String value of the object */ - public static final String coerceToString(final ELContext ctx, final Object obj) { + public static String coerceToString(final ELContext ctx, final Object obj) { if (ctx != null) { boolean originalIsPropertyResolved = ctx.isPropertyResolved(); try { - Object result = ctx.getELResolver().convertToType(ctx, obj, String.class); + String result = ctx.getELResolver().convertToType(ctx, obj, String.class); if (ctx.isPropertyResolved()) { - return (String) result; + return result; } } finally { ctx.setPropertyResolved(originalIsPropertyResolved); @@ -489,8 +485,7 @@ } } - public static final T coerceToType(final ELContext ctx, final Object obj, final Class type) - throws ELException { + public static T coerceToType(final ELContext ctx, final Object obj, final Class type) throws ELException { if (ctx != null) { boolean originalIsPropertyResolved = ctx.isPropertyResolved(); @@ -585,8 +580,7 @@ } if (obj instanceof LambdaExpression && isFunctionalInterface(type)) { - T result = coerceToFunctionalInterface(ctx, (LambdaExpression) obj, type); - return result; + return coerceToFunctionalInterface(ctx, (LambdaExpression) obj, type); } throw new ELException(MessageFactory.get("error.convert", obj, obj.getClass(), type)); @@ -640,25 +634,25 @@ } - public static final boolean isBigDecimalOp(final Object obj0, final Object obj1) { + public static boolean isBigDecimalOp(final Object obj0, final Object obj1) { return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal); } - public static final boolean isBigIntegerOp(final Object obj0, final Object obj1) { + public static boolean isBigIntegerOp(final Object obj0, final Object obj1) { return (obj0 instanceof BigInteger || obj1 instanceof BigInteger); } - public static final boolean isDoubleOp(final Object obj0, final Object obj1) { + public static boolean isDoubleOp(final Object obj0, final Object obj1) { return (obj0 instanceof Double || obj1 instanceof Double || obj0 instanceof Float || obj1 instanceof Float); } - public static final boolean isLongOp(final Object obj0, final Object obj1) { + public static boolean isLongOp(final Object obj0, final Object obj1) { return (obj0 instanceof Long || obj1 instanceof Long || obj0 instanceof Integer || obj1 instanceof Integer || obj0 instanceof Character || obj1 instanceof Character || obj0 instanceof Short || obj1 instanceof Short || obj0 instanceof Byte || obj1 instanceof Byte); } - public static final boolean isStringFloat(final String str) { + public static boolean isStringFloat(final String str) { int len = str.length(); if (len > 1) { for (int i = 0; i < len; i++) { @@ -714,22 +708,16 @@ if ("equals".equals(method.getName())) { if (method.getReturnType().equals(boolean.class)) { if (method.getParameterCount() == 1) { - if (method.getParameterTypes()[0].equals(Object.class)) { - return true; - } + return method.getParameterTypes()[0].equals(Object.class); } } } else if ("hashCode".equals(method.getName())) { if (method.getReturnType().equals(int.class)) { - if (method.getParameterCount() == 0) { - return true; - } + return method.getParameterCount() == 0; } } else if ("toString".equals(method.getName())) { if (method.getReturnType().equals(String.class)) { - if (method.getParameterCount() == 0) { - return true; - } + return method.getParameterCount() == 0; } } @@ -738,6 +726,6 @@ private ELSupport() { - // Uility class - hide default constructor; + // Utility class - hide default constructor; } } diff -Nru tomcat10-10.1.34/java/org/apache/el/lang/ExpressionBuilder.java tomcat10-10.1.52/java/org/apache/el/lang/ExpressionBuilder.java --- tomcat10-10.1.34/java/org/apache/el/lang/ExpressionBuilder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/lang/ExpressionBuilder.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,9 +44,6 @@ import org.apache.el.util.ExceptionUtils; import org.apache.el.util.MessageFactory; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class ExpressionBuilder implements NodeVisitor { private static final SynchronizedStack parserCache = new SynchronizedStack<>(); @@ -88,8 +85,7 @@ } public static Node createNode(String expr) throws ELException { - Node n = createNodeInternal(expr); - return n; + return createNodeInternal(expr); } private static Node createNodeInternal(String expr) throws ELException { @@ -114,7 +110,7 @@ n = n.jjtGetChild(0); } else { Class type = null; - Node child = null; + Node child; for (int i = 0; i < numChildren; i++) { child = n.jjtGetChild(i); if (child instanceof AstLiteralExpression) { @@ -188,7 +184,7 @@ // References to variables that refer to lambda expressions will be // parsed as functions. This is handled at runtime but at this point // need to treat it as a variable rather than a function. - if (m == null && this.varMapper != null && funcNode.getPrefix().length() == 0) { + if (m == null && this.varMapper != null && funcNode.getPrefix().isEmpty()) { this.varMapper.resolveVariable(funcNode.getLocalName()); return; } diff -Nru tomcat10-10.1.34/java/org/apache/el/lang/FunctionMapperFactory.java tomcat10-10.1.52/java/org/apache/el/lang/FunctionMapperFactory.java --- tomcat10-10.1.34/java/org/apache/el/lang/FunctionMapperFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/lang/FunctionMapperFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.util.MessageFactory; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public class FunctionMapperFactory extends FunctionMapper { protected FunctionMapperImpl memento = null; diff -Nru tomcat10-10.1.34/java/org/apache/el/lang/FunctionMapperImpl.java tomcat10-10.1.52/java/org/apache/el/lang/FunctionMapperImpl.java --- tomcat10-10.1.34/java/org/apache/el/lang/FunctionMapperImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/lang/FunctionMapperImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,10 +29,6 @@ import org.apache.el.util.MessageFactory; import org.apache.el.util.ReflectionUtil; - -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public class FunctionMapperImpl extends FunctionMapper implements Externalizable { private static final long serialVersionUID = 1L; diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/ArithmeticNode.java tomcat10-10.1.52/java/org/apache/el/parser/ArithmeticNode.java --- tomcat10-10.1.34/java/org/apache/el/parser/ArithmeticNode.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/ArithmeticNode.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,9 +20,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public abstract class ArithmeticNode extends SimpleNode { public ArithmeticNode(int i) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstAbstractEmpty.java tomcat10-10.1.52/java/org/apache/el/parser/AstAbstractEmpty.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstAbstractEmpty.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstAbstractEmpty.java 2026-01-23 19:33:36.000000000 +0000 @@ -52,7 +52,7 @@ @Override public Object getValue(EvaluationContext ctx) throws ELException { Object obj = this.children[0].getValue(ctx); - if (obj == null || obj instanceof String && ((String) obj).length() == 0 || + if (obj == null || obj instanceof String && ((String) obj).isEmpty() || obj instanceof Object[] && ((Object[]) obj).length == 0 || obj instanceof Collection && ((Collection) obj).isEmpty() || obj instanceof Map && ((Map) obj).isEmpty()) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstAnd.java tomcat10-10.1.52/java/org/apache/el/parser/AstAnd.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstAnd.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstAnd.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstAnd extends BooleanNode { public AstAnd(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstBracketSuffix.java tomcat10-10.1.52/java/org/apache/el/parser/AstBracketSuffix.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstBracketSuffix.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstBracketSuffix.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstBracketSuffix extends SimpleNode { public AstBracketSuffix(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstChoice.java tomcat10-10.1.52/java/org/apache/el/parser/AstChoice.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstChoice.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstChoice.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstChoice extends SimpleNode { public AstChoice(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstCompositeExpression.java tomcat10-10.1.52/java/org/apache/el/parser/AstCompositeExpression.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstCompositeExpression.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstCompositeExpression.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstCompositeExpression extends SimpleNode { public AstCompositeExpression(int id) { @@ -41,8 +38,8 @@ @Override public Object getValue(EvaluationContext ctx) throws ELException { StringBuilder sb = new StringBuilder(16); - Object obj = null; if (this.children != null) { + Object obj; for (Node child : this.children) { obj = child.getValue(ctx); if (obj != null) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstDeferredExpression.java tomcat10-10.1.52/java/org/apache/el/parser/AstDeferredExpression.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstDeferredExpression.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstDeferredExpression.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstDeferredExpression extends SimpleNode { public AstDeferredExpression(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstDiv.java tomcat10-10.1.52/java/org/apache/el/parser/AstDiv.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstDiv.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstDiv.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELArithmetic; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstDiv extends ArithmeticNode { public AstDiv(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstDotSuffix.java tomcat10-10.1.52/java/org/apache/el/parser/AstDotSuffix.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstDotSuffix.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstDotSuffix.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,6 @@ import org.apache.el.util.MessageFactory; import org.apache.el.util.Validation; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstDotSuffix extends SimpleNode { public AstDotSuffix(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstDynamicExpression.java tomcat10-10.1.52/java/org/apache/el/parser/AstDynamicExpression.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstDynamicExpression.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstDynamicExpression.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstDynamicExpression extends SimpleNode { public AstDynamicExpression(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstEqual.java tomcat10-10.1.52/java/org/apache/el/parser/AstEqual.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstEqual.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstEqual.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstEqual extends BooleanNode { public AstEqual(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstFalse.java tomcat10-10.1.52/java/org/apache/el/parser/AstFalse.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstFalse.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstFalse.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstFalse extends BooleanNode { public AstFalse(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstFloatingPoint.java tomcat10-10.1.52/java/org/apache/el/parser/AstFloatingPoint.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstFloatingPoint.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstFloatingPoint.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstFloatingPoint extends SimpleNode { public AstFloatingPoint(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstFunction.java tomcat10-10.1.52/java/org/apache/el/parser/AstFunction.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstFunction.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstFunction.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,14 +31,11 @@ import org.apache.el.lang.EvaluationContext; import org.apache.el.util.MessageFactory; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstFunction extends SimpleNode { - protected String localName = ""; + private String localName = ""; - protected String prefix = ""; + private String prefix = ""; public AstFunction(int id) { @@ -93,7 +90,7 @@ } Method m = fnMapper.resolveFunction(this.prefix, this.localName); - if (m == null && this.prefix.length() == 0) { + if (m == null && this.prefix.isEmpty()) { // TODO: Do we need to think about precedence of the various ways // a lambda expression may be obtained from something that // the parser thinks is a function? @@ -105,7 +102,7 @@ VariableMapper varMapper = ctx.getVariableMapper(); if (varMapper != null) { obj = varMapper.resolveVariable(this.localName); - if (obj instanceof ValueExpression) { + if (obj != null) { // See if this returns a LambdaExpression obj = ((ValueExpression) obj).getValue(ctx); } @@ -156,7 +153,6 @@ Node parameters = jjtGetChild(0); Class[] paramTypes = m.getParameterTypes(); Object[] params = null; - Object result = null; int inputParameterCount = parameters.jjtGetNumChildren(); int methodParameterCount = paramTypes.length; if (inputParameterCount == 0 && methodParameterCount == 1 && m.isVarArgs()) { @@ -189,6 +185,7 @@ throw new ELException(MessageFactory.get("error.function", this.getOutputName()), ele); } } + Object result; try { result = m.invoke(null, params); } catch (IllegalAccessException iae) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstGreaterThan.java tomcat10-10.1.52/java/org/apache/el/parser/AstGreaterThan.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstGreaterThan.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstGreaterThan.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstGreaterThan extends BooleanNode { public AstGreaterThan(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstGreaterThanEqual.java tomcat10-10.1.52/java/org/apache/el/parser/AstGreaterThanEqual.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstGreaterThanEqual.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstGreaterThanEqual.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstGreaterThanEqual extends BooleanNode { public AstGreaterThanEqual(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstIdentifier.java tomcat10-10.1.52/java/org/apache/el/parser/AstIdentifier.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstIdentifier.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstIdentifier.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,9 +32,6 @@ import org.apache.el.util.MessageFactory; import org.apache.el.util.Validation; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstIdentifier extends SimpleNode { public AstIdentifier(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstInteger.java tomcat10-10.1.52/java/org/apache/el/parser/AstInteger.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstInteger.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstInteger.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstInteger extends SimpleNode { public AstInteger(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstLessThan.java tomcat10-10.1.52/java/org/apache/el/parser/AstLessThan.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstLessThan.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstLessThan.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstLessThan extends BooleanNode { public AstLessThan(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstLessThanEqual.java tomcat10-10.1.52/java/org/apache/el/parser/AstLessThanEqual.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstLessThanEqual.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstLessThanEqual.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstLessThanEqual extends BooleanNode { public AstLessThanEqual(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstLiteralExpression.java tomcat10-10.1.52/java/org/apache/el/parser/AstLiteralExpression.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstLiteralExpression.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstLiteralExpression.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstLiteralExpression extends SimpleNode { public AstLiteralExpression(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstMinus.java tomcat10-10.1.52/java/org/apache/el/parser/AstMinus.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstMinus.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstMinus.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELArithmetic; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstMinus extends ArithmeticNode { public AstMinus(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstMod.java tomcat10-10.1.52/java/org/apache/el/parser/AstMod.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstMod.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstMod.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELArithmetic; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstMod extends ArithmeticNode { public AstMod(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstMult.java tomcat10-10.1.52/java/org/apache/el/parser/AstMult.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstMult.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstMult.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELArithmetic; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstMult extends ArithmeticNode { public AstMult(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstNegative.java tomcat10-10.1.52/java/org/apache/el/parser/AstNegative.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstNegative.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstNegative.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,9 +25,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstNegative extends SimpleNode { public AstNegative(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstNot.java tomcat10-10.1.52/java/org/apache/el/parser/AstNot.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstNot.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstNot.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstNot extends SimpleNode { public AstNot(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstNotEqual.java tomcat10-10.1.52/java/org/apache/el/parser/AstNotEqual.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstNotEqual.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstNotEqual.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstNotEqual extends BooleanNode { public AstNotEqual(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstNull.java tomcat10-10.1.52/java/org/apache/el/parser/AstNull.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstNull.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstNull.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstNull extends SimpleNode { public AstNull(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstOr.java tomcat10-10.1.52/java/org/apache/el/parser/AstOr.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstOr.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstOr.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstOr extends BooleanNode { public AstOr(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstPlus.java tomcat10-10.1.52/java/org/apache/el/parser/AstPlus.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstPlus.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstPlus.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import org.apache.el.lang.ELArithmetic; import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstPlus extends ArithmeticNode { public AstPlus(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstString.java tomcat10-10.1.52/java/org/apache/el/parser/AstString.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstString.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstString.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstString extends SimpleNode { public AstString(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstTrue.java tomcat10-10.1.52/java/org/apache/el/parser/AstTrue.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstTrue.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstTrue.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstTrue extends BooleanNode { public AstTrue(int id) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/AstValue.java tomcat10-10.1.52/java/org/apache/el/parser/AstValue.java --- tomcat10-10.1.34/java/org/apache/el/parser/AstValue.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/AstValue.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,9 +35,6 @@ import org.apache.el.util.MessageFactory; import org.apache.el.util.ReflectionUtil; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class AstValue extends SimpleNode { private static final Object[] EMPTY_ARRAY = new Object[0]; @@ -98,7 +95,8 @@ i += 2; if (property == null) { - throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.property", property)); + throw new PropertyNotFoundException( + MessageFactory.get("error.unreachable.property", this.children[i].getImage())); } } else if (i + 1 < propCount) { // Object with property not at end of expression @@ -114,11 +112,13 @@ i++; if (property == null) { - throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.property", property)); + throw new PropertyNotFoundException( + MessageFactory.get("error.unreachable.property", this.children[i].getImage())); } } if (base == null) { - throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.property", property)); + throw new PropertyNotFoundException( + MessageFactory.get("error.unreachable.property", this.children[i].getImage())); } } @@ -201,7 +201,7 @@ public MethodInfo getMethodInfo(EvaluationContext ctx, @SuppressWarnings("rawtypes") Class[] paramTypes) throws ELException { Target t = getTarget(ctx); - Class[] types = null; + Class[] types; if (isParametersProvided()) { Object[] values = ((AstMethodParameters) this.jjtGetChild(this.jjtGetNumChildren() - 1)).getParameters(ctx); types = getTypesFromValues(values); @@ -219,9 +219,9 @@ throws ELException { Target t = getTarget(ctx); - Method m = null; - Object[] values = null; - Class[] types = null; + Method m; + Object[] values; + Class[] types; if (isParametersProvided()) { values = ((AstMethodParameters) this.jjtGetChild(this.jjtGetNumChildren() - 1)).getParameters(ctx); types = getTypesFromValues(values); @@ -234,7 +234,7 @@ // Handle varArgs and any coercion required values = convertArgs(ctx, values, m); - Object result = null; + Object result; try { result = m.invoke(t.base, values); } catch (IllegalAccessException | IllegalArgumentException e) { @@ -256,7 +256,7 @@ @Override public MethodReference getMethodReference(EvaluationContext ctx) { Target t = getTarget(ctx); - Method m = null; + Method m; Object[] values = null; Class[] types = null; if (isParametersProvided()) { @@ -282,7 +282,7 @@ int paramCount = types.length; if (m.isVarArgs() && paramCount > 1 && (src == null || paramCount > src.length) || - !m.isVarArgs() && (paramCount > 0 && src == null || src != null && src.length != paramCount)) { + !m.isVarArgs() && (src == null || src.length != paramCount)) { String srcCount = null; if (src != null) { srcCount = Integer.toString(src.length); @@ -331,7 +331,7 @@ return null; } - Class result[] = new Class[values.length]; + Class[] result = new Class[values.length]; for (int i = 0; i < values.length; i++) { if (values[i] == null) { result[i] = null; @@ -367,9 +367,7 @@ // child int len = children.length; if (len > 2) { - if (this.jjtGetChild(len - 1) instanceof AstMethodParameters) { - return true; - } + return this.jjtGetChild(len - 1) instanceof AstMethodParameters; } return false; } diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/BooleanNode.java tomcat10-10.1.52/java/org/apache/el/parser/BooleanNode.java --- tomcat10-10.1.34/java/org/apache/el/parser/BooleanNode.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/BooleanNode.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,9 +20,6 @@ import org.apache.el.lang.EvaluationContext; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public abstract class BooleanNode extends SimpleNode { public BooleanNode(int i) { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/ELParser.java tomcat10-10.1.52/java/org/apache/el/parser/ELParser.java --- tomcat10-10.1.34/java/org/apache/el/parser/ELParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/ELParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,7 +26,8 @@ boolean jjtc000 = true; jjtree.openNodeScope(jjtn000); try { - label_1: while (true) { + label_1: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case LITERAL_EXPRESSION: case START_DYNAMIC_EXPRESSION: @@ -219,7 +220,8 @@ */ final public void Semicolon() throws ParseException { Assignment(); - label_2: while (true) { + label_2: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case SEMICOLON: { ; @@ -292,7 +294,8 @@ case MINUS: case IDENTIFIER: { Choice(); - label_3: while (true) { + label_3: + while (true) { if (jj_2_1(2)) { ; } else { @@ -434,7 +437,8 @@ switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case IDENTIFIER: { Identifier(); - label_4: while (true) { + label_4: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case COMMA: { ; @@ -534,7 +538,8 @@ } } jj_consume_token(RPAREN); - label_5: while (true) { + label_5: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case LPAREN: { ; @@ -584,7 +589,8 @@ */ final public void Choice() throws ParseException { Or(); - label_6: while (true) { + label_6: + while (true) { if (jj_2_5(3)) { ; } else { @@ -644,7 +650,8 @@ boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); try { - label_7: while (true) { + label_7: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case OR0: { jj_consume_token(OR0); @@ -722,7 +729,8 @@ boolean jjtc001 = true; jjtree.openNodeScope(jjtn001); try { - label_8: while (true) { + label_8: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case AND0: { jj_consume_token(AND0); @@ -793,7 +801,8 @@ */ final public void Equality() throws ParseException { Compare(); - label_9: while (true) { + label_9: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case EQ0: case EQ1: @@ -928,7 +937,8 @@ */ final public void Compare() throws ParseException { Concatenation(); - label_10: while (true) { + label_10: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case GT0: case GT1: @@ -1176,7 +1186,8 @@ */ final public void Concatenation() throws ParseException { Math(); - label_11: while (true) { + label_11: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case CONCAT: { ; @@ -1231,7 +1242,8 @@ */ final public void Math() throws ParseException { Multiplication(); - label_12: while (true) { + label_12: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case PLUS: case MINUS: { @@ -1336,7 +1348,8 @@ */ final public void Multiplication() throws ParseException { Unary(); - label_13: while (true) { + label_13: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case MULT: case DIV0: @@ -1733,7 +1746,8 @@ jjtree.openNodeScope(jjtn001); try { ValuePrefix(); - label_14: while (true) { + label_14: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case DOT: case LBRACK: { @@ -1926,7 +1940,8 @@ case MINUS: case IDENTIFIER: { Expression(); - label_15: while (true) { + label_15: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case COMMA: { ; @@ -2055,7 +2070,8 @@ case MINUS: case IDENTIFIER: { Expression(); - label_16: while (true) { + label_16: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case COMMA: { ; @@ -2130,7 +2146,8 @@ case MINUS: case IDENTIFIER: { Expression(); - label_17: while (true) { + label_17: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case COMMA: { ; @@ -2209,7 +2226,8 @@ case MINUS: case IDENTIFIER: { MapEntry(); - label_18: while (true) { + label_18: + while (true) { switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case COMMA: { ; @@ -2350,7 +2368,8 @@ } else { jjtn000.setLocalName(t0.image); } - label_19: while (true) { + label_19: + while (true) { MethodParameters(); switch ((jj_ntk == -1) ? jj_ntk_f() : jj_ntk) { case LPAREN: { @@ -3417,9 +3436,9 @@ private boolean jj_3R_Multiplication_247_9_71() { Token xsp; xsp = jj_scanpos; - if (jj_scan_token(51)) { + if (jj_scan_token(50)) { jj_scanpos = xsp; - if (jj_scan_token(52)) { + if (jj_scan_token(51)) { return true; } } @@ -3432,9 +3451,9 @@ private boolean jj_3R_Multiplication_245_9_70() { Token xsp; xsp = jj_scanpos; - if (jj_scan_token(49)) { + if (jj_scan_token(48)) { jj_scanpos = xsp; - if (jj_scan_token(50)) { + if (jj_scan_token(49)) { return true; } } @@ -3891,11 +3910,10 @@ } private static void jj_la1_init_1() { - jj_la1_1 = new int[] { 0x0, 0x0, 0x0, 0x1008860, 0x1008860, 0x0, 0x1000000, 0x1000000, 0x1008860, 0x0, 0x600, - 0x600, 0x600, 0x180, 0x180, 0x180, 0x1e, 0x6, 0x18, 0x1e, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x200000, - 0xc000, 0xc000, 0x1e2000, 0x60000, 0x180000, 0x1e2000, 0x60, 0x60, 0x8000, 0x1000860, 0x0, 0x1000000, - 0x0, 0x0, 0x0, 0x1008860, 0x0, 0x1000000, 0x0, 0x0, 0x1008860, 0x0, 0x1008860, 0x0, 0x1008860, 0x0, 0x0, - 0x0, 0x0, }; + jj_la1_1 = new int[] { 0x0, 0x0, 0x0, 0x804860, 0x804860, 0x0, 0x800000, 0x800000, 0x804860, 0x0, 0x600, 0x600, + 0x600, 0x180, 0x180, 0x180, 0x1e, 0x6, 0x18, 0x1e, 0x1, 0x0, 0x0, 0x1, 0x0, 0x1, 0x100000, 0x6000, + 0x6000, 0xf1000, 0x30000, 0xc0000, 0xf1000, 0x60, 0x60, 0x4000, 0x800860, 0x0, 0x800000, 0x0, 0x0, 0x0, + 0x804860, 0x0, 0x800000, 0x0, 0x0, 0x804860, 0x0, 0x804860, 0x0, 0x804860, 0x0, 0x0, 0x0, 0x0, }; } final private JJCalls[] jj_2_rtns = new JJCalls[9]; @@ -4170,7 +4188,7 @@ /** Generate ParseException. */ public ParseException generateParseException() { jj_expentries.clear(); - boolean[] la1tokens = new boolean[62]; + boolean[] la1tokens = new boolean[59]; if (jj_kind >= 0) { la1tokens[jj_kind] = true; jj_kind = -1; @@ -4187,7 +4205,7 @@ } } } - for (int i = 0; i < 62; i++) { + for (int i = 0; i < 59; i++) { if (la1tokens[i]) { jj_expentry = new int[1]; jj_expentry[0] = i; diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/ELParser.jjt tomcat10-10.1.52/java/org/apache/el/parser/ELParser.jjt --- tomcat10-10.1.34/java/org/apache/el/parser/ELParser.jjt 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/ELParser.jjt 2026-01-23 19:33:36.000000000 +0000 @@ -15,11 +15,6 @@ * limitations under the License. */ -/* - Author: Jacob Hookom - Email: jacob at hookom.net -*/ - /* == Option Declaration == */ options { @@ -535,7 +530,6 @@ | < OR0 : "||" > | < OR1 : "or" > | < EMPTY : "empty" > -| < INSTANCEOF : "instanceof" > | < MULT : "*" > | < PLUS : "+" > | < MINUS : "-" > @@ -547,43 +541,645 @@ | < CONCAT : "+=" > | < ASSIGN : "=" > | < ARROW : "->" > -| < IDENTIFIER : (|) (|)* > -| < FUNCTIONSUFFIX : () > -| < #IMPL_OBJ_START: "#" > -| < #LETTER: +| < IDENTIFIER : (|)* > +| < #JAVALETTER: [ "\u0024", "\u0041"-"\u005a", "\u005f", "\u0061"-"\u007a", + "\u00a2"-"\u00a5", + "\u00aa", + "\u00b5", + "\u00ba", "\u00c0"-"\u00d6", "\u00d8"-"\u00f6", - "\u00f8"-"\u00ff", - "\u0100"-"\u1fff", - "\u3040"-"\u318f", - "\u3300"-"\u337f", - "\u3400"-"\u3d2d", - "\u4e00"-"\u9fff", - "\uf900"-"\ufaff" + "\u00f8"-"\u02c1", + "\u02c6"-"\u02d1", + "\u02e0"-"\u02e4", + "\u02ec", + "\u02ee", + "\u0370"-"\u0374", + "\u0376"-"\u0377", + "\u037a"-"\u037d", + "\u037f", + "\u0386", + "\u0388"-"\u038a", + "\u038c", + "\u038e"-"\u03a1", + "\u03a3"-"\u03f5", + "\u03f7"-"\u0481", + "\u048a"-"\u052f", + "\u0531"-"\u0556", + "\u0559", + "\u0561"-"\u0587", + "\u058f", + "\u05d0"-"\u05ea", + "\u05f0"-"\u05f2", + "\u060b", + "\u0620"-"\u064a", + "\u066e"-"\u066f", + "\u0671"-"\u06d3", + "\u06d5", + "\u06e5"-"\u06e6", + "\u06ee"-"\u06ef", + "\u06fa"-"\u06fc", + "\u06ff", + "\u0710", + "\u0712"-"\u072f", + "\u074d"-"\u07a5", + "\u07b1", + "\u07ca"-"\u07ea", + "\u07f4"-"\u07f5", + "\u07fa", + "\u0800"-"\u0815", + "\u081a", + "\u0824", + "\u0828", + "\u0840"-"\u0858", + "\u0860"-"\u086a", + "\u08a0"-"\u08b4", + "\u08b6"-"\u08bd", + "\u0904"-"\u0939", + "\u093d", + "\u0950", + "\u0958"-"\u0961", + "\u0971"-"\u0980", + "\u0985"-"\u098c", + "\u098f"-"\u0990", + "\u0993"-"\u09a8", + "\u09aa"-"\u09b0", + "\u09b2", + "\u09b6"-"\u09b9", + "\u09bd", + "\u09ce", + "\u09dc"-"\u09dd", + "\u09df"-"\u09e1", + "\u09f0"-"\u09f3", + "\u09fb"-"\u09fc", + "\u0a05"-"\u0a0a", + "\u0a0f"-"\u0a10", + "\u0a13"-"\u0a28", + "\u0a2a"-"\u0a30", + "\u0a32"-"\u0a33", + "\u0a35"-"\u0a36", + "\u0a38"-"\u0a39", + "\u0a59"-"\u0a5c", + "\u0a5e", + "\u0a72"-"\u0a74", + "\u0a85"-"\u0a8d", + "\u0a8f"-"\u0a91", + "\u0a93"-"\u0aa8", + "\u0aaa"-"\u0ab0", + "\u0ab2"-"\u0ab3", + "\u0ab5"-"\u0ab9", + "\u0abd", + "\u0ad0", + "\u0ae0"-"\u0ae1", + "\u0af1", + "\u0af9", + "\u0b05"-"\u0b0c", + "\u0b0f"-"\u0b10", + "\u0b13"-"\u0b28", + "\u0b2a"-"\u0b30", + "\u0b32"-"\u0b33", + "\u0b35"-"\u0b39", + "\u0b3d", + "\u0b5c"-"\u0b5d", + "\u0b5f"-"\u0b61", + "\u0b71", + "\u0b83", + "\u0b85"-"\u0b8a", + "\u0b8e"-"\u0b90", + "\u0b92"-"\u0b95", + "\u0b99"-"\u0b9a", + "\u0b9c", + "\u0b9e"-"\u0b9f", + "\u0ba3"-"\u0ba4", + "\u0ba8"-"\u0baa", + "\u0bae"-"\u0bb9", + "\u0bd0", + "\u0bf9", + "\u0c05"-"\u0c0c", + "\u0c0e"-"\u0c10", + "\u0c12"-"\u0c28", + "\u0c2a"-"\u0c39", + "\u0c3d", + "\u0c58"-"\u0c5a", + "\u0c60"-"\u0c61", + "\u0c80", + "\u0c85"-"\u0c8c", + "\u0c8e"-"\u0c90", + "\u0c92"-"\u0ca8", + "\u0caa"-"\u0cb3", + "\u0cb5"-"\u0cb9", + "\u0cbd", + "\u0cde", + "\u0ce0"-"\u0ce1", + "\u0cf1"-"\u0cf2", + "\u0d05"-"\u0d0c", + "\u0d0e"-"\u0d10", + "\u0d12"-"\u0d3a", + "\u0d3d", + "\u0d4e", + "\u0d54"-"\u0d56", + "\u0d5f"-"\u0d61", + "\u0d7a"-"\u0d7f", + "\u0d85"-"\u0d96", + "\u0d9a"-"\u0db1", + "\u0db3"-"\u0dbb", + "\u0dbd", + "\u0dc0"-"\u0dc6", + "\u0e01"-"\u0e30", + "\u0e32"-"\u0e33", + "\u0e3f"-"\u0e46", + "\u0e81"-"\u0e82", + "\u0e84", + "\u0e87"-"\u0e88", + "\u0e8a", + "\u0e8d", + "\u0e94"-"\u0e97", + "\u0e99"-"\u0e9f", + "\u0ea1"-"\u0ea3", + "\u0ea5", + "\u0ea7", + "\u0eaa"-"\u0eab", + "\u0ead"-"\u0eb0", + "\u0eb2"-"\u0eb3", + "\u0ebd", + "\u0ec0"-"\u0ec4", + "\u0ec6", + "\u0edc"-"\u0edf", + "\u0f00", + "\u0f40"-"\u0f47", + "\u0f49"-"\u0f6c", + "\u0f88"-"\u0f8c", + "\u1000"-"\u102a", + "\u103f", + "\u1050"-"\u1055", + "\u105a"-"\u105d", + "\u1061", + "\u1065"-"\u1066", + "\u106e"-"\u1070", + "\u1075"-"\u1081", + "\u108e", + "\u10a0"-"\u10c5", + "\u10c7", + "\u10cd", + "\u10d0"-"\u10fa", + "\u10fc"-"\u1248", + "\u124a"-"\u124d", + "\u1250"-"\u1256", + "\u1258", + "\u125a"-"\u125d", + "\u1260"-"\u1288", + "\u128a"-"\u128d", + "\u1290"-"\u12b0", + "\u12b2"-"\u12b5", + "\u12b8"-"\u12be", + "\u12c0", + "\u12c2"-"\u12c5", + "\u12c8"-"\u12d6", + "\u12d8"-"\u1310", + "\u1312"-"\u1315", + "\u1318"-"\u135a", + "\u1380"-"\u138f", + "\u13a0"-"\u13f5", + "\u13f8"-"\u13fd", + "\u1401"-"\u166c", + "\u166f"-"\u167f", + "\u1681"-"\u169a", + "\u16a0"-"\u16ea", + "\u16ee"-"\u16f8", + "\u1700"-"\u170c", + "\u170e"-"\u1711", + "\u1720"-"\u1731", + "\u1740"-"\u1751", + "\u1760"-"\u176c", + "\u176e"-"\u1770", + "\u1780"-"\u17b3", + "\u17d7", + "\u17db"-"\u17dc", + "\u1820"-"\u1877", + "\u1880"-"\u1884", + "\u1887"-"\u18a8", + "\u18aa", + "\u18b0"-"\u18f5", + "\u1900"-"\u191e", + "\u1950"-"\u196d", + "\u1970"-"\u1974", + "\u1980"-"\u19ab", + "\u19b0"-"\u19c9", + "\u1a00"-"\u1a16", + "\u1a20"-"\u1a54", + "\u1aa7", + "\u1b05"-"\u1b33", + "\u1b45"-"\u1b4b", + "\u1b83"-"\u1ba0", + "\u1bae"-"\u1baf", + "\u1bba"-"\u1be5", + "\u1c00"-"\u1c23", + "\u1c4d"-"\u1c4f", + "\u1c5a"-"\u1c7d", + "\u1c80"-"\u1c88", + "\u1ce9"-"\u1cec", + "\u1cee"-"\u1cf1", + "\u1cf5"-"\u1cf6", + "\u1d00"-"\u1dbf", + "\u1e00"-"\u1f15", + "\u1f18"-"\u1f1d", + "\u1f20"-"\u1f45", + "\u1f48"-"\u1f4d", + "\u1f50"-"\u1f57", + "\u1f59", + "\u1f5b", + "\u1f5d", + "\u1f5f"-"\u1f7d", + "\u1f80"-"\u1fb4", + "\u1fb6"-"\u1fbc", + "\u1fbe", + "\u1fc2"-"\u1fc4", + "\u1fc6"-"\u1fcc", + "\u1fd0"-"\u1fd3", + "\u1fd6"-"\u1fdb", + "\u1fe0"-"\u1fec", + "\u1ff2"-"\u1ff4", + "\u1ff6"-"\u1ffc", + "\u203f"-"\u2040", + "\u2054", + "\u2071", + "\u207f", + "\u2090"-"\u209c", + "\u20a0"-"\u20bf", + "\u2102", + "\u2107", + "\u210a"-"\u2113", + "\u2115", + "\u2119"-"\u211d", + "\u2124", + "\u2126", + "\u2128", + "\u212a"-"\u212d", + "\u212f"-"\u2139", + "\u213c"-"\u213f", + "\u2145"-"\u2149", + "\u214e", + "\u2160"-"\u2188", + "\u2c00"-"\u2c2e", + "\u2c30"-"\u2c5e", + "\u2c60"-"\u2ce4", + "\u2ceb"-"\u2cee", + "\u2cf2"-"\u2cf3", + "\u2d00"-"\u2d25", + "\u2d27", + "\u2d2d", + "\u2d30"-"\u2d67", + "\u2d6f", + "\u2d80"-"\u2d96", + "\u2da0"-"\u2da6", + "\u2da8"-"\u2dae", + "\u2db0"-"\u2db6", + "\u2db8"-"\u2dbe", + "\u2dc0"-"\u2dc6", + "\u2dc8"-"\u2dce", + "\u2dd0"-"\u2dd6", + "\u2dd8"-"\u2dde", + "\u2e2f", + "\u3005"-"\u3007", + "\u3021"-"\u3029", + "\u3031"-"\u3035", + "\u3038"-"\u303c", + "\u3041"-"\u3096", + "\u309d"-"\u309f", + "\u30a1"-"\u30fa", + "\u30fc"-"\u30ff", + "\u3105"-"\u312e", + "\u3131"-"\u318e", + "\u31a0"-"\u31ba", + "\u31f0"-"\u31ff", + "\u3400"-"\u4db5", + "\u4e00"-"\u9fea", + "\ua000"-"\ua48c", + "\ua4d0"-"\ua4fd", + "\ua500"-"\ua60c", + "\ua610"-"\ua61f", + "\ua62a"-"\ua62b", + "\ua640"-"\ua66e", + "\ua67f"-"\ua69d", + "\ua6a0"-"\ua6ef", + "\ua717"-"\ua71f", + "\ua722"-"\ua788", + "\ua78b"-"\ua7ae", + "\ua7b0"-"\ua7b7", + "\ua7f7"-"\ua801", + "\ua803"-"\ua805", + "\ua807"-"\ua80a", + "\ua80c"-"\ua822", + "\ua838", + "\ua840"-"\ua873", + "\ua882"-"\ua8b3", + "\ua8f2"-"\ua8f7", + "\ua8fb", + "\ua8fd", + "\ua90a"-"\ua925", + "\ua930"-"\ua946", + "\ua960"-"\ua97c", + "\ua984"-"\ua9b2", + "\ua9cf", + "\ua9e0"-"\ua9e4", + "\ua9e6"-"\ua9ef", + "\ua9fa"-"\ua9fe", + "\uaa00"-"\uaa28", + "\uaa40"-"\uaa42", + "\uaa44"-"\uaa4b", + "\uaa60"-"\uaa76", + "\uaa7a", + "\uaa7e"-"\uaaaf", + "\uaab1", + "\uaab5"-"\uaab6", + "\uaab9"-"\uaabd", + "\uaac0", + "\uaac2", + "\uaadb"-"\uaadd", + "\uaae0"-"\uaaea", + "\uaaf2"-"\uaaf4", + "\uab01"-"\uab06", + "\uab09"-"\uab0e", + "\uab11"-"\uab16", + "\uab20"-"\uab26", + "\uab28"-"\uab2e", + "\uab30"-"\uab5a", + "\uab5c"-"\uab65", + "\uab70"-"\uabe2", + "\uac00"-"\ud7a3", + "\ud7b0"-"\ud7c6", + "\ud7cb"-"\ud7fb", + "\uf900"-"\ufa6d", + "\ufa70"-"\ufad9", + "\ufb00"-"\ufb06", + "\ufb13"-"\ufb17", + "\ufb1d", + "\ufb1f"-"\ufb28", + "\ufb2a"-"\ufb36", + "\ufb38"-"\ufb3c", + "\ufb3e", + "\ufb40"-"\ufb41", + "\ufb43"-"\ufb44", + "\ufb46"-"\ufbb1", + "\ufbd3"-"\ufd3d", + "\ufd50"-"\ufd8f", + "\ufd92"-"\ufdc7", + "\ufdf0"-"\ufdfc", + "\ufe33"-"\ufe34", + "\ufe4d"-"\ufe4f", + "\ufe69", + "\ufe70"-"\ufe74", + "\ufe76"-"\ufefc", + "\uff04", + "\uff21"-"\uff3a", + "\uff3f", + "\uff41"-"\uff5a", + "\uff66"-"\uffbe", + "\uffc2"-"\uffc7", + "\uffca"-"\uffcf", + "\uffd2"-"\uffd7", + "\uffda"-"\uffdc", + "\uffe0"-"\uffe1", + "\uffe5"-"\uffe6" ] > -| < #DIGIT: +| < #JAVADIGIT: [ + "\u0000"-"\u0008", + "\u000e"-"\u001b", "\u0030"-"\u0039", - "\u0660"-"\u0669", + "\u007f"-"\u009f", + "\u00ad", + "\u0300"-"\u036f", + "\u0483"-"\u0487", + "\u0591"-"\u05bd", + "\u05bf", + "\u05c1"-"\u05c2", + "\u05c4"-"\u05c5", + "\u05c7", + "\u0600"-"\u0605", + "\u0610"-"\u061a", + "\u061c", + "\u064b"-"\u0669", + "\u0670", + "\u06d6"-"\u06dd", + "\u06df"-"\u06e4", + "\u06e7"-"\u06e8", + "\u06ea"-"\u06ed", "\u06f0"-"\u06f9", + "\u070f", + "\u0711", + "\u0730"-"\u074a", + "\u07a6"-"\u07b0", + "\u07c0"-"\u07c9", + "\u07eb"-"\u07f3", + "\u0816"-"\u0819", + "\u081b"-"\u0823", + "\u0825"-"\u0827", + "\u0829"-"\u082d", + "\u0859"-"\u085b", + "\u08d4"-"\u0903", + "\u093a"-"\u093c", + "\u093e"-"\u094f", + "\u0951"-"\u0957", + "\u0962"-"\u0963", "\u0966"-"\u096f", + "\u0981"-"\u0983", + "\u09bc", + "\u09be"-"\u09c4", + "\u09c7"-"\u09c8", + "\u09cb"-"\u09cd", + "\u09d7", + "\u09e2"-"\u09e3", "\u09e6"-"\u09ef", - "\u0a66"-"\u0a6f", + "\u0a01"-"\u0a03", + "\u0a3c", + "\u0a3e"-"\u0a42", + "\u0a47"-"\u0a48", + "\u0a4b"-"\u0a4d", + "\u0a51", + "\u0a66"-"\u0a71", + "\u0a75", + "\u0a81"-"\u0a83", + "\u0abc", + "\u0abe"-"\u0ac5", + "\u0ac7"-"\u0ac9", + "\u0acb"-"\u0acd", + "\u0ae2"-"\u0ae3", "\u0ae6"-"\u0aef", + "\u0afa"-"\u0aff", + "\u0b01"-"\u0b03", + "\u0b3c", + "\u0b3e"-"\u0b44", + "\u0b47"-"\u0b48", + "\u0b4b"-"\u0b4d", + "\u0b56"-"\u0b57", + "\u0b62"-"\u0b63", "\u0b66"-"\u0b6f", - "\u0be7"-"\u0bef", + "\u0b82", + "\u0bbe"-"\u0bc2", + "\u0bc6"-"\u0bc8", + "\u0bca"-"\u0bcd", + "\u0bd7", + "\u0be6"-"\u0bef", + "\u0c00"-"\u0c03", + "\u0c3e"-"\u0c44", + "\u0c46"-"\u0c48", + "\u0c4a"-"\u0c4d", + "\u0c55"-"\u0c56", + "\u0c62"-"\u0c63", "\u0c66"-"\u0c6f", + "\u0c81"-"\u0c83", + "\u0cbc", + "\u0cbe"-"\u0cc4", + "\u0cc6"-"\u0cc8", + "\u0cca"-"\u0ccd", + "\u0cd5"-"\u0cd6", + "\u0ce2"-"\u0ce3", "\u0ce6"-"\u0cef", + "\u0d00"-"\u0d03", + "\u0d3b"-"\u0d3c", + "\u0d3e"-"\u0d44", + "\u0d46"-"\u0d48", + "\u0d4a"-"\u0d4d", + "\u0d57", + "\u0d62"-"\u0d63", "\u0d66"-"\u0d6f", + "\u0d82"-"\u0d83", + "\u0dca", + "\u0dcf"-"\u0dd4", + "\u0dd6", + "\u0dd8"-"\u0ddf", + "\u0de6"-"\u0def", + "\u0df2"-"\u0df3", + "\u0e31", + "\u0e34"-"\u0e3a", + "\u0e47"-"\u0e4e", "\u0e50"-"\u0e59", + "\u0eb1", + "\u0eb4"-"\u0eb9", + "\u0ebb"-"\u0ebc", + "\u0ec8"-"\u0ecd", "\u0ed0"-"\u0ed9", - "\u1040"-"\u1049" + "\u0f18"-"\u0f19", + "\u0f20"-"\u0f29", + "\u0f35", + "\u0f37", + "\u0f39", + "\u0f3e"-"\u0f3f", + "\u0f71"-"\u0f84", + "\u0f86"-"\u0f87", + "\u0f8d"-"\u0f97", + "\u0f99"-"\u0fbc", + "\u0fc6", + "\u102b"-"\u103e", + "\u1040"-"\u1049", + "\u1056"-"\u1059", + "\u105e"-"\u1060", + "\u1062"-"\u1064", + "\u1067"-"\u106d", + "\u1071"-"\u1074", + "\u1082"-"\u108d", + "\u108f"-"\u109d", + "\u135d"-"\u135f", + "\u1712"-"\u1714", + "\u1732"-"\u1734", + "\u1752"-"\u1753", + "\u1772"-"\u1773", + "\u17b4"-"\u17d3", + "\u17dd", + "\u17e0"-"\u17e9", + "\u180b"-"\u180e", + "\u1810"-"\u1819", + "\u1885"-"\u1886", + "\u18a9", + "\u1920"-"\u192b", + "\u1930"-"\u193b", + "\u1946"-"\u194f", + "\u19d0"-"\u19d9", + "\u1a17"-"\u1a1b", + "\u1a55"-"\u1a5e", + "\u1a60"-"\u1a7c", + "\u1a7f"-"\u1a89", + "\u1a90"-"\u1a99", + "\u1ab0"-"\u1abd", + "\u1b00"-"\u1b04", + "\u1b34"-"\u1b44", + "\u1b50"-"\u1b59", + "\u1b6b"-"\u1b73", + "\u1b80"-"\u1b82", + "\u1ba1"-"\u1bad", + "\u1bb0"-"\u1bb9", + "\u1be6"-"\u1bf3", + "\u1c24"-"\u1c37", + "\u1c40"-"\u1c49", + "\u1c50"-"\u1c59", + "\u1cd0"-"\u1cd2", + "\u1cd4"-"\u1ce8", + "\u1ced", + "\u1cf2"-"\u1cf4", + "\u1cf7"-"\u1cf9", + "\u1dc0"-"\u1df9", + "\u1dfb"-"\u1dff", + "\u200b"-"\u200f", + "\u202a"-"\u202e", + "\u2060"-"\u2064", + "\u2066"-"\u206f", + "\u20d0"-"\u20dc", + "\u20e1", + "\u20e5"-"\u20f0", + "\u2cef"-"\u2cf1", + "\u2d7f", + "\u2de0"-"\u2dff", + "\u302a"-"\u302f", + "\u3099"-"\u309a", + "\ua620"-"\ua629", + "\ua66f", + "\ua674"-"\ua67d", + "\ua69e"-"\ua69f", + "\ua6f0"-"\ua6f1", + "\ua802", + "\ua806", + "\ua80b", + "\ua823"-"\ua827", + "\ua880"-"\ua881", + "\ua8b4"-"\ua8c5", + "\ua8d0"-"\ua8d9", + "\ua8e0"-"\ua8f1", + "\ua900"-"\ua909", + "\ua926"-"\ua92d", + "\ua947"-"\ua953", + "\ua980"-"\ua983", + "\ua9b3"-"\ua9c0", + "\ua9d0"-"\ua9d9", + "\ua9e5", + "\ua9f0"-"\ua9f9", + "\uaa29"-"\uaa36", + "\uaa43", + "\uaa4c"-"\uaa4d", + "\uaa50"-"\uaa59", + "\uaa7b"-"\uaa7d", + "\uaab0", + "\uaab2"-"\uaab4", + "\uaab7"-"\uaab8", + "\uaabe"-"\uaabf", + "\uaac1", + "\uaaeb"-"\uaaef", + "\uaaf5"-"\uaaf6", + "\uabe3"-"\uabea", + "\uabec"-"\uabed", + "\uabf0"-"\uabf9", + "\ufb1e", + "\ufe00"-"\ufe0f", + "\ufe20"-"\ufe2f", + "\ufeff", + "\uff10"-"\uff19", + "\ufff9"-"\ufffb" ] > | < ILLEGAL_CHARACTER: (~[]) > diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/ELParserConstants.java tomcat10-10.1.52/java/org/apache/el/parser/ELParserConstants.java --- tomcat10-10.1.34/java/org/apache/el/parser/ELParserConstants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/ELParserConstants.java 2026-01-23 19:33:36.000000000 +0000 @@ -88,41 +88,35 @@ /** RegularExpression Id. */ int EMPTY = 43; /** RegularExpression Id. */ - int INSTANCEOF = 44; + int MULT = 44; /** RegularExpression Id. */ - int MULT = 45; + int PLUS = 45; /** RegularExpression Id. */ - int PLUS = 46; + int MINUS = 46; /** RegularExpression Id. */ - int MINUS = 47; + int QUESTIONMARK = 47; /** RegularExpression Id. */ - int QUESTIONMARK = 48; + int DIV0 = 48; /** RegularExpression Id. */ - int DIV0 = 49; + int DIV1 = 49; /** RegularExpression Id. */ - int DIV1 = 50; + int MOD0 = 50; /** RegularExpression Id. */ - int MOD0 = 51; + int MOD1 = 51; /** RegularExpression Id. */ - int MOD1 = 52; + int CONCAT = 52; /** RegularExpression Id. */ - int CONCAT = 53; + int ASSIGN = 53; /** RegularExpression Id. */ - int ASSIGN = 54; + int ARROW = 54; /** RegularExpression Id. */ - int ARROW = 55; + int IDENTIFIER = 55; /** RegularExpression Id. */ - int IDENTIFIER = 56; + int JAVALETTER = 56; /** RegularExpression Id. */ - int FUNCTIONSUFFIX = 57; + int JAVADIGIT = 57; /** RegularExpression Id. */ - int IMPL_OBJ_START = 58; - /** RegularExpression Id. */ - int LETTER = 59; - /** RegularExpression Id. */ - int DIGIT = 60; - /** RegularExpression Id. */ - int ILLEGAL_CHARACTER = 61; + int ILLEGAL_CHARACTER = 58; /** Lexical state. */ int DEFAULT = 0; @@ -137,8 +131,7 @@ "", "\"true\"", "\"false\"", "\"null\"", "\".\"", "\"(\"", "\")\"", "\"[\"", "\"]\"", "\":\"", "\";\"", "\",\"", "\">\"", "\"gt\"", "\"<\"", "\"lt\"", "\">=\"", "\"ge\"", "\"<=\"", "\"le\"", "\"==\"", "\"eq\"", "\"!=\"", "\"ne\"", "\"!\"", "\"not\"", "\"&&\"", "\"and\"", "\"||\"", "\"or\"", - "\"empty\"", "\"instanceof\"", "\"*\"", "\"+\"", "\"-\"", "\"?\"", "\"/\"", "\"div\"", "\"%\"", "\"mod\"", - "\"+=\"", "\"=\"", "\"->\"", "", "", "\"#\"", "", "", - "", }; + "\"empty\"", "\"*\"", "\"+\"", "\"-\"", "\"?\"", "\"/\"", "\"div\"", "\"%\"", "\"mod\"", "\"+=\"", "\"=\"", + "\"->\"", "", "", "", "", }; } diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/ELParserTokenManager.java tomcat10-10.1.52/java/org/apache/el/parser/ELParserTokenManager.java --- tomcat10-10.1.34/java/org/apache/el/parser/ELParserTokenManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/ELParserTokenManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -301,77 +301,39 @@ if ((active0 & 0x20000L) != 0L) { return 1; } - if ((active0 & 0x141d555401c000L) != 0L) { - jjmatchedKind = 56; - return 30; + if ((active0 & 0xa0d555401c000L) != 0L) { + jjmatchedKind = 55; + return 16; } return -1; case 1: - if ((active0 & 0x41554000000L) != 0L) { - return 30; - } - if ((active0 & 0x1419400001c000L) != 0L) { - jjmatchedKind = 56; + if ((active0 & 0xa09400001c000L) != 0L) { + jjmatchedKind = 55; jjmatchedPos = 1; - return 30; + return 16; + } + if ((active0 & 0x41554000000L) != 0L) { + return 16; } return -1; case 2: - if ((active0 & 0x14014000000000L) != 0L) { - return 30; + if ((active0 & 0xa014000000000L) != 0L) { + return 16; } - if ((active0 & 0x18000001c000L) != 0L) { - jjmatchedKind = 56; + if ((active0 & 0x8000001c000L) != 0L) { + jjmatchedKind = 55; jjmatchedPos = 2; - return 30; + return 16; } return -1; case 3: if ((active0 & 0x14000L) != 0L) { - return 30; + return 16; } - if ((active0 & 0x180000008000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 3; - return 30; - } - return -1; - case 4: if ((active0 & 0x80000008000L) != 0L) { - return 30; - } - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 4; - return 30; - } - return -1; - case 5: - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 5; - return 30; - } - return -1; - case 6: - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 6; - return 30; - } - return -1; - case 7: - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 7; - return 30; - } - return -1; - case 8: - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 8; - return 30; + jjmatchedKind = 55; + jjmatchedPos = 3; + return 16; } return -1; default: @@ -389,7 +351,7 @@ jjmatchedKind = 37; return jjMoveStringLiteralDfa1_2(0x800000000L); case 37: - return jjStopAtPos(0, 51); + return jjStopAtPos(0, 50); case 38: return jjMoveStringLiteralDfa1_2(0x8000000000L); case 40: @@ -397,19 +359,19 @@ case 41: return jjStopAtPos(0, 19); case 42: - return jjStopAtPos(0, 45); + return jjStopAtPos(0, 44); case 43: - jjmatchedKind = 46; - return jjMoveStringLiteralDfa1_2(0x20000000000000L); + jjmatchedKind = 45; + return jjMoveStringLiteralDfa1_2(0x10000000000000L); case 44: return jjStopAtPos(0, 24); case 45: - jjmatchedKind = 47; - return jjMoveStringLiteralDfa1_2(0x80000000000000L); + jjmatchedKind = 46; + return jjMoveStringLiteralDfa1_2(0x40000000000000L); case 46: return jjStartNfaWithStates_2(0, 17, 1); case 47: - return jjStopAtPos(0, 49); + return jjStopAtPos(0, 48); case 58: return jjStopAtPos(0, 22); case 59: @@ -418,13 +380,13 @@ jjmatchedKind = 27; return jjMoveStringLiteralDfa1_2(0x80000000L); case 61: - jjmatchedKind = 54; + jjmatchedKind = 53; return jjMoveStringLiteralDfa1_2(0x200000000L); case 62: jjmatchedKind = 25; return jjMoveStringLiteralDfa1_2(0x20000000L); case 63: - return jjStopAtPos(0, 48); + return jjStopAtPos(0, 47); case 91: return jjStopAtPos(0, 20); case 93: @@ -432,19 +394,17 @@ case 97: return jjMoveStringLiteralDfa1_2(0x10000000000L); case 100: - return jjMoveStringLiteralDfa1_2(0x4000000000000L); + return jjMoveStringLiteralDfa1_2(0x2000000000000L); case 101: return jjMoveStringLiteralDfa1_2(0x80400000000L); case 102: return jjMoveStringLiteralDfa1_2(0x8000L); case 103: return jjMoveStringLiteralDfa1_2(0x44000000L); - case 105: - return jjMoveStringLiteralDfa1_2(0x100000000000L); case 108: return jjMoveStringLiteralDfa1_2(0x110000000L); case 109: - return jjMoveStringLiteralDfa1_2(0x10000000000000L); + return jjMoveStringLiteralDfa1_2(0x8000000000000L); case 110: return jjMoveStringLiteralDfa1_2(0x5000010000L); case 111: @@ -484,49 +444,49 @@ return jjStopAtPos(1, 33); } else if ((active0 & 0x800000000L) != 0L) { return jjStopAtPos(1, 35); - } else if ((active0 & 0x20000000000000L) != 0L) { - return jjStopAtPos(1, 53); + } else if ((active0 & 0x10000000000000L) != 0L) { + return jjStopAtPos(1, 52); } break; case 62: - if ((active0 & 0x80000000000000L) != 0L) { - return jjStopAtPos(1, 55); + if ((active0 & 0x40000000000000L) != 0L) { + return jjStopAtPos(1, 54); } break; case 97: return jjMoveStringLiteralDfa2_2(active0, 0x8000L); case 101: if ((active0 & 0x40000000L) != 0L) { - return jjStartNfaWithStates_2(1, 30, 30); + return jjStartNfaWithStates_2(1, 30, 16); } else if ((active0 & 0x100000000L) != 0L) { - return jjStartNfaWithStates_2(1, 32, 30); + return jjStartNfaWithStates_2(1, 32, 16); } else if ((active0 & 0x1000000000L) != 0L) { - return jjStartNfaWithStates_2(1, 36, 30); + return jjStartNfaWithStates_2(1, 36, 16); } break; case 105: - return jjMoveStringLiteralDfa2_2(active0, 0x4000000000000L); + return jjMoveStringLiteralDfa2_2(active0, 0x2000000000000L); case 109: return jjMoveStringLiteralDfa2_2(active0, 0x80000000000L); case 110: - return jjMoveStringLiteralDfa2_2(active0, 0x110000000000L); + return jjMoveStringLiteralDfa2_2(active0, 0x10000000000L); case 111: - return jjMoveStringLiteralDfa2_2(active0, 0x10004000000000L); + return jjMoveStringLiteralDfa2_2(active0, 0x8004000000000L); case 113: if ((active0 & 0x400000000L) != 0L) { - return jjStartNfaWithStates_2(1, 34, 30); + return jjStartNfaWithStates_2(1, 34, 16); } break; case 114: if ((active0 & 0x40000000000L) != 0L) { - return jjStartNfaWithStates_2(1, 42, 30); + return jjStartNfaWithStates_2(1, 42, 16); } return jjMoveStringLiteralDfa2_2(active0, 0x4000L); case 116: if ((active0 & 0x4000000L) != 0L) { - return jjStartNfaWithStates_2(1, 26, 30); + return jjStartNfaWithStates_2(1, 26, 16); } else if ((active0 & 0x10000000L) != 0L) { - return jjStartNfaWithStates_2(1, 28, 30); + return jjStartNfaWithStates_2(1, 28, 16); } break; case 117: @@ -555,27 +515,25 @@ switch (curChar) { case 100: if ((active0 & 0x10000000000L) != 0L) { - return jjStartNfaWithStates_2(2, 40, 30); - } else if ((active0 & 0x10000000000000L) != 0L) { - return jjStartNfaWithStates_2(2, 52, 30); + return jjStartNfaWithStates_2(2, 40, 16); + } else if ((active0 & 0x8000000000000L) != 0L) { + return jjStartNfaWithStates_2(2, 51, 16); } break; case 108: return jjMoveStringLiteralDfa3_2(active0, 0x18000L); case 112: return jjMoveStringLiteralDfa3_2(active0, 0x80000000000L); - case 115: - return jjMoveStringLiteralDfa3_2(active0, 0x100000000000L); case 116: if ((active0 & 0x4000000000L) != 0L) { - return jjStartNfaWithStates_2(2, 38, 30); + return jjStartNfaWithStates_2(2, 38, 16); } break; case 117: return jjMoveStringLiteralDfa3_2(active0, 0x4000L); case 118: - if ((active0 & 0x4000000000000L) != 0L) { - return jjStartNfaWithStates_2(2, 50, 30); + if ((active0 & 0x2000000000000L) != 0L) { + return jjStartNfaWithStates_2(2, 49, 16); } break; default: @@ -597,18 +555,18 @@ switch (curChar) { case 101: if ((active0 & 0x4000L) != 0L) { - return jjStartNfaWithStates_2(3, 14, 30); + return jjStartNfaWithStates_2(3, 14, 16); } break; case 108: if ((active0 & 0x10000L) != 0L) { - return jjStartNfaWithStates_2(3, 16, 30); + return jjStartNfaWithStates_2(3, 16, 16); } break; case 115: return jjMoveStringLiteralDfa4_2(active0, 0x8000L); case 116: - return jjMoveStringLiteralDfa4_2(active0, 0x180000000000L); + return jjMoveStringLiteralDfa4_2(active0, 0x80000000000L); default: break; } @@ -626,16 +584,14 @@ return 4; } switch (curChar) { - case 97: - return jjMoveStringLiteralDfa5_2(active0, 0x100000000000L); case 101: if ((active0 & 0x8000L) != 0L) { - return jjStartNfaWithStates_2(4, 15, 30); + return jjStartNfaWithStates_2(4, 15, 16); } break; case 121: if ((active0 & 0x80000000000L) != 0L) { - return jjStartNfaWithStates_2(4, 43, 30); + return jjStartNfaWithStates_2(4, 43, 16); } break; default: @@ -644,104 +600,6 @@ return jjStartNfa_2(3, active0); } - private int jjMoveStringLiteralDfa5_2(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_2(3, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_2(4, active0); - return 5; - } - switch (curChar) { - case 110: - return jjMoveStringLiteralDfa6_2(active0, 0x100000000000L); - default: - break; - } - return jjStartNfa_2(4, active0); - } - - private int jjMoveStringLiteralDfa6_2(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_2(4, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_2(5, active0); - return 6; - } - switch (curChar) { - case 99: - return jjMoveStringLiteralDfa7_2(active0, 0x100000000000L); - default: - break; - } - return jjStartNfa_2(5, active0); - } - - private int jjMoveStringLiteralDfa7_2(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_2(5, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_2(6, active0); - return 7; - } - switch (curChar) { - case 101: - return jjMoveStringLiteralDfa8_2(active0, 0x100000000000L); - default: - break; - } - return jjStartNfa_2(6, active0); - } - - private int jjMoveStringLiteralDfa8_2(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_2(6, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_2(7, active0); - return 8; - } - switch (curChar) { - case 111: - return jjMoveStringLiteralDfa9_2(active0, 0x100000000000L); - default: - break; - } - return jjStartNfa_2(7, active0); - } - - private int jjMoveStringLiteralDfa9_2(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_2(7, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_2(8, active0); - return 9; - } - switch (curChar) { - case 102: - if ((active0 & 0x100000000000L) != 0L) { - return jjStartNfaWithStates_2(9, 44, 30); - } - break; - default: - break; - } - return jjStartNfa_2(8, active0); - } - private int jjStartNfaWithStates_2(int pos, int kind, int state) { jjmatchedKind = kind; jjmatchedPos = pos; @@ -753,16 +611,130 @@ return jjMoveNfa_2(state, pos + 1); } - static final long[] jjbitVec3 = { 0x1ff00000fffffffeL, 0xffffffffffffc000L, 0xffffffffL, 0x600000000000000L }; - static final long[] jjbitVec4 = { 0x0L, 0x0L, 0x0L, 0xff7fffffff7fffffL }; - static final long[] jjbitVec5 = { 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL }; - static final long[] jjbitVec6 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffL, 0x0L }; - static final long[] jjbitVec7 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0x0L, 0x0L }; - static final long[] jjbitVec8 = { 0x3fffffffffffL, 0x0L, 0x0L, 0x0L }; + static final long[] jjbitVec3 = + { 0xfff0000040220002L, 0xffffffffffffdfffL, 0xfffff02f7fffffffL, 0x12000000007fffffL }; + static final long[] jjbitVec4 = { 0x0L, 0x0L, 0x420043c00000000L, 0xff7fffffff7fffffL }; + static final long[] jjbitVec5 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0x501f0003ffc3L }; + static final long[] jjbitVec6 = { 0x0L, 0xbcdf000000000000L, 0xfffffffbffffd740L, 0xffbfffffffffffffL }; + static final long[] jjbitVec7 = + { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xfffffffffffffc03L, 0xffffffffffffffffL }; + static final long[] jjbitVec8 = { 0xfffeffffffffffffL, 0xfffffffe027fffffL, 0x80ffL, 0x707ffffff0000L }; + static final long[] jjbitVec9 = + { 0xffffffff00000800L, 0xfffec000000007ffL, 0xffffffffffffffffL, 0x9c00c060002fffffL }; + static final long[] jjbitVec10 = { 0xfffffffd0000L, 0xffffffffffffe000L, 0x2003fffffffffL, 0x43007fffffffc00L }; + static final long[] jjbitVec11 = { 0x110043fffffL, 0x7ff01ffffffL, 0x3fdfffff00000000L, 0x0L }; + static final long[] jjbitVec12 = + { 0x23fffffffffffff0L, 0xfffe0003ff010000L, 0x23c5fdfffff99fe1L, 0x180f0003b0004000L }; + static final long[] jjbitVec13 = { 0x36dfdfffff987e0L, 0x1c00005e000000L, 0x23edfdfffffbbfe0L, 0x202000300010000L }; + static final long[] jjbitVec14 = { 0x23edfdfffff99fe0L, 0x20003b0000000L, 0x3ffc718d63dc7e8L, 0x200000000010000L }; + static final long[] jjbitVec15 = { 0x23fffdfffffddfe0L, 0x307000000L, 0x23effdfffffddfe1L, 0x6000340000000L }; + static final long[] jjbitVec16 = { 0x27fffffffffddfe0L, 0xfc00000380704000L, 0x2ffbfffffc7fffe0L, 0x7fL }; + static final long[] jjbitVec17 = { 0x800dfffffffffffeL, 0x7fL, 0x200decaefef02596L, 0xf000005fL }; + static final long[] jjbitVec18 = { 0x1L, 0x1ffffffffeffL, 0x1f00L, 0x0L }; + static final long[] jjbitVec19 = + { 0x800007ffffffffffL, 0xffe1c0623c3f0000L, 0xffffffff00004003L, 0xf7ffffffffff20bfL }; + static final long[] jjbitVec20 = + { 0xffffffffffffffffL, 0xffffffff3d7f3dffL, 0x7f3dffffffff3dffL, 0xffffffffff7fff3dL }; + static final long[] jjbitVec21 = { 0xffffffffff3dffffL, 0x7ffffffL, 0xffffffff0000ffffL, 0x3f3fffffffffffffL }; + static final long[] jjbitVec22 = + { 0xffffffffffffffffL, 0xffff9fffffffffffL, 0xffffffff07fffffeL, 0x1ffc7ffffffffffL }; + static final long[] jjbitVec23 = { 0x3ffff0003dfffL, 0x1dfff0003ffffL, 0xfffffffffffffL, 0x18800000L }; + static final long[] jjbitVec24 = { 0xffffffff00000000L, 0xffffffffffffffL, 0xffff05ffffffff9fL, 0x3fffffffffffffL }; + static final long[] jjbitVec25 = { 0x7fffffffL, 0x1f3fffffff0000L, 0xffff0fffffffffffL, 0x3ffL }; + static final long[] jjbitVec26 = { 0xffffffff007fffffL, 0x1fffffL, 0x8000000000L, 0x0L }; + static final long[] jjbitVec27 = { 0xfffffffffffe0L, 0xfe0L, 0xfc00c001fffffff8L, 0x3fffffffffL }; + static final long[] jjbitVec28 = { 0xfffffffffL, 0x3ffffffffc00e000L, 0x1ffL, 0x63de0000000000L }; + static final long[] jjbitVec29 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0x0L }; + static final long[] jjbitVec30 = + { 0xffffffff3f3fffffL, 0x3fffffffaaff3f3fL, 0x5fdfffffffffffffL, 0x1fdc1fff0fcf1fdcL }; + static final long[] jjbitVec31 = { 0x8000000000000000L, 0x8002000000100001L, 0xffffffff1fff0000L, 0x0L }; + static final long[] jjbitVec32 = { 0xf3ffbd503e2ffc84L, 0xffffffff000043e0L, 0x1ffL, 0x0L }; + static final long[] jjbitVec33 = + { 0xffff7fffffffffffL, 0xffffffff7fffffffL, 0xffffffffffffffffL, 0xc781fffffffffL }; + static final long[] jjbitVec34 = { 0xffff20bfffffffffL, 0x80ffffffffffL, 0x7f7f7f7f007fffffL, 0x7f7f7f7fL }; + static final long[] jjbitVec35 = { 0x800000000000L, 0x0L, 0x0L, 0x0L }; + static final long[] jjbitVec36 = + { 0x1f3e03fe000000e0L, 0xfffffffffffffffeL, 0xfffffffee07fffffL, 0xf7ffffffffffffffL }; + static final long[] jjbitVec37 = + { 0xfffe7fffffffffe0L, 0xffffffffffffffffL, 0x7ffffff00007fffL, 0xffff000000000000L }; + static final long[] jjbitVec38 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0x3fffffffffffffL, 0x0L }; + static final long[] jjbitVec39 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0x7ffffffffffL }; + static final long[] jjbitVec40 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0x1fffL, 0x3fffffffffff0000L }; + static final long[] jjbitVec41 = { 0xc00ffff1fffL, 0x80007fffffffffffL, 0xffffffff3fffffffL, 0xffffffffffffL }; + static final long[] jjbitVec42 = + { 0xfffffffcff800000L, 0xffffffffffffffffL, 0xff7ffffffff9ffL, 0xff80000000000000L }; + static final long[] jjbitVec43 = { 0x1000007fffff7bbL, 0xfffffffffffffL, 0xffffffffffffcL, 0x28fc000000000000L }; + static final long[] jjbitVec44 = + { 0xffff003ffffffc00L, 0x1fffffff0000007fL, 0x7fffffffffff0L, 0x7c00ffdf00008000L }; + static final long[] jjbitVec45 = { 0x1ffffffffffL, 0xc47fffff00000ff7L, 0x3e62ffffffffffffL, 0x1c07ff38000005L }; + static final long[] jjbitVec46 = { 0xffff7f7f007e7e7eL, 0xffff003ff7ffffffL, 0xffffffffffffffffL, 0x7ffffffffL }; + static final long[] jjbitVec47 = + { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffff000fffffffffL, 0xffffffffffff87fL }; + static final long[] jjbitVec48 = { 0xffffffffffffffffL, 0xffff3fffffffffffL, 0xffffffffffffffffL, 0x3ffffffL }; + static final long[] jjbitVec49 = + { 0x5f7ffdffa0f8007fL, 0xffffffffffffffdbL, 0x3ffffffffffffL, 0xfffffffffff80000L }; + static final long[] jjbitVec50 = + { 0x3fffffffffffffffL, 0xffffffffffff0000L, 0xfffffffffffcffffL, 0x1fff0000000000ffL }; + static final long[] jjbitVec51 = + { 0x18000000000000L, 0xffdf02000000e000L, 0xffffffffffffffffL, 0x1fffffffffffffffL }; + static final long[] jjbitVec52 = { 0x87fffffe00000010L, 0xffffffc007fffffeL, 0x7fffffffffffffffL, 0x631cfcfcfcL }; + static final long[] jjbitVec53 = { 0x0L, 0x0L, 0x420243cffffffffL, 0xff7fffffff7fffffL }; + static final long[] jjbitVec54 = + { 0xffffffffffffffffL, 0xbcdfffffffffffffL, 0xfffffffbffffd740L, 0xffbfffffffffffffL }; + static final long[] jjbitVec55 = + { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xfffffffffffffcfbL, 0xffffffffffffffffL }; + static final long[] jjbitVec56 = + { 0xfffeffffffffffffL, 0xfffffffe027fffffL, 0xbffffffffffe80ffL, 0x707ffffff00b6L }; + static final long[] jjbitVec57 = + { 0xffffffff17ff083fL, 0xffffc3ffffffffffL, 0xffffffffffffffffL, 0x9ffffdffbfefffffL }; + static final long[] jjbitVec58 = { 0xffffffffffff8000L, 0xffffffffffffe7ffL, 0x3ffffffffffffL, 0x43fffffffffffffL }; + static final long[] jjbitVec59 = { 0x3fffffffffffL, 0x7ff0fffffffL, 0x3fdfffff00000000L, 0xfffffffffff00000L }; + static final long[] jjbitVec60 = + { 0xffffffffffffffffL, 0xfffeffcfffffffffL, 0xf3c5fdfffff99fefL, 0x180fffcfb080799fL }; + static final long[] jjbitVec61 = + { 0xd36dfdfffff987eeL, 0x3fffc05e023987L, 0xf3edfdfffffbbfeeL, 0xfe02ffcf00013bbfL }; + static final long[] jjbitVec62 = { 0xf3edfdfffff99feeL, 0x2ffcfb0c0399fL, 0xc3ffc718d63dc7ecL, 0x200ffc000813dc7L }; + static final long[] jjbitVec63 = { 0xe3fffdfffffddfefL, 0xffcf07603ddfL, 0xf3effdfffffddfefL, 0x6ffcf40603ddfL }; + static final long[] jjbitVec64 = + { 0xfffffffffffddfefL, 0xfc00ffcf80f07ddfL, 0x2ffbfffffc7fffecL, 0xcffc0ff5f847fL }; + static final long[] jjbitVec65 = { 0x87fffffffffffffeL, 0x3ff7fffL, 0x3bffecaefef02596L, 0xf3ff3f5fL }; + static final long[] jjbitVec66 = { 0xc2a003ff03000001L, 0xfffe1ffffffffeffL, 0x1ffffffffeffffdfL, 0x40L }; + static final long[] jjbitVec67 = + { 0xffffffffffffffffL, 0xffffffffffff03ffL, 0xffffffff3fffffffL, 0xf7ffffffffff20bfL }; + static final long[] jjbitVec68 = { 0xffffffffff3dffffL, 0xe7ffffffL, 0xffffffff0000ffffL, 0x3f3fffffffffffffL }; + static final long[] jjbitVec69 = { 0x1fffff001fdfffL, 0xddfff000fffffL, 0xffffffffffffffffL, 0x3ff388fffffL }; + static final long[] jjbitVec70 = { 0xffffffff03ff7800L, 0xffffffffffffffL, 0xffff07ffffffffffL, 0x3fffffffffffffL }; + static final long[] jjbitVec71 = { 0xfff0fff7fffffffL, 0x1f3fffffffffc0L, 0xffff0fffffffffffL, 0x3ff03ffL }; + static final long[] jjbitVec72 = { 0xffffffff0fffffffL, 0x9fffffff7fffffffL, 0x3fff008003ff03ffL, 0x0L }; + static final long[] jjbitVec73 = { 0xffffffffffffffffL, 0xff80003ff0fffL, 0xffffffffffffffffL, 0xfffffffffffffL }; + static final long[] jjbitVec74 = { 0xffffffffffffffL, 0x3fffffffffffe3ffL, 0x1ffL, 0x3fffffffff70000L }; + static final long[] jjbitVec75 = + { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xfbffffffffffffffL }; + static final long[] jjbitVec76 = + { 0x80007c000000f800L, 0x8002ffdf00100001L, 0xffffffff1fff0000L, 0x1ffe21fff0000L }; + static final long[] jjbitVec77 = + { 0xffff7fffffffffffL, 0xffffffff7fffffffL, 0xffffffffffffffffL, 0xff81fffffffffL }; + static final long[] jjbitVec78 = + { 0xffff20bfffffffffL, 0x800080ffffffffffL, 0x7f7f7f7f007fffffL, 0xffffffff7f7f7f7fL }; + static final long[] jjbitVec79 = + { 0x1f3efffe000000e0L, 0xfffffffffffffffeL, 0xfffffffee67fffffL, 0xf7ffffffffffffffL }; + static final long[] jjbitVec80 = { 0xfffffff1fffL, 0xbff0ffffffffffffL, 0xffffffffffffffffL, 0x3ffffffffffffL }; + static final long[] jjbitVec81 = { 0x10000ffffffffffL, 0xfffffffffffffL, 0xffffffffffffffffL, 0x28ffffff03ff003fL }; + static final long[] jjbitVec82 = + { 0xffff3fffffffffffL, 0x1fffffff000fffffL, 0xffffffffffffffffL, 0x7fffffff03ff8001L }; + static final long[] jjbitVec83 = { 0x7fffffffffffffL, 0xfc7fffff03ff3fffL, 0xffffffffffffffffL, 0x7cffff38000007L }; + static final long[] jjbitVec84 = + { 0xffff7f7f007e7e7eL, 0xffff003ff7ffffffL, 0xffffffffffffffffL, 0x3ff37ffffffffffL }; + static final long[] jjbitVec85 = + { 0x5f7ffdffe0f8007fL, 0xffffffffffffffdbL, 0x3ffffffffffffL, 0xfffffffffff80000L }; + static final long[] jjbitVec86 = + { 0x18ffff0000ffffL, 0xffdf02000000e000L, 0xffffffffffffffffL, 0x9fffffffffffffffL }; + static final long[] jjbitVec87 = + { 0x87fffffe03ff0010L, 0xffffffc007fffffeL, 0x7fffffffffffffffL, 0xe0000631cfcfcfcL }; private int jjMoveNfa_2(int startState, int curPos) { int startsAt = 0; - jjnewStateCnt = 30; + jjnewStateCnt = 29; int i = 1; jjstateSet[0] = startState; int kind = 0x7fffffff; @@ -782,12 +754,12 @@ { jjCheckNAddStates(18, 22); } - } else if ((0x1800000000L & l) != 0L) { - if (kind > 56) { - kind = 56; + } else if (curChar == 36) { + if (kind > 55) { + kind = 55; } { - jjCheckNAddTwoStates(28, 29); + jjCheckNAdd(16); } } else if (curChar == 39) { jjCheckNAddStates(23, 25); @@ -797,24 +769,6 @@ jjCheckNAdd(1); } break; - case 30: - if ((0x3ff001000000000L & l) != 0L) { - if (kind > 57) { - kind = 57; - } - { - jjCheckNAdd(29); - } - } - if ((0x3ff001000000000L & l) != 0L) { - if (kind > 56) { - kind = 56; - } - { - jjCheckNAdd(28); - } - } - break; case 1: if ((0x3ff000000000000L & l) == 0L) { break; @@ -881,6 +835,26 @@ } break; case 15: + if (curChar != 36) { + break; + } + if (kind > 55) { + kind = 55; + } { + jjCheckNAdd(16); + } + break; + case 16: + if ((0x3ff00100fffc1ffL & l) == 0L) { + break; + } + if (kind > 55) { + kind = 55; + } { + jjCheckNAdd(16); + } + break; + case 17: if ((0x3ff000000000000L & l) == 0L) { break; } @@ -890,106 +864,76 @@ jjCheckNAddStates(18, 22); } break; - case 16: + case 18: if ((0x3ff000000000000L & l) == 0L) { break; } if (kind > 10) { kind = 10; } { - jjCheckNAdd(16); + jjCheckNAdd(18); } break; - case 17: + case 19: if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(17, 18); + jjCheckNAddTwoStates(19, 20); } break; - case 18: + case 20: if (curChar != 46) { break; } if (kind > 11) { kind = 11; } { - jjCheckNAddTwoStates(19, 20); + jjCheckNAddTwoStates(21, 22); } break; - case 19: + case 21: if ((0x3ff000000000000L & l) == 0L) { break; } if (kind > 11) { kind = 11; } { - jjCheckNAddTwoStates(19, 20); + jjCheckNAddTwoStates(21, 22); } break; - case 21: + case 23: if ((0x280000000000L & l) != 0L) { - jjCheckNAdd(22); + jjCheckNAdd(24); } break; - case 22: + case 24: if ((0x3ff000000000000L & l) == 0L) { break; } if (kind > 11) { kind = 11; } { - jjCheckNAdd(22); + jjCheckNAdd(24); } break; - case 23: + case 25: if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(23, 24); + jjCheckNAddTwoStates(25, 26); } break; - case 25: + case 27: if ((0x280000000000L & l) != 0L) { - jjCheckNAdd(26); + jjCheckNAdd(28); } break; - case 26: + case 28: if ((0x3ff000000000000L & l) == 0L) { break; } if (kind > 11) { kind = 11; } { - jjCheckNAdd(26); - } - break; - case 27: - if ((0x1800000000L & l) == 0L) { - break; - } - if (kind > 56) { - kind = 56; - } { - jjCheckNAddTwoStates(28, 29); - } - break; - case 28: - if ((0x3ff001000000000L & l) == 0L) { - break; - } - if (kind > 56) { - kind = 56; - } { jjCheckNAdd(28); } break; - case 29: - if ((0x3ff001000000000L & l) == 0L) { - break; - } - if (kind > 57) { - kind = 57; - } { - jjCheckNAdd(29); - } - break; default: break; } @@ -1002,30 +946,12 @@ if ((0x7fffffe87fffffeL & l) == 0L) { break; } - if (kind > 56) { - kind = 56; + if (kind > 55) { + kind = 55; } { - jjCheckNAddTwoStates(28, 29); + jjCheckNAdd(16); } break; - case 30: - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 57) { - kind = 57; - } - { - jjCheckNAdd(29); - } - } - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 56) { - kind = 56; - } - { - jjCheckNAdd(28); - } - } - break; case 2: if ((0x2000000020L & l) != 0L) { jjAddStates(29, 30); @@ -1061,36 +987,26 @@ jjCheckNAddStates(23, 25); } break; - case 20: + case 16: + if ((0x87fffffe87fffffeL & l) == 0L) { + break; + } + if (kind > 55) { + kind = 55; + } { + jjCheckNAdd(16); + } + break; + case 22: if ((0x2000000020L & l) != 0L) { jjAddStates(31, 32); } break; - case 24: + case 26: if ((0x2000000020L & l) != 0L) { jjAddStates(33, 34); } break; - case 28: - if ((0x7fffffe87fffffeL & l) == 0L) { - break; - } - if (kind > 56) { - kind = 56; - } { - jjCheckNAdd(28); - } - break; - case 29: - if ((0x7fffffe87fffffeL & l) == 0L) { - break; - } - if (kind > 57) { - kind = 57; - } { - jjCheckNAdd(29); - } - break; default: break; } @@ -1107,30 +1023,12 @@ if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) { break; } - if (kind > 56) { - kind = 56; + if (kind > 55) { + kind = 55; } { - jjCheckNAddTwoStates(28, 29); + jjCheckNAdd(16); } break; - case 30: - if (jjCanMove_1(hiByte, i1, i2, l1, l2)) { - if (kind > 56) { - kind = 56; - } - { - jjCheckNAdd(28); - } - } - if (jjCanMove_1(hiByte, i1, i2, l1, l2)) { - if (kind > 57) { - kind = 57; - } - { - jjCheckNAdd(29); - } - } - break; case 6: if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { jjAddStates(26, 28); @@ -1141,24 +1039,14 @@ jjAddStates(23, 25); } break; - case 28: - if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) { - break; - } - if (kind > 56) { - kind = 56; - } { - jjCheckNAdd(28); - } - break; - case 29: - if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) { + case 16: + if (!jjCanMove_2(hiByte, i1, i2, l1, l2)) { break; } - if (kind > 57) { - kind = 57; + if (kind > 55) { + kind = 55; } { - jjCheckNAdd(29); + jjCheckNAdd(16); } break; default: @@ -1176,7 +1064,7 @@ kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 30 - (jjnewStateCnt = startsAt))) { + if ((i = jjnewStateCnt) == (startsAt = 29 - (jjnewStateCnt = startsAt))) { return curPos; } try { @@ -1193,77 +1081,39 @@ if ((active0 & 0x20000L) != 0L) { return 1; } - if ((active0 & 0x141d555401c000L) != 0L) { - jjmatchedKind = 56; - return 30; + if ((active0 & 0xa0d555401c000L) != 0L) { + jjmatchedKind = 55; + return 16; } return -1; case 1: - if ((active0 & 0x41554000000L) != 0L) { - return 30; - } - if ((active0 & 0x1419400001c000L) != 0L) { - jjmatchedKind = 56; + if ((active0 & 0xa09400001c000L) != 0L) { + jjmatchedKind = 55; jjmatchedPos = 1; - return 30; + return 16; + } + if ((active0 & 0x41554000000L) != 0L) { + return 16; } return -1; case 2: - if ((active0 & 0x14014000000000L) != 0L) { - return 30; + if ((active0 & 0xa014000000000L) != 0L) { + return 16; } - if ((active0 & 0x18000001c000L) != 0L) { - jjmatchedKind = 56; + if ((active0 & 0x8000001c000L) != 0L) { + jjmatchedKind = 55; jjmatchedPos = 2; - return 30; + return 16; } return -1; case 3: if ((active0 & 0x14000L) != 0L) { - return 30; + return 16; } - if ((active0 & 0x180000008000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 3; - return 30; - } - return -1; - case 4: if ((active0 & 0x80000008000L) != 0L) { - return 30; - } - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 4; - return 30; - } - return -1; - case 5: - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 5; - return 30; - } - return -1; - case 6: - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 6; - return 30; - } - return -1; - case 7: - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 7; - return 30; - } - return -1; - case 8: - if ((active0 & 0x100000000000L) != 0L) { - jjmatchedKind = 56; - jjmatchedPos = 8; - return 30; + jjmatchedKind = 55; + jjmatchedPos = 3; + return 16; } return -1; default: @@ -1281,7 +1131,7 @@ jjmatchedKind = 37; return jjMoveStringLiteralDfa1_1(0x800000000L); case 37: - return jjStopAtPos(0, 51); + return jjStopAtPos(0, 50); case 38: return jjMoveStringLiteralDfa1_1(0x8000000000L); case 40: @@ -1289,19 +1139,19 @@ case 41: return jjStopAtPos(0, 19); case 42: - return jjStopAtPos(0, 45); + return jjStopAtPos(0, 44); case 43: - jjmatchedKind = 46; - return jjMoveStringLiteralDfa1_1(0x20000000000000L); + jjmatchedKind = 45; + return jjMoveStringLiteralDfa1_1(0x10000000000000L); case 44: return jjStopAtPos(0, 24); case 45: - jjmatchedKind = 47; - return jjMoveStringLiteralDfa1_1(0x80000000000000L); + jjmatchedKind = 46; + return jjMoveStringLiteralDfa1_1(0x40000000000000L); case 46: return jjStartNfaWithStates_1(0, 17, 1); case 47: - return jjStopAtPos(0, 49); + return jjStopAtPos(0, 48); case 58: return jjStopAtPos(0, 22); case 59: @@ -1310,13 +1160,13 @@ jjmatchedKind = 27; return jjMoveStringLiteralDfa1_1(0x80000000L); case 61: - jjmatchedKind = 54; + jjmatchedKind = 53; return jjMoveStringLiteralDfa1_1(0x200000000L); case 62: jjmatchedKind = 25; return jjMoveStringLiteralDfa1_1(0x20000000L); case 63: - return jjStopAtPos(0, 48); + return jjStopAtPos(0, 47); case 91: return jjStopAtPos(0, 20); case 93: @@ -1324,19 +1174,17 @@ case 97: return jjMoveStringLiteralDfa1_1(0x10000000000L); case 100: - return jjMoveStringLiteralDfa1_1(0x4000000000000L); + return jjMoveStringLiteralDfa1_1(0x2000000000000L); case 101: return jjMoveStringLiteralDfa1_1(0x80400000000L); case 102: return jjMoveStringLiteralDfa1_1(0x8000L); case 103: return jjMoveStringLiteralDfa1_1(0x44000000L); - case 105: - return jjMoveStringLiteralDfa1_1(0x100000000000L); case 108: return jjMoveStringLiteralDfa1_1(0x110000000L); case 109: - return jjMoveStringLiteralDfa1_1(0x10000000000000L); + return jjMoveStringLiteralDfa1_1(0x8000000000000L); case 110: return jjMoveStringLiteralDfa1_1(0x5000010000L); case 111: @@ -1376,49 +1224,49 @@ return jjStopAtPos(1, 33); } else if ((active0 & 0x800000000L) != 0L) { return jjStopAtPos(1, 35); - } else if ((active0 & 0x20000000000000L) != 0L) { - return jjStopAtPos(1, 53); + } else if ((active0 & 0x10000000000000L) != 0L) { + return jjStopAtPos(1, 52); } break; case 62: - if ((active0 & 0x80000000000000L) != 0L) { - return jjStopAtPos(1, 55); + if ((active0 & 0x40000000000000L) != 0L) { + return jjStopAtPos(1, 54); } break; case 97: return jjMoveStringLiteralDfa2_1(active0, 0x8000L); case 101: if ((active0 & 0x40000000L) != 0L) { - return jjStartNfaWithStates_1(1, 30, 30); + return jjStartNfaWithStates_1(1, 30, 16); } else if ((active0 & 0x100000000L) != 0L) { - return jjStartNfaWithStates_1(1, 32, 30); + return jjStartNfaWithStates_1(1, 32, 16); } else if ((active0 & 0x1000000000L) != 0L) { - return jjStartNfaWithStates_1(1, 36, 30); + return jjStartNfaWithStates_1(1, 36, 16); } break; case 105: - return jjMoveStringLiteralDfa2_1(active0, 0x4000000000000L); + return jjMoveStringLiteralDfa2_1(active0, 0x2000000000000L); case 109: return jjMoveStringLiteralDfa2_1(active0, 0x80000000000L); case 110: - return jjMoveStringLiteralDfa2_1(active0, 0x110000000000L); + return jjMoveStringLiteralDfa2_1(active0, 0x10000000000L); case 111: - return jjMoveStringLiteralDfa2_1(active0, 0x10004000000000L); + return jjMoveStringLiteralDfa2_1(active0, 0x8004000000000L); case 113: if ((active0 & 0x400000000L) != 0L) { - return jjStartNfaWithStates_1(1, 34, 30); + return jjStartNfaWithStates_1(1, 34, 16); } break; case 114: if ((active0 & 0x40000000000L) != 0L) { - return jjStartNfaWithStates_1(1, 42, 30); + return jjStartNfaWithStates_1(1, 42, 16); } return jjMoveStringLiteralDfa2_1(active0, 0x4000L); case 116: if ((active0 & 0x4000000L) != 0L) { - return jjStartNfaWithStates_1(1, 26, 30); + return jjStartNfaWithStates_1(1, 26, 16); } else if ((active0 & 0x10000000L) != 0L) { - return jjStartNfaWithStates_1(1, 28, 30); + return jjStartNfaWithStates_1(1, 28, 16); } break; case 117: @@ -1447,27 +1295,25 @@ switch (curChar) { case 100: if ((active0 & 0x10000000000L) != 0L) { - return jjStartNfaWithStates_1(2, 40, 30); - } else if ((active0 & 0x10000000000000L) != 0L) { - return jjStartNfaWithStates_1(2, 52, 30); + return jjStartNfaWithStates_1(2, 40, 16); + } else if ((active0 & 0x8000000000000L) != 0L) { + return jjStartNfaWithStates_1(2, 51, 16); } break; case 108: return jjMoveStringLiteralDfa3_1(active0, 0x18000L); case 112: return jjMoveStringLiteralDfa3_1(active0, 0x80000000000L); - case 115: - return jjMoveStringLiteralDfa3_1(active0, 0x100000000000L); case 116: if ((active0 & 0x4000000000L) != 0L) { - return jjStartNfaWithStates_1(2, 38, 30); + return jjStartNfaWithStates_1(2, 38, 16); } break; case 117: return jjMoveStringLiteralDfa3_1(active0, 0x4000L); case 118: - if ((active0 & 0x4000000000000L) != 0L) { - return jjStartNfaWithStates_1(2, 50, 30); + if ((active0 & 0x2000000000000L) != 0L) { + return jjStartNfaWithStates_1(2, 49, 16); } break; default: @@ -1489,18 +1335,18 @@ switch (curChar) { case 101: if ((active0 & 0x4000L) != 0L) { - return jjStartNfaWithStates_1(3, 14, 30); + return jjStartNfaWithStates_1(3, 14, 16); } break; case 108: if ((active0 & 0x10000L) != 0L) { - return jjStartNfaWithStates_1(3, 16, 30); + return jjStartNfaWithStates_1(3, 16, 16); } break; case 115: return jjMoveStringLiteralDfa4_1(active0, 0x8000L); case 116: - return jjMoveStringLiteralDfa4_1(active0, 0x180000000000L); + return jjMoveStringLiteralDfa4_1(active0, 0x80000000000L); default: break; } @@ -1518,16 +1364,14 @@ return 4; } switch (curChar) { - case 97: - return jjMoveStringLiteralDfa5_1(active0, 0x100000000000L); case 101: if ((active0 & 0x8000L) != 0L) { - return jjStartNfaWithStates_1(4, 15, 30); + return jjStartNfaWithStates_1(4, 15, 16); } break; case 121: if ((active0 & 0x80000000000L) != 0L) { - return jjStartNfaWithStates_1(4, 43, 30); + return jjStartNfaWithStates_1(4, 43, 16); } break; default: @@ -1536,104 +1380,6 @@ return jjStartNfa_1(3, active0); } - private int jjMoveStringLiteralDfa5_1(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_1(3, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_1(4, active0); - return 5; - } - switch (curChar) { - case 110: - return jjMoveStringLiteralDfa6_1(active0, 0x100000000000L); - default: - break; - } - return jjStartNfa_1(4, active0); - } - - private int jjMoveStringLiteralDfa6_1(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_1(4, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_1(5, active0); - return 6; - } - switch (curChar) { - case 99: - return jjMoveStringLiteralDfa7_1(active0, 0x100000000000L); - default: - break; - } - return jjStartNfa_1(5, active0); - } - - private int jjMoveStringLiteralDfa7_1(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_1(5, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_1(6, active0); - return 7; - } - switch (curChar) { - case 101: - return jjMoveStringLiteralDfa8_1(active0, 0x100000000000L); - default: - break; - } - return jjStartNfa_1(6, active0); - } - - private int jjMoveStringLiteralDfa8_1(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_1(6, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_1(7, active0); - return 8; - } - switch (curChar) { - case 111: - return jjMoveStringLiteralDfa9_1(active0, 0x100000000000L); - default: - break; - } - return jjStartNfa_1(7, active0); - } - - private int jjMoveStringLiteralDfa9_1(long old0, long active0) { - if (((active0 &= old0)) == 0L) { - return jjStartNfa_1(7, old0); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_1(8, active0); - return 9; - } - switch (curChar) { - case 102: - if ((active0 & 0x100000000000L) != 0L) { - return jjStartNfaWithStates_1(9, 44, 30); - } - break; - default: - break; - } - return jjStartNfa_1(8, active0); - } - private int jjStartNfaWithStates_1(int pos, int kind, int state) { jjmatchedKind = kind; jjmatchedPos = pos; @@ -1647,7 +1393,7 @@ private int jjMoveNfa_1(int startState, int curPos) { int startsAt = 0; - jjnewStateCnt = 30; + jjnewStateCnt = 29; int i = 1; jjstateSet[0] = startState; int kind = 0x7fffffff; @@ -1667,12 +1413,12 @@ { jjCheckNAddStates(18, 22); } - } else if ((0x1800000000L & l) != 0L) { - if (kind > 56) { - kind = 56; + } else if (curChar == 36) { + if (kind > 55) { + kind = 55; } { - jjCheckNAddTwoStates(28, 29); + jjCheckNAdd(16); } } else if (curChar == 39) { jjCheckNAddStates(23, 25); @@ -1682,24 +1428,6 @@ jjCheckNAdd(1); } break; - case 30: - if ((0x3ff001000000000L & l) != 0L) { - if (kind > 57) { - kind = 57; - } - { - jjCheckNAdd(29); - } - } - if ((0x3ff001000000000L & l) != 0L) { - if (kind > 56) { - kind = 56; - } - { - jjCheckNAdd(28); - } - } - break; case 1: if ((0x3ff000000000000L & l) == 0L) { break; @@ -1766,6 +1494,26 @@ } break; case 15: + if (curChar != 36) { + break; + } + if (kind > 55) { + kind = 55; + } { + jjCheckNAdd(16); + } + break; + case 16: + if ((0x3ff00100fffc1ffL & l) == 0L) { + break; + } + if (kind > 55) { + kind = 55; + } { + jjCheckNAdd(16); + } + break; + case 17: if ((0x3ff000000000000L & l) == 0L) { break; } @@ -1775,106 +1523,76 @@ jjCheckNAddStates(18, 22); } break; - case 16: + case 18: if ((0x3ff000000000000L & l) == 0L) { break; } if (kind > 10) { kind = 10; } { - jjCheckNAdd(16); + jjCheckNAdd(18); } break; - case 17: + case 19: if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(17, 18); + jjCheckNAddTwoStates(19, 20); } break; - case 18: + case 20: if (curChar != 46) { break; } if (kind > 11) { kind = 11; } { - jjCheckNAddTwoStates(19, 20); + jjCheckNAddTwoStates(21, 22); } break; - case 19: + case 21: if ((0x3ff000000000000L & l) == 0L) { break; } if (kind > 11) { kind = 11; } { - jjCheckNAddTwoStates(19, 20); + jjCheckNAddTwoStates(21, 22); } break; - case 21: + case 23: if ((0x280000000000L & l) != 0L) { - jjCheckNAdd(22); + jjCheckNAdd(24); } break; - case 22: + case 24: if ((0x3ff000000000000L & l) == 0L) { break; } if (kind > 11) { kind = 11; } { - jjCheckNAdd(22); + jjCheckNAdd(24); } break; - case 23: + case 25: if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(23, 24); + jjCheckNAddTwoStates(25, 26); } break; - case 25: + case 27: if ((0x280000000000L & l) != 0L) { - jjCheckNAdd(26); + jjCheckNAdd(28); } break; - case 26: + case 28: if ((0x3ff000000000000L & l) == 0L) { break; } if (kind > 11) { kind = 11; } { - jjCheckNAdd(26); - } - break; - case 27: - if ((0x1800000000L & l) == 0L) { - break; - } - if (kind > 56) { - kind = 56; - } { - jjCheckNAddTwoStates(28, 29); - } - break; - case 28: - if ((0x3ff001000000000L & l) == 0L) { - break; - } - if (kind > 56) { - kind = 56; - } { jjCheckNAdd(28); } break; - case 29: - if ((0x3ff001000000000L & l) == 0L) { - break; - } - if (kind > 57) { - kind = 57; - } { - jjCheckNAdd(29); - } - break; default: break; } @@ -1887,30 +1605,12 @@ if ((0x7fffffe87fffffeL & l) == 0L) { break; } - if (kind > 56) { - kind = 56; + if (kind > 55) { + kind = 55; } { - jjCheckNAddTwoStates(28, 29); + jjCheckNAdd(16); } break; - case 30: - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 57) { - kind = 57; - } - { - jjCheckNAdd(29); - } - } - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 56) { - kind = 56; - } - { - jjCheckNAdd(28); - } - } - break; case 2: if ((0x2000000020L & l) != 0L) { jjAddStates(29, 30); @@ -1946,36 +1646,26 @@ jjCheckNAddStates(23, 25); } break; - case 20: + case 16: + if ((0x87fffffe87fffffeL & l) == 0L) { + break; + } + if (kind > 55) { + kind = 55; + } { + jjCheckNAdd(16); + } + break; + case 22: if ((0x2000000020L & l) != 0L) { jjAddStates(31, 32); } break; - case 24: + case 26: if ((0x2000000020L & l) != 0L) { jjAddStates(33, 34); } break; - case 28: - if ((0x7fffffe87fffffeL & l) == 0L) { - break; - } - if (kind > 56) { - kind = 56; - } { - jjCheckNAdd(28); - } - break; - case 29: - if ((0x7fffffe87fffffeL & l) == 0L) { - break; - } - if (kind > 57) { - kind = 57; - } { - jjCheckNAdd(29); - } - break; default: break; } @@ -1992,30 +1682,12 @@ if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) { break; } - if (kind > 56) { - kind = 56; + if (kind > 55) { + kind = 55; } { - jjCheckNAddTwoStates(28, 29); + jjCheckNAdd(16); } break; - case 30: - if (jjCanMove_1(hiByte, i1, i2, l1, l2)) { - if (kind > 56) { - kind = 56; - } - { - jjCheckNAdd(28); - } - } - if (jjCanMove_1(hiByte, i1, i2, l1, l2)) { - if (kind > 57) { - kind = 57; - } - { - jjCheckNAdd(29); - } - } - break; case 6: if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { jjAddStates(26, 28); @@ -2026,24 +1698,14 @@ jjAddStates(23, 25); } break; - case 28: - if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) { - break; - } - if (kind > 56) { - kind = 56; - } { - jjCheckNAdd(28); - } - break; - case 29: - if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) { + case 16: + if (!jjCanMove_2(hiByte, i1, i2, l1, l2)) { break; } - if (kind > 57) { - kind = 57; + if (kind > 55) { + kind = 55; } { - jjCheckNAdd(29); + jjCheckNAdd(16); } break; default: @@ -2061,7 +1723,7 @@ kind = 0x7fffffff; } ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 30 - (jjnewStateCnt = startsAt))) { + if ((i = jjnewStateCnt) == (startsAt = 29 - (jjnewStateCnt = startsAt))) { return curPos; } try { @@ -2077,9 +1739,8 @@ "\175", null, null, null, null, "\164\162\165\145", "\146\141\154\163\145", "\156\165\154\154", "\56", "\50", "\51", "\133", "\135", "\72", "\73", "\54", "\76", "\147\164", "\74", "\154\164", "\76\75", "\147\145", "\74\75", "\154\145", "\75\75", "\145\161", "\41\75", "\156\145", "\41", "\156\157\164", - "\46\46", "\141\156\144", "\174\174", "\157\162", "\145\155\160\164\171", - "\151\156\163\164\141\156\143\145\157\146", "\52", "\53", "\55", "\77", "\57", "\144\151\166", "\45", - "\155\157\144", "\53\75", "\75", "\55\76", null, null, null, null, null, null, }; + "\46\46", "\141\156\144", "\174\174", "\157\162", "\145\155\160\164\171", "\52", "\53", "\55", "\77", "\57", + "\144\151\166", "\45", "\155\157\144", "\53\75", "\75", "\55\76", null, null, null, null, }; protected Token jjFillToken() { final Token t; @@ -2104,8 +1765,8 @@ return t; } - static final int[] jjnextStates = { 0, 1, 3, 4, 2, 0, 1, 4, 2, 0, 1, 4, 5, 2, 0, 1, 2, 6, 16, 17, 18, 23, 24, 11, - 12, 14, 6, 7, 9, 3, 4, 21, 22, 25, 26, }; + static final int[] jjnextStates = { 0, 1, 3, 4, 2, 0, 1, 4, 2, 0, 1, 4, 5, 2, 0, 1, 2, 6, 18, 19, 20, 25, 26, 11, + 12, 14, 6, 7, 9, 3, 4, 23, 24, 27, 28, }; private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) { switch (hiByte) { @@ -2123,14 +1784,214 @@ switch (hiByte) { case 0: return ((jjbitVec4[i2] & l2) != 0L); - case 48: + case 2: return ((jjbitVec5[i2] & l2) != 0L); - case 49: + case 3: return ((jjbitVec6[i2] & l2) != 0L); - case 51: + case 4: return ((jjbitVec7[i2] & l2) != 0L); - case 61: + case 5: return ((jjbitVec8[i2] & l2) != 0L); + case 6: + return ((jjbitVec9[i2] & l2) != 0L); + case 7: + return ((jjbitVec10[i2] & l2) != 0L); + case 8: + return ((jjbitVec11[i2] & l2) != 0L); + case 9: + return ((jjbitVec12[i2] & l2) != 0L); + case 10: + return ((jjbitVec13[i2] & l2) != 0L); + case 11: + return ((jjbitVec14[i2] & l2) != 0L); + case 12: + return ((jjbitVec15[i2] & l2) != 0L); + case 13: + return ((jjbitVec16[i2] & l2) != 0L); + case 14: + return ((jjbitVec17[i2] & l2) != 0L); + case 15: + return ((jjbitVec18[i2] & l2) != 0L); + case 16: + return ((jjbitVec19[i2] & l2) != 0L); + case 18: + return ((jjbitVec20[i2] & l2) != 0L); + case 19: + return ((jjbitVec21[i2] & l2) != 0L); + case 20: + return ((jjbitVec0[i2] & l2) != 0L); + case 22: + return ((jjbitVec22[i2] & l2) != 0L); + case 23: + return ((jjbitVec23[i2] & l2) != 0L); + case 24: + return ((jjbitVec24[i2] & l2) != 0L); + case 25: + return ((jjbitVec25[i2] & l2) != 0L); + case 26: + return ((jjbitVec26[i2] & l2) != 0L); + case 27: + return ((jjbitVec27[i2] & l2) != 0L); + case 28: + return ((jjbitVec28[i2] & l2) != 0L); + case 29: + return ((jjbitVec29[i2] & l2) != 0L); + case 31: + return ((jjbitVec30[i2] & l2) != 0L); + case 32: + return ((jjbitVec31[i2] & l2) != 0L); + case 33: + return ((jjbitVec32[i2] & l2) != 0L); + case 44: + return ((jjbitVec33[i2] & l2) != 0L); + case 45: + return ((jjbitVec34[i2] & l2) != 0L); + case 46: + return ((jjbitVec35[i2] & l2) != 0L); + case 48: + return ((jjbitVec36[i2] & l2) != 0L); + case 49: + return ((jjbitVec37[i2] & l2) != 0L); + case 77: + return ((jjbitVec38[i2] & l2) != 0L); + case 159: + return ((jjbitVec39[i2] & l2) != 0L); + case 164: + return ((jjbitVec40[i2] & l2) != 0L); + case 166: + return ((jjbitVec41[i2] & l2) != 0L); + case 167: + return ((jjbitVec42[i2] & l2) != 0L); + case 168: + return ((jjbitVec43[i2] & l2) != 0L); + case 169: + return ((jjbitVec44[i2] & l2) != 0L); + case 170: + return ((jjbitVec45[i2] & l2) != 0L); + case 171: + return ((jjbitVec46[i2] & l2) != 0L); + case 215: + return ((jjbitVec47[i2] & l2) != 0L); + case 250: + return ((jjbitVec48[i2] & l2) != 0L); + case 251: + return ((jjbitVec49[i2] & l2) != 0L); + case 253: + return ((jjbitVec50[i2] & l2) != 0L); + case 254: + return ((jjbitVec51[i2] & l2) != 0L); + case 255: + return ((jjbitVec52[i2] & l2) != 0L); + default: + if ((jjbitVec3[i1] & l1) != 0L) { + return true; + } + return false; + } + } + + private static final boolean jjCanMove_2(int hiByte, int i1, int i2, long l1, long l2) { + switch (hiByte) { + case 0: + return ((jjbitVec53[i2] & l2) != 0L); + case 2: + return ((jjbitVec5[i2] & l2) != 0L); + case 3: + return ((jjbitVec54[i2] & l2) != 0L); + case 4: + return ((jjbitVec55[i2] & l2) != 0L); + case 5: + return ((jjbitVec56[i2] & l2) != 0L); + case 6: + return ((jjbitVec57[i2] & l2) != 0L); + case 7: + return ((jjbitVec58[i2] & l2) != 0L); + case 8: + return ((jjbitVec59[i2] & l2) != 0L); + case 9: + return ((jjbitVec60[i2] & l2) != 0L); + case 10: + return ((jjbitVec61[i2] & l2) != 0L); + case 11: + return ((jjbitVec62[i2] & l2) != 0L); + case 12: + return ((jjbitVec63[i2] & l2) != 0L); + case 13: + return ((jjbitVec64[i2] & l2) != 0L); + case 14: + return ((jjbitVec65[i2] & l2) != 0L); + case 15: + return ((jjbitVec66[i2] & l2) != 0L); + case 16: + return ((jjbitVec67[i2] & l2) != 0L); + case 18: + return ((jjbitVec20[i2] & l2) != 0L); + case 19: + return ((jjbitVec68[i2] & l2) != 0L); + case 20: + return ((jjbitVec0[i2] & l2) != 0L); + case 22: + return ((jjbitVec22[i2] & l2) != 0L); + case 23: + return ((jjbitVec69[i2] & l2) != 0L); + case 24: + return ((jjbitVec70[i2] & l2) != 0L); + case 25: + return ((jjbitVec71[i2] & l2) != 0L); + case 26: + return ((jjbitVec72[i2] & l2) != 0L); + case 27: + return ((jjbitVec73[i2] & l2) != 0L); + case 28: + return ((jjbitVec74[i2] & l2) != 0L); + case 29: + return ((jjbitVec75[i2] & l2) != 0L); + case 31: + return ((jjbitVec30[i2] & l2) != 0L); + case 32: + return ((jjbitVec76[i2] & l2) != 0L); + case 33: + return ((jjbitVec32[i2] & l2) != 0L); + case 44: + return ((jjbitVec77[i2] & l2) != 0L); + case 45: + return ((jjbitVec78[i2] & l2) != 0L); + case 46: + return ((jjbitVec35[i2] & l2) != 0L); + case 48: + return ((jjbitVec79[i2] & l2) != 0L); + case 49: + return ((jjbitVec37[i2] & l2) != 0L); + case 77: + return ((jjbitVec38[i2] & l2) != 0L); + case 159: + return ((jjbitVec39[i2] & l2) != 0L); + case 164: + return ((jjbitVec40[i2] & l2) != 0L); + case 166: + return ((jjbitVec80[i2] & l2) != 0L); + case 167: + return ((jjbitVec42[i2] & l2) != 0L); + case 168: + return ((jjbitVec81[i2] & l2) != 0L); + case 169: + return ((jjbitVec82[i2] & l2) != 0L); + case 170: + return ((jjbitVec83[i2] & l2) != 0L); + case 171: + return ((jjbitVec84[i2] & l2) != 0L); + case 215: + return ((jjbitVec47[i2] & l2) != 0L); + case 250: + return ((jjbitVec48[i2] & l2) != 0L); + case 251: + return ((jjbitVec85[i2] & l2) != 0L); + case 253: + return ((jjbitVec50[i2] & l2) != 0L); + case 254: + return ((jjbitVec86[i2] & l2) != 0L); + case 255: + return ((jjbitVec87[i2] & l2) != 0L); default: if ((jjbitVec3[i1] & l1) != 0L) { return true; @@ -2151,7 +2012,8 @@ Token matchedToken; int curPos = 0; - EOFLoop: for (;;) { + EOFLoop: + for (;;) { try { curChar = input_stream.BeginToken(); } catch (Exception e) { @@ -2182,8 +2044,8 @@ jjmatchedKind = 0x7fffffff; jjmatchedPos = 0; curPos = jjMoveStringLiteralDfa0_1(); - if (jjmatchedPos == 0 && jjmatchedKind > 61) { - jjmatchedKind = 61; + if (jjmatchedPos == 0 && jjmatchedKind > 58) { + jjmatchedKind = 58; } break; case 2: @@ -2198,8 +2060,8 @@ jjmatchedKind = 0x7fffffff; jjmatchedPos = 0; curPos = jjMoveStringLiteralDfa0_2(); - if (jjmatchedPos == 0 && jjmatchedKind > 61) { - jjmatchedKind = 61; + if (jjmatchedPos == 0 && jjmatchedKind > 58) { + jjmatchedKind = 58; } break; } @@ -2343,7 +2205,7 @@ private void ReInitRounds() { int i; jjround = 0x80000001; - for (i = 30; i-- > 0;) { + for (i = 29; i-- > 0;) { jjrounds[i] = 0x80000000; } } @@ -2373,15 +2235,15 @@ /** Lex State array. */ public static final int[] jjnewLexState = { -1, -1, 1, 1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; - static final long[] jjtoToken = { 0x23ffffffffffef0fL, }; + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; + static final long[] jjtoToken = { 0x4ffffffffffef0fL, }; static final long[] jjtoSkip = { 0xf0L, }; static final long[] jjtoSpecial = { 0x0L, }; static final long[] jjtoMore = { 0x0L, }; protected SimpleCharStream input_stream; - private final int[] jjrounds = new int[30]; - private final int[] jjstateSet = new int[2 * 30]; + private final int[] jjrounds = new int[29]; + private final int[] jjstateSet = new int[2 * 29]; private final StringBuilder jjimage = new StringBuilder(); private StringBuilder image = jjimage; private int jjimageLen; diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/Node.java tomcat10-10.1.52/java/org/apache/el/parser/Node.java --- tomcat10-10.1.34/java/org/apache/el/parser/Node.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/Node.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,10 +28,6 @@ /* All AST nodes must implement this interface. It provides basic machinery for constructing the parent and child relationships between nodes. */ - -/** - * @author Jacob Hookom [jacob@hookom.net] - */ @SuppressWarnings("all") // Ignore warnings in generated code public interface Node { diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/NodeVisitor.java tomcat10-10.1.52/java/org/apache/el/parser/NodeVisitor.java --- tomcat10-10.1.34/java/org/apache/el/parser/NodeVisitor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/NodeVisitor.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,9 +16,6 @@ */ package org.apache.el.parser; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public interface NodeVisitor { void visit(Node node) throws Exception; diff -Nru tomcat10-10.1.34/java/org/apache/el/parser/SimpleNode.java tomcat10-10.1.52/java/org/apache/el/parser/SimpleNode.java --- tomcat10-10.1.34/java/org/apache/el/parser/SimpleNode.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/parser/SimpleNode.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,9 +28,6 @@ import org.apache.el.lang.EvaluationContext; import org.apache.el.util.MessageFactory; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public abstract class SimpleNode implements Node { /* @@ -81,7 +78,7 @@ if (children == null) { children = new SimpleNode[i + 1]; } else if (i >= children.length) { - SimpleNode c[] = new SimpleNode[i + 1]; + SimpleNode[] c = new SimpleNode[i + 1]; System.arraycopy(children, 0, c, 0, children.length); children = c; } @@ -154,7 +151,7 @@ @Override public void accept(NodeVisitor visitor) throws Exception { visitor.visit(this); - if (this.children != null && this.children.length > 0) { + if (this.children != null) { for (Node child : this.children) { child.accept(visitor); } @@ -210,10 +207,7 @@ } else if (!image.equals(other.image)) { return false; } - if (!Arrays.equals(children, other.children)) { - return false; - } - return true; + return Arrays.equals(children, other.children); } diff -Nru tomcat10-10.1.34/java/org/apache/el/stream/Stream.java tomcat10-10.1.52/java/org/apache/el/stream/Stream.java --- tomcat10-10.1.34/java/org/apache/el/stream/Stream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/stream/Stream.java 2026-01-23 19:33:36.000000000 +0000 @@ -102,7 +102,7 @@ public Stream distinct() { Iterator downStream = new OpIterator() { - private Set values = new HashSet<>(); + private final Set values = new HashSet<>(); @Override protected void findNext() { @@ -440,8 +440,7 @@ Object result = null; if (iterator.hasNext()) { - Object obj = iterator.next(); - result = obj; + result = iterator.next(); } while (iterator.hasNext()) { diff -Nru tomcat10-10.1.34/java/org/apache/el/util/ExceptionUtils.java tomcat10-10.1.52/java/org/apache/el/util/ExceptionUtils.java --- tomcat10-10.1.34/java/org/apache/el/util/ExceptionUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/util/ExceptionUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,7 +17,7 @@ package org.apache.el.util; /** - * Utilities for handling Throwables and Exceptions. + * Utilities for handling Throwable and Exceptions. */ /* * Copied from o.a.t.u.ExceptionUtils @@ -45,8 +45,8 @@ /** - * NO-OP method provided to enable simple pre-loading of this class. Since the class is used extensively in error - * handling, it is prudent to pre-load it to avoid any failure to load this class masking the true problem during + * NO-OP method provided to enable simple preloading of this class. Since the class is used extensively in error + * handling, it is prudent to preload it to avoid any failure to load this class masking the true problem during * error handling. */ public static void preload() { diff -Nru tomcat10-10.1.34/java/org/apache/el/util/MessageFactory.java tomcat10-10.1.52/java/org/apache/el/util/MessageFactory.java --- tomcat10-10.1.34/java/org/apache/el/util/MessageFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/util/MessageFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ import java.util.MissingResourceException; import java.util.ResourceBundle; -/** - * @author Jacob Hookom [jacob@hookom.net] - */ public final class MessageFactory { private static final ResourceBundle DEFAULT_BUNDLE = ResourceBundle.getBundle("org.apache.el.LocalStrings"); diff -Nru tomcat10-10.1.34/java/org/apache/el/util/ReflectionUtil.java tomcat10-10.1.52/java/org/apache/el/util/ReflectionUtil.java --- tomcat10-10.1.34/java/org/apache/el/util/ReflectionUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/util/ReflectionUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,11 +32,8 @@ import org.apache.el.lang.ELSupport; import org.apache.el.lang.EvaluationContext; - /** - * Utilities for Managing Serialization and Reflection - * - * @author Jacob Hookom [jacob@hookom.net] + * Utilities for Managing Serialization and Reflection. */ public class ReflectionUtil { @@ -155,12 +152,20 @@ // Fast path: when no arguments exist, there can only be one matching method and no need for coercion. if (paramCount == 0) { + Method result = null; + Throwable t = null; try { Method method = clazz.getMethod(methodName, paramTypes); - return getMethod(clazz, base, method); + result = getMethod(clazz, base, method); } catch (NoSuchMethodException | SecurityException e) { - // Fall through to broader, slower logic + // Fall through + t = e; + } + if (result == null) { + throw new MethodNotFoundException( + MessageFactory.get("error.method.notfound", base, property, paramString(paramTypes)), t); } + return result; } Method[] methods = clazz.getMethods(); @@ -427,8 +432,7 @@ * This class duplicates code in jakarta.el.Util. When making changes keep the code in sync. */ private static boolean isCoercibleFrom(EvaluationContext ctx, Object src, Class target) { - // TODO: This isn't pretty but it works. Significant refactoring would - // be required to avoid the exception. + // TODO: This isn't pretty but it works. Significant refactoring would be required to avoid the exception. try { ELSupport.coerceToType(ctx, src, target); } catch (ELException e) { @@ -447,7 +451,7 @@ return m; } Class[] interfaces = type.getInterfaces(); - Method mp = null; + Method mp; for (Class iface : interfaces) { try { mp = iface.getMethod(m.getName(), m.getParameterTypes()); diff -Nru tomcat10-10.1.34/java/org/apache/el/util/Validation.java tomcat10-10.1.52/java/org/apache/el/util/Validation.java --- tomcat10-10.1.34/java/org/apache/el/util/Validation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/el/util/Validation.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,11 @@ public class Validation { - // Java keywords, boolean literals & the null literal in alphabetical order - private static final String invalidIdentifiers[] = { "abstract", "assert", "boolean", "break", "byte", "case", + /* + * Java keywords, boolean literals & the null literal in alphabetical order. As per the Java Language Specification, + * none of these are permitted to be used as an identifier. + */ + private static final String[] invalidIdentifiers = { "_", "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "public", "return", @@ -64,7 +67,7 @@ } // Should not be the case but check to be sure - if (key == null || key.length() == 0) { + if (key == null || key.isEmpty()) { return false; } @@ -84,18 +87,10 @@ } } - // Check the start character that has more restrictions - if (!Character.isJavaIdentifierStart(key.charAt(0))) { - return false; - } - - // Check each remaining character used is permitted - for (int idx = 1; idx < key.length(); idx++) { - if (!Character.isJavaIdentifierPart(key.charAt(idx))) { - return false; - } - } - - return true; + /* + * The parser checks Character.isJavaIdentifierStart() and Character.isJavaIdentifierPart() so no need to check + * them again here. However, we do need to check that '#' hasn't been used at the start of the identifier. + */ + return key.charAt(0) != '#'; } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/Constants.java tomcat10-10.1.52/java/org/apache/jasper/Constants.java --- tomcat10-10.1.34/java/org/apache/jasper/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,33 +16,20 @@ */ package org.apache.jasper; -import java.util.Arrays; -import java.util.Collections; import java.util.List; /** * Some constants and other global data that are used by the compiler and the runtime. - * - * @author Anil K. Vijendran - * @author Harish Prabandham - * @author Shawn Bayern - * @author Mark Roth */ public class Constants { public static final String SPEC_VERSION = "3.1"; /** - * These classes/packages are automatically imported by the - * generated code. + * These classes/packages are automatically imported by the generated code. */ - private static final String[] PRIVATE_STANDARD_IMPORTS = { - "jakarta.servlet.*", - "jakarta.servlet.http.*", - "jakarta.servlet.jsp.*" - }; public static final List STANDARD_IMPORTS = - Collections.unmodifiableList(Arrays.asList(PRIVATE_STANDARD_IMPORTS)); + List.of("jakarta.servlet.*", "jakarta.servlet.http.*", "jakarta.servlet.jsp.*"); /** * Default size of the JSP buffer. @@ -62,40 +49,34 @@ /** * Has security been turned on? */ - public static final boolean IS_SECURITY_ENABLED = - (System.getSecurityManager() != null); + public static final boolean IS_SECURITY_ENABLED = (System.getSecurityManager() != null); /** - * Name of the system property containing - * the tomcat product installation path + * Name of the system property containing the tomcat product installation path */ public static final String CATALINA_HOME_PROP = "catalina.home"; /** - * Name of the ServletContext init-param that determines if the XML parsers - * used for *.tld files will be validating or not. + * Name of the ServletContext init-param that determines if the XML parsers used for *.tld files will be validating + * or not. *

          * This must be kept in sync with org.apache.catalina.Globals */ - public static final String XML_VALIDATION_TLD_INIT_PARAM = - "org.apache.jasper.XML_VALIDATE_TLD"; + public static final String XML_VALIDATION_TLD_INIT_PARAM = "org.apache.jasper.XML_VALIDATE_TLD"; /** - * Name of the ServletContext init-param that determines if the XML parsers - * will block the resolution of external entities. + * Name of the ServletContext init-param that determines if the XML parsers will block the resolution of external + * entities. *

          * This must be kept in sync with org.apache.catalina.Globals */ - public static final String XML_BLOCK_EXTERNAL_INIT_PARAM = - "org.apache.jasper.XML_BLOCK_EXTERNAL"; + public static final String XML_BLOCK_EXTERNAL_INIT_PARAM = "org.apache.jasper.XML_BLOCK_EXTERNAL"; /** - * Name of the ServletContext init-param that determines the JSP - * factory pool size. Set the value to a positive integer to enable it. - * The default value is 8 per thread. + * Name of the ServletContext init-param that determines the JSP factory pool size. Set the value to a positive + * integer to enable it. The default value is 8 per thread. */ - public static final String JSP_FACTORY_POOL_SIZE_INIT_PARAM = - "org.apache.jasper.runtime.JspFactoryImpl.POOL_SIZE"; + public static final String JSP_FACTORY_POOL_SIZE_INIT_PARAM = "org.apache.jasper.runtime.JspFactoryImpl.POOL_SIZE"; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/EmbeddedServletOptions.java tomcat10-10.1.52/java/org/apache/jasper/EmbeddedServletOptions.java --- tomcat10-10.1.34/java/org/apache/jasper/EmbeddedServletOptions.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/EmbeddedServletOptions.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,17 +35,13 @@ /** * A class to hold all init parameters specific to the JSP engine. - * - * @author Anil K. Vijendran - * @author Hans Bergsten - * @author Pierre Delisle */ public final class EmbeddedServletOptions implements Options { // Logger private final Log log = LogFactory.getLog(EmbeddedServletOptions.class); // must not be static - private Properties settings = new Properties(); + private final Properties settings = new Properties(); /** * Is Jasper being used in development mode? @@ -73,9 +69,8 @@ private boolean isPoolingEnabled = true; /** - * Do you want support for "mapped" files? This will generate - * servlet that has a print statement per line of the JSP file. - * This seems like a really nice feature to have for debugging. + * Do you want support for "mapped" files? This will generate servlet that has a print statement per line of the JSP + * file. This seems like a really nice feature to have for debugging. */ private boolean mappedFile = true; @@ -107,10 +102,9 @@ private boolean errorOnUseBeanInvalidClassAttribute = true; /** - * I want to see my generated servlets. Which directory are they - * in? + * I want to see my generated servlets. Which directory are they in? */ - private File scratchDir; + private final File scratchDir; /** * What classpath should I use while compiling generated servlets? @@ -153,8 +147,7 @@ private TagPluginManager tagPluginManager = null; /** - * Java platform encoding to generate the JSP - * page servlet. + * Java platform encoding to generate the JSP page servlet. */ private String javaEncoding = "UTF-8"; @@ -174,33 +167,29 @@ private boolean xpoweredBy; /** - * Should we include a source fragment in exception messages, which could be displayed - * to the developer ? + * Should we include a source fragment in exception messages, which could be displayed to the developer ? */ private boolean displaySourceFragment = true; /** - * The maximum number of loaded jsps per web-application. If there are more - * jsps loaded, they will be unloaded. + * The maximum number of loaded jsps per web-application. If there are more jsps loaded, they will be unloaded. */ private int maxLoadedJsps = -1; /** - * The idle time in seconds after which a JSP is unloaded. - * If unset or less or equal than 0, no jsps are unloaded. + * The idle time in seconds after which a JSP is unloaded. If unset or less or equal than 0, no jsps are unloaded. */ private int jspIdleTimeout = -1; /** - * Should JSP.1.6 be applied strictly to attributes defined using scriptlet - * expressions? + * Should JSP.1.6 be applied strictly to attributes defined using scriptlet expressions? */ private boolean strictQuoteEscaping = true; /** - * When EL is used in JSP attribute values, should the rules for quoting of - * attributes described in JSP.1.6 be applied to the expression? + * When EL is used in JSP attribute values, should the rules for quoting of attributes described in JSP.1.6 be + * applied to the expression? */ private boolean quoteAttributeEL = true; @@ -230,13 +219,15 @@ private boolean useInstanceManagerForTags = false; - public String getProperty(String name ) { - return settings.getProperty( name ); + private String useNonstandardTagOptimizations; + + public String getProperty(String name) { + return settings.getProperty(name); } - public void setProperty(String name, String value ) { - if (name != null && value != null){ - settings.setProperty( name, value ); + public void setProperty(String name, String value) { + if (name != null && value != null) { + settings.setProperty(name, value); } } @@ -388,7 +379,7 @@ } @Override - public Map getCache() { + public Map getCache() { return null; } @@ -477,19 +468,24 @@ return useInstanceManagerForTags; } + @Override + public String getUseNonstandardTagOptimizations() { + return useNonstandardTagOptimizations; + } + /** - * Create an EmbeddedServletOptions object using data available from - * ServletConfig and ServletContext. - * @param config The Servlet config + * Create an EmbeddedServletOptions object using data available from ServletConfig and ServletContext. + * + * @param config The Servlet config * @param context The Servlet context */ public EmbeddedServletOptions(ServletConfig config, ServletContext context) { - Enumeration enumeration=config.getInitParameterNames(); - while( enumeration.hasMoreElements() ) { - String k=enumeration.nextElement(); - String v=config.getInitParameter( k ); - setProperty( k, v); + Enumeration enumeration = config.getInitParameterNames(); + while (enumeration.hasMoreElements()) { + String k = enumeration.nextElement(); + String v = config.getInitParameter(k); + setProperty(k, v); } String keepgen = config.getInitParameter("keepgenerated"); @@ -519,8 +515,7 @@ this.isPoolingEnabled = true; String poolingEnabledParam = config.getInitParameter("enablePooling"); - if (poolingEnabledParam != null - && !poolingEnabledParam.equalsIgnoreCase("true")) { + if (poolingEnabledParam != null && !poolingEnabledParam.equalsIgnoreCase("true")) { if (poolingEnabledParam.equalsIgnoreCase("false")) { this.isPoolingEnabled = false; } else { @@ -546,9 +541,9 @@ String debugInfo = config.getInitParameter("classdebuginfo"); if (debugInfo != null) { if (debugInfo.equalsIgnoreCase("true")) { - this.classDebugInfo = true; + this.classDebugInfo = true; } else if (debugInfo.equalsIgnoreCase("false")) { - this.classDebugInfo = false; + this.classDebugInfo = false; } else { if (log.isWarnEnabled()) { log.warn(Localizer.getMessage("jsp.warning.classDebugInfo")); @@ -560,7 +555,7 @@ if (checkInterval != null) { try { this.checkInterval = Integer.parseInt(checkInterval); - } catch(NumberFormatException ex) { + } catch (NumberFormatException ex) { if (log.isWarnEnabled()) { log.warn(Localizer.getMessage("jsp.warning.checkInterval")); } @@ -571,7 +566,7 @@ if (modificationTestInterval != null) { try { this.modificationTestInterval = Integer.parseInt(modificationTestInterval); - } catch(NumberFormatException ex) { + } catch (NumberFormatException ex) { if (log.isWarnEnabled()) { log.warn(Localizer.getMessage("jsp.warning.modificationTestInterval")); } @@ -660,6 +655,11 @@ this.classpath = classpath; } + String useNonstandardTagOptimizations = config.getInitParameter("useNonstandardTagOptimizations"); + if (useNonstandardTagOptimizations != null) { + this.useNonstandardTagOptimizations = useNonstandardTagOptimizations; + } + /* * scratchdir */ @@ -678,21 +678,19 @@ return; } - if (!(scratchDir.exists() && scratchDir.canRead() && - scratchDir.canWrite() && scratchDir.isDirectory())) { - log.fatal(Localizer.getMessage("jsp.error.bad.scratch.dir", - scratchDir.getAbsolutePath())); + if (!(scratchDir.exists() && scratchDir.canRead() && scratchDir.canWrite() && scratchDir.isDirectory())) { + log.fatal(Localizer.getMessage("jsp.error.bad.scratch.dir", scratchDir.getAbsolutePath())); } this.compiler = config.getInitParameter("compiler"); String compilerTargetVM = config.getInitParameter("compilerTargetVM"); - if(compilerTargetVM != null) { + if (compilerTargetVM != null) { this.compilerTargetVM = compilerTargetVM; } String compilerSourceVM = config.getInitParameter("compilerSourceVM"); - if(compilerSourceVM != null) { + if (compilerSourceVM != null) { this.compilerSourceVM = compilerSourceVM; } @@ -749,9 +747,9 @@ if (maxLoadedJsps != null) { try { this.maxLoadedJsps = Integer.parseInt(maxLoadedJsps); - } catch(NumberFormatException ex) { + } catch (NumberFormatException ex) { if (log.isWarnEnabled()) { - log.warn(Localizer.getMessage("jsp.warning.maxLoadedJsps", ""+this.maxLoadedJsps)); + log.warn(Localizer.getMessage("jsp.warning.maxLoadedJsps", "" + this.maxLoadedJsps)); } } } @@ -760,9 +758,9 @@ if (jspIdleTimeout != null) { try { this.jspIdleTimeout = Integer.parseInt(jspIdleTimeout); - } catch(NumberFormatException ex) { + } catch (NumberFormatException ex) { if (log.isWarnEnabled()) { - log.warn(Localizer.getMessage("jsp.warning.jspIdleTimeout", ""+this.jspIdleTimeout)); + log.warn(Localizer.getMessage("jsp.warning.jspIdleTimeout", "" + this.jspIdleTimeout)); } } } @@ -890,11 +888,11 @@ } } - // Setup the global Tag Libraries location cache for this + // Set up the global Tag Libraries location cache for this // web-application. tldCache = TldCache.getInstance(context); - // Setup the jsp config info for this web app. + // Set up the jsp config info for this web app. jspConfig = new JspConfig(context); // Create a Tag plugin instance diff -Nru tomcat10-10.1.34/java/org/apache/jasper/JasperException.java tomcat10-10.1.52/java/org/apache/jasper/JasperException.java --- tomcat10-10.1.34/java/org/apache/jasper/JasperException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/JasperException.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,10 +17,7 @@ package org.apache.jasper; /** - * Base class for all exceptions generated by the JSP engine. Makes it - * convenient to catch just this at the top-level. - * - * @author Anil K. Vijendran + * Base class for all exceptions generated by the JSP engine. Makes it convenient to catch just this at the top-level. */ public class JasperException extends jakarta.servlet.ServletException { @@ -31,9 +28,9 @@ } /** - * Creates a JasperException with the embedded exception and the reason for - * throwing a JasperException. - * @param reason The exception message + * Creates a JasperException with the embedded exception and the reason for throwing a JasperException. + * + * @param reason The exception message * @param exception The root cause */ public JasperException(String reason, Throwable exception) { @@ -42,6 +39,7 @@ /** * Creates a JasperException with the embedded exception. + * * @param exception The root cause */ public JasperException(Throwable exception) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/JspC.java tomcat10-10.1.52/java/org/apache/jasper/JspC.java --- tomcat10-10.1.34/java/org/apache/jasper/JspC.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/JspC.java 2026-01-23 19:33:36.000000000 +0000 @@ -65,14 +65,10 @@ import org.xml.sax.SAXException; /** - * Shell for the jspc compiler. Handles all options associated with the - * command line and creates compilation contexts which it then compiles - * according to the specified options. + * Shell for the jspc compiler. Handles all options associated with the command line and creates compilation contexts + * which it then compiles according to the specified options. This version can process files from a _single_ webapp at + * once, i.e. a single docbase can be specified. It can be used as an Ant task using: * - * This version can process files from a _single_ webapp at once, i.e. - * a single docbase can be specified. - * - * It can be used as an Ant task using: *

            *   <taskdef classname="org.apache.jasper.JspC" name="jasper" >
            *      <classpath>
          @@ -90,11 +86,6 @@
            *           webXmlFragment="${build.dir}/generated_web.xml"
            *           outputDir="${webapp.dir}/${webapp.name}/WEB-INF/src/my/package" />
            * 
          - * - * @author Danno Ferrin - * @author Pierre Delisle - * @author Costin Manolache - * @author Yoav Shapira */ public class JspC extends Task implements Options { @@ -141,7 +132,7 @@ protected static final String SWITCH_QUOTE_ATTRIBUTE_EL = "-quoteAttributeEL"; protected static final String SWITCH_NO_QUOTE_ATTRIBUTE_EL = "-no-quoteAttributeEL"; protected static final String SWITCH_THREAD_COUNT = "-threadCount"; - protected static final String SHOW_SUCCESS ="-s"; + protected static final String SHOW_SUCCESS = "-s"; protected static final String LIST_ERRORS = "-l"; protected static final int INC_WEBXML = 10; protected static final int FRG_WEBXML = 15; @@ -193,7 +184,7 @@ protected boolean smapSuppressed = true; protected boolean smapDumped = false; protected boolean caching = true; - protected final Map cache = new HashMap<>(); + protected final Map cache = new HashMap<>(); protected String compiler = null; @@ -203,8 +194,7 @@ protected boolean classDebugInfo = true; /** - * Throw an exception if there's a compilation error, or swallow it. - * Default is true to preserve old behavior. + * Throw an exception if there's a compilation error, or swallow it. Default is true to preserve old behavior. */ protected boolean failOnError = true; @@ -214,8 +204,7 @@ private boolean fork = false; /** - * The file extensions to be handled as JSP files. - * Default list is .jsp and .jspx. + * The file extensions to be handled as JSP files. Default list is .jsp and .jspx. */ protected List extensions; @@ -225,14 +214,12 @@ protected final List pages = new ArrayList<>(); /** - * Needs better documentation, this data member does. - * True by default. + * Needs better documentation, this data member does. True by default. */ protected boolean errorOnUseBeanInvalidClassAttribute = true; /** - * The java file encoding. Default - * is UTF-8. Added per bugzilla 19622. + * The java file encoding. Default is UTF-8. Added per bugzilla 19622. */ protected String javaEncoding = "UTF-8"; @@ -255,8 +242,7 @@ protected JspCServletContext context; /** - * The runtime context. - * Maintain a dummy JspRuntimeContext for compiling tag files. + * The runtime context. Maintain a dummy JspRuntimeContext for compiling tag files. */ protected JspRuntimeContext rctxt; @@ -275,9 +261,11 @@ protected boolean showSuccess = false; protected int argPos; protected boolean fullstop = false; - protected String args[]; + protected String[] args; + + protected String useNonstandardTagOptimizations; - public static void main(String arg[]) { + public static void main(String[] arg) { if (arg.length == 0) { System.out.println(Localizer.getMessage("jspc.usage")); } else { @@ -300,7 +288,9 @@ /** * Apply command-line arguments. + * * @param arg The arguments + * * @throws JasperException JSPC error */ public void setArgs(String[] arg) throws JasperException { @@ -316,24 +306,24 @@ listErrors = true; } else if (tok.equals(SWITCH_OUTPUT_DIR)) { tok = nextArg(); - setOutputDir( tok ); + setOutputDir(tok); } else if (tok.equals(SWITCH_PACKAGE_NAME)) { targetPackage = nextArg(); } else if (tok.equals(SWITCH_COMPILE)) { - compile=true; + compile = true; } else if (tok.equals(SWITCH_FAIL_FAST)) { failFast = true; } else if (tok.equals(SWITCH_CLASS_NAME)) { targetClassName = nextArg(); } else if (tok.equals(SWITCH_URI_BASE)) { - uriBase=nextArg(); + uriBase = nextArg(); } else if (tok.equals(SWITCH_URI_ROOT)) { - setUriroot( nextArg()); + setUriroot(nextArg()); } else if (tok.equals(SWITCH_FILE_WEBAPP)) { - setUriroot( nextArg()); - } else if ( tok.equals( SHOW_SUCCESS ) ) { + setUriroot(nextArg()); + } else if (tok.equals(SHOW_SUCCESS)) { showSuccess = true; - } else if ( tok.equals( LIST_ERRORS ) ) { + } else if (tok.equals(LIST_ERRORS)) { listErrors = true; } else if (tok.equals(SWITCH_WEBAPP_INC)) { webxmlFile = nextArg(); @@ -368,17 +358,12 @@ } } else if (tok.equals(SWITCH_CACHE)) { tok = nextArg(); - if ("false".equals(tok)) { - caching = false; - } else { - caching = true; - } + caching = !"false".equals(tok); } else if (tok.equals(SWITCH_CLASSPATH)) { setClassPath(nextArg()); } else if (tok.startsWith(SWITCH_DIE)) { try { - dieLevel = Integer.parseInt( - tok.substring(SWITCH_DIE.length())); + dieLevel = Integer.parseInt(tok.substring(SWITCH_DIE.length())); } catch (NumberFormatException nfe) { dieLevel = DEFAULT_DIE_LEVEL; } @@ -386,11 +371,7 @@ helpNeeded = true; } else if (tok.equals(SWITCH_POOLING)) { tok = nextArg(); - if ("false".equals(tok)) { - poolingEnabled = false; - } else { - poolingEnabled = true; - } + poolingEnabled = !"false".equals(tok); } else if (tok.equals(SWITCH_ENCODING)) { setJavaEncoding(nextArg()); } else if (tok.equals(SWITCH_SOURCE)) { @@ -428,18 +409,17 @@ } // Add all extra arguments to the list of files - while( true ) { + while (true) { String file = nextFile(); - if( file==null ) { + if (file == null) { break; } - pages.add( file ); + pages.add(file); } } /** - * In JspC this always returns true. - * {@inheritDoc} + * In JspC this always returns true. {@inheritDoc} */ @Override public boolean getKeepGenerated() { @@ -457,8 +437,7 @@ } /** - * Sets the option to control handling of template text that consists - * entirely of whitespace. + * Sets the option to control handling of template text that consists entirely of whitespace. * * @param ts New value */ @@ -484,6 +463,7 @@ /** * Sets the option to enable the tag handler pooling. + * * @param poolingEnabled New value */ public void setPoolingEnabled(boolean poolingEnabled) { @@ -497,6 +477,7 @@ /** * Sets the option to enable generation of X-Powered-By response header. + * * @param xpoweredBy New value */ public void setXpoweredBy(boolean xpoweredBy) { @@ -504,8 +485,7 @@ } /** - * In JspC this always returns true. - * {@inheritDoc} + * In JspC this always returns true. {@inheritDoc} */ @Override public boolean getDisplaySourceFragment() { @@ -528,8 +508,8 @@ } /** - * Sets the option to issue a compilation error if the class attribute - * specified in useBean action is invalid. + * Sets the option to issue a compilation error if the class attribute specified in useBean action is invalid. + * * @param b New value */ public void setErrorOnUseBeanInvalidClassAttribute(boolean b) { @@ -547,10 +527,11 @@ /** * Sets the option to include debug information in compiled class. + * * @param b New value */ - public void setClassDebugInfo( boolean b ) { - classDebugInfo=b; + public void setClassDebugInfo(boolean b) { + classDebugInfo = b; } @Override @@ -566,6 +547,7 @@ /** * Sets the option to enable caching. + * * @param caching New value * * @see Options#isCaching() @@ -575,13 +557,12 @@ } @Override - public Map getCache() { + public Map getCache() { return cache; } /** - * In JspC this always returns 0. - * {@inheritDoc} + * In JspC this always returns 0. {@inheritDoc} */ @Override public int getCheckInterval() { @@ -589,8 +570,7 @@ } /** - * In JspC this always returns 0. - * {@inheritDoc} + * In JspC this always returns 0. {@inheritDoc} */ @Override public int getModificationTestInterval() { @@ -599,8 +579,7 @@ /** - * In JspC this always returns false. - * {@inheritDoc} + * In JspC this always returns false. {@inheritDoc} */ @Override public boolean getRecompileOnFail() { @@ -609,8 +588,7 @@ /** - * In JspC this always returns false. - * {@inheritDoc} + * In JspC this always returns false. {@inheritDoc} */ @Override public boolean getDevelopment() { @@ -624,6 +602,7 @@ /** * Sets smapSuppressed flag. + * * @param smapSuppressed New value */ public void setSmapSuppressed(boolean smapSuppressed) { @@ -637,6 +616,7 @@ /** * Sets smapDumped flag. + * * @param smapDumped New value * * @see Options#isSmapDumped() @@ -647,11 +627,9 @@ /** - * Determines whether text strings are to be generated as char arrays, - * which improves performance in some cases. + * Determines whether text strings are to be generated as char arrays, which improves performance in some cases. * - * @param genStringAsCharArray true if text strings are to be generated as - * char arrays, false otherwise + * @param genStringAsCharArray true if text strings are to be generated as char arrays, false otherwise */ public void setGenStringAsCharArray(boolean genStringAsCharArray) { this.genStringAsCharArray = genStringAsCharArray; @@ -674,12 +652,13 @@ /** * Sets the option to determine what compiler to use. + * * @param c New value * * @see Options#getCompiler() */ public void setCompiler(String c) { - compiler=c; + compiler = c; } @Override @@ -694,6 +673,7 @@ /** * Sets the compiler target VM. + * * @param vm New value * * @see Options#getCompilerTargetVM() @@ -702,17 +682,18 @@ compilerTargetVM = vm; } - @Override + @Override public String getCompilerSourceVM() { - return compilerSourceVM; - } + return compilerSourceVM; + } - /** - * Sets the compiler source VM. - * @param vm New value - * - * @see Options#getCompilerSourceVM() - */ + /** + * Sets the compiler source VM. + * + * @param vm New value + * + * @see Options#getCompilerSourceVM() + */ public void setCompilerSourceVM(String vm) { compilerSourceVM = vm; } @@ -723,8 +704,7 @@ } /** - * Returns the encoding to use for - * java files. The default is UTF-8. + * Returns the encoding to use for java files. The default is UTF-8. * * @return String The encoding */ @@ -734,8 +714,7 @@ } /** - * Sets the encoding to use for - * java files. + * Sets the encoding to use for java files. * * @param encodingName The name, e.g. "UTF-8" */ @@ -754,24 +733,23 @@ @Override public String getClassPath() { - if( classPath != null ) { + if (classPath != null) { return classPath; } return System.getProperty("java.class.path"); } /** - * Sets the classpath used while compiling the servlets generated from JSP - * files - * @param s New value + * Sets the classpath used while compiling the servlets generated from JSP files + * + * @param s New value */ public void setClassPath(String s) { - classPath=s; + classPath = s; } /** - * Returns the list of file extensions - * that are treated as JSP files. + * Returns the list of file extensions that are treated as JSP files. * * @return The list of extensions */ @@ -780,14 +758,13 @@ } /** - * Adds the given file extension to the - * list of extensions handled as JSP files. + * Adds the given file extension to the list of extensions handled as JSP files. * * @param extension The extension to add, e.g. "myjsp" */ protected void addExtension(final String extension) { - if(extension != null) { - if(extensions == null) { + if (extension != null) { + if (extensions == null) { extensions = new ArrayList<>(); } @@ -796,33 +773,32 @@ } /** - * Base dir for the webapp. Used to generate class names and resolve - * includes. + * Base dir for the webapp. Used to generate class names and resolve includes. + * * @param s New value */ - public void setUriroot( String s ) { + public void setUriroot(String s) { if (s == null) { uriRoot = null; return; } try { uriRoot = resolveFile(s).getCanonicalPath(); - } catch( Exception ex ) { + } catch (Exception e) { uriRoot = s; } } /** - * Parses comma-separated list of JSP files to be processed. If the argument - * is null, nothing is done. - * - *

          Each file is interpreted relative to uriroot, unless it is absolute, - * in which case it must start with uriroot.

          + * Parses comma-separated list of JSP files to be processed. If the argument is null, nothing is done. + *

          + * Each file is interpreted relative to uriroot, unless it is absolute, in which case it must start with uriroot. + *

          * * @param jspFiles Comma-separated list of JSP files to be processed */ public void setJspFiles(final String jspFiles) { - if(jspFiles == null) { + if (jspFiles == null) { return; } @@ -837,18 +813,17 @@ * * @param b Flag value */ - public void setCompile( final boolean b ) { + public void setCompile(final boolean b) { compile = b; } /** - * Sets the verbosity level. The actual number doesn't - * matter: if it's greater than zero, the verbose flag will - * be true. + * Sets the verbosity level. The actual number doesn't matter: if it's greater than zero, the verbose flag will be + * true. * * @param level Positive means verbose */ - public void setVerbose( final int level ) { + public void setVerbose(final int level) { if (level > 0) { verbose = true; showSuccess = true; @@ -856,7 +831,7 @@ } } - public void setValidateTld( boolean b ) { + public void setValidateTld(boolean b) { this.validateTld = b; } @@ -864,7 +839,7 @@ return validateTld; } - public void setValidateXml( boolean b ) { + public void setValidateXml(boolean b) { this.validateXml = b; } @@ -872,7 +847,7 @@ return validateXml; } - public void setBlockExternal( boolean b ) { + public void setBlockExternal(boolean b) { this.blockExternal = b; } @@ -880,7 +855,7 @@ return blockExternal; } - public void setStrictQuoteEscaping( boolean b ) { + public void setStrictQuoteEscaping(boolean b) { this.strictQuoteEscaping = b; } @@ -918,94 +893,91 @@ throw new BuildException(Localizer.getMessage("jspc.error.parseThreadCount", threadCount)); } if (newThreadCount < 1) { - throw new BuildException(Localizer.getMessage( - "jspc.error.minThreadCount", Integer.valueOf(newThreadCount))); + throw new BuildException( + Localizer.getMessage("jspc.error.minThreadCount", Integer.valueOf(newThreadCount))); } this.threadCount = newThreadCount; } - public void setListErrors( boolean b ) { + public void setListErrors(boolean b) { listErrors = b; } - public void setOutputDir( String s ) { - if( s!= null ) { + public void setOutputDir(String s) { + if (s != null) { scratchDir = resolveFile(s).getAbsoluteFile(); } else { - scratchDir=null; + scratchDir = null; } } /** * Sets the package name to be used for the generated servlet classes. + * * @param p New value */ - public void setPackage( String p ) { - targetPackage=p; + public void setPackage(String p) { + targetPackage = p; } /** - * Class name of the generated file ( without package ). - * Can only be used if a single file is converted. - * XXX Do we need this feature ? + * Class name of the generated file ( without package ). Can only be used if a single file is converted. XXX Do we + * need this feature ? + * * @param p New value */ - public void setClassName( String p ) { - targetClassName=p; + public void setClassName(String p) { + targetClassName = p; } /** - * File where we generate configuration with the class definitions to be - * included in a web.xml file. + * File where we generate configuration with the class definitions to be included in a web.xml file. + * * @param s New value */ - public void setWebXmlInclude( String s ) { - webxmlFile=resolveFile(s).getAbsolutePath(); - webxmlLevel=INC_WEBXML; + public void setWebXmlInclude(String s) { + webxmlFile = resolveFile(s).getAbsolutePath(); + webxmlLevel = INC_WEBXML; } /** - * File where we generate a complete web-fragment.xml with the class - * definitions. + * File where we generate a complete web-fragment.xml with the class definitions. + * * @param s New value */ - public void setWebFragmentXml( String s ) { - webxmlFile=resolveFile(s).getAbsolutePath(); - webxmlLevel=FRG_WEBXML; + public void setWebFragmentXml(String s) { + webxmlFile = resolveFile(s).getAbsolutePath(); + webxmlLevel = FRG_WEBXML; } /** * File where we generate a complete web.xml with the class definitions. + * * @param s New value */ - public void setWebXml( String s ) { - webxmlFile=resolveFile(s).getAbsolutePath(); - webxmlLevel=ALL_WEBXML; + public void setWebXml(String s) { + webxmlFile = resolveFile(s).getAbsolutePath(); + webxmlLevel = ALL_WEBXML; } /** * Sets the encoding to be used to read and write web.xml files. - * *

          * If not specified, defaults to UTF-8. *

          * - * @param encoding - * Encoding, e.g. "UTF-8". + * @param encoding Encoding, e.g. "UTF-8". */ public void setWebXmlEncoding(String encoding) { webxmlEncoding = encoding; } /** - * Sets the option to merge generated web.xml fragment into the - * WEB-INF/web.xml file of the web application that we were processing. + * Sets the option to merge generated web.xml fragment into the WEB-INF/web.xml file of the web application that we + * were processing. * - * @param b - * true to merge the fragment into the existing - * web.xml file of the processed web application - * ({uriroot}/WEB-INF/web.xml), false to keep the - * generated web.xml fragment + * @param b true to merge the fragment into the existing web.xml file of the processed web application + * ({uriroot}/WEB-INF/web.xml), false to keep the generated web.xml fragment */ public void setAddWebXmlMappings(boolean b) { addWebXmlMappings = b; @@ -1013,6 +985,7 @@ /** * Sets the option that throws an exception in case of a compilation error. + * * @param b New value */ public void setFailOnError(final boolean b) { @@ -1020,8 +993,16 @@ } /** - * @return true if an exception will be thrown - * in case of a compilation error. + * Sets the set of custom tags to use nonstandard optimizations. + * + * @param useNonstandardTagOptimizations which tags to override + */ + public void setUseNonstandardTagOptimizations(String useNonstandardTagOptimizations) { + this.useNonstandardTagOptimizations = useNonstandardTagOptimizations; + } + + /** + * @return true if an exception will be thrown in case of a compilation error. */ public boolean getFailOnError() { return failOnError; @@ -1041,8 +1022,7 @@ /** * {@inheritDoc} *

          - * Hard-coded to {@code false} for pre-compiled code to enable repeatable - * builds. + * Hard-coded to {@code false} for pre-compiled code to enable repeatable builds. */ @Override public boolean getGeneratedJavaAddTimestamp() { @@ -1051,19 +1031,14 @@ /** - * Adds servlet declaration and mapping for the JSP page servlet to the - * generated web.xml fragment. + * Adds servlet declaration and mapping for the JSP page servlet to the generated web.xml fragment. + * + * @param file Context-relative path to the JSP file, e.g. /index.jsp + * @param clctxt Compilation context of the servlet * - * @param file - * Context-relative path to the JSP file, e.g. - * /index.jsp - * @param clctxt - * Compilation context of the servlet * @throws IOException An IO error occurred */ - public void generateWebMapping( String file, JspCompilationContext clctxt ) - throws IOException - { + public void generateWebMapping(String file, JspCompilationContext clctxt) throws IOException { if (log.isDebugEnabled()) { log.debug(Localizer.getMessage("jspc.generatingMapping", file, clctxt)); } @@ -1072,14 +1047,14 @@ String packageName = clctxt.getServletPackageName(); String thisServletName; - if (packageName.isEmpty()) { + if (packageName.isEmpty()) { thisServletName = className; } else { thisServletName = packageName + '.' + className; } if (servletout != null) { - synchronized(servletout) { + synchronized (servletout) { servletout.write("\n \n "); servletout.write(thisServletName); servletout.write("\n "); @@ -1088,7 +1063,7 @@ } } if (mappingout != null) { - synchronized(mappingout) { + synchronized (mappingout) { mappingout.write("\n \n "); mappingout.write(thisServletName); mappingout.write("\n "); @@ -1100,6 +1075,7 @@ /** * Include the generated web.xml inside the webapp's web.xml. + * * @throws IOException An IO error occurred */ protected void mergeIntoWebXml() throws IOException { @@ -1107,14 +1083,11 @@ File webappBase = new File(uriRoot); File webXml = new File(webappBase, "WEB-INF/web.xml"); File webXml2 = new File(webappBase, "WEB-INF/web2.xml"); - String insertStartMarker = - Localizer.getMessage("jspc.webinc.insertStart"); - String insertEndMarker = - Localizer.getMessage("jspc.webinc.insertEnd"); + String insertStartMarker = Localizer.getMessage("jspc.webinc.insertStart"); + String insertEndMarker = Localizer.getMessage("jspc.webinc.insertEnd"); try (BufferedReader reader = new BufferedReader(openWebxmlReader(webXml)); - BufferedReader fragmentReader = - new BufferedReader(openWebxmlReader(new File(webxmlFile))); + BufferedReader fragmentReader = new BufferedReader(openWebxmlReader(new File(webxmlFile))); PrintWriter writer = new PrintWriter(openWebxmlWriter(webXml2))) { // Insert the and declarations @@ -1152,10 +1125,9 @@ } } } - current = reader.read(); - while (current == '\n' || current == '\r') { + do { current = reader.read(); - } + } while (current == '\n' || current == '\r'); continue; } else { writer.write(element); @@ -1167,10 +1139,9 @@ } } - try (FileInputStream fis = new FileInputStream(webXml2); - FileOutputStream fos = new FileOutputStream(webXml)) { + try (FileInputStream fis = new FileInputStream(webXml2); FileOutputStream fos = new FileOutputStream(webXml)) { - byte buf[] = new byte[512]; + byte[] buf = new byte[512]; while (true) { int n = fis.read(buf); if (n < 0) { @@ -1180,9 +1151,8 @@ } } - if(!webXml2.delete() && log.isDebugEnabled()) { - log.debug(Localizer.getMessage("jspc.delete.fail", - webXml2.toString())); + if (!webXml2.delete() && log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jspc.delete.fail", webXml2.toString())); } if (!(new File(webxmlFile)).delete() && log.isDebugEnabled()) { @@ -1245,12 +1215,11 @@ scratchDir = new File(temp).getAbsoluteFile(); } - String jspUri=file.replace('\\','/'); - JspCompilationContext clctxt = new JspCompilationContext - ( jspUri, this, context, null, rctxt ); + String jspUri = file.replace('\\', '/'); + JspCompilationContext clctxt = new JspCompilationContext(jspUri, this, context, null, rctxt); /* Override the defaults */ - if ((targetClassName != null) && (targetClassName.length() > 0)) { + if ((targetClassName != null) && (!targetClassName.isEmpty())) { clctxt.setServletClassName(targetClassName); targetClassName = null; } @@ -1270,7 +1239,7 @@ // .jsp file is newer than .class file; // Otherwise only generate .java, if .jsp file is newer than // the .java file - if( clc.isOutDated(compile) ) { + if (clc.isOutDated(compile)) { if (log.isDebugEnabled()) { log.debug(Localizer.getMessage("jspc.outdated", jspUri)); } @@ -1279,27 +1248,23 @@ } // Generate mapping - generateWebMapping( file, clctxt ); - if ( showSuccess ) { + generateWebMapping(file, clctxt); + if (showSuccess) { log.info(Localizer.getMessage("jspc.built", file)); } } catch (JasperException je) { Throwable rootCause = je; - while (rootCause instanceof JasperException - && ((JasperException) rootCause).getRootCause() != null) { + while (rootCause instanceof JasperException && ((JasperException) rootCause).getRootCause() != null) { rootCause = ((JasperException) rootCause).getRootCause(); } if (rootCause != je) { - log.error(Localizer.getMessage("jspc.error.generalException", - file), - rootCause); + log.error(Localizer.getMessage("jspc.error.generalException", file), rootCause); } throw je; } catch (Exception e) { if ((e instanceof FileNotFoundException) && log.isWarnEnabled()) { - log.warn(Localizer.getMessage("jspc.error.fileDoesNotExist", - e.getMessage())); + log.warn(Localizer.getMessage("jspc.error.fileDoesNotExist", e.getMessage())); } throw new JasperException(e); } finally { @@ -1311,9 +1276,8 @@ /** - * Locate all jsp files in the webapp. Used if no explicit jsps are - * specified. Scan is performed via the ServletContext and will include any - * JSPs located in resource JARs. + * Locate all jsp files in the webapp. Used if no explicit jsps are specified. Scan is performed via the + * ServletContext and will include any JSPs located in resource JARs. */ public void scanFiles() { // Make sure default extensions are always included @@ -1348,20 +1312,19 @@ */ @Override public void execute() { - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug(Localizer.getMessage("jspc.start", Integer.toString(pages.size()))); } try { if (uriRoot == null) { - if (pages.size() == 0) { + if (pages.isEmpty()) { throw new JasperException(Localizer.getMessage("jsp.error.jspc.missingTarget")); } String firstJsp = pages.get(0); File firstJspF = new File(firstJsp); if (!firstJspF.exists()) { - throw new JasperException(Localizer.getMessage( - "jspc.error.fileDoesNotExist", firstJsp)); + throw new JasperException(Localizer.getMessage("jspc.error.fileDoesNotExist", firstJsp)); } locateUriRoot(firstJspF); } @@ -1383,7 +1346,7 @@ } // No explicit pages, we'll process all .jsp in the webapp - if (pages.size() == 0) { + if (pages.isEmpty()) { scanFiles(); } else { // Ensure pages are all relative to the uriRoot. @@ -1399,8 +1362,7 @@ } if (!fjsp.exists()) { if (log.isWarnEnabled()) { - log.warn(Localizer.getMessage( - "jspc.error.fileDoesNotExist", fjsp.toString())); + log.warn(Localizer.getMessage("jspc.error.fileDoesNotExist", fjsp.toString())); } continue; } @@ -1447,7 +1409,7 @@ errorCount++; log.error(Localizer.getMessage("jspc.error.compilation"), e); } - } catch (InterruptedException e) { + } catch (InterruptedException ignore) { // Ignore } } @@ -1459,11 +1421,10 @@ } long time = System.currentTimeMillis() - start; - String msg = Localizer.getMessage("jspc.generation.result", - Integer.toString(errorCount), Long.toString(time)); + String msg = + Localizer.getMessage("jspc.generation.result", Integer.toString(errorCount), Long.toString(time)); if (failOnError && errorCount > 0) { - System.out.println(Localizer.getMessage( - "jspc.errorCount", Integer.valueOf(errorCount))); + System.out.println(Localizer.getMessage("jspc.errorCount", Integer.valueOf(errorCount))); throw new BuildException(msg); } else { log.info(msg); @@ -1492,8 +1453,7 @@ // ==================== protected utility methods ==================== protected String nextArg() { - if ((argPos >= args.length) - || (fullstop = SWITCH_FULL_STOP.equals(args[argPos]))) { + if ((argPos >= args.length) || (fullstop = SWITCH_FULL_STOP.equals(args[argPos]))) { return null; } else { return args[argPos++]; @@ -1526,9 +1486,9 @@ mapout.write(Localizer.getMessage("jspc.webxml.header", webxmlEncoding)); mapout.flush(); } else if (webxmlLevel >= FRG_WEBXML) { - mapout.write(Localizer.getMessage("jspc.webfrg.header", webxmlEncoding)); - mapout.flush(); - } else if ((webxmlLevel>= INC_WEBXML) && !addWebXmlMappings) { + mapout.write(Localizer.getMessage("jspc.webfrg.header", webxmlEncoding)); + mapout.flush(); + } else if ((webxmlLevel >= INC_WEBXML) && !addWebXmlMappings) { mapout.write(Localizer.getMessage("jspc.webinc.header")); mapout.flush(); } @@ -1548,13 +1508,13 @@ if (webxmlLevel >= ALL_WEBXML) { mapout.write(Localizer.getMessage("jspc.webxml.footer")); } else if (webxmlLevel >= FRG_WEBXML) { - mapout.write(Localizer.getMessage("jspc.webfrg.footer")); + mapout.write(Localizer.getMessage("jspc.webfrg.footer")); } else if ((webxmlLevel >= INC_WEBXML) && !addWebXmlMappings) { mapout.write(Localizer.getMessage("jspc.webinc.footer")); } mapout.close(); - } catch (IOException ioe) { - // nothing to do if it fails since we are done with it + } catch (IOException ignore) { + // Nothing to do if it fails since we are done with it. } } } @@ -1570,20 +1530,18 @@ } - protected TldScanner newTldScanner(JspCServletContext context, boolean namespaceAware, - boolean validate, boolean blockExternal) { + protected TldScanner newTldScanner(JspCServletContext context, boolean namespaceAware, boolean validate, + boolean blockExternal) { return new TldScanner(context, namespaceAware, validate, blockExternal); } - protected void initServletContext(ClassLoader classLoader) - throws IOException, JasperException { + protected void initServletContext(ClassLoader classLoader) throws IOException, JasperException { // TODO: should we use the Ant Project's log? PrintWriter log = new PrintWriter(System.out); URL resourceBase = new File(uriRoot).getCanonicalFile().toURI().toURL(); - context = new JspCServletContext(log, resourceBase, classLoader, - isValidateXml(), isBlockExternal()); + context = new JspCServletContext(log, resourceBase, classLoader, isValidateXml(), isBlockExternal()); if (isValidateTld()) { context.setInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM, "true"); } @@ -1596,8 +1554,7 @@ } catch (SAXException e) { throw new JasperException(e); } - tldCache = new TldCache(context, scanner.getUriTldResourcePathMap(), - scanner.getTldResourcePathTaglibXmlMap()); + tldCache = new TldCache(context, scanner.getUriTldResourcePathMap(), scanner.getTldResourcePathTaglibXmlMap()); context.setAttribute(TldCache.SERVLET_CONTEXT_ATTRIBUTE_NAME, tldCache); rctxt = new JspRuntimeContext(context, this); jspConfig = new JspConfig(context); @@ -1605,9 +1562,10 @@ } /** - * Initializes the classloader as/if needed for the given - * compilation context. + * Initializes the classloader as/if needed for the given compilation context. + * * @return the classloader that will be used + * * @throws IOException If an error occurs */ protected ClassLoader initClassLoader() throws IOException { @@ -1616,14 +1574,12 @@ ClassLoader jspcLoader = getClass().getClassLoader(); if (jspcLoader instanceof AntClassLoader) { - classPath += File.pathSeparator - + ((AntClassLoader) jspcLoader).getClasspath(); + classPath += File.pathSeparator + ((AntClassLoader) jspcLoader).getClasspath(); } // Turn the classPath into URLs List urls = new ArrayList<>(); - StringTokenizer tokenizer = new StringTokenizer(classPath, - File.pathSeparator); + StringTokenizer tokenizer = new StringTokenizer(classPath, File.pathSeparator); while (tokenizer.hasMoreTokens()) { String path = tokenizer.nextToken(); try { @@ -1642,8 +1598,7 @@ File classes = new File(webappBase, "/WEB-INF/classes"); try { if (classes.exists()) { - classPath = classPath + File.pathSeparator - + classes.getCanonicalPath(); + classPath = classPath + File.pathSeparator + classes.getCanonicalPath(); urls.add(classes.getCanonicalFile().toURI().toURL()); } } catch (IOException ioe) { @@ -1688,12 +1643,12 @@ } /** - * Find the WEB-INF dir by looking up in the directory tree. - * This is used if no explicit docbase is set, but only files. + * Find the WEB-INF dir by looking up in the directory tree. This is used if no explicit docbase is set, but only + * files. * * @param f The path from which it will start looking */ - protected void locateUriRoot( File f ) { + protected void locateUriRoot(File f) { String tUriBase = uriBase; if (tUriBase == null) { tUriBase = "/"; @@ -1707,9 +1662,7 @@ uriRoot = f.getCanonicalPath(); uriBase = tUriBase; if (log.isInfoEnabled()) { - log.info(Localizer.getMessage( - "jspc.implicit.uriRoot", - uriRoot)); + log.info(Localizer.getMessage("jspc.implicit.uriRoot", uriRoot)); } break; } @@ -1733,53 +1686,53 @@ uriRoot = froot.getCanonicalPath(); } } - } catch (IOException ioe) { + } catch (IOException ignore) { // Missing uriRoot will be handled in the caller. } } /** - * Resolves the relative or absolute pathname correctly - * in both Ant and command-line situations. If Ant launched - * us, we should use the basedir of the current project - * to resolve relative paths. - * - * See Bugzilla 35571. + * Resolves the relative or absolute pathname correctly in both Ant and command-line situations. If Ant launched us, + * we should use the basedir of the current project to resolve relative paths. See Bugzilla 35571. * * @param s The file + * * @return The file resolved */ - protected File resolveFile(final String s) { - if(getProject() == null) { - // Note FileUtils.getFileUtils replaces FileUtils.newFileUtils in Ant 1.6.3 - return FileUtils.getFileUtils().resolveFile(null, s); - } else { - return FileUtils.getFileUtils().resolveFile(getProject().getBaseDir(), s); - } - } + protected File resolveFile(final String s) { + if (getProject() == null) { + // Note FileUtils.getFileUtils replaces FileUtils.newFileUtils in Ant 1.6.3 + return FileUtils.getFileUtils().resolveFile(null, s); + } else { + return FileUtils.getFileUtils().resolveFile(getProject().getBaseDir(), s); + } + } private Reader openWebxmlReader(File file) throws IOException { FileInputStream fis = new FileInputStream(file); try { - return webxmlEncoding != null ? new InputStreamReader(fis, - webxmlEncoding) : new InputStreamReader(fis); - } catch (IOException ex) { + return webxmlEncoding != null ? new InputStreamReader(fis, webxmlEncoding) : new InputStreamReader(fis); + } catch (IOException ioe) { fis.close(); - throw ex; + throw ioe; } } private Writer openWebxmlWriter(File file) throws IOException { FileOutputStream fos = new FileOutputStream(file); try { - return webxmlEncoding != null ? new OutputStreamWriter(fos, - webxmlEncoding) : new OutputStreamWriter(fos); - } catch (IOException ex) { + return webxmlEncoding != null ? new OutputStreamWriter(fos, webxmlEncoding) : new OutputStreamWriter(fos); + } catch (IOException ioe) { fos.close(); - throw ex; + throw ioe; } } + @Override + public String getUseNonstandardTagOptimizations() { + return useNonstandardTagOptimizations; + } + private class ProcessFile implements Callable { private final String file; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/JspCompilationContext.java tomcat10-10.1.52/java/org/apache/jasper/JspCompilationContext.java --- tomcat10-10.1.34/java/org/apache/jasper/JspCompilationContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/JspCompilationContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,18 +43,9 @@ import org.apache.tomcat.util.descriptor.tld.TldResourcePath; /** - * A place holder for various things that are used through out the JSP - * engine. This is a per-request/per-context data structure. Some of - * the instance variables are set at different points. - * - * Most of the path-related stuff is here - mangling names, versions, dirs, - * loading resources and dealing with uris. - * - * @author Anil K. Vijendran - * @author Harish Prabandham - * @author Pierre Delisle - * @author Costin Manolache - * @author Kin-man Chung + * A placeholder for various things that are used throughout the JSP engine. This is a per-request/per-context data + * structure. Some of the instance variables are set at different points. Most of the path-related stuff is here - + * mangling names, versions, dirs, loading resources and dealing with uris. */ public class JspCompilationContext { @@ -94,21 +85,18 @@ private Jar tagJar; // jspURI _must_ be relative to the context - public JspCompilationContext(String jspUri, Options options, - ServletContext context, JspServletWrapper jsw, + public JspCompilationContext(String jspUri, Options options, ServletContext context, JspServletWrapper jsw, JspRuntimeContext rctxt) { this(jspUri, null, options, context, jsw, rctxt, null, false); } - public JspCompilationContext(String tagfile, TagInfo tagInfo, - Options options, ServletContext context, JspServletWrapper jsw, - JspRuntimeContext rctxt, Jar tagJar) { + public JspCompilationContext(String tagfile, TagInfo tagInfo, Options options, ServletContext context, + JspServletWrapper jsw, JspRuntimeContext rctxt, Jar tagJar) { this(tagfile, tagInfo, options, context, jsw, rctxt, tagJar, true); } - private JspCompilationContext(String jspUri, TagInfo tagInfo, - Options options, ServletContext context, JspServletWrapper jsw, - JspRuntimeContext rctxt, Jar tagJar, boolean isTagFile) { + private JspCompilationContext(String jspUri, TagInfo tagInfo, Options options, ServletContext context, + JspServletWrapper jsw, JspRuntimeContext rctxt, Jar tagJar, boolean isTagFile) { this.jspUri = canonicalURI(jspUri); this.options = options; @@ -146,7 +134,7 @@ * @return the classpath that is passed off to the Java compiler. */ public String getClassPath() { - if( classPath != null ) { + if (classPath != null) { return classPath; } return rctxt.getClassPath(); @@ -154,6 +142,7 @@ /** * The classpath that is passed off to the Java compiler. + * * @param classPath The class path to use */ public void setClassPath(String classPath) { @@ -161,12 +150,12 @@ } /** - * What class loader to use for loading classes while compiling - * this JSP? + * What class loader to use for loading classes while compiling this JSP? + * * @return the class loader used to load all compiled classes */ public ClassLoader getClassLoader() { - if( loader != null ) { + if (loader != null) { return loader; } return rctxt.getParentClassLoader(); @@ -177,9 +166,9 @@ } public ClassLoader getJspLoader() { - if( jspLoader == null ) { - jspLoader = new JasperLoader(new URL[] {baseUrl}, getClassLoader(), - basePackageName, rctxt.getPermissionCollection()); + if (jspLoader == null) { + jspLoader = new JasperLoader(new URL[] { baseUrl }, getClassLoader(), basePackageName, + rctxt.getPermissionCollection()); } return jspLoader; } @@ -189,12 +178,12 @@ } - // ---------- Input/Output ---------- + // ---------- Input/Output ---------- /** - * The output directory to generate code into. The output directory - * is make up of the scratch directory, which is provide in Options, - * plus the directory derived from the package name. + * The output directory to generate code into. The output directory is make up of the scratch directory, which is + * provided in Options, plus the directory derived from the package name. + * * @return the output directory in which the generated sources are placed */ public String getOutputDir() { @@ -206,16 +195,15 @@ } /** - * Create a "Compiler" object based on some init param data. This - * is not done yet. Right now we're just hardcoding the actual - * compilers that are created. + * Create a "Compiler" object based on some init param data. This is not done yet. Right now we're just hardcoding + * the actual compilers that are created. + * * @return the Java compiler wrapper */ public Compiler createCompiler() { - if (jspCompiler != null ) { + if (jspCompiler != null) { return jspCompiler; } - jspCompiler = null; if (options.getCompilerClassName() != null) { jspCompiler = createCompiler(options.getCompilerClassName()); } else { @@ -260,9 +248,10 @@ // ---------- Access resources in the webapp ---------- /** - * Get the full value of a URI relative to this compilations context - * uses current file as the base. + * Get the full value of a URI relative to this compilations context uses current file as the base. + * * @param uri The relative URI + * * @return absolute URI */ public String resolveRelativeUri(String uri) { @@ -276,11 +265,11 @@ } /** - * Gets a resource as a stream, relative to the meanings of this - * context's implementation. + * Gets a resource as a stream, relative to the meanings of this context's implementation. + * * @param res the resource to look for - * @return a null if the resource cannot be found or represented - * as an InputStream. + * + * @return a null if the resource cannot be found or represented as an InputStream. */ public java.io.InputStream getResourceAsStream(String res) { return context.getResourceAsStream(canonicalURI(res)); @@ -297,9 +286,10 @@ } /** - * Gets the actual path of a URI relative to the context of - * the compilation. + * Gets the actual path of a URI relative to the context of the compilation. + * * @param path The webapp path + * * @return the corresponding path in the filesystem */ public String getRealPath(String path) { @@ -310,10 +300,10 @@ } /** - * Returns the JAR file in which the tag file for which this - * JspCompilationContext was created is packaged, or null if this - * JspCompilationContext does not correspond to a tag file, or if the - * corresponding tag file is not packaged in a JAR. + * Returns the JAR file in which the tag file for which this JspCompilationContext was created is packaged, or null + * if this JspCompilationContext does not correspond to a tag file, or if the corresponding tag file is not packaged + * in a JAR. + * * @return a JAR file */ public Jar getTagFileJar() { @@ -327,8 +317,8 @@ /* ==================== Common implementation ==================== */ /** - * Just the class name (does not include package name) of the - * generated class. + * Just the class name (does not include package name) of the generated class. + * * @return the class name */ public String getServletClassName() { @@ -355,8 +345,8 @@ } /** - * Path of the JSP URI. Note that this is not a file name. This is - * the context rooted URI of the JSP file. + * Path of the JSP URI. Note that this is not a file name. This is the context rooted URI of the JSP file. + * * @return the path to the JSP */ public String getJspFile() { @@ -396,20 +386,17 @@ result = uc.getLastModified(); } } - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(Localizer.getMessage( - "jsp.error.lastModified", getJspFile()), e); + log.debug(Localizer.getMessage("jsp.error.lastModified", getJspFile()), ioe); } - result = -1; } finally { if (uc != null) { try { uc.getInputStream().close(); - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(Localizer.getMessage( - "jsp.error.lastModified", getJspFile()), e); + log.debug(Localizer.getMessage("jsp.error.lastModified", getJspFile()), ioe); } result = -1; } @@ -431,10 +418,8 @@ } /** - * @return true if we are compiling a tag file - * in prototype mode. - * ie we only generate codes with class for the tag handler with empty - * method bodies. + * @return true if we are compiling a tag file in prototype mode. ie we only generate codes with class + * for the tag handler with empty method bodies. */ public boolean isPrototypeMode() { return protoTypeMode; @@ -445,9 +430,9 @@ } /** - * Package name for the generated class is made up of the base package - * name, which is user settable, and the derived package name. The - * derived package name directly mirrors the file hierarchy of the JSP page. + * Package name for the generated class is made up of the base package name, which is user settable, and the derived + * package name. The derived package name directly mirrors the file hierarchy of the JSP page. + * * @return the package name */ public String getServletPackageName() { @@ -461,7 +446,7 @@ return packageName; } else { String dPackageName = getDerivedPackageName(); - if (dPackageName.length() == 0) { + if (dPackageName.isEmpty()) { return basePackageName; } return basePackageName + '.' + getDerivedPackageName(); @@ -471,15 +456,13 @@ protected String getDerivedPackageName() { if (derivedPackageName == null) { int iSep = jspUri.lastIndexOf('/'); - derivedPackageName = (iSep > 0) ? - JspUtil.makeJavaPackage(jspUri.substring(1,iSep)) : ""; + derivedPackageName = (iSep > 0) ? JspUtil.makeJavaPackage(jspUri.substring(1, iSep)) : ""; } return derivedPackageName; } /** - * @return The base package name into which all servlet and associated code - * is generated + * @return The base package name into which all servlet and associated code is generated */ public String getBasePackageName() { return basePackageName; @@ -487,6 +470,7 @@ /** * The package name into which the servlet class is generated. + * * @param basePackageName The package name to use */ public void setBasePackageName(String basePackageName) { @@ -494,8 +478,7 @@ } /** - * @return Full path name of the Java file into which the servlet is being - * generated. + * @return Full path name of the Java file into which the servlet is being generated. */ public String getServletJavaFileName() { if (servletJavaFileName == null) { @@ -532,8 +515,7 @@ String tagName = tagInfo.getTagClassName(); javaPath = tagName.replace('.', '/') + ".java"; } else { - javaPath = getServletPackageName().replace('.', '/') + '/' + - getServletClassName() + ".java"; + javaPath = getServletPackageName().replace('.', '/') + '/' + getServletClassName() + ".java"; } return javaPath; } @@ -558,12 +540,13 @@ /** * Gets the 'location' of the TLD associated with the given taglib 'uri'. + * * @param uri The taglib URI - * @return An array of two Strings: The first element denotes the real - * path to the TLD. If the path to the TLD points to a jar file, then the - * second element denotes the name of the TLD entry in the jar file. - * Returns null if the given uri is not associated with any tag library - * 'exposed' in the web application. + * + * @return An array of two Strings: The first element denotes the real path to the TLD. If the path to the TLD + * points to a jar file, then the second element denotes the name of the TLD entry in the jar file. + * Returns null if the given uri is not associated with any tag library 'exposed' in the web + * application. */ public TldResourcePath getTldResourcePath(String uri) { return getOptions().getTldCache().getTldResourcePath(uri); @@ -579,7 +562,7 @@ // ==================== Removal ==================== public void incrementRemoved() { - if (removed == false && rctxt != null) { + if (!removed && rctxt != null) { rctxt.removeWrapper(jspUri); } removed = true; @@ -601,7 +584,6 @@ jspCompiler.removeGeneratedFiles(); jspLoader = null; jspCompiler.compile(); - jsw.setReload(true); jsw.setCompilationException(null); } catch (JasperException ex) { // Cache compilation exception @@ -614,13 +596,13 @@ } catch (FileNotFoundException fnfe) { // Re-throw to let caller handle this - will result in a 404 throw fnfe; - } catch (Exception ex) { - JasperException je = new JasperException( - Localizer.getMessage("jsp.error.unable.compile"), - ex); + } catch (Exception e) { + JasperException je = new JasperException(Localizer.getMessage("jsp.error.unable.compile"), e); // Cache compilation exception jsw.setCompilationException(je); throw je; + } finally { + jsw.setReload(true); } } } @@ -634,11 +616,9 @@ String name = getFQCN(); servletClass = jspLoader.loadClass(name); } catch (ClassNotFoundException cex) { - throw new JasperException(Localizer.getMessage("jsp.error.unable.load"), - cex); - } catch (Exception ex) { - throw new JasperException(Localizer.getMessage("jsp.error.unable.compile"), - ex); + throw new JasperException(Localizer.getMessage("jsp.error.unable.load"), cex); + } catch (Exception e) { + throw new JasperException(Localizer.getMessage("jsp.error.unable.compile"), e); } removed = false; return servletClass; @@ -669,20 +649,20 @@ } protected boolean makeOutputDir() { - synchronized(outputDirLock) { + synchronized (outputDirLock) { File outDirFile = new File(outputDir); return (outDirFile.mkdirs() || outDirFile.isDirectory()); } } protected void createOutputDir() { - String path = null; + String path; if (isTagFile()) { String tagName = tagInfo.getTagClassName(); path = tagName.replace('.', File.separatorChar); path = path.substring(0, path.lastIndexOf(File.separatorChar)); } else { - path = getServletPackageName().replace('.',File.separatorChar); + path = getServletPackageName().replace('.', File.separatorChar); } // Append servlet or tag handler path to scratch dir @@ -699,11 +679,11 @@ } } - protected static final boolean isPathSeparator(char c) { + protected static boolean isPathSeparator(char c) { return (c == '/' || c == '\\'); } - protected static final String canonicalURI(String s) { + protected static String canonicalURI(String s) { if (s == null) { return null; } @@ -712,52 +692,47 @@ int pos = 0; while (pos < len) { char c = s.charAt(pos); - if ( isPathSeparator(c) ) { + if (isPathSeparator(c)) { /* - * multiple path separators. - * 'foo///bar' -> 'foo/bar' + * multiple path separators. 'foo///bar' -> 'foo/bar' */ - while (pos+1 < len && isPathSeparator(s.charAt(pos+1))) { + while (pos + 1 < len && isPathSeparator(s.charAt(pos + 1))) { ++pos; } - if (pos+1 < len && s.charAt(pos+1) == '.') { + if (pos + 1 < len && s.charAt(pos + 1) == '.') { /* * a single dot at the end of the path - we are done. */ - if (pos+2 >= len) { + if (pos + 2 >= len) { break; } - switch (s.charAt(pos+2)) { + switch (s.charAt(pos + 2)) { /* - * self directory in path - * foo/./bar -> foo/bar + * self directory in path foo/./bar -> foo/bar */ case '/': case '\\': pos += 2; continue; - /* - * two dots in a path: go back one hierarchy. - * foo/bar/../baz -> foo/baz - */ + /* + * two dots in a path: go back one hierarchy. foo/bar/../baz -> foo/baz + */ case '.': // only if we have exactly _two_ dots. - if (pos+3 < len && isPathSeparator(s.charAt(pos+3))) { + if (pos + 3 < len && isPathSeparator(s.charAt(pos + 3))) { pos += 3; - int separatorPos = result.length()-1; - while (separatorPos >= 0 && - ! isPathSeparator(result - .charAt(separatorPos))) { + int separatorPos = result.length() - 1; + while (separatorPos >= 0 && !isPathSeparator(result.charAt(separatorPos))) { --separatorPos; } if (separatorPos >= 0) { result.setLength(separatorPos); } continue; - } + } } } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/Options.java tomcat10-10.1.52/java/org/apache/jasper/Options.java --- tomcat10-10.1.34/java/org/apache/jasper/Options.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/Options.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,17 +27,13 @@ /** * A class to hold all init parameters specific to the JSP engine. - * - * @author Anil K. Vijendran - * @author Hans Bergsten - * @author Pierre Delisle */ public interface Options { /** - * Returns true if Jasper issues a compilation error instead of a runtime - * Instantiation error if the class attribute specified in useBean action - * is invalid. + * Returns true if Jasper issues a compilation error instead of a runtime Instantiation error if the class attribute + * specified in useBean action is invalid. + * * @return true to get an error */ boolean getErrorOnUseBeanInvalidClassAttribute(); @@ -48,8 +44,7 @@ boolean getKeepGenerated(); /** - * @return true if tag handler pooling is enabled, - * false otherwise. + * @return true if tag handler pooling is enabled, false otherwise. */ boolean isPoolingEnabled(); @@ -59,8 +54,7 @@ boolean getMappedFile(); /** - * @return true if debug information in included - * in compiled classes. + * @return true if debug information in included in compiled classes. */ boolean getClassDebugInfo(); @@ -70,42 +64,36 @@ int getCheckInterval(); /** - * Main development flag, which enables detailed error reports with - * sources, as well automatic recompilation of JSPs and tag files. - * This setting should usually be false when running - * in production. + * Main development flag, which enables detailed error reports with sources, as well automatic recompilation of JSPs + * and tag files. This setting should usually be false when running in production. + * * @return true if Jasper is in development mode */ boolean getDevelopment(); /** - * @return true to include a source fragment in exception - * messages. + * @return true to include a source fragment in exception messages. */ boolean getDisplaySourceFragment(); /** - * @return true to suppress generation of SMAP info for - * JSR45 debugging. + * @return true to suppress generation of SMAP info for JSR45 debugging. */ boolean isSmapSuppressed(); /** * This setting is ignored if suppressSmap() is true. - * @return true to write SMAP info for JSR45 debugging to a - * file. + * + * @return true to write SMAP info for JSR45 debugging to a file. */ boolean isSmapDumped(); /** - * @return {@link TrimSpacesOption#TRUE} to remove template text that - * consists only of whitespace from the output completely, - * {@link TrimSpacesOption#SINGLE} to replace such template text - * with a single space, {@link TrimSpacesOption#FALSE} to leave - * such template text unchanged or {@link TrimSpacesOption#EXTENDED} - * to remove template text that consists only of whitespace and to - * replace any sequence of whitespace and new lines within template - * text with a single new line. + * @return {@link TrimSpacesOption#TRUE} to remove template text that consists only of whitespace from the output + * completely, {@link TrimSpacesOption#SINGLE} to replace such template text with a single space, + * {@link TrimSpacesOption#FALSE} to leave such template text unchanged or + * {@link TrimSpacesOption#EXTENDED} to remove template text that consists only of whitespace and to + * replace any sequence of whitespace and new lines within template text with a single new line. */ TrimSpacesOption getTrimSpaces(); @@ -121,13 +109,11 @@ /** * Compiler to use. - * *

          - * If null (the default), the java compiler from Eclipse JDT - * project, bundled with Tomcat, will be used. Otherwise, the - * javac task from Apache Ant will be used to call an external - * java compiler and the value of this option will be passed to it. See - * Apache Ant documentation for the possible values. + * If null (the default), the java compiler from Eclipse JDT project, bundled with Tomcat, will be + * used. Otherwise, the javac task from Apache Ant will be used to call an external java compiler and + * the value of this option will be passed to it. See Apache Ant documentation for the possible values. + * * @return the compiler name */ String getCompiler(); @@ -148,14 +134,11 @@ String getCompilerClassName(); /** - * The cache that maps URIs, resource paths and parsed TLD files for the - * various tag libraries 'exposed' by the web application. - * A tag library is 'exposed' either explicitly in - * web.xml or implicitly via the uri tag in the TLD - * of a taglib deployed in a jar file (WEB-INF/lib). + * The cache that maps URIs, resource paths and parsed TLD files for the various tag libraries 'exposed' by the web + * application. A tag library is 'exposed' either explicitly in web.xml or implicitly via the uri tag in the TLD of + * a taglib deployed in a jar file (WEB-INF/lib). * - * @return the instance of the TldCache - * for the web-application. + * @return the instance of the TldCache for the web-application. */ TldCache getTldCache(); @@ -166,10 +149,9 @@ /** * The boolean flag to tell Ant whether to fork JSP page compilations. - * *

          - * Is used only when Jasper uses an external java compiler (wrapped through - * a javac Apache Ant task). + * Is used only when Jasper uses an external java compiler (wrapped through a javac Apache Ant task). + * * @return true to fork a process during compilation */ boolean getFork(); @@ -192,8 +174,7 @@ /** * Indicates whether text strings are to be generated as char arrays. * - * @return true if text strings are to be generated as char - * arrays, false otherwise + * @return true if text strings are to be generated as char arrays, false otherwise */ boolean genStringAsCharArray(); @@ -209,86 +190,79 @@ boolean getRecompileOnFail(); /** - * @return true is caching is enabled - * (used for precompilation). + * @return true is caching is enabled (used for precompilation). */ boolean isCaching(); /** - * The web-application wide cache for the TagLibraryInfo tag library - * descriptors, used if {@link #isCaching()} returns true. - * + * The web-application wide cache for the TagLibraryInfo tag library descriptors, used if {@link #isCaching()} + * returns true. *

          - * Using this cache avoids the cost of repeating the parsing of a tag - * library descriptor XML file (performed by TagLibraryInfoImpl.parseTLD). + * Using this cache avoids the cost of repeating the parsing of a tag library descriptor XML file (performed by + * TagLibraryInfoImpl.parseTLD). *

          * * @return the Map(String uri, TagLibraryInfo tld) instance. */ - Map getCache(); + Map getCache(); /** - * The maximum number of loaded jsps per web-application. If there are more - * jsps loaded, they will be unloaded. If unset or less than 0, no jsps - * are unloaded. + * The maximum number of loaded jsps per web-application. If there are more jsps loaded, they will be unloaded. If + * unset or less than 0, no jsps are unloaded. + * * @return The JSP count */ int getMaxLoadedJsps(); /** - * @return the idle time in seconds after which a JSP is unloaded. - * If unset or less or equal than 0, no jsps are unloaded. + * @return the idle time in seconds after which a JSP is unloaded. If unset or less or equal than 0, no jsps are + * unloaded. */ int getJspIdleTimeout(); /** - * @return {@code true} if the quote escaping required by section JSP.1.6 of - * the JSP specification should be applied to scriplet expression. + * @return {@code true} if the quote escaping required by section JSP.1.6 of the JSP specification should be applied + * to scriplet expression. */ boolean getStrictQuoteEscaping(); /** - * @return {@code true} if EL expressions used within attributes should have - * the quoting rules in JSP.1.6 applied to the expression. + * @return {@code true} if EL expressions used within attributes should have the quoting rules in JSP.1.6 applied to + * the expression. */ boolean getQuoteAttributeEL(); /** - * @return the name of the variable that will be used in the generated - * JSP code for the expression factory + * @return the name of the variable that will be used in the generated JSP code for the expression factory */ default String getVariableForExpressionFactory() { return "_el_expressionfactory"; } /** - * @return the name of the variable that will be used in the generated - * JSP code for the instance manager + * @return the name of the variable that will be used in the generated JSP code for the instance manager */ default String getVariableForInstanceManager() { return "_jsp_instancemanager"; } /** - * @return {@code true} if tag pooling is disabled with page that uses - * extends. + * @return {@code true} if tag pooling is disabled with page that uses extends. */ default boolean getPoolTagsWithExtends() { return false; } /** - * @return {@code true} if the requirement to have the object - * used in jsp:getProperty action to be previously "introduced" - * to the JSP processor (see JSP.5.3) is enforced. + * @return {@code true} if the requirement to have the object used in jsp:getProperty action to be previously + * "introduced" to the JSP processor (see JSP.5.3) is enforced. */ default boolean getStrictGetProperty() { return true; } /** - * @return {@code true} if the strict white space rules are - * applied. + * @return {@code true} if the strict white space rules are applied. */ default boolean getStrictWhitespace() { return true; @@ -302,9 +276,9 @@ } /** - * _jspService is the name of the method that is called by - * HttpJspBase.service(). This is where most of the code generated - * from JSPs go. + * _jspService is the name of the method that is called by HttpJspBase.service(). This is where most of the code + * generated from JSPs go. + * * @return the method name */ default String getServiceMethodName() { @@ -312,17 +286,15 @@ } /** - * @return ServletContext attribute for classpath. This is tomcat specific. - * Other servlet engines may choose to support this attribute if they - * want to have this JSP engine running on them. + * @return ServletContext attribute for classpath. This is tomcat specific. Other servlet engines may choose to + * support this attribute if they want to have this JSP engine running on them. */ default String getServletClasspathAttribute() { return "org.apache.catalina.jsp_classpath"; } /** - * @return The query parameter that causes the JSP engine to just - * pregenerated the servlet but not invoke it. + * @return The query parameter that causes the JSP engine to just pregenerated the servlet but not invoke it. */ default String getJspPrecompilationQueryParameter() { return "jsp_precompile"; @@ -350,8 +322,7 @@ } /** - * @return {@code true} if the container instance manager will be used - * to create the bean instances + * @return {@code true} if the container instance manager will be used to create the bean instances */ default boolean getUseInstanceManagerForTags() { return false; @@ -359,13 +330,20 @@ /** - * Should the container include the time the file was generated in the - * comments at the start of a Java file generated from a JSP or tag. - * Defaults to {@code true}. + * Should the container include the time the file was generated in the comments at the start of a Java file + * generated from a JSP or tag. Defaults to {@code true}. * * @return {@code true} to include the timestamp, otherwise don't include it */ default boolean getGeneratedJavaAddTimestamp() { return true; } + + /** + * A string containing a comma-separated list of names to which custom tag implementations should be applied. + * Unknown or unused tag entries are harmless. Generally defined via an init parameter on the JspServlet. + * + * @return which tags to use + */ + String getUseNonstandardTagOptimizations(); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/AntCompiler.java tomcat10-10.1.52/java/org/apache/jasper/compiler/AntCompiler.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/AntCompiler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/AntCompiler.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,13 +37,6 @@ /** * Main JSP compiler class. This class uses Ant for compiling. - * - * @author Anil K. Vijendran - * @author Mandar Raje - * @author Pierre Delisle - * @author Kin-man Chung - * @author Remy Maucherat - * @author Mark Roth */ public class AntCompiler extends Compiler { @@ -130,8 +123,8 @@ StringBuilder errorReport = new StringBuilder(); StringBuilder info = new StringBuilder(); - info.append("Compile: javaFileName=" + javaFileName + "\n"); - info.append(" classpath=" + classpath + "\n"); + info.append("Compile: javaFileName=").append(javaFileName).append("\n"); + info.append(" classpath=").append(classpath).append("\n"); // Start capturing the System.err output for this thread SystemLogHandler.setThread(); @@ -143,13 +136,13 @@ // Initializing classpath Path path = new Path(project); path.setPath(System.getProperty("java.class.path")); - info.append(" cp=" + System.getProperty("java.class.path") + "\n"); + info.append(" cp=").append(System.getProperty("java.class.path")).append("\n"); StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator); while (tokenizer.hasMoreElements()) { String pathElement = tokenizer.nextToken(); File repository = new File(pathElement); path.setLocation(repository); - info.append(" cp=" + repository + "\n"); + info.append(" cp=").append(repository).append("\n"); } if (log.isTraceEnabled()) { @@ -160,7 +153,7 @@ Path srcPath = new Path(project); srcPath.setLocation(options.getScratchDir()); - info.append(" work dir=" + options.getScratchDir() + "\n"); + info.append(" work dir=").append(options.getScratchDir()).append("\n"); // Initialize and set java extensions String exts = System.getProperty("java.ext.dirs"); @@ -168,7 +161,7 @@ Path extdirs = new Path(project); extdirs.setPath(exts); javac.setExtdirs(extdirs); - info.append(" extension dir=" + exts + "\n"); + info.append(" extension dir=").append(exts).append("\n"); } // Configure the compiler object @@ -178,29 +171,29 @@ javac.setSrcdir(srcPath); javac.setTempdir(options.getScratchDir()); javac.setFork(ctxt.getOptions().getFork()); - info.append(" srcDir=" + srcPath + "\n"); + info.append(" srcDir=").append(srcPath).append("\n"); // Set the Java compiler to use if (options.getCompiler() != null) { javac.setCompiler(options.getCompiler()); - info.append(" compiler=" + options.getCompiler() + "\n"); + info.append(" compiler=").append(options.getCompiler()).append("\n"); } if (options.getCompilerTargetVM() != null) { javac.setTarget(options.getCompilerTargetVM()); - info.append(" compilerTargetVM=" + options.getCompilerTargetVM() + "\n"); + info.append(" compilerTargetVM=").append(options.getCompilerTargetVM()).append("\n"); } if (options.getCompilerSourceVM() != null) { javac.setSource(options.getCompilerSourceVM()); - info.append(" compilerSourceVM=" + options.getCompilerSourceVM() + "\n"); + info.append(" compilerSourceVM=").append(options.getCompilerSourceVM()).append("\n"); } // Build includes path PatternSet.NameEntry includes = javac.createInclude(); includes.setName(ctxt.getJavaPath()); - info.append(" include=" + ctxt.getJavaPath() + "\n"); + info.append(" include=").append(ctxt.getJavaPath()).append("\n"); BuildException be = null; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/BeanRepository.java tomcat10-10.1.52/java/org/apache/jasper/compiler/BeanRepository.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/BeanRepository.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/BeanRepository.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,6 @@ /** * Repository of {page, request, session, application}-scoped beans - * - * @author Mandar Raje - * @author Remy Maucherat */ public class BeanRepository { @@ -56,7 +53,7 @@ } public Class getBeanType(String bean) throws JasperException { - Class clazz = null; + Class clazz; try { clazz = loader.loadClass(beanTypes.get(bean)); } catch (ClassNotFoundException ex) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/Collector.java tomcat10-10.1.52/java/org/apache/jasper/compiler/Collector.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/Collector.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/Collector.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,11 +20,7 @@ /** * Collect info about the page and nodes, and make them available through the PageInfo object. - * - * @author Kin-man Chung - * @author Mark Roth */ - class Collector { /** diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/Compiler.java tomcat10-10.1.52/java/org/apache/jasper/compiler/Compiler.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/Compiler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/Compiler.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,13 +41,6 @@ /** * Main JSP compiler class. This class uses Ant for compiling. - * - * @author Anil K. Vijendran - * @author Mandar Raje - * @author Pierre Delisle - * @author Kin-man Chung - * @author Remy Maucherat - * @author Mark Roth */ public abstract class Compiler { @@ -108,9 +101,10 @@ */ protected Map generateJava() throws Exception { - long t1, t2, t3, t4; - - t1 = t2 = t3 = t4 = 0; + long t1 = 0; + long t2 = 0; + long t3 = 0; + long t4; if (log.isDebugEnabled()) { t1 = System.currentTimeMillis(); @@ -244,9 +238,9 @@ Generator.generate(writer, this, pageNodes); } - // The writer is only used during the compile, dereference + // The writer is only used during compile, dereference // it in the JspCompilationContext when done to allow it - // to be GC'd and save memory. + // to be GCed and save memory. ctxt.setWriter(null); if (log.isTraceEnabled()) { @@ -276,7 +270,7 @@ ctxt.getRuntimeContext().getSmaps().putAll(smaps); } - // If any proto type .java and .class files was generated, + // If any prototype .java and .class files was generated, // the prototype .java may have been replaced by the current // compilation (if the tag file is self referencing), but the // .class file need to be removed, to make sure that javac would @@ -288,7 +282,7 @@ private ServletWriter setupContextWriter(String javaFileName) throws FileNotFoundException, JasperException { ServletWriter writer; - // Setup the ServletWriter + // Set up the ServletWriter String javaEncoding = ctxt.getOptions().getJavaEncoding(); OutputStreamWriter osw = null; @@ -332,7 +326,7 @@ } /** - * Compile the jsp file from the current engine context. As an side- effect, tag files that are referenced by this + * Compile the jsp file from the current engine context. As a side effect, tag files that are referenced by this * page are also compiled. * * @param compileClass If true, generate both .java and .class file If false, generate only .java file @@ -346,7 +340,7 @@ } /** - * Compile the jsp file from the current engine context. As an side- effect, tag files that are referenced by this + * Compile the jsp file from the current engine context. As a side effect, tag files that are referenced by this * page are also compiled. * * @param compileClass If true, generate both .java and .class file If false, generate only .java file @@ -389,7 +383,7 @@ } // Make sure these object which are only used during the // generation and compilation of the JSP page get - // dereferenced so that they can be GC'd and reduce the + // dereferenced so that they can be GCed and reduce the // memory footprint. tfp = null; errDispatcher = null; @@ -416,7 +410,7 @@ /** * Determine if a compilation is necessary by checking the time stamp of the JSP page with that of the corresponding * .class or .java file. If the page has dependencies, the check is also extended to its dependents, and so on. This - * method can by overridden by a subclasses of Compiler. + * method can be overridden by a subclasses of Compiler. * * @param checkClass If true, check against .class file, if false, check against .java file. * @@ -426,7 +420,7 @@ if (jsw != null && (ctxt.getOptions().getModificationTestInterval() > 0)) { - if (jsw.getLastModificationTest() + (ctxt.getOptions().getModificationTestInterval() * 1000) > System + if (jsw.getLastModificationTest() + (ctxt.getOptions().getModificationTestInterval() * 1000L) > System .currentTimeMillis()) { return false; } @@ -479,7 +473,7 @@ try { String key = include.getKey(); URL includeUrl; - long includeLastModified = 0; + long includeLastModified; if (key.startsWith("jar:jar:")) { // Assume we constructed this correctly int entryStart = key.lastIndexOf("!/"); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/DefaultErrorHandler.java tomcat10-10.1.52/java/org/apache/jasper/compiler/DefaultErrorHandler.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/DefaultErrorHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/DefaultErrorHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Default implementation of ErrorHandler interface. - * - * @author Jan Luehe */ class DefaultErrorHandler implements ErrorHandler { @@ -44,7 +42,7 @@ return; } - Object[] args = null; + Object[] args; StringBuilder buf = new StringBuilder(); for (JavacErrorDetail detail : details) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/ELFunctionMapper.java tomcat10-10.1.52/java/org/apache/jasper/compiler/ELFunctionMapper.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/ELFunctionMapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/ELFunctionMapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,8 +34,6 @@ /** * This class generates functions mappers for the EL expressions in the page. Instead of a global mapper, a mapper is * used for each call to EL evaluator, thus avoiding the prefix overlapping and redefinition issues. - * - * @author Kin-man Chung */ public class ELFunctionMapper { @@ -60,7 +58,7 @@ // Append the declarations to the root node String ds = map.ds.toString(); - if (ds.length() > 0) { + if (!ds.isEmpty()) { Node root = page.getRoot(); @SuppressWarnings("unused") Node unused = new Node.Declaration(map.ss.toString(), null, root); @@ -185,7 +183,7 @@ el.visit(fv); List functions = fv.funcs; - if (functions.size() == 0) { + if (functions.isEmpty()) { return; } @@ -198,13 +196,14 @@ // Generate declaration for the map statically decName = getMapName(); - ss.append("private static org.apache.jasper.runtime.ProtectedFunctionMapper " + decName + ";\n"); + ss.append("private static org.apache.jasper.runtime.ProtectedFunctionMapper ").append(decName) + .append(";\n"); - ds.append(" " + decName + "= "); + ds.append(" ").append(decName).append("= "); ds.append("org.apache.jasper.runtime.ProtectedFunctionMapper"); // Special case if there is only one function in the map - String funcMethod = null; + String funcMethod; if (functions.size() == 1) { funcMethod = ".getMapForFunction"; } else { @@ -219,18 +218,19 @@ if (funcInfo == null) { // Added via Lambda or ImportHandler. EL will expect a // function mapper even if one isn't used so just pass null - ds.append(funcMethod + "(null, null, null, null);\n"); + ds.append(funcMethod).append("(null, null, null, null);\n"); } else { - ds.append(funcMethod + "(\"" + fnQName + "\", " + getCanonicalName(funcInfo.getFunctionClass()) + - ".class, " + '\"' + f.getMethodName() + "\", " + "new Class[] {"); - String params[] = f.getParameters(); + ds.append(funcMethod).append("(\"").append(fnQName).append("\", "); + ds.append(getCanonicalName(funcInfo.getFunctionClass())).append(".class, ").append('\"'); + ds.append(f.getMethodName()).append("\", ").append("new Class[] {"); + String[] params = f.getParameters(); for (int k = 0; k < params.length; k++) { if (k != 0) { ds.append(", "); } int iArray = params[k].indexOf('['); if (iArray < 0) { - ds.append(params[k] + ".class"); + ds.append(params[k]).append(".class"); } else { String baseType = params[k].substring(0, iArray); ds.append("java.lang.reflect.Array.newInstance("); @@ -247,7 +247,7 @@ if (aCount == 1) { ds.append("0).getClass()"); } else { - ds.append("new int[" + aCount + "]).getClass()"); + ds.append("new int[").append(aCount).append("]).getClass()"); } } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/ELNode.java tomcat10-10.1.52/java/org/apache/jasper/compiler/ELNode.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/ELNode.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/ELNode.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,11 +27,8 @@ /** * This class defines internal representation for an EL Expression. It currently only defines functions. It can be * expanded to define all the components of an EL expression, if need to. - * - * @author Kin-man Chung */ - -abstract class ELNode { +public abstract class ELNode { public abstract void accept(Visitor v) throws JasperException; @@ -211,7 +208,7 @@ } public boolean isEmpty() { - return list.size() == 0; + return list.isEmpty(); } /** diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/ELParser.java tomcat10-10.1.52/java/org/apache/jasper/compiler/ELParser.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/ELParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/ELParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,10 +25,7 @@ /** * This class implements a parser for EL expressions. It takes strings of the form xxx${..}yyy${..}zzz etc, and turn it * into a ELNode.Nodes. Currently, it only handles text outside ${..} and functions in ${ ..}. - * - * @author Kin-man Chung */ - public class ELParser { private Token curToken; // current token @@ -47,7 +44,7 @@ private final boolean isDeferredSyntaxAllowedAsLiteral; - private static final String reservedWords[] = { "and", "div", "empty", "eq", "false", "ge", "gt", "instanceof", + private static final String[] reservedWords = { "and", "div", "empty", "eq", "false", "ge", "gt", "instanceof", "le", "lt", "mod", "ne", "not", "null", "or", "true" }; public ELParser(String expression, boolean isDeferredSyntaxAllowedAsLiteral) { @@ -69,7 +66,7 @@ ELParser parser = new ELParser(expression, isDeferredSyntaxAllowedAsLiteral); while (parser.hasNextChar()) { String text = parser.skipUntilEL(); - if (text.length() > 0) { + if (!text.isEmpty()) { parser.expr.add(new ELNode.Text(text)); } ELNode.Nodes elexpr = parser.parseEL(); @@ -234,7 +231,7 @@ if (output == null) { output = new StringBuilder(len + 20); } - output.append(input.substring(lastAppend, i)); + output.append(input, lastAppend, i); lastAppend = i + 1; output.append('\\'); output.append(ch); @@ -244,7 +241,7 @@ if (output == null) { return input; } else { - output.append(input.substring(lastAppend, len)); + output.append(input, lastAppend, len); return output.toString(); } } @@ -289,7 +286,7 @@ if (output == null) { output = new StringBuilder(len + 20); } - output.append(input.substring(lastAppend, i)); + output.append(input, lastAppend, i); lastAppend = i + 1; output.append('\\'); output.append(ch); @@ -298,7 +295,7 @@ if (output == null) { return input; } else { - output.append(input.substring(lastAppend, len)); + output.append(input, lastAppend, len); return output.toString(); } } @@ -471,7 +468,7 @@ */ private static class Char extends Token { - private char ch; + private final char ch; Char(String whiteSpace, char ch) { super(whiteSpace); @@ -499,7 +496,7 @@ */ private static class QuotedString extends Token { - private String value; + private final String value; QuotedString(String whiteSpace, String v) { super(whiteSpace); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/ErrorDispatcher.java tomcat10-10.1.52/java/org/apache/jasper/compiler/ErrorDispatcher.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/ErrorDispatcher.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/ErrorDispatcher.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,9 +36,6 @@ *

          * In the case of a Java compilation error, the compiler error message is parsed into an array of JavacErrorDetail * instances, which is passed on to the configured error handler. - * - * @author Jan Luehe - * @author Kin-man Chung */ public class ErrorDispatcher { @@ -250,7 +247,7 @@ file = where.getFile(); } } else { - // Get the context-relative resource path, so as to not disclose any local file system details + // Get the context-relative resource path, to avoid disclosing any local file system details file = where.getFile(); } line = where.getLineNumber(); @@ -291,7 +288,7 @@ List errors = new ArrayList<>(); StringBuilder errMsgBuf = null; - int lineNum = -1; + int lineNum; JavacErrorDetail javacError = null; BufferedReader reader = new BufferedReader(new StringReader(errMsg)); @@ -300,7 +297,7 @@ * Parse compilation errors. Each compilation error consists of a file path and error line number, followed by a * number of lines describing the error. */ - String line = null; + String line; while ((line = reader.readLine()) != null) { /* @@ -344,7 +341,7 @@ reader.close(); JavacErrorDetail[] errDetails = null; - if (errors.size() > 0) { + if (!errors.isEmpty()) { errDetails = errors.toArray(new JavacErrorDetail[0]); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/ErrorHandler.java tomcat10-10.1.52/java/org/apache/jasper/compiler/ErrorHandler.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/ErrorHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/ErrorHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,14 +22,14 @@ * Interface for handling JSP parse and javac compilation errors. An implementation of this interface may be registered * with the ErrorDispatcher by setting the XXX initialization parameter in the JSP page compiler and execution servlet * in Catalina's web.xml file to the implementation's fully qualified class name. - * - * @author Jan Luehe - * @author Kin-man Chung */ public interface ErrorHandler { /** * Processes the given JSP parse error. + *

          + * It is expected (and Jasper is coded based on this) that calls to this method will always result in a + * {@code JasperException} being thrown. * * @param fname Name of the JSP file in which the parse error occurred * @param line Parse error line number @@ -43,6 +43,9 @@ /** * Processes the given JSP parse error. + *

          + * It is expected (and Jasper is coded based on this) that calls to this method will always result in a + * {@code JasperException} being thrown. * * @param msg Parse error message * @param exception Parse exception @@ -53,6 +56,9 @@ /** * Processes the given javac compilation errors. + *

          + * It is expected (and Jasper is coded based on this) that calls to this method will always result in a + * {@code JasperException} being thrown. * * @param details Array of JavacErrorDetail instances corresponding to the compilation errors * @@ -62,6 +68,9 @@ /** * Processes the given javac error report and exception. + *

          + * It is expected (and Jasper is coded based on this) that calls to this method will always result in a + * {@code JasperException} being thrown. * * @param errorReport Compilation error report * @param exception Compilation exception diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/Generator.java tomcat10-10.1.52/java/org/apache/jasper/compiler/Generator.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/Generator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/Generator.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,6 +34,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.TimeZone; import java.util.regex.Matcher; @@ -50,6 +51,7 @@ import org.apache.jasper.JspCompilationContext; import org.apache.jasper.TrimSpacesOption; import org.apache.jasper.compiler.Node.ChildInfoBase; +import org.apache.jasper.compiler.Node.JspAttribute; import org.apache.jasper.compiler.Node.NamedAttribute; import org.apache.jasper.runtime.JspRuntimeLibrary; import org.apache.juli.logging.Log; @@ -57,11 +59,7 @@ import org.xml.sax.Attributes; /** - * Generate Java source from Nodes - * - * @author Anil K. Vijendran, Danno Ferrin, Mandar Raje, Rajiv Mordani, Pierre Delisle - * @author Tomcat 4.1.x and Tomcat 5: Kin-man Chung, Jan Luehe, Shawn Bayern, Mark Roth, Denis Benoit - * @author Tomcat 6.x: Jacob Hookom, Remy Maucherat + * Generate Java source from Nodes. */ class Generator { @@ -73,6 +71,8 @@ private static final Pattern BLANK_LINE_PATTERN = Pattern.compile("(\\s*(\\n|\\r)+\\s*)"); + private static final String CORE_LIBS_URI = "http://java.sun.com/jsp/jstl/core"; + private final ServletWriter out; private final ArrayList methodsBuffered; @@ -105,6 +105,8 @@ private final ELInterpreter elInterpreter; + private final Set nonstandardCustomTagNames; + private final StringInterpreter stringInterpreter; /** @@ -469,8 +471,8 @@ out.pushIndent(); if (isPoolingEnabled) { - for (int i = 0; i < tagHandlerPoolNames.size(); i++) { - out.printin(tagHandlerPoolNames.get(i)); + for (String tagHandlerPoolName : tagHandlerPoolNames) { + out.printin(tagHandlerPoolName); out.print(" = org.apache.jasper.runtime.TagHandlerPool.getTagHandlerPool("); if (ctxt.isTagFile()) { out.print("config"); @@ -505,8 +507,8 @@ out.pushIndent(); if (isPoolingEnabled) { - for (int i = 0; i < tagHandlerPoolNames.size(); i++) { - out.printin(tagHandlerPoolNames.get(i)); + for (String tagHandlerPoolName : tagHandlerPoolNames) { + out.printin(tagHandlerPoolName); out.println(".release();"); } } @@ -600,7 +602,7 @@ out.println("\");"); } // classes however, may be empty depending on the import declarations - if (classes.size() == 0) { + if (classes.isEmpty()) { out.printin("_jspx_imports_classes = null;"); out.println(); } else { @@ -627,8 +629,8 @@ */ private void genPreambleClassVariableDeclarations() { if (isPoolingEnabled && !tagHandlerPoolNames.isEmpty()) { - for (int i = 0; i < tagHandlerPoolNames.size(); i++) { - out.printil("private org.apache.jasper.runtime.TagHandlerPool " + tagHandlerPoolNames.get(i) + ";"); + for (String tagHandlerPoolName : tagHandlerPoolNames) { + out.printil("private org.apache.jasper.runtime.TagHandlerPool " + tagHandlerPoolName + ";"); } out.println(); } @@ -1086,14 +1088,10 @@ // If any of the params have their values specified by // jsp:attribute, prepare those values first. Node jspBody = findJspBody(n); - if (jspBody != null) { - prepareParams(jspBody); - } else { - prepareParams(n); - } + prepareParams(Objects.requireNonNullElse(jspBody, n)); if (n.getBody() != null) { - generateIncludeWithParameters(n, page, isFlush, pageParam); + generateIncludeWithParameters(n, isFlush, pageParam); } else { generateInclude(n, page, isFlush, pageParam); } @@ -1108,8 +1106,8 @@ out.println(", out, " + isFlush + ");"); } - private void generateIncludeWithParameters(Node.IncludeAction n, Node.JspAttribute page, boolean isFlush, - String pageParam) throws JasperException { + private void generateIncludeWithParameters(Node.IncludeAction n, boolean isFlush, String pageParam) + throws JasperException { // jsp:include contains jsp:param - reuse some calculations String temporaryVariableName = n.getRoot().nextTemporaryVariableName(); String urlVariableName = temporaryVariableName + "_url"; @@ -1119,7 +1117,8 @@ out.printin("String " + requestEncodingVariableName + " = " + REQUEST_CHARACTER_ENCODING_TEXT + ";"); out.println(); out.printin("org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, " + urlVariableName); - printParams(n, urlVariableName, page.isLiteral(), requestEncodingVariableName); + // literal is hard-coded to false for this call since it always uses a variable + printParams(n, urlVariableName, false, requestEncodingVariableName); out.println(", out, " + isFlush + ");"); } @@ -1188,11 +1187,7 @@ // If any of the params have their values specified by // jsp:attribute, prepare those values first. Node jspBody = findJspBody(n); - if (jspBody != null) { - prepareParams(jspBody); - } else { - prepareParams(n); - } + prepareParams(Objects.requireNonNullElse(jspBody, n)); out.printin("_jspx_page_context.forward("); out.print(pageParam); @@ -1320,7 +1315,7 @@ canonicalName = klass; } // Check that there is a 0 arg constructor - Constructor constructor = bean.getConstructor(new Class[] {}); + Constructor constructor = bean.getConstructor(); // Check the bean is public, not an interface, not abstract // and in an exported module int modifiers = bean.getModifiers(); @@ -1505,6 +1500,9 @@ @Override public void visit(Node.CustomTag n) throws JasperException { + if (visitPotentiallyNonstandardCustomTag(n)) { + return; + } // Use plugin to generate more efficient code if there is one. if (n.useTagPlugin()) { @@ -1598,26 +1596,22 @@ VariableInfo[] infos = n.getVariableInfos(); // The Validator always calls setTagData() which ensures infos is // non-null - if (infos.length > 0) { - for (VariableInfo info : infos) { - // A null variable name will trigger multiple compilation - // failures so assume non-null here - pageInfo.getVarInfoNames().add(info.getVarName()); - } + for (VariableInfo info : infos) { + // A null variable name will trigger multiple compilation + // failures so assume non-null here + pageInfo.getVarInfoNames().add(info.getVarName()); } TagVariableInfo[] tagInfos = n.getTagVariableInfos(); // The way Tomcat constructs the TagInfo, getTagVariableInfos() // will never return null. - if (tagInfos.length > 0) { - for (TagVariableInfo tagInfo : tagInfos) { - // tagInfo is always non-null - String name = tagInfo.getNameGiven(); - if (name == null) { - String nameFromAttribute = tagInfo.getNameFromAttribute(); - name = n.getAttributeValue(nameFromAttribute); - } - pageInfo.getVarInfoNames().add(name); + for (TagVariableInfo tagInfo : tagInfos) { + // tagInfo is always non-null + String name = tagInfo.getNameGiven(); + if (name == null) { + String nameFromAttribute = tagInfo.getNameFromAttribute(); + name = n.getAttributeValue(nameFromAttribute); } + pageInfo.getVarInfoNames().add(name); } @@ -1743,11 +1737,11 @@ Map map = new HashMap<>(); // Validator ensures this is non-null Node.JspAttribute[] attrs = n.getJspAttributes(); - for (int i = 0; i < attrs.length; i++) { - String value = null; - String nvp = null; - if (attrs[i].isNamedAttribute()) { - NamedAttribute attr = attrs[i].getNamedAttributeNode(); + for (Node.JspAttribute jspAttribute : attrs) { + String value; + String nvp; + if (jspAttribute.isNamedAttribute()) { + NamedAttribute attr = jspAttribute.getNamedAttributeNode(); Node.JspAttribute omitAttr = attr.getOmit(); String omit; if (omitAttr == null) { @@ -1760,18 +1754,18 @@ continue; } } - value = generateNamedAttributeValue(attrs[i].getNamedAttributeNode()); + value = generateNamedAttributeValue(jspAttribute.getNamedAttributeNode()); if ("\"false\"".equals(omit)) { - nvp = " + \" " + attrs[i].getName() + "=\\\"\" + " + value + " + \"\\\"\""; + nvp = " + \" " + jspAttribute.getName() + "=\\\"\" + " + value + " + \"\\\"\""; } else { - nvp = " + (java.lang.Boolean.valueOf(" + omit + ")?\"\":\" " + attrs[i].getName() + + nvp = " + (java.lang.Boolean.valueOf(" + omit + ")?\"\":\" " + jspAttribute.getName() + "=\\\"\" + " + value + " + \"\\\"\")"; } } else { - value = attributeValue(attrs[i], false, Object.class); - nvp = " + \" " + attrs[i].getName() + "=\\\"\" + " + value + " + \"\\\"\""; + value = attributeValue(jspAttribute, false, Object.class); + nvp = " + \" " + jspAttribute.getName() + "=\\\"\" + " + value + " + \"\\\"\""; } - map.put(attrs[i].getName(), nvp); + map.put(jspAttribute.getName(), nvp); } // Write begin tag, using XML-style 'name' attribute as the @@ -1876,12 +1870,7 @@ int textIndex = 0; int textLength = text.length(); while (textIndex < textLength) { - int len = 0; - if (textLength - textIndex > 16384) { - len = 16384; - } else { - len = textLength - textIndex; - } + int len = Math.min(textLength - textIndex, 16384); String output = text.substring(textIndex, textIndex + len); String charArrayName = textMap.get(output); if (charArrayName == null) { @@ -2066,9 +2055,9 @@ Node.JspAttribute[] attrs = tag.getJspAttributes(); // The TagPluginManager only creates AttributeGenerator nodes for // attributes that are present. - for (int i = 0; i < attrs.length; i++) { - if (attrs[i].getName().equals(n.getName())) { - out.print(evaluateAttribute(getTagHandlerInfo(tag), attrs[i], tag, null)); + for (Node.JspAttribute attr : attrs) { + if (attr.getName().equals(n.getName())) { + out.print(evaluateAttribute(getTagHandlerInfo(tag), attr, tag, null)); break; } } @@ -2121,6 +2110,10 @@ writeNewInstance(tagHandlerVar, tagHandlerClass); } + // Wrap use of tag in try/finally to ensure clean-up takes place + out.printil("try {"); + out.pushIndent(); + // includes setting the context generateSetters(n, tagHandlerVar, handlerInfo, false); @@ -2300,13 +2293,15 @@ out.pushIndent(); out.printin(tagHandlerVar); out.println(".doFinally();"); - } - - if (n.implementsTryCatchFinally()) { out.popIndent(); out.printil("}"); } + // Ensure clean-up takes place + out.popIndent(); + out.printil("} finally {"); + out.pushIndent(); + if (usePooling(n)) { // Print tag reuse out.printin(n.getTagHandlerPoolName()); @@ -2319,6 +2314,8 @@ out.print(tagHandlerVar); out.println(", _jsp_getInstanceManager());"); } + out.popIndent(); + out.printil("}"); // Declare and synchronize AT_END scripting variables (must do this // outside the try/catch/finally block) @@ -2459,7 +2456,7 @@ // JspFragment, because a fragment is always scriptless. // Thus, there is no need to save/ restore/ sync them. // Note, that JspContextWrapper.syncFoo() methods will take - // care of saving/ restoring/ sync'ing of JspContext attributes. + // care of saving/ restoring/ syncing of JspContext attributes. return; } @@ -2531,7 +2528,7 @@ // JspFragment, because a fragment is always scriptless. // Thus, there is no need to save/ restore/ sync them. // Note, that JspContextWrapper.syncFoo() methods will take - // care of saving/ restoring/ sync'ing of JspContext attributes. + // care of saving/ restoring/ syncing of JspContext attributes. return; } @@ -2597,7 +2594,7 @@ // JspFragment, because a fragment is always scriptless. // Thus, there is no need to save/ restore/ sync them. // Note, that JspContextWrapper.syncFoo() methods will take - // care of saving/ restoring/ sync'ing of JspContext attributes. + // care of saving/ restoring/ syncing of JspContext attributes. return; } @@ -2685,8 +2682,8 @@ String localName = attr.getLocalName(); - Method m = null; - Class[] c = null; + Method m; + Class[] c; if (attr.isDynamic()) { c = OBJECT_CLASS; } else { @@ -2755,10 +2752,8 @@ sb.append("))"); // should the expression be evaluated before passing to // the setter? - boolean evaluate = false; - if (tai.canBeRequestTime()) { - evaluate = true; // JSP.2.3.2 - } + boolean evaluate = tai.canBeRequestTime(); + // JSP.2.3.2 if (attr.isDeferredInput()) { evaluate = false; // JSP.2.3.3 } @@ -2993,7 +2988,7 @@ fragmentHelperClass.closeFragment(fragment, methodNesting); // XXX - Need to change pageContext to jspContext if // we're not in a place where pageContext is defined (e.g. - // in a fragment or in a tag file. + // in a fragment or in a tag file). out.print("new " + fragmentHelperClass.getClassName() + "( " + fragment.getId() + ", _jspx_page_context, " + tagHandlerVar + ", " + pushBodyCountVar + ")"); } @@ -3065,6 +3060,216 @@ return varName; } + + /** + * Determines whether a tag should be handled via nonstandard code (typically faster). Considers both + * configuration and level of support within Tomcat. + *

          + * Note that Tomcat is free to ignore any case it cannot handle, as long as it reports it accurately to the + * caller by returning false. For example, the initial implementation for c:set excludes support for body + * content. c:set tags with body content will be generated with the standard code and tags without body content + * will be generated via non-standard code. + * + * @param n tag + * @param jspAttributes jsp attributes + * + * @return whether code was generated + * + * @throws JasperException unexpected error + */ + private boolean visitPotentiallyNonstandardCustomTag(Node.CustomTag n) throws JasperException { + if (!nonstandardCustomTagNames.contains(n.getQName())) { + // tag is not configured, move along + return false; + } + + // collect the attributes into one Map for further checks + Map jspAttributes = new HashMap<>(); + if (n.getJspAttributes() != null) { + for (JspAttribute jspAttr : n.getJspAttributes()) { + jspAttributes.put(jspAttr.getLocalName(), jspAttr); + } + } + switch (n.qName) { + case "c:set": + // requires var and value, scope is optional, body is prohibited, value cannot be deferred + if (n.hasEmptyBody() && jspAttributes.containsKey("var") && jspAttributes.containsKey("value") && + CORE_LIBS_URI.equals(n.getURI())) { + // verify value is not a deferred expression + String valueText = jspAttributes.get("value").getValue(); + if (valueText.startsWith("#")) { + return false; + } else if (jspAttributes.size() == 2 || + (jspAttributes.size() == 3 && jspAttributes.containsKey("scope"))) { + generateNonstandardSetLogic(n, jspAttributes); + return true; + } + } + break; + case "c:remove": + // requires var, scope is optional, body is prohibited + if (n.hasEmptyBody() && jspAttributes.containsKey("var") && CORE_LIBS_URI.equals(n.getURI()) && + (jspAttributes.size() == 1 || + (jspAttributes.size() == 2 && jspAttributes.containsKey("scope")))) { + generateNonstandardRemoveLogic(n, jspAttributes); + return true; + + } + break; + default: + // This indicates someone configured a tag with no non-standard implementation. + // Harmless, fall back to the standard implementation. + } + return false; + } + + private void generateNonstandardSetLogic(Node.CustomTag n, Map jspAttributes) + throws JasperException { + String baseVar = createTagVarName(n.getQName(), n.getPrefix(), n.getLocalName()); + String tagMethod = "_jspx_meth_" + baseVar; + ServletWriter outSave = out; + + // generate method call + out.printin(tagMethod); + out.print("("); + out.print("_jspx_page_context"); + out.println(");"); + GenBuffer genBuffer = new GenBuffer(n, n.getBody()); + methodsBuffered.add(genBuffer); + out = genBuffer.getOut(); + + // Generate code for method declaration + methodNesting++; + out.println(); + out.pushIndent(); + out.printin("private void "); + out.print(tagMethod); + out.println("(jakarta.servlet.jsp.PageContext _jspx_page_context)"); + out.printil(" throws java.lang.Throwable {"); + out.pushIndent(); + // Generated body of method + out.printil("// " + n.getQName()); + + JspAttribute varAttribute = jspAttributes.get("var"); + Mark m = n.getStart(); + out.printil("// " + m.getFile() + "(" + m.getLineNumber() + "," + m.getColumnNumber() + ") " + + varAttribute.getTagAttributeInfo()); + + JspAttribute valueAttribute = jspAttributes.get("value"); + m = n.getStart(); + out.printil("// " + m.getFile() + "(" + m.getLineNumber() + "," + m.getColumnNumber() + ") " + + valueAttribute.getTagAttributeInfo()); + + String varValue = varAttribute.getValue(); + + // get the scope constant at compile-time, where blank means page + String scopeValue = translateScopeToConstant(jspAttributes); + + // translates the specified value attributes into EL-interpretation code using standard logic + String evaluatedAttribute = evaluateAttribute(getTagHandlerInfo(n), valueAttribute, n, null); + + // call the multi-line logic equivalent of SetTag + out.printil("org.apache.jasper.runtime.JspRuntimeLibrary.nonstandardSetTag(_jspx_page_context, \"" + + varValue + "\", " + evaluatedAttribute + ", " + scopeValue + ");"); + + // Generate end of method + out.popIndent(); + out.printil("}"); + out.popIndent(); + + methodNesting--; + // restore previous writer + out = outSave; + } + + /** + * Compile-time translation of the scope variable into the constant equivalent. Avoids runtime evaluation as + * performed by SetTag. Unspecified scope means page. + * + * @param jspAttributes attributes + * + * @return equivalent constant from PageContext + */ + private String translateScopeToConstant(Map jspAttributes) { + String scopeValue; + JspAttribute scopeAttribute = jspAttributes.get("scope"); + if (scopeAttribute == null) { + scopeValue = "jakarta.servlet.jsp.PageContext.PAGE_SCOPE"; + } else { + switch (scopeAttribute.getValue()) { + case "": + case "page": + scopeValue = "jakarta.servlet.jsp.PageContext.PAGE_SCOPE"; + break; + case "request": + scopeValue = "jakarta.servlet.jsp.PageContext.REQUEST_SCOPE"; + break; + case "session": + scopeValue = "jakarta.servlet.jsp.PageContext.SESSION_SCOPE"; + break; + case "application": + scopeValue = "jakarta.servlet.jsp.PageContext.APPLICATION_SCOPE"; + break; + default: + throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); + } + } + return scopeValue; + } + + /** + * Generates the code for a non-standard remove. Note that removes w/o a specified scope will remove from all + * scopes. + * + * @param n tag + * @param jspAttributes attributes + * + * @throws JasperException unspecified error + */ + private void generateNonstandardRemoveLogic(Node.CustomTag n, Map jspAttributes) + throws JasperException { + String baseVar = createTagVarName(n.getQName(), n.getPrefix(), n.getLocalName()); + String tagMethod = "_jspx_meth_" + baseVar; + ServletWriter outSave = out; + + // generate method call + out.printin(tagMethod); + out.print("("); + out.print("_jspx_page_context"); + out.println(");"); + GenBuffer genBuffer = new GenBuffer(n, n.getBody()); + methodsBuffered.add(genBuffer); + out = genBuffer.getOut(); + + // Generate code for method declaration + methodNesting++; + out.println(); + out.pushIndent(); + out.printin("private void "); + out.print(tagMethod); + out.println("(jakarta.servlet.jsp.PageContext pageContext)"); + out.printil(" throws java.lang.Throwable {"); + out.pushIndent(); + // Generated body of method + String varValue = jspAttributes.get("var").getValue(); + JspAttribute scope = jspAttributes.get("scope"); + if (scope == null) { + // c:remove without a scope means remove from all scopes + out.printil("pageContext.removeAttribute(\"" + varValue + "\");"); + } else { + // c:remove with a scope means remove only from the specified scope + String scopeValue = translateScopeToConstant(jspAttributes); + out.printil("pageContext.removeAttribute(\"" + varValue + "\", " + scopeValue + ");"); + } + // Generate end of method + out.popIndent(); + out.printil("}"); + out.popIndent(); + + methodNesting--; + // restore previous writer + out = outSave; + } } private static void generateLocalVariables(ServletWriter out, ChildInfoBase n) { @@ -3207,6 +3412,12 @@ } timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); timestampFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + String nonstandardOptionsList = ctxt.getOptions().getUseNonstandardTagOptimizations(); + if (nonstandardOptionsList == null) { + nonstandardCustomTagNames = Collections.emptySet(); + } else { + nonstandardCustomTagNames = new HashSet<>(Arrays.asList(nonstandardOptionsList.split(","))); + } } /** @@ -3672,11 +3883,11 @@ */ private static class TagHandlerInfo { - private Map methodMaps; + private final Map methodMaps; - private Map> propertyEditorMaps; + private final Map> propertyEditorMaps; - private Class tagHandlerClass; + private final Class tagHandlerClass; /** * Constructor. @@ -3737,13 +3948,13 @@ /* * For a CustomTag, the codes that are generated at the beginning of the tag may not be in the same buffer as * those for the body of the tag. Two fields are used here to keep this straight. For codes that do not - * corresponds to any JSP lines, they should be null. + * correspond to any JSP lines, they should be null. */ - private Node node; + private final Node node; - private Node.Nodes body; + private final Node.Nodes body; - private java.io.CharArrayWriter charWriter; + private final java.io.CharArrayWriter charWriter; protected ServletWriter out; @@ -3822,9 +4033,9 @@ private static class FragmentHelperClass { private static class Fragment { - private GenBuffer genBuffer; + private final GenBuffer genBuffer; - private int id; + private final int id; Fragment(int id, Node node) { this.id = id; @@ -3843,12 +4054,12 @@ // True if the helper class should be generated. private boolean used = false; - private List fragments = new ArrayList<>(); + private final List fragments = new ArrayList<>(); - private String className; + private final String className; // Buffer for entire helper class - private GenBuffer classBuffer = new GenBuffer(); + private final GenBuffer classBuffer = new GenBuffer(); FragmentHelperClass(String className) { this.className = className; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java tomcat10-10.1.52/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/ImplicitTagLibraryInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,8 +44,6 @@ /** * Class responsible for generating an implicit tag library containing tag handlers corresponding to the tag files in * "/WEB-INF/tags/" or a subdirectory of it. - * - * @author Jan Luehe */ class ImplicitTagLibraryInfo extends TagLibraryInfo { @@ -165,7 +163,7 @@ return null; } - TagInfo tagInfo = null; + TagInfo tagInfo; try { tagInfo = TagFileProcessor.parseTagFileDirectives(pc, shortName, path, null, this); } catch (JasperException je) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/JDTCompiler.java tomcat10-10.1.52/java/org/apache/jasper/compiler/JDTCompiler.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/JDTCompiler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/JDTCompiler.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,6 +35,7 @@ import java.util.StringTokenizer; import org.apache.jasper.JasperException; +import org.apache.jasper.runtime.ExceptionUtils; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.eclipse.jdt.core.compiler.IProblem; @@ -51,13 +52,13 @@ import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment; +import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; /** * JDT class compiler. This compiler will load source dependencies from the context classloader, reducing dramatically * disk access during the compilation process. Based on code from Cocoon2. - * - * @author Remy Maucherat */ public class JDTCompiler extends org.apache.jasper.compiler.Compiler { @@ -111,8 +112,8 @@ } result = new char[buf.length()]; buf.getChars(0, result.length, result, 0); - } catch (IOException e) { - log.error(Localizer.getMessage("jsp.error.compilation.source", sourceFile), e); + } catch (IOException ioe) { + log.error(Localizer.getMessage("jsp.error.compilation.source", sourceFile), ioe); } return result; } @@ -141,6 +142,16 @@ public boolean ignoreOptionalProblems() { return false; } + + @Override + public ModuleBinding module(LookupEnvironment environment) { + return environment.getModule(ModuleBinding.UNNAMED); + } + + @Override + public char[] getModuleName() { + return ModuleBinding.UNNAMED; + } } final INameEnvironment env = new INameEnvironment() { @@ -208,13 +219,20 @@ if (result.equals(targetClassName) || result.startsWith(targetClassName + '$')) { return false; } - String resourceName = result.replace('.', '/') + ".class"; - try (InputStream is = classLoader.getResourceAsStream(resourceName)) { - return is == null; - } catch (IOException e) { - // we are here, since close on is failed. That means it was not null - return false; + /* + * This might look heavy-weight but, with only the ClassLoader API available, trying to load the + * resource as a class is the only reliable way found so far to differentiate between a class and a + * package. Other options, such as getResource(), fail for some edge cases on case insensitive file + * systems. As this code is only called at compile time, the performance impact is not a significant + * concern. + */ + try { + classLoader.loadClass(result); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + return true; } + return false; } @Override @@ -330,6 +348,11 @@ // Java 11. // This is checked against the actual version below. settings.put(CompilerOptions.OPTION_Source, "24"); + } else if (opt.equals("25")) { + // Constant not available in latest ECJ version shipped with + // Tomcat. May be supported in a snapshot build. + // This is checked against the actual version below. + settings.put(CompilerOptions.OPTION_Source, "25"); } else { log.warn(Localizer.getMessage("jsp.warning.unknown.sourceVM", opt)); settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_11); @@ -427,6 +450,12 @@ // This is checked against the actual version below. settings.put(CompilerOptions.OPTION_TargetPlatform, "24"); settings.put(CompilerOptions.OPTION_Compliance, "24"); + } else if (opt.equals("25")) { + // Constant not available in latest ECJ version shipped with + // Tomcat. May be supported in a snapshot build. + // This is checked against the actual version below. + settings.put(CompilerOptions.OPTION_TargetPlatform, "25"); + settings.put(CompilerOptions.OPTION_Compliance, "25"); } else { log.warn(Localizer.getMessage("jsp.warning.unknown.targetVM", opt)); settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_11); @@ -477,8 +506,8 @@ } } } - } catch (IOException exc) { - log.error(Localizer.getMessage("jsp.error.compilation.jdt"), exc); + } catch (IOException ioe) { + log.error(Localizer.getMessage("jsp.error.compilation.jdt"), ioe); } } }; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/JavaCompiler.java tomcat10-10.1.52/java/org/apache/jasper/compiler/JavaCompiler.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/JavaCompiler.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/JavaCompiler.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * 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.jasper.compiler; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + +import org.apache.jasper.JasperException; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; + +/** + * Main JSP compiler class. This class uses the Java Compiler API for compiling. + */ +public class JavaCompiler extends Compiler { + + private final Log log = LogFactory.getLog(JavaCompiler.class); // must not be static + + @Override + protected void generateClass(Map smaps) throws JasperException, IOException { + + long t1 = 0; + if (log.isDebugEnabled()) { + t1 = System.currentTimeMillis(); + } + + javax.tools.JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector diagnostics = new DiagnosticCollector<>(); + StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, + Charset.forName(ctxt.getOptions().getJavaEncoding())); + Iterable compilationUnits = + fileManager.getJavaFileObjectsFromFiles(List.of(new File(ctxt.getServletJavaFileName()))); + // Perform Java compilation using the appropriate options + List compilerOptions = List.of("-classpath", ctxt.getClassPath(), "-source", + ctxt.getOptions().getCompilerSourceVM(), "-target", ctxt.getOptions().getCompilerTargetVM()); + Boolean result = + compiler.getTask(null, fileManager, diagnostics, compilerOptions, null, compilationUnits).call(); + + List problemList = new ArrayList<>(); + if (!result.booleanValue()) { + for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { + if (diagnostic.getKind() == Diagnostic.Kind.ERROR) { + try { + problemList.add(ErrorDispatcher.createJavacError(diagnostic.getSource().getName(), pageNodes, + new StringBuilder(diagnostic.getMessage(Locale.getDefault())), + (int) diagnostic.getLineNumber(), ctxt)); + } catch (JasperException e) { + log.error(Localizer.getMessage("jsp.error.compilation.jdtProblemError"), e); + } + } + } + } + + if (!ctxt.keepGenerated()) { + File javaFile = new File(ctxt.getServletJavaFileName()); + if (!javaFile.delete()) { + throw new JasperException(Localizer.getMessage("jsp.warning.compiler.javafile.delete.fail", javaFile)); + } + } + + if (!problemList.isEmpty()) { + JavacErrorDetail[] jeds = problemList.toArray(new JavacErrorDetail[0]); + errDispatcher.javacError(jeds); + } + + if (log.isDebugEnabled()) { + long t2 = System.currentTimeMillis(); + log.debug(Localizer.getMessage("jsp.compiled", ctxt.getServletJavaFileName(), Long.valueOf(t2 - t1))); + } + + if (ctxt.isPrototypeMode()) { + return; + } + + // JSR45 Support + if (!options.isSmapSuppressed()) { + SmapUtil.installSmap(smaps); + } + + } + +} diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/JavacErrorDetail.java tomcat10-10.1.52/java/org/apache/jasper/compiler/JavacErrorDetail.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/JavacErrorDetail.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/JavacErrorDetail.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,9 +29,6 @@ /** * Class providing details about a javac compilation error. - * - * @author Jan Luehe - * @author Kin-man Chung */ public class JavacErrorDetail { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/JspConfig.java tomcat10-10.1.52/java/org/apache/jasper/compiler/JspConfig.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/JspConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/JspConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,11 +30,7 @@ /** * Handles the jsp-config element in WEB_INF/web.xml. This is used for specifying the JSP configuration information on a * JSP page - * - * @author Kin-man Chung - * @author Remy Maucherat */ - public class JspConfig { // Logger @@ -46,7 +42,7 @@ private static final String defaultIsXml = null; // unspecified private String defaultIsELIgnored = null; // unspecified - private String defaultErrorOnELNotFound = "false"; + private static final String defaultErrorOnELNotFound = "false"; private static final String defaultIsScriptingInvalid = null; private String defaultDeferedSyntaxAllowedAsLiteral = null; private static final String defaultTrimDirectiveWhitespaces = null; @@ -90,7 +86,7 @@ Collection urlPatterns = jspPropertyGroup.getUrlPatterns(); - if (urlPatterns.size() == 0) { + if (urlPatterns.isEmpty()) { continue; } @@ -166,7 +162,6 @@ /** * Select the property group that has more restrictive url-pattern. In case of tie, select the first. */ - @SuppressWarnings("null") // NPE not possible private JspPropertyGroup selectProperty(JspPropertyGroup prev, JspPropertyGroup curr) { if (prev == null) { return curr; @@ -185,10 +180,10 @@ // Both specifies a *.ext, keep the first one return prev; } - if (prevPath == null && currPath != null) { + if (prevPath == null) { return curr; } - if (prevPath != null && currPath == null) { + if (currPath == null) { return prev; } if (prevPath.length() >= currPath.length()) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/JspDocumentParser.java tomcat10-10.1.52/java/org/apache/jasper/compiler/JspDocumentParser.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/JspDocumentParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/JspDocumentParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -50,11 +50,7 @@ /** * Class implementing a parser for a JSP document, that is, a JSP page in XML syntax. - * - * @author Jan Luehe - * @author Kin-man Chung */ - class JspDocumentParser extends DefaultHandler2 implements TagConstants { private static final String LEXICAL_HANDLER_PROPERTY = "http://xml.org/sax/properties/lexical-handler"; @@ -171,7 +167,7 @@ jspDocParser.isValidating = true; try { source.getByteStream().close(); - } catch (IOException e2) { + } catch (IOException ioe) { // ignore } source = JspUtil.getInputSource(path, jar, jspDocParser.ctxt); @@ -179,7 +175,7 @@ } finally { try { source.getByteStream().close(); - } catch (IOException e) { + } catch (IOException ioe) { // ignore } } @@ -278,9 +274,7 @@ /* * Notice that due to a bug in the underlying SAX parser, the attributes must be enumerated in descending order. */ - boolean isTaglib = false; for (int i = attrs.getLength() - 1; i >= 0; i--) { - isTaglib = false; String attrQName = attrs.getQName(i); if (!attrQName.startsWith("xmlns")) { if (nonTaglibAttrs == null) { @@ -289,6 +283,7 @@ nonTaglibAttrs.addAttribute(attrs.getURI(i), attrs.getLocalName(i), attrs.getQName(i), attrs.getType(i), attrs.getValue(i)); } else { + boolean isTaglib; if (attrQName.startsWith("xmlns:jsp")) { isTaglib = true; } else { @@ -313,7 +308,7 @@ } } - Node node = null; + Node node; if (tagDependentPending && JSP_URI.equals(uri) && localName.equals(BODY_ACTION)) { tagDependentPending = false; @@ -365,8 +360,8 @@ * * The SAX does not call this method with all of the template text, but may invoke this method with chunks of it. * This is a problem when we try to determine if the text contains only whitespaces, or when we are looking for an - * EL expression string. Therefore it is necessary to buffer and concatenate the chunks and process the concatenated - * text later (at beginTag and endTag) + * EL expression string. Therefore, it is necessary to buffer and concatenate the chunks and process the + * concatenated text later (at beginTag and endTag) * * @param buf The characters * @@ -429,7 +424,8 @@ int column = startMark.getColumnNumber(); CharArrayWriter ttext = new CharArrayWriter(); - int lastCh = 0, elType = 0; + int lastCh = 0; + int elType; for (int i = 0; i < charBuffer.length(); i++) { int ch = charBuffer.charAt(i); @@ -535,12 +531,12 @@ continue; } // Ignore any whitespace (including spaces, carriage returns, - // line feeds, and tabs, that appear at the beginning and at + // line feeds, and tabs) that appear at the beginning and at // the end of the body of the action, if the // action's 'trim' attribute is set to TRUE (default). // In addition, any textual nodes in the that - // have only white space are dropped from the document, with - // the exception of leading and trailing white-space-only + // have only white space are dropped from the document, + // except for leading and trailing white-space-only // textual nodes in a whose 'trim' attribute // is set to FALSE, which must be kept verbatim. if (i == 0) { @@ -565,7 +561,7 @@ tagDependentNesting--; } - if (scriptlessBodyNode != null && current.equals(scriptlessBodyNode)) { + if (current.equals(scriptlessBodyNode)) { scriptlessBodyNode = null; } @@ -719,7 +715,7 @@ private Node parseStandardAction(String qName, String localName, Attributes nonTaglibAttrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start) throws SAXException { - Node node = null; + Node node; if (localName.equals(ROOT_ACTION)) { if (!(current instanceof Node.Root)) { @@ -858,7 +854,7 @@ String prefix = getPrefix(qName); - Node.CustomTag ret = null; + Node.CustomTag ret; if (tagInfo != null) { ret = new Node.CustomTag(qName, prefix, localName, uri, nonTaglibAttrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent, tagInfo, tagHandlerClass); @@ -1000,7 +996,7 @@ private void checkPrefix(String uri, String qName) { String prefix = getPrefix(qName); - if (prefix.length() > 0) { + if (!prefix.isEmpty()) { pageInfo.addPrefix(prefix); if ("jsp".equals(prefix) && !JSP_URI.equals(uri)) { pageInfo.setIsJspPrefixHijacked(true); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/JspReader.java tomcat10-10.1.52/java/org/apache/jasper/compiler/JspReader.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/JspReader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/JspReader.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,21 +29,9 @@ import org.apache.tomcat.Jar; /** - * JspReader is an input buffer for the JSP parser. It should allow - * unlimited lookahead and pushback. It also has a bunch of parsing - * utility methods for understanding htmlesque thingies. - * - * @author Anil K. Vijendran - * @author Anselm Baird-Smith - * @author Harish Prabandham - * @author Rajiv Mordani - * @author Mandar Raje - * @author Danno Ferrin - * @author Kin-man Chung - * @author Shawn Bayern - * @author Mark Roth + * JspReader is an input buffer for the JSP parser. It should allow unlimited lookahead and pushback. It also has a + * bunch of parsing utility methods for understanding html style content. */ - class JspReader { /** @@ -69,41 +57,33 @@ /** * Constructor. * - * @param ctxt The compilation context - * @param fname The file name + * @param ctxt The compilation context + * @param fname The file name * @param encoding The file encoding - * @param jar ? - * @param err The error dispatcher - * @throws JasperException If a Jasper-internal error occurs + * @param jar ? + * @param err The error dispatcher + * + * @throws JasperException If a Jasper-internal error occurs * @throws FileNotFoundException If the JSP file is not found (or is unreadable) - * @throws IOException If an IO-level error occurs, e.g. reading the file + * @throws IOException If an IO-level error occurs, e.g. reading the file */ - JspReader(JspCompilationContext ctxt, - String fname, - String encoding, - Jar jar, - ErrorDispatcher err) + JspReader(JspCompilationContext ctxt, String fname, String encoding, Jar jar, ErrorDispatcher err) throws JasperException, FileNotFoundException, IOException { - this(ctxt, fname, JspUtil.getReader(fname, encoding, jar, ctxt, err), - err); + this(ctxt, fname, JspUtil.getReader(fname, encoding, jar, ctxt, err), err); } /** - * Constructor: same as above constructor but with initialized reader - * to the file given. + * Constructor: same as above constructor but with initialized reader to the file given. * * @param ctxt The compilation context * @param fname The file name * @param reader A reader for the JSP source file - * @param err The error dispatcher + * @param err The error dispatcher * * @throws JasperException If an error occurs parsing the JSP file */ - JspReader(JspCompilationContext ctxt, - String fname, - InputStreamReader reader, - ErrorDispatcher err) + JspReader(JspCompilationContext ctxt, String fname, InputStreamReader reader, ErrorDispatcher err) throws JasperException { this.context = ctxt; @@ -111,8 +91,8 @@ try { CharArrayWriter caw = new CharArrayWriter(); - char buf[] = new char[1024]; - for (int i = 0 ; (i = reader.read(buf)) != -1 ;) { + char[] buf = new char[1024]; + for (int i; (i = reader.read(buf)) != -1;) { caw.write(buf, 0, i); } caw.close(); @@ -125,9 +105,9 @@ if (reader != null) { try { reader.close(); - } catch (Exception any) { - if(log.isDebugEnabled()) { - log.debug(Localizer.getMessage("jsp.error.file.close"), any); + } catch (Exception e) { + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.file.close"), e); } } } @@ -136,8 +116,7 @@ /** - * @return JSP compilation context with which this JspReader is - * associated + * @return JSP compilation context with which this JspReader is associated */ JspCompilationContext getJspCompilationContext() { return context; @@ -171,9 +150,8 @@ } /** - * A faster approach than calling {@link #mark()} & {@link #nextChar()}. - * However, this approach is only safe if the mark is only used within the - * JspReader. + * A faster approach than calling {@link #mark()} & {@link #nextChar()}. However, this approach is only safe if the + * mark is only used within the JspReader. */ private int nextChar(Mark mark) { if (!hasMoreInput()) { @@ -196,8 +174,7 @@ } /** - * Search the given character, If it was found, then mark the current cursor - * and the cursor point to next character. + * Search the given character, If it was found, then mark the current cursor and the cursor point to next character. */ private Boolean indexOf(char c, Mark mark) { if (!hasMoreInput()) { @@ -209,30 +186,30 @@ int line = current.line; int col = current.col; int i = current.cursor; - for(; i < end; i ++) { - ch = current.stream[i]; + for (; i < end; i++) { + ch = current.stream[i]; - if (ch == c) { - mark.update(i, line, col); - } - if (ch == '\n') { + if (ch == c) { + mark.update(i, line, col); + } + if (ch == '\n') { line++; col = 0; } else { col++; } - if (ch == c) { - current.update(i+1, line, col); - return Boolean.TRUE; - } + if (ch == c) { + current.update(i + 1, line, col); + return Boolean.TRUE; + } } current.update(i, line, col); return Boolean.FALSE; } /** - * Back up the current cursor by one char, assumes current.cursor > 0, - * and that the char to be pushed back is not '\n'. + * Back up the current cursor by one char, assumes current.cursor > 0, and that the char to be pushed back is not + * '\n'. */ void pushChar() { current.cursor--; @@ -263,11 +240,9 @@ /** * Read ahead the given number of characters without moving the cursor. * - * @param readAhead The number of characters to read ahead. NOTE: This is - * zero based. + * @param readAhead The number of characters to read ahead. NOTE: This is zero based. * - * @return The requested character or -1 if the end of the input is reached - * first + * @return The requested character or -1 if the end of the input is reached first */ int peekChar(int readAhead) { int target = current.cursor + readAhead; @@ -286,7 +261,7 @@ * This method avoids a call to {@link #mark()} when doing comparison. */ private boolean markEquals(Mark another) { - return another.equals(current); + return another.equals(current); } void reset(Mark mark) { @@ -294,55 +269,56 @@ } /** - * Similar to {@link #reset(Mark)} but no new Mark will be created. - * Therefore, the parameter mark must NOT be used in other places. + * Similar to {@link #reset(Mark)} but no new Mark will be created. Therefore, the parameter mark must NOT be used + * in other places. */ private void setCurrent(Mark mark) { - current = mark; + current = mark; } /** * search the stream for a match to a string + * * @param string The string to match - * @return true is one is found, the current position - * in stream is positioned after the search string, - * false otherwise, position in stream unchanged. + * + * @return true is one is found, the current position in stream is positioned after the search + * string, false otherwise, position in stream unchanged. */ boolean matches(String string) { - int len = string.length(); - int cursor = current.cursor; - int streamSize = current.stream.length; - if (cursor + len < streamSize) { //Try to scan in memory - int line = current.line; - int col = current.col; - int ch; - int i = 0; - for(; i < len; i ++) { - ch = current.stream[i+cursor]; - if (string.charAt(i) != ch) { - return false; - } - if (ch == '\n') { - line ++; - col = 0; - } else { - col++; - } - } - current.update(i+cursor, line, col); - } else { - Mark mark = mark(); - int ch = 0; - int i = 0; - do { - ch = nextChar(); - if (((char) ch) != string.charAt(i++)) { - setCurrent(mark); - return false; - } - } while (i < len); - } - return true; + int len = string.length(); + int cursor = current.cursor; + int streamSize = current.stream.length; + if (cursor + len < streamSize) { // Try to scan in memory + int line = current.line; + int col = current.col; + int ch; + int i = 0; + for (; i < len; i++) { + ch = current.stream[i + cursor]; + if (string.charAt(i) != ch) { + return false; + } + if (ch == '\n') { + line++; + col = 0; + } else { + col++; + } + } + current.update(i + cursor, line, col); + } else { + Mark mark = mark(); + int ch; + int i = 0; + do { + ch = nextChar(); + if (((char) ch) != string.charAt(i++)) { + setCurrent(mark); + return false; + } + } while (i < len); + } + return true; } boolean matchesETag(String tagName) { @@ -361,33 +337,32 @@ } boolean matchesETagWithoutLessThan(String tagName) { - Mark mark = mark(); + Mark mark = mark(); - if (!matches("/" + tagName)) { - return false; - } - skipSpaces(); - if (nextChar() == '>') { - return true; - } + if (!matches("/" + tagName)) { + return false; + } + skipSpaces(); + if (nextChar() == '>') { + return true; + } - setCurrent(mark); - return false; + setCurrent(mark); + return false; } /** - * Looks ahead to see if there are optional spaces followed by - * the given String. If so, true is returned and those spaces and - * characters are skipped. If not, false is returned and the - * position is restored to where we were before. + * Looks ahead to see if there are optional spaces followed by the given String. If so, true is returned and those + * spaces and characters are skipped. If not, false is returned and the position is restored to where we were + * before. */ boolean matchesOptionalSpacesFollowedBy(String s) { Mark mark = mark(); skipSpaces(); - boolean result = matches( s ); - if( !result ) { + boolean result = matches(s); + if (!result) { setCurrent(mark); } @@ -404,73 +379,71 @@ } /** - * Skip until the given string is matched in the stream. - * When returned, the context is positioned past the end of the match. + * Skip until the given string is matched in the stream. When returned, the context is positioned past the end of + * the match. * * @param limit The String to match. - * @return A non-null Mark instance (positioned immediately - * before the search string) if found, null - * otherwise. + * + * @return A non-null Mark instance (positioned immediately before the search string) if found, + * null otherwise. */ Mark skipUntil(String limit) { Mark ret = mark(); int limlen = limit.length(); char firstChar = limit.charAt(0); - Boolean result = null; + Boolean result; Mark restart = null; - skip: - while((result = indexOf(firstChar, ret)) != null) { - if (result.booleanValue()) { - if (restart != null) { - restart.init(current, true); - } else { - restart = mark(); - } - for (int i = 1 ; i < limlen ; i++) { - if (peekChar() == limit.charAt(i)) { - nextChar(); - } else { - current.init(restart, true); - continue skip; - } - } - return ret; + skip: + while ((result = indexOf(firstChar, ret)) != null) { + if (result.booleanValue()) { + if (restart != null) { + restart.init(current, true); + } else { + restart = mark(); + } + for (int i = 1; i < limlen; i++) { + if (peekChar() == limit.charAt(i)) { + nextChar(); + } else { + current.init(restart, true); + continue skip; + } + } + return ret; } } return null; } /** - * Skip until the given string is matched in the stream, but ignoring - * chars initially escaped by a '\' and any EL expressions. - * When returned, the context is positioned past the end of the match. + * Skip until the given string is matched in the stream, but ignoring chars initially escaped by a '\' and any EL + * expressions. When returned, the context is positioned past the end of the match. * * @param limit The String to match. - * @param ignoreEL true if something that looks like EL should - * not be treated as EL. - * @return A non-null Mark instance (positioned immediately - * before the search string) if found, null - * otherwise. + * @param ignoreEL true if something that looks like EL should not be treated as EL. + * + * @return A non-null Mark instance (positioned immediately before the search string) if found, + * null otherwise. */ Mark skipUntilIgnoreEsc(String limit, boolean ignoreEL) { Mark ret = mark(); int limlen = limit.length(); int ch; - int prev = 'x'; // Doesn't matter + int prev = 'x'; // Doesn't matter char firstChar = limit.charAt(0); - skip: - for (ch = nextChar(ret) ; ch != -1 ; prev = ch, ch = nextChar(ret)) { + skip: + for (ch = nextChar(ret); ch != -1; prev = ch, ch = nextChar(ret)) { if (ch == '\\' && prev == '\\') { - ch = 0; // Double \ is not an escape char anymore + ch = 0; // Double \ is not an escape char anymore } else if (prev == '\\') { continue; - } else if (!ignoreEL && (ch == '$' || ch == '#') && peekChar() == '{' ) { + } else if (!ignoreEL && (ch == '$' || ch == '#') && peekChar() == '{') { // Move beyond the '{' nextChar(); skipELExpression(); } else if (ch == firstChar) { - for (int i = 1 ; i < limlen ; i++) { + for (int i = 1; i < limlen; i++) { if (peekChar() == limit.charAt(i)) { nextChar(); } else { @@ -484,12 +457,13 @@ } /** - * Skip until the given end tag is matched in the stream. - * When returned, the context is positioned past the end of the tag. + * Skip until the given end tag is matched in the stream. When returned, the context is positioned past the end of + * the tag. * * @param tag The name of the tag whose ETag (</tag>) to match. - * @return A non-null Mark instance (positioned immediately - * before the ETag) if found, null otherwise. + * + * @return A non-null Mark instance (positioned immediately before the ETag) if found, + * null otherwise. */ Mark skipUntilETag(String tag) { Mark ret = skipUntil(" - * In case of success, this method returns Mark for the last - * character before the terminating '}' and reader is positioned just after - * the '}' character. If no terminating '}' is encountered, this method - * returns null. + * In case of success, this method returns Mark for the last character before the terminating '}' and + * reader is positioned just after the '}' character. If no terminating '}' is encountered, this method returns + * null. *

          * Starting with EL 3.0, nested paired {}s are supported. * @@ -517,8 +490,8 @@ */ Mark skipELExpression() { // ELExpressionBody. - // Starts with "#{" or "${". Ends with "}". - // May contain quoted "{", "}", '{', or '}' and nested "{...}" + // Starts with "#{" or "${". Ends with "}". + // May contain quoted "{", "}", '{', or '}' and nested "{...}" Mark last = mark(); boolean singleQuoted = false; boolean doubleQuoted = false; @@ -542,11 +515,11 @@ singleQuoted = !singleQuoted; } else if (currentChar == '{' && !doubleQuoted && !singleQuoted) { nesting++; - } else if (currentChar =='}' && !doubleQuoted && !singleQuoted) { + } else if (currentChar == '}' && !doubleQuoted && !singleQuoted) { // Note: This also matches the terminating '}' at which point - // nesting will be set to -1 - hence the test for - // while (currentChar != '}' || nesting > -1 ||...) below - // to continue the loop until the final '}' is detected + // nesting will be set to -1 - hence the test for + // while (currentChar != '}' || nesting > -1 ||...) below + // to continue the loop until the final '}' is detected nesting--; } } while (currentChar != '}' || singleQuoted || doubleQuoted || nesting > -1); @@ -560,9 +533,8 @@ } /** - * Parse a space delimited token. - * If quoted the token will consume all characters up to a matching quote, - * otherwise, it consumes up to the first delimiter character. + * Parse a space delimited token. If quoted the token will consume all characters up to a matching quote, otherwise, + * it consumes up to the first delimiter character. * * @param quoted If true accept quoted strings. */ @@ -583,8 +555,7 @@ char endQuote = ch == '"' ? '"' : '\''; // Consume the open quote: ch = nextChar(); - for (ch = nextChar(); ch != -1 && ch != endQuote; - ch = nextChar()) { + for (ch = nextChar(); ch != -1 && ch != endQuote; ch = nextChar()) { if (ch == '\\') { ch = nextChar(); } @@ -604,8 +575,7 @@ ch = nextChar(); // Take care of the quoting here. if (ch == '\\') { - if (peekChar() == '"' || peekChar() == '\'' || - peekChar() == '>' || peekChar() == '%') { + if (peekChar() == '"' || peekChar() == '\'' || peekChar() == '>' || peekChar() == '%') { ch = nextChar(); } } @@ -619,25 +589,22 @@ /** - * Parse utils - Is current character a token delimiter ? - * Delimiters are currently defined to be =, >, <, ", and ' or any - * any space character as defined by isSpace. + * Parse utils - Is current character a token delimiter ? Delimiters are currently defined to be =, >, <, ", + * and ' or any space character as defined by isSpace. * * @return A boolean. */ private boolean isDelimiter() { - if (! isSpace()) { + if (!isSpace()) { int ch = peekChar(); // Look for a single-char work delimiter: - if (ch == '=' || ch == '>' || ch == '"' || ch == '\'' - || ch == '/') { + if (ch == '=' || ch == '>' || ch == '"' || ch == '\'' || ch == '/') { return true; } // Look for an end-of-comment or end-of-tag: if (ch == '-') { Mark mark = mark(); - if (((ch = nextChar()) == '>') - || ((ch == '-') && (nextChar() == '>'))) { + if (((ch = nextChar()) == '>') || ((ch == '-') && (nextChar() == '>'))) { setCurrent(mark); return true; } else { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/JspRuntimeContext.java tomcat10-10.1.52/java/org/apache/jasper/compiler/JspRuntimeContext.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/JspRuntimeContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/JspRuntimeContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,16 +47,9 @@ /** - * Class for tracking JSP compile time file dependencies when the - * >%@include file="..."%< directive is used. - * - * A background thread periodically checks the files a JSP page - * is dependent upon. If a dependent file changes the JSP page - * which included it is recompiled. - * - * Only used if a web application context is a directory. - * - * @author Glenn L. Nielsen + * Class for tracking JSP compile time file dependencies when the >%@include file="..."%< directive is used. A + * background thread periodically checks the files a JSP page is dependent upon. If a dependent file changes the JSP + * page which included it is recompiled. Only used if a web application context is a directory. */ public final class JspRuntimeContext { @@ -78,9 +71,8 @@ // ----------------------------------------------------------- Constructors /** - * Create a JspRuntimeContext for a web application context. - * - * Loads in any previously generated dependencies from file. + * Create a JspRuntimeContext for a web application context. Loads in any previously generated dependencies from + * file. * * @param context ServletContext for web application * @param options The main Jasper options @@ -98,15 +90,13 @@ if (log.isTraceEnabled()) { if (loader != null) { - log.trace(Localizer.getMessage("jsp.message.parent_class_loader_is", - loader.toString())); + log.trace(Localizer.getMessage("jsp.message.parent_class_loader_is", loader.toString())); } else { - log.trace(Localizer.getMessage("jsp.message.parent_class_loader_is", - "")); + log.trace(Localizer.getMessage("jsp.message.parent_class_loader_is", "")); } } - parentClassLoader = loader; + parentClassLoader = loader; classpath = initClassPath(); if (context instanceof org.apache.jasper.servlet.JspCServletContext) { @@ -127,22 +117,20 @@ // If this web application context is running from a // directory, start the background compilation thread String appBase = context.getRealPath("/"); - if (!options.getDevelopment() - && appBase != null - && options.getCheckInterval() > 0) { + if (!options.getDevelopment() && appBase != null && options.getCheckInterval() > 0) { lastCompileCheck = System.currentTimeMillis(); } if (options.getMaxLoadedJsps() > 0) { jspQueue = new FastRemovalDequeue<>(options.getMaxLoadedJsps()); if (log.isTraceEnabled()) { - log.trace(Localizer.getMessage("jsp.message.jsp_queue_created", - "" + options.getMaxLoadedJsps(), context.getContextPath())); + log.trace(Localizer.getMessage("jsp.message.jsp_queue_created", "" + options.getMaxLoadedJsps(), + context.getContextPath())); } } /* Init parameter is in seconds, locally we use milliseconds */ - jspIdleTimeout = options.getJspIdleTimeout() * 1000; + jspIdleTimeout = options.getJspIdleTimeout() * 1000L; } // ----------------------------------------------------- Instance Variables @@ -164,7 +152,7 @@ /** * Maps JSP pages to their JspServletWrapper's */ - private final Map jsps = new ConcurrentHashMap<>(); + private final Map jsps = new ConcurrentHashMap<>(); /** * Keeps JSP pages ordered by last access. @@ -172,9 +160,8 @@ private FastRemovalDequeue jspQueue = null; /** - * Map of class name to associated source map. This is maintained here as - * multiple JSPs can depend on the same file (included JSP, tag file, etc.) - * so a web application scoped Map is required. + * Map of class name to associated source map. This is maintained here as multiple JSPs can depend on the same file + * (included JSP, tag file, etc.) so a web application scoped Map is required. */ private final Map smaps = new ConcurrentHashMap<>(); @@ -190,7 +177,7 @@ * Add a new JspServletWrapper. * * @param jspUri JSP URI - * @param jsw Servlet wrapper for JSP + * @param jsw Servlet wrapper for JSP */ public void addWrapper(String jspUri, JspServletWrapper jsw) { jsps.put(jspUri, jsw); @@ -200,6 +187,7 @@ * Get an already existing JspServletWrapper. * * @param jspUri JSP URI + * * @return JspServletWrapper for JSP */ public JspServletWrapper getWrapper(String jspUri) { @@ -207,7 +195,7 @@ } /** - * Remove a JspServletWrapper. + * Remove a JspServletWrapper. * * @param jspUri JSP URI of JspServletWrapper to remove */ @@ -216,23 +204,23 @@ } /** - * Push a newly compiled JspServletWrapper into the queue at first - * execution of jsp. Destroy any JSP that has been replaced in the queue. + * Push a newly compiled JspServletWrapper into the queue at first execution of jsp. Destroy any JSP that has been + * replaced in the queue. * * @param jsw Servlet wrapper for jsp. + * * @return an unloadHandle that can be pushed to front of queue at later execution times. - * */ + */ public FastRemovalDequeue.Entry push(JspServletWrapper jsw) { if (log.isTraceEnabled()) { - log.trace(Localizer.getMessage("jsp.message.jsp_added", - jsw.getJspUri(), context.getContextPath())); + log.trace(Localizer.getMessage("jsp.message.jsp_added", jsw.getJspUri(), context.getContextPath())); } FastRemovalDequeue.Entry entry = jspQueue.push(jsw); JspServletWrapper replaced = entry.getReplaced(); if (replaced != null) { if (log.isTraceEnabled()) { - log.trace(Localizer.getMessage("jsp.message.jsp_removed_excess", - replaced.getJspUri(), context.getContextPath())); + log.trace(Localizer.getMessage("jsp.message.jsp_removed_excess", replaced.getJspUri(), + context.getContextPath())); } unloadJspServletWrapper(replaced); entry.clearReplaced(); @@ -244,19 +232,18 @@ * Push unloadHandle for JspServletWrapper to front of the queue. * * @param unloadHandle the unloadHandle for the jsp. - * */ + */ public void makeYoungest(FastRemovalDequeue.Entry unloadHandle) { if (log.isTraceEnabled()) { JspServletWrapper jsw = unloadHandle.getContent(); - log.trace(Localizer.getMessage("jsp.message.jsp_queue_update", - jsw.getJspUri(), context.getContextPath())); + log.trace(Localizer.getMessage("jsp.message.jsp_queue_update", jsw.getJspUri(), context.getContextPath())); } jspQueue.moveFirst(unloadHandle); } /** - * Returns the number of JSPs for which JspServletWrappers exist, i.e., - * the number of JSPs that have been loaded into the webapp. + * Returns the number of JSPs for which JspServletWrappers exist, i.e., the number of JSPs that have been loaded + * into the webapp. * * @return The number of JSPs that have been loaded into the webapp */ @@ -265,8 +252,7 @@ } /** - * Get the SecurityManager Policy CodeSource for this web - * application context. + * Get the SecurityManager Policy CodeSource for this web application context. * * @return CodeSource for JSP */ @@ -284,8 +270,7 @@ } /** - * Get the SecurityManager PermissionCollection for this - * web application context. + * Get the SecurityManager PermissionCollection for this web application context. * * @return PermissionCollection permissions */ @@ -330,8 +315,8 @@ /** * Gets the number of JSPs that are in the JSP limiter queue * - * @return The number of JSPs (in the webapp with which this JspServlet is - * associated) that are in the JSP limiter queue + * @return The number of JSPs (in the webapp with which this JspServlet is associated) that are in the JSP limiter + * queue */ public int getJspQueueLength() { if (jspQueue != null) { @@ -343,8 +328,7 @@ /** * Gets the number of JSPs that have been unloaded. * - * @return The number of JSPs (in the webapp with which this JspServlet is - * associated) that have been unloaded + * @return The number of JSPs (in the webapp with which this JspServlet is associated) that have been unloaded */ public int getJspUnloadCount() { return jspUnloadCount.intValue(); @@ -352,8 +336,7 @@ /** - * Method used by background thread to check the JSP dependencies - * registered with this class for JSP's. + * Method used by background thread to check the JSP dependencies registered with this class for JSPs. */ public void checkCompile() { @@ -373,7 +356,7 @@ // check is in progress. See BZ 62603. compileCheckInProgress = true; - Object [] wrappers = jsps.values().toArray(); + Object[] wrappers = jsps.values().toArray(); for (Object wrapper : wrappers) { JspServletWrapper jsw = (JspServletWrapper) wrapper; JspCompilationContext ctxt = jsw.getJspEngineContext(); @@ -402,7 +385,7 @@ try { if (jsw.isTagFile()) { // Although this is a public method, all other paths to this - // method use this sync and it is required to prevent race + // method use this sync, and it is required to prevent race // conditions during the reload. synchronized (this) { jsw.loadTagFile(); @@ -448,6 +431,7 @@ /** * Method used to initialize classpath for compiles. + * * @return the compilation classpath */ private String initClassPath() { @@ -455,7 +439,7 @@ StringBuilder cpath = new StringBuilder(); if (parentClassLoader instanceof URLClassLoader) { - URL [] urls = ((URLClassLoader)parentClassLoader).getURLs(); + URL[] urls = ((URLClassLoader) parentClassLoader).getURLs(); for (URL url : urls) { // Tomcat can use URLs other than file URLs. However, a protocol @@ -467,7 +451,7 @@ // Need to decode the URL, primarily to convert %20 // sequences back to spaces String decoded = url.toURI().getPath(); - cpath.append(decoded + File.pathSeparator); + cpath.append(decoded).append(File.pathSeparator); } catch (URISyntaxException e) { log.warn(Localizer.getMessage("jsp.warning.classpathUrl"), e); } @@ -475,16 +459,16 @@ } } - cpath.append(options.getScratchDir() + File.pathSeparator); + cpath.append(options.getScratchDir()).append(File.pathSeparator); String cp = (String) context.getAttribute(options.getServletClasspathAttribute()); - if (cp == null || cp.equals("")) { + if (cp == null || cp.isEmpty()) { cp = options.getClassPath(); } String path = cpath.toString() + cp; - if(log.isTraceEnabled()) { + if (log.isTraceEnabled()) { log.trace("Compilation classpath initialized: " + path); } return path; @@ -493,14 +477,16 @@ /** * Helper class to allow initSecurity() to return two items */ - private static class SecurityHolder{ + private static class SecurityHolder { private final CodeSource cs; private final PermissionCollection pc; - private SecurityHolder(CodeSource cs, PermissionCollection pc){ + + private SecurityHolder(CodeSource cs, PermissionCollection pc) { this.cs = cs; this.pc = pc; } } + /** * Method used to initialize SecurityManager data. */ @@ -513,52 +499,46 @@ Policy policy = Policy.getPolicy(); CodeSource source = null; PermissionCollection permissions = null; - if( policy != null ) { + if (policy != null) { try { // Get the permissions for the web app context String docBase = context.getRealPath("/"); - if( docBase == null ) { + if (docBase == null) { docBase = options.getScratchDir().toString(); } String codeBase = docBase; - if (!codeBase.endsWith(File.separator)){ + if (!codeBase.endsWith(File.separator)) { codeBase = codeBase + File.separator; } File contextDir = new File(codeBase); URL url = contextDir.getCanonicalFile().toURI().toURL(); - source = new CodeSource(url,(Certificate[])null); + source = new CodeSource(url, (Certificate[]) null); permissions = policy.getPermissions(source); // Create a file read permission for web app context directory - if (!docBase.endsWith(File.separator)){ - permissions.add - (new FilePermission(docBase,"read")); + if (!docBase.endsWith(File.separator)) { + permissions.add(new FilePermission(docBase, "read")); docBase = docBase + File.separator; } else { - permissions.add - (new FilePermission - (docBase.substring(0,docBase.length() - 1),"read")); + permissions.add(new FilePermission(docBase.substring(0, docBase.length() - 1), "read")); } docBase = docBase + "-"; - permissions.add(new FilePermission(docBase,"read")); + permissions.add(new FilePermission(docBase, "read")); // Spec says apps should have read/write for their temp // directory. This is fine, as no security sensitive files, at // least any that the app doesn't have full control of anyway, // will be written here. String workDir = options.getScratchDir().toString(); - if (!workDir.endsWith(File.separator)){ - permissions.add - (new FilePermission(workDir,"read,write")); + if (!workDir.endsWith(File.separator)) { + permissions.add(new FilePermission(workDir, "read,write")); workDir = workDir + File.separator; } workDir = workDir + "-"; - permissions.add(new FilePermission( - workDir,"read,write,delete")); + permissions.add(new FilePermission(workDir, "read,write,delete")); // Allow the JSP to access org.apache.jasper.runtime.HttpJspBase - permissions.add( new RuntimePermission( - "accessClassInPackage.org.apache.jasper.runtime") ); + permissions.add(new RuntimePermission("accessClassInPackage.org.apache.jasper.runtime")); } catch (RuntimeException | IOException e) { context.log(Localizer.getMessage("jsp.error.security"), e); } @@ -568,7 +548,7 @@ private void unloadJspServletWrapper(JspServletWrapper jsw) { removeWrapper(jsw.getJspUri()); - synchronized(jsw) { + synchronized (jsw) { jsw.destroy(); } jspUnloadCount.incrementAndGet(); @@ -576,7 +556,7 @@ /** - * Method used by background thread to check if any JSP's should be unloaded. + * Method used by background thread to check if any JSPs should be unloaded. */ public void checkUnload() { @@ -585,21 +565,20 @@ if (jspQueue != null) { queueLength = jspQueue.getSize(); } - log.trace(Localizer.getMessage("jsp.message.jsp_unload_check", - context.getContextPath(), "" + jsps.size(), "" + queueLength)); + log.trace(Localizer.getMessage("jsp.message.jsp_unload_check", context.getContextPath(), "" + jsps.size(), + "" + queueLength)); } long now = System.currentTimeMillis(); if (jspIdleTimeout > 0) { long unloadBefore = now - jspIdleTimeout; - Object [] wrappers = jsps.values().toArray(); + Object[] wrappers = jsps.values().toArray(); for (Object wrapper : wrappers) { JspServletWrapper jsw = (JspServletWrapper) wrapper; synchronized (jsw) { if (jsw.getLastUsageTime() < unloadBefore) { if (log.isTraceEnabled()) { - log.trace(Localizer.getMessage("jsp.message.jsp_removed_idle", - jsw.getJspUri(), context.getContextPath(), - "" + (now - jsw.getLastUsageTime()))); + log.trace(Localizer.getMessage("jsp.message.jsp_removed_idle", jsw.getJspUri(), + context.getContextPath(), "" + (now - jsw.getLastUsageTime()))); } if (jspQueue != null) { jspQueue.remove(jsw.getUnloadHandle()); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/JspUtil.java tomcat10-10.1.52/java/org/apache/jasper/compiler/JspUtil.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/JspUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/JspUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,15 +32,7 @@ import org.xml.sax.InputSource; /** - * This class has all the utility method(s). Ideally should move all the bean - * containers here. - * - * @author Mandar Raje. - * @author Rajiv Mordani. - * @author Danno Ferrin - * @author Pierre Delisle - * @author Shawn Bayern - * @author Mark Roth + * This class has all the utility method(s). Ideally should move all the bean containers here. */ public class JspUtil { @@ -51,15 +43,12 @@ private static final String OPEN_EXPR = "<%="; private static final String CLOSE_EXPR = "%>"; - private static final String javaKeywords[] = { "abstract", "assert", - "boolean", "break", "byte", "case", "catch", "char", "class", - "const", "continue", "default", "do", "double", "else", "enum", - "extends", "final", "finally", "float", "for", "goto", "if", - "implements", "import", "instanceof", "int", "interface", "long", - "native", "new", "package", "private", "protected", "public", - "return", "short", "static", "strictfp", "super", "switch", - "synchronized", "this", "throw", "throws", "transient", "try", - "void", "volatile", "while" }; + private static final String[] javaKeywords = + { "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", + "default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", + "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", + "private", "protected", "public", "return", "short", "static", "strictfp", "super", "switch", + "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while" }; static final int JSP_INPUT_STREAM_BUFFER_SIZE = 1024; @@ -67,15 +56,16 @@ /** * Takes a potential expression and converts it into XML form. + * * @param expression The expression to convert + * * @return XML view */ public static String getExprInXml(String expression) { String returnString; int length = expression.length(); - if (expression.startsWith(OPEN_EXPR) && - expression.endsWith(CLOSE_EXPR)) { + if (expression.startsWith(OPEN_EXPR) && expression.endsWith(CLOSE_EXPR)) { returnString = expression.substring(1, length - 1); } else { returnString = expression; @@ -87,40 +77,32 @@ /** * Checks to see if the given scope is valid. * - * @param scope - * The scope to be checked - * @param n - * The Node containing the 'scope' attribute whose value is to be - * checked - * @param err - * error dispatcher - * - * @throws JasperException - * if scope is not null and different from "page", - * "request", "session", and - * "application" - */ - public static void checkScope(String scope, Node n, ErrorDispatcher err) - throws JasperException { - if (scope != null && !scope.equals("page") && !scope.equals("request") - && !scope.equals("session") && !scope.equals("application")) { + * @param scope The scope to be checked + * @param n The Node containing the 'scope' attribute whose value is to be checked + * @param err error dispatcher + * + * @throws JasperException if scope is not null and different from "page", "request", + * "session", and "application" + */ + public static void checkScope(String scope, Node n, ErrorDispatcher err) throws JasperException { + if (scope != null && !scope.equals("page") && !scope.equals("request") && !scope.equals("session") && + !scope.equals("application")) { err.jspError(n, "jsp.error.invalid.scope", scope); } } /** - * Checks if all mandatory attributes are present and if all attributes - * present have valid names. Checks attributes specified as XML-style - * attributes as well as attributes specified using the jsp:attribute - * standard action. - * @param typeOfTag The tag type - * @param n The corresponding node + * Checks if all mandatory attributes are present and if all attributes present have valid names. Checks attributes + * specified as XML-style attributes as well as attributes specified using the jsp:attribute standard action. + * + * @param typeOfTag The tag type + * @param n The corresponding node * @param validAttributes The array with the valid attributes - * @param err Dispatcher for errors + * @param err Dispatcher for errors + * * @throws JasperException An error occurred */ - public static void checkAttributes(String typeOfTag, Node n, - ValidAttribute[] validAttributes, ErrorDispatcher err) + public static void checkAttributes(String typeOfTag, Node n, ValidAttribute[] validAttributes, ErrorDispatcher err) throws JasperException { Attributes attrs = n.getAttributes(); Mark start = n.getStart(); @@ -130,7 +112,7 @@ int tempLength = (attrs == null) ? 0 : attrs.getLength(); ArrayList temp = new ArrayList<>(tempLength); for (int i = 0; i < tempLength; i++) { - @SuppressWarnings("null") // If attrs==null, tempLength == 0 + @SuppressWarnings("null") // If attrs==null, tempLength == 0 String qName = attrs.getQName(i); if ((!qName.equals("xmlns")) && (!qName.startsWith("xmlns:"))) { temp.add(qName); @@ -148,9 +130,7 @@ temp.add(attrName); // Check if this value appear in the attribute of the node if (n.getAttributeValue(attrName) != null) { - err.jspError(n, - "jsp.error.duplicate.name.jspattribute", - attrName); + err.jspError(n, "jsp.error.duplicate.name.jspattribute", attrName); } } else { // Nothing can come before jsp:attribute, and only @@ -161,9 +141,8 @@ } /* - * First check to see if all the mandatory attributes are present. If so - * only then proceed to see if the other attributes are valid for the - * particular tag. + * First check to see if all the mandatory attributes are present. If so only then proceed to see if the other + * attributes are valid for the particular tag. */ String missingAttribute = null; @@ -173,7 +152,6 @@ attrPos = temp.indexOf(validAttribute.name); if (attrPos != -1) { temp.remove(attrPos); - valid = true; } else { valid = false; missingAttribute = validAttribute.name; @@ -184,8 +162,7 @@ // If mandatory attribute is missing then the exception is thrown if (!valid) { - err.jspError(start, "jsp.error.mandatory.attribute", typeOfTag, - missingAttribute); + err.jspError(start, "jsp.error.mandatory.attribute", typeOfTag, missingAttribute); } // Check to see if there are any more attributes for the specified tag. @@ -195,7 +172,7 @@ } // Now check to see if the rest of the attributes are valid too. - for(String attribute : temp) { + for (String attribute : temp) { valid = false; for (ValidAttribute validAttribute : validAttributes) { if (attribute.equals(validAttribute.name)) { @@ -204,8 +181,7 @@ } } if (!valid) { - err.jspError(start, "jsp.error.invalid.attribute", typeOfTag, - attribute); + err.jspError(start, "jsp.error.invalid.attribute", typeOfTag, attribute); } } // XXX *could* move EL-syntax validation here... (sb) @@ -227,12 +203,11 @@ } /** - * Convert a String value to 'boolean'. Besides the standard conversions - * done by Boolean.parseBoolean(s), the value "yes" (ignore case) - * is also converted to 'true'. If 's' is null, then 'false' is returned. + * Convert a String value to 'boolean'. Besides the standard conversions done by Boolean.parseBoolean(s), the value + * "yes" (ignore case) is also converted to 'true'. If 's' is null, then 'false' is returned. + * + * @param s the string to be converted * - * @param s - * the string to be converted * @return the boolean value associated with the string s */ public static boolean booleanValue(String s) { @@ -248,24 +223,21 @@ } /** - * Returns the Class object associated with the class or - * interface with the given string name. - * + * Returns the Class object associated with the class or interface with the given string name. *

          - * The Class object is determined by passing the given string - * name to the Class.forName() method, unless the given string - * name represents a primitive type, in which case it is converted to a - * Class object by appending ".class" to it (e.g., - * "int.class"). - * @param type The class name, array or primitive type + * The Class object is determined by passing the given string name to the Class.forName() + * method, unless the given string name represents a primitive type, in which case it is converted to a + * Class object by appending ".class" to it (e.g., "int.class"). + * + * @param type The class name, array or primitive type * @param loader The class loader + * * @return the loaded class + * * @throws ClassNotFoundException Loading class failed */ - public static Class toClass(String type, ClassLoader loader) - throws ClassNotFoundException { + public static Class toClass(String type, ClassLoader loader) throws ClassNotFoundException { - Class c = null; int i0 = type.indexOf('['); int dims = 0; if (i0 > 0) { @@ -278,6 +250,7 @@ type = type.substring(0, i0); } + Class c = null; if ("boolean".equals(type)) { c = boolean.class; } else if ("char".equals(type)) { @@ -315,22 +288,18 @@ /** * Produces a String representing a call to the EL interpreter. * - * @param isTagFile true if the file is a tag file - * rather than a JSP - * @param expression - * a String containing zero or more "${}" expressions - * @param expectedType - * the expected type of the interpreted result - * @param fnmapvar - * Variable pointing to a function map. + * @param isTagFile true if the file is a tag file rather than a JSP + * @param expression a String containing zero or more "${}" expressions + * @param expectedType the expected type of the interpreted result + * @param fnmapvar Variable pointing to a function map. + * * @return a String representing a call to the EL interpreter. */ - public static String interpreterCall(boolean isTagFile, String expression, - Class expectedType, String fnmapvar) { + public static String interpreterCall(boolean isTagFile, String expression, Class expectedType, String fnmapvar) { /* * Determine which context object to use. */ - String jspCtxt = null; + String jspCtxt; if (isTagFile) { jspCtxt = "this.getJspContext()"; } else { @@ -338,8 +307,8 @@ } /* - * Determine whether to use the expected type's textual name or, if it's - * a primitive, the name of its correspondent boxed type. + * Determine whether to use the expected type's textual name or, if it's a primitive, the name of its + * correspondent boxed type. */ String returnType = expectedType.getCanonicalName(); String targetType = returnType; @@ -375,45 +344,27 @@ /* * Build up the base call to the interpreter. */ - // XXX - We use a proprietary call to the interpreter for now - // as the current standard machinery is inefficient and requires - // lots of wrappers and adapters. This should all clear up once - // the EL interpreter moves out of JSTL and into its own project. - // In the future, this should be replaced by code that calls - // ExpressionEvaluator.parseExpression() and then cache the resulting - // expression objects. The interpreterCall would simply select - // one of the pre-cached expressions and evaluate it. - // Note that PageContextImpl implements VariableResolver and - // the generated Servlet/SimpleTag implements FunctionMapper, so - // that machinery is already in place (mroth). targetType = toJavaSourceType(targetType); - StringBuilder call = new StringBuilder( - "(" - + returnType - + ") " - + "org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate" - + "(" + Generator.quote(expression) + ", " + targetType - + ".class, " + "(jakarta.servlet.jsp.PageContext)" + jspCtxt + ", " - + fnmapvar + ")"); + StringBuilder call = new StringBuilder("(" + returnType + ") " + + "org.apache.jasper.runtime.PageContextImpl.proprietaryEvaluate" + "(" + Generator.quote(expression) + + ", " + targetType + ".class, " + "(jakarta.servlet.jsp.PageContext)" + jspCtxt + ", " + fnmapvar + ")"); /* * Add the primitive converter method if we need to. */ if (primitiveConverterMethod != null) { call.insert(0, "("); - call.append(")." + primitiveConverterMethod + "()"); + call.append(").").append(primitiveConverterMethod).append("()"); } return call.toString(); } - public static String coerceToPrimitiveBoolean(String s, - boolean isNamedAttribute) { + public static String coerceToPrimitiveBoolean(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToBoolean(" - + s + ")"; + return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToBoolean(" + s + ")"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "false"; } else { return Boolean.valueOf(s).toString(); @@ -423,10 +374,10 @@ public static String coerceToBoolean(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "(java.lang.Boolean) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" - + s + ", java.lang.Boolean.class)"; + return "(java.lang.Boolean) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + + ", java.lang.Boolean.class)"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "java.lang.Boolean.FALSE"; } else { // Detect format error at translation time @@ -435,13 +386,11 @@ } } - public static String coerceToPrimitiveByte(String s, - boolean isNamedAttribute) { + public static String coerceToPrimitiveByte(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToByte(" - + s + ")"; + return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToByte(" + s + ")"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "(byte) 0"; } else { return "((byte)" + Byte.valueOf(s).toString() + ")"; @@ -451,10 +400,10 @@ public static String coerceToByte(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "(java.lang.Byte) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" - + s + ", java.lang.Byte.class)"; + return "(java.lang.Byte) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + + ", java.lang.Byte.class)"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "java.lang.Byte.valueOf((byte) 0)"; } else { // Detect format error at translation time @@ -465,10 +414,9 @@ public static String coerceToChar(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToChar(" - + s + ")"; + return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToChar(" + s + ")"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "(char) 0"; } else { char ch = s.charAt(0); @@ -480,10 +428,10 @@ public static String coerceToCharacter(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "(java.lang.Character) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" - + s + ", java.lang.Character.class)"; + return "(java.lang.Character) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + + ", java.lang.Character.class)"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "java.lang.Character.valueOf((char) 0)"; } else { char ch = s.charAt(0); @@ -493,13 +441,11 @@ } } - public static String coerceToPrimitiveDouble(String s, - boolean isNamedAttribute) { + public static String coerceToPrimitiveDouble(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToDouble(" - + s + ")"; + return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToDouble(" + s + ")"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "(double) 0"; } else { return Double.valueOf(s).toString(); @@ -509,10 +455,9 @@ public static String coerceToDouble(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "(java.lang.Double) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" - + s + ", Double.class)"; + return "(java.lang.Double) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + ", Double.class)"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "java.lang.Double.valueOf(0)"; } else { // Detect format error at translation time @@ -521,13 +466,11 @@ } } - public static String coerceToPrimitiveFloat(String s, - boolean isNamedAttribute) { + public static String coerceToPrimitiveFloat(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToFloat(" - + s + ")"; + return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToFloat(" + s + ")"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "(float) 0"; } else { return Float.valueOf(s).toString() + "f"; @@ -537,10 +480,10 @@ public static String coerceToFloat(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "(java.lang.Float) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" - + s + ", java.lang.Float.class)"; + return "(java.lang.Float) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + + ", java.lang.Float.class)"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "java.lang.Float.valueOf(0)"; } else { // Detect format error at translation time @@ -551,10 +494,9 @@ public static String coerceToInt(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToInt(" - + s + ")"; + return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToInt(" + s + ")"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "0"; } else { return Integer.valueOf(s).toString(); @@ -564,10 +506,10 @@ public static String coerceToInteger(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "(java.lang.Integer) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" - + s + ", java.lang.Integer.class)"; + return "(java.lang.Integer) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + + ", java.lang.Integer.class)"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "java.lang.Integer.valueOf(0)"; } else { // Detect format error at translation time @@ -576,13 +518,11 @@ } } - public static String coerceToPrimitiveShort(String s, - boolean isNamedAttribute) { + public static String coerceToPrimitiveShort(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToShort(" - + s + ")"; + return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToShort(" + s + ")"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "(short) 0"; } else { return "((short) " + Short.valueOf(s).toString() + ")"; @@ -592,10 +532,10 @@ public static String coerceToShort(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "(java.lang.Short) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" - + s + ", java.lang.Short.class)"; + return "(java.lang.Short) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + + ", java.lang.Short.class)"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "java.lang.Short.valueOf((short) 0)"; } else { // Detect format error at translation time @@ -604,13 +544,11 @@ } } - public static String coerceToPrimitiveLong(String s, - boolean isNamedAttribute) { + public static String coerceToPrimitiveLong(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToLong(" - + s + ")"; + return "org.apache.jasper.runtime.JspRuntimeLibrary.coerceToLong(" + s + ")"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "(long) 0"; } else { return Long.valueOf(s).toString() + "l"; @@ -620,10 +558,10 @@ public static String coerceToLong(String s, boolean isNamedAttribute) { if (isNamedAttribute) { - return "(java.lang.Long) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" - + s + ", java.lang.Long.class)"; + return "(java.lang.Long) org.apache.jasper.runtime.JspRuntimeLibrary.coerce(" + s + + ", java.lang.Long.class)"; } else { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return "java.lang.Long.valueOf(0)"; } else { // Detect format error at translation time @@ -632,10 +570,10 @@ } } - public static BufferedInputStream getInputStream(String fname, Jar jar, - JspCompilationContext ctxt) throws IOException { + public static BufferedInputStream getInputStream(String fname, Jar jar, JspCompilationContext ctxt) + throws IOException { - InputStream in = null; + InputStream in; if (jar != null) { String jarEntryName = fname.substring(1); @@ -645,15 +583,13 @@ } if (in == null) { - throw new FileNotFoundException(Localizer.getMessage( - "jsp.error.file.not.found", fname)); + throw new FileNotFoundException(Localizer.getMessage("jsp.error.file.not.found", fname)); } return new BufferedInputStream(in, JSP_INPUT_STREAM_BUFFER_SIZE); } - public static InputSource getInputSource(String fname, Jar jar, JspCompilationContext ctxt) - throws IOException { + public static InputSource getInputSource(String fname, Jar jar, JspCompilationContext ctxt) throws IOException { InputSource source; if (jar != null) { String jarEntryName = fname.substring(1); @@ -667,20 +603,19 @@ } /** - * Gets the fully-qualified class name of the tag handler corresponding to - * the given tag file path. + * Gets the fully-qualified class name of the tag handler corresponding to the given tag file path. * - * @param path Tag file path + * @param path Tag file path * @param packageName The package name - * @param urn The tag identifier - * @param err Error dispatcher + * @param urn The tag identifier + * @param err Error dispatcher + * + * @return Fully-qualified class name of the tag handler corresponding to the given tag file path * - * @return Fully-qualified class name of the tag handler corresponding to - * the given tag file path * @throws JasperException Failed to generate a class name for the tag */ - public static String getTagHandlerClassName(String path, String packageName, String urn, - ErrorDispatcher err) throws JasperException { + public static String getTagHandlerClassName(String path, String packageName, String urn, ErrorDispatcher err) + throws JasperException { String className = null; @@ -722,8 +657,7 @@ } private static String getClassNameBase(String packageName, String urn) { - StringBuilder base = - new StringBuilder(packageName + ".meta."); + StringBuilder base = new StringBuilder(packageName + ".meta."); if (urn != null) { base.append(makeJavaPackage(urn)); base.append('.'); @@ -734,16 +668,15 @@ /** * Converts the given path to a Java package or fully-qualified class name * - * @param path - * Path to convert + * @param path Path to convert * * @return Java package corresponding to the given path */ - public static final String makeJavaPackage(String path) { - String classNameComponents[] = path.split("/"); + public static String makeJavaPackage(String path) { + String[] classNameComponents = path.split("/"); StringBuilder legalClassNames = new StringBuilder(); for (String classNameComponent : classNameComponents) { - if (classNameComponent.length() > 0) { + if (!classNameComponent.isEmpty()) { if (legalClassNames.length() > 0) { legalClassNames.append('.'); } @@ -756,46 +689,40 @@ /** * Converts the given identifier to a legal Java identifier * - * @param identifier - * Identifier to convert + * @param identifier Identifier to convert * * @return Legal Java identifier corresponding to the given identifier */ - public static final String makeJavaIdentifier(String identifier) { + public static String makeJavaIdentifier(String identifier) { return makeJavaIdentifier(identifier, true); } /** - * Converts the given identifier to a legal Java identifier - * to be used for JSP Tag file attribute names. + * Converts the given identifier to a legal Java identifier to be used for JSP Tag file attribute names. * - * @param identifier - * Identifier to convert + * @param identifier Identifier to convert * * @return Legal Java identifier corresponding to the given identifier */ - public static final String makeJavaIdentifierForAttribute(String identifier) { + public static String makeJavaIdentifierForAttribute(String identifier) { return makeJavaIdentifier(identifier, false); } /** * Converts the given identifier to a legal Java identifier. * - * @param identifier - * Identifier to convert + * @param identifier Identifier to convert * * @return Legal Java identifier corresponding to the given identifier */ - private static String makeJavaIdentifier(String identifier, - boolean periodToUnderscore) { + private static String makeJavaIdentifier(String identifier, boolean periodToUnderscore) { StringBuilder modifiedIdentifier = new StringBuilder(identifier.length()); if (!Character.isJavaIdentifierStart(identifier.charAt(0))) { modifiedIdentifier.append('_'); } for (int i = 0; i < identifier.length(); i++) { char ch = identifier.charAt(i); - if (Character.isJavaIdentifierPart(ch) && - (ch != '_' || !periodToUnderscore)) { + if (Character.isJavaIdentifierPart(ch) && (ch != '_' || !periodToUnderscore)) { modifiedIdentifier.append(ch); } else if (ch == '.' && periodToUnderscore) { modifiedIdentifier.append('_'); @@ -811,10 +738,12 @@ /** * Mangle the specified character to create a legal Java class name. + * * @param ch The character + * * @return the replacement character as a string */ - public static final String mangleChar(char ch) { + public static String mangleChar(char ch) { char[] result = new char[5]; result[0] = '_'; result[1] = Character.forDigit((ch >> 12) & 0xf, 16); @@ -826,7 +755,9 @@ /** * Test whether the argument is a Java keyword. + * * @param key The name + * * @return true if the name is a java identifier */ public static boolean isJavaKeyword(String key) { @@ -847,16 +778,14 @@ return false; } - static InputStreamReader getReader(String fname, String encoding, - Jar jar, JspCompilationContext ctxt, ErrorDispatcher err) - throws JasperException, IOException { + static InputStreamReader getReader(String fname, String encoding, Jar jar, JspCompilationContext ctxt, + ErrorDispatcher err) throws JasperException, IOException { return getReader(fname, encoding, jar, ctxt, err, 0); } - static InputStreamReader getReader(String fname, String encoding, - Jar jar, JspCompilationContext ctxt, ErrorDispatcher err, int skip) - throws JasperException, IOException { + static InputStreamReader getReader(String fname, String encoding, Jar jar, JspCompilationContext ctxt, + ErrorDispatcher err, int skip) throws JasperException, IOException { InputStreamReader reader = null; InputStream in = getInputStream(fname, jar, ctxt); @@ -867,7 +796,7 @@ } catch (IOException ioe) { try { in.close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } throw ioe; @@ -882,11 +811,11 @@ } /** - * Handles taking input from TLDs 'java.lang.Object' -> - * 'java.lang.Object.class' 'int' -> 'int.class' 'void' -> 'Void.TYPE' - * 'int[]' -> 'int[].class' + * Handles taking input from TLDs 'java.lang.Object' -> 'java.lang.Object.class' 'int' -> 'int.class' 'void' + * -> 'Void.TYPE' 'int[]' -> 'int[].class' * * @param type The type from the TLD + * * @return the Java type */ public static String toJavaSourceTypeFromTld(String type) { @@ -897,10 +826,11 @@ } /** - * Class.getName() return arrays in the form "[[[<et>", where et, the - * element type can be one of ZBCDFIJS or L<classname>;. It is - * converted into forms that can be understood by javac. + * Class.getName() return arrays in the form "[[[<et>", where et, the element type can be one of ZBCDFIJS or + * L<classname>;. It is converted into forms that can be understood by javac. + * * @param type the type to convert + * * @return the equivalent type in Java sources */ public static String toJavaSourceType(String type) { @@ -916,15 +846,33 @@ dims++; } else { switch (type.charAt(i)) { - case 'Z': t = "boolean"; break; - case 'B': t = "byte"; break; - case 'C': t = "char"; break; - case 'D': t = "double"; break; - case 'F': t = "float"; break; - case 'I': t = "int"; break; - case 'J': t = "long"; break; - case 'S': t = "short"; break; - case 'L': t = type.substring(i+1, type.indexOf(';')); break; + case 'Z': + t = "boolean"; + break; + case 'B': + t = "byte"; + break; + case 'C': + t = "char"; + break; + case 'D': + t = "double"; + break; + case 'F': + t = "float"; + break; + case 'I': + t = "int"; + break; + case 'J': + t = "long"; + break; + case 'S': + t = "short"; + break; + case 'L': + t = type.substring(i + 1, type.indexOf(';')); + break; } break; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/Localizer.java tomcat10-10.1.52/java/org/apache/jasper/compiler/Localizer.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/Localizer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/Localizer.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,10 +23,7 @@ import org.apache.jasper.runtime.ExceptionUtils; /** - * Class responsible for converting error codes to corresponding localized - * error messages. - * - * @author Jan Luehe + * Class responsible for converting error codes to corresponding localized error messages. */ public class Localizer { @@ -41,11 +38,10 @@ } /* - * Returns the localized error message corresponding to the given error - * code. + * Returns the localized error message corresponding to the given error code. * - * If the given error code is not defined in the resource bundle for - * localized error messages, it is used as the error message. + * If the given error code is not defined in the resource bundle for localized error messages, it is used as the + * error message. * * @param errCode Error code to localize * @@ -58,18 +54,19 @@ errMsg = bundle.getString(errCode); } } catch (MissingResourceException e) { + // Ignore } return errMsg; } /* - * Returns the localized error message corresponding to the given error - * code. + * Returns the localized error message corresponding to the given error code. * - * If the given error code is not defined in the resource bundle for - * localized error messages, it is used as the error message. + * If the given error code is not defined in the resource bundle for localized error messages, it is used as the + * error message. * * @param errCode Error code to localize + * * @param args Arguments for parametric replacement * * @return Localized error message diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/Mark.java tomcat10-10.1.52/java/org/apache/jasper/compiler/Mark.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/Mark.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/Mark.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,10 +23,8 @@ /** * Mark represents a point in the JSP input. - * - * @author Anil K. Vijendran */ -final class Mark { +public final class Mark { // position within current stream int cursor, line, col; @@ -42,9 +40,9 @@ /** * Constructor * - * @param reader JspReader this mark belongs to + * @param reader JspReader this mark belongs to * @param inStream current stream for this mark - * @param name JSP file name + * @param name JSP file name */ Mark(JspReader reader, char[] inStream, String name) { this.ctxt = reader.getJspCompilationContext(); @@ -60,7 +58,7 @@ * Constructor */ Mark(Mark other) { - init(other, false); + init(other, false); } void update(int cursor, int line, int col) { @@ -105,7 +103,7 @@ @Override public String toString() { - return getFile()+"("+line+","+col+")"; + return getFile() + "(" + line + "," + col + ")"; } public String getFile() { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/NewlineReductionServletWriter.java tomcat10-10.1.52/java/org/apache/jasper/compiler/NewlineReductionServletWriter.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/NewlineReductionServletWriter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/NewlineReductionServletWriter.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,13 +19,10 @@ import java.io.PrintWriter; /** - * This class filters duplicate newlines instructions from the compiler output, - * and therefore from the runtime JSP. The duplicates typically happen because - * the compiler has multiple branches that write them, but they operate - * independently and don't realize that the previous output was identical. - * - * Removing these lines makes the JSP more efficient by executing fewer - * operations during runtime. + * This class filters duplicate newlines instructions from the compiler output, and therefore from the runtime JSP. The + * duplicates typically happen because the compiler has multiple branches that write them, but they operate + * independently and don't realize that the previous output was identical. Removing these lines makes the JSP more + * efficient by executing fewer operations during runtime. */ public class NewlineReductionServletWriter extends ServletWriter { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/Node.java tomcat10-10.1.52/java/org/apache/jasper/compiler/Node.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/Node.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/Node.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,16 +40,10 @@ import org.xml.sax.Attributes; /** - * An internal data representation of a JSP page or a JSP document (XML). Also - * included here is a visitor class for traversing nodes. - * - * @author Kin-man Chung - * @author Jan Luehe - * @author Shawn Bayern - * @author Mark Roth + * An internal data representation of a JSP page or a JSP document (XML). Also included here is a visitor class for + * traversing nodes. */ - -abstract class Node implements TagConstants { +public abstract class Node implements TagConstants { private static final VariableInfo[] ZERO_VARIABLE_INFO = {}; @@ -82,10 +76,9 @@ protected String localName; /* - * The name of the inner class to which the codes for this node and its body - * are generated. For instance, for in foo.jsp, this is - * "foo_jspHelper". This is primarily used for communicating such info from - * Generator to Smap generator. + * The name of the inner class to which the codes for this node and its body are generated. For instance, for + * in foo.jsp, this is "foo_jspHelper". This is primarily used for communicating such info from Generator + * to Smap generator. */ protected String innerClassName; @@ -99,10 +92,8 @@ /** * Constructor. * - * @param start - * The location of the jsp page - * @param parent - * The enclosing node + * @param start The location of the jsp page + * @param parent The enclosing node */ Node(Mark start, Node parent) { this.startMark = start; @@ -112,19 +103,13 @@ /** * Constructor for Nodes parsed from standard syntax. * - * @param qName - * The action's qualified name - * @param localName - * The action's local name - * @param attrs - * The attributes for this node - * @param start - * The location of the jsp page - * @param parent - * The enclosing node + * @param qName The action's qualified name + * @param localName The action's local name + * @param attrs The attributes for this node + * @param start The location of the jsp page + * @param parent The enclosing node */ - Node(String qName, String localName, Attributes attrs, Mark start, - Node parent) { + Node(String qName, String localName, Attributes attrs, Mark start, Node parent) { this.qName = qName; this.localName = localName; this.attrs = attrs; @@ -135,25 +120,16 @@ /** * Constructor for Nodes parsed from XML syntax. * - * @param qName - * The action's qualified name - * @param localName - * The action's local name - * @param attrs - * The action's attributes whose name does not start with xmlns - * @param nonTaglibXmlnsAttrs - * The action's xmlns attributes that do not represent tag - * libraries - * @param taglibAttrs - * The action's xmlns attributes that represent tag libraries - * @param start - * The location of the jsp page - * @param parent - * The enclosing node - */ - Node(String qName, String localName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, - Node parent) { + * @param qName The action's qualified name + * @param localName The action's local name + * @param attrs The action's attributes whose name does not start with xmlns + * @param nonTaglibXmlnsAttrs The action's xmlns attributes that do not represent tag libraries + * @param taglibAttrs The action's xmlns attributes that represent tag libraries + * @param start The location of the jsp page + * @param parent The enclosing node + */ + Node(String qName, String localName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, + Mark start, Node parent) { this.qName = qName; this.localName = localName; this.attrs = attrs; @@ -166,12 +142,10 @@ /* * Constructor. * - * @param qName The action's qualified name @param localName The action's - * local name @param text The text associated with this node @param start - * The location of the jsp page @param parent The enclosing node + * @param qName The action's qualified name @param localName The action's local name @param text The text associated + * with this node @param start The location of the jsp page @param parent The enclosing node */ - Node(String qName, String localName, String text, Mark start, - Node parent) { + Node(String qName, String localName, String text, Mark start, Node parent) { this.qName = qName; this.localName = localName; this.text = text; @@ -190,27 +164,25 @@ /* * Gets this Node's attributes. * - * In the case of a Node parsed from standard syntax, this method returns - * all the Node's attributes. + * In the case of a Node parsed from standard syntax, this method returns all the Node's attributes. * - * In the case of a Node parsed from XML syntax, this method returns only - * those attributes whose name does not start with xmlns. + * In the case of a Node parsed from XML syntax, this method returns only those attributes whose name does not start + * with xmlns. */ public Attributes getAttributes() { return this.attrs; } /* - * Gets this Node's xmlns attributes that represent tag libraries (only - * meaningful for Nodes parsed from XML syntax) + * Gets this Node's xmlns attributes that represent tag libraries (only meaningful for Nodes parsed from XML syntax) */ public Attributes getTaglibAttributes() { return this.taglibAttrs; } /* - * Gets this Node's xmlns attributes that do not represent tag libraries - * (only meaningful for Nodes parsed from XML syntax) + * Gets this Node's xmlns attributes that do not represent tag libraries (only meaningful for Nodes parsed from XML + * syntax) */ public Attributes getNonTaglibXmlnsAttributes() { return this.nonTaglibXmlnsAttrs; @@ -225,8 +197,8 @@ } /** - * Get the attribute that is non request time expression, either from the - * attribute of the node, or from a jsp:attribute + * Get the attribute that is non request time expression, either from the attribute of the node, or from a + * jsp:attribute * * @param name The name of the attribute * @@ -248,15 +220,13 @@ } /** - * Searches all sub-nodes of this node for jsp:attribute standard actions - * with the given name. + * Searches all sub-nodes of this node for jsp:attribute standard actions with the given name. *

          - * This should always be called and only be called for nodes that accept - * dynamic runtime attribute expressions. + * This should always be called and only be called for nodes that accept dynamic runtime attribute expressions. * * @param name The name of the attribute - * @return the NamedAttribute node of the matching named attribute, nor null - * if no such node is found. + * + * @return the NamedAttribute node of the matching named attribute, nor null if no such node is found. */ public NamedAttribute getNamedAttributeNode(String name) { NamedAttribute result = null; @@ -266,7 +236,7 @@ int numChildNodes = nodes.size(); for (int i = 0; i < numChildNodes; i++) { NamedAttribute na = (NamedAttribute) nodes.getNode(i); - boolean found = false; + boolean found; int index = name.indexOf(':'); if (index != -1) { // qualified name @@ -284,11 +254,10 @@ } /** - * Searches all subnodes of this node for jsp:attribute standard actions, - * and returns that set of nodes as a Node.Nodes object. + * Searches all subnodes of this node for jsp:attribute standard actions, and returns that set of nodes as a + * Node.Nodes object. * - * @return Possibly empty Node.Nodes object containing any jsp:attribute - * subnodes of this Node + * @return Possibly empty Node.Nodes object containing any jsp:attribute subnodes of this Node */ public Node.Nodes getNamedAttributeNodes() { @@ -371,11 +340,10 @@ } /** - * Selects and invokes a method in the visitor class based on the node type. - * This is abstract and should be overrode by the extending classes. + * Selects and invokes a method in the visitor class based on the node type. This is abstract and should be + * overridden by the extending classes. * - * @param v - * The visitor class + * @param v The visitor class */ abstract void accept(Visitor v) throws JasperException; @@ -416,28 +384,23 @@ private String jspConfigPageEnc; /* - * Flag indicating if the default page encoding is being used (only - * applicable with standard syntax). + * Flag indicating if the default page encoding is being used (only applicable with standard syntax). * - * True if the page does not provide a page directive with a - * 'contentType' attribute (or the 'contentType' attribute doesn't have - * a CHARSET value), the page does not provide a page directive with a - * 'pageEncoding' attribute, and there is no JSP configuration element - * page-encoding whose URL pattern matches the page. + * True if the page does not provide a page directive with a 'contentType' attribute (or the 'contentType' + * attribute doesn't have a CHARSET value), the page does not provide a page directive with a 'pageEncoding' + * attribute, and there is no JSP configuration element page-encoding whose URL pattern matches the page. */ private boolean isDefaultPageEncoding; /* - * Indicates whether an encoding has been explicitly specified in the - * page's XML prolog (only used for pages in XML syntax). This - * information is used to decide whether a translation error must be - * reported for encoding conflicts. + * Indicates whether an encoding has been explicitly specified in the page's XML prolog (only used for pages in + * XML syntax). This information is used to decide whether a translation error must be reported for encoding + * conflicts. */ private boolean isEncodingSpecifiedInProlog; /* - * Indicates whether an encoding has been explicitly specified in the - * page's dom. + * Indicates whether an encoding has been explicitly specified in the page's dom. */ private boolean isBomPresent; @@ -474,16 +437,16 @@ } /* - * Sets the encoding specified in the JSP config element whose URL - * pattern matches the page containing this Root. + * Sets the encoding specified in the JSP config element whose URL pattern matches the page containing this + * Root. */ public void setJspConfigPageEncoding(String enc) { jspConfigPageEnc = enc; } /* - * Gets the encoding specified in the JSP config element whose URL - * pattern matches the page containing this Root. + * Gets the encoding specified in the JSP config element whose URL pattern matches the page containing this + * Root. */ public String getJspConfigPageEncoding() { return jspConfigPageEnc; @@ -541,11 +504,9 @@ */ public static class JspRoot extends Node { - JspRoot(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, ROOT_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + JspRoot(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, ROOT_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -565,11 +526,9 @@ this(JSP_PAGE_DIRECTIVE_ACTION, attrs, null, null, start, parent); } - PageDirective(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, + PageDirective(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { - super(qName, PAGE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + super(qName, PAGE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); imports = new ArrayList<>(); } @@ -579,12 +538,10 @@ } /** - * Parses the comma-separated list of class or package names in the - * given attribute value and adds each component to this PageDirective's - * vector of imported classes and packages. + * Parses the comma-separated list of class or package names in the given attribute value and adds each + * component to this PageDirective's vector of imported classes and packages. * - * @param value - * A comma-separated string of imports. + * @param value A comma-separated string of imports. */ public void addImport(String value) { int start = 0; @@ -606,16 +563,14 @@ } /** - * Just need enough validation to make sure nothing strange is going on. - * The compiler will validate this thoroughly when it tries to compile - * the resulting .java file. + * Just need enough validation to make sure nothing strange is going on. The compiler will validate this + * thoroughly when it tries to compile the resulting .java file. */ private String validateImport(String importEntry) { // This should either be a fully-qualified class name or a package // name with a wildcard if (importEntry.indexOf(';') > -1) { - throw new IllegalArgumentException( - Localizer.getMessage("jsp.error.page.invalid.import")); + throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.import")); } return importEntry.trim(); } @@ -630,11 +585,9 @@ this(JSP_INCLUDE_DIRECTIVE_ACTION, attrs, null, null, start, parent); } - IncludeDirective(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, + IncludeDirective(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { - super(qName, INCLUDE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + super(qName, INCLUDE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -649,8 +602,7 @@ public static class TaglibDirective extends Node { TaglibDirective(Attributes attrs, Mark start, Node parent) { - super(JSP_TAGLIB_DIRECTIVE_ACTION, TAGLIB_DIRECTIVE_ACTION, attrs, - start, parent); + super(JSP_TAGLIB_DIRECTIVE_ACTION, TAGLIB_DIRECTIVE_ACTION, attrs, start, parent); } @Override @@ -669,11 +621,9 @@ this(JSP_TAG_DIRECTIVE_ACTION, attrs, null, null, start, parent); } - TagDirective(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, TAG_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + TagDirective(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, TAG_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); imports = new ArrayList<>(); } @@ -683,12 +633,10 @@ } /** - * Parses the comma-separated list of class or package names in the - * given attribute value and adds each component to this PageDirective's - * vector of imported classes and packages. + * Parses the comma-separated list of class or package names in the given attribute value and adds each + * component to this PageDirective's vector of imported classes and packages. * - * @param value - * A comma-separated string of imports. + * @param value A comma-separated string of imports. */ public void addImport(String value) { int start = 0; @@ -716,15 +664,12 @@ public static class AttributeDirective extends Node { AttributeDirective(Attributes attrs, Mark start, Node parent) { - this(JSP_ATTRIBUTE_DIRECTIVE_ACTION, attrs, null, null, start, - parent); + this(JSP_ATTRIBUTE_DIRECTIVE_ACTION, attrs, null, null, start, parent); } - AttributeDirective(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, + AttributeDirective(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { - super(qName, ATTRIBUTE_DIRECTIVE_ACTION, attrs, - nonTaglibXmlnsAttrs, taglibAttrs, start, parent); + super(qName, ATTRIBUTE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -739,15 +684,12 @@ public static class VariableDirective extends Node { VariableDirective(Attributes attrs, Mark start, Node parent) { - this(JSP_VARIABLE_DIRECTIVE_ACTION, attrs, null, null, start, - parent); + this(JSP_VARIABLE_DIRECTIVE_ACTION, attrs, null, null, start, parent); } - VariableDirective(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, + VariableDirective(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { - super(qName, VARIABLE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + super(qName, VARIABLE_DIRECTIVE_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -765,11 +707,9 @@ this(JSP_INVOKE_ACTION, attrs, null, null, start, parent); } - InvokeAction(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, INVOKE_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + InvokeAction(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, INVOKE_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -787,11 +727,9 @@ this(JSP_DOBODY_ACTION, attrs, null, null, start, parent); } - DoBodyAction(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, DOBODY_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + DoBodyAction(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, DOBODY_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -820,22 +758,18 @@ */ public abstract static class ScriptingElement extends Node { - ScriptingElement(String qName, String localName, String text, - Mark start, Node parent) { + ScriptingElement(String qName, String localName, String text, Mark start, Node parent) { super(qName, localName, text, start, parent); } - ScriptingElement(String qName, String localName, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, + ScriptingElement(String qName, String localName, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { - super(qName, localName, null, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + super(qName, localName, null, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } /** - * When this node was created from a JSP page in JSP syntax, its text - * was stored as a String in the "text" field, whereas when this node - * was created from a JSP document, its text was stored as one or more + * When this node was created from a JSP page in JSP syntax, its text was stored as a String in the "text" + * field, whereas when this node was created from a JSP document, its text was stored as one or more * TemplateText nodes in its body. This method handles either case. * * @return The text string @@ -859,8 +793,7 @@ } /** - * For the same reason as above, the source line information in the - * contained TemplateText node should be used. + * For the same reason as above, the source line information in the contained TemplateText node should be used. */ @Override public Mark getStart() { @@ -878,14 +811,11 @@ public static class Declaration extends ScriptingElement { Declaration(String text, Mark start, Node parent) { - super(JSP_DECLARATION_ACTION, DECLARATION_ACTION, text, start, - parent); + super(JSP_DECLARATION_ACTION, DECLARATION_ACTION, text, start, parent); } - Declaration(String qName, Attributes nonTaglibXmlnsAttrs, - Attributes taglibAttrs, Mark start, Node parent) { - super(qName, DECLARATION_ACTION, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + Declaration(String qName, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { + super(qName, DECLARATION_ACTION, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -895,8 +825,7 @@ } /** - * Represents an expression. Expressions in attributes are embedded in the - * attribute string and not here. + * Represents an expression. Expressions in attributes are embedded in the attribute string and not here. */ public static class Expression extends ScriptingElement { @@ -904,10 +833,8 @@ super(JSP_EXPRESSION_ACTION, EXPRESSION_ACTION, text, start, parent); } - Expression(String qName, Attributes nonTaglibXmlnsAttrs, - Attributes taglibAttrs, Mark start, Node parent) { - super(qName, EXPRESSION_ACTION, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + Expression(String qName, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { + super(qName, EXPRESSION_ACTION, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -925,10 +852,8 @@ super(JSP_SCRIPTLET_ACTION, SCRIPTLET_ACTION, text, start, parent); } - Scriptlet(String qName, Attributes nonTaglibXmlnsAttrs, - Attributes taglibAttrs, Mark start, Node parent) { - super(qName, SCRIPTLET_ACTION, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + Scriptlet(String qName, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { + super(qName, SCRIPTLET_ACTION, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -938,8 +863,7 @@ } /** - * Represents an EL expression. Expressions in attributes are embedded in - * the attribute string and not here. + * Represents an EL expression. Expressions in attributes are embedded in the attribute string and not here. */ public static class ELExpression extends Node { @@ -981,11 +905,9 @@ this(JSP_PARAM_ACTION, attrs, null, null, start, parent); } - ParamAction(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, PARAM_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + ParamAction(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, PARAM_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1011,10 +933,8 @@ this(JSP_PARAMS_ACTION, null, null, start, parent); } - ParamsAction(String qName, Attributes nonTaglibXmlnsAttrs, - Attributes taglibAttrs, Mark start, Node parent) { - super(qName, PARAMS_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + ParamsAction(String qName, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { + super(qName, PARAMS_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1032,10 +952,8 @@ this(JSP_FALLBACK_ACTION, null, null, start, parent); } - FallBackAction(String qName, Attributes nonTaglibXmlnsAttrs, - Attributes taglibAttrs, Mark start, Node parent) { - super(qName, FALLBACK_ACTION, null, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + FallBackAction(String qName, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { + super(qName, FALLBACK_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1055,11 +973,9 @@ this(JSP_INCLUDE_ACTION, attrs, null, null, start, parent); } - IncludeAction(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, + IncludeAction(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { - super(qName, INCLUDE_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + super(qName, INCLUDE_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1087,11 +1003,9 @@ this(JSP_FORWARD_ACTION, attrs, null, null, start, parent); } - ForwardAction(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, + ForwardAction(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { - super(qName, FORWARD_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + super(qName, FORWARD_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1117,11 +1031,9 @@ this(JSP_GET_PROPERTY_ACTION, attrs, null, null, start, parent); } - GetProperty(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, GET_PROPERTY_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + GetProperty(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, GET_PROPERTY_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1141,11 +1053,9 @@ this(JSP_SET_PROPERTY_ACTION, attrs, null, null, start, parent); } - SetProperty(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, SET_PROPERTY_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + SetProperty(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, SET_PROPERTY_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1173,11 +1083,9 @@ this(JSP_USE_BEAN_ACTION, attrs, null, null, start, parent); } - UseBean(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, USE_BEAN_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + UseBean(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, USE_BEAN_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1207,11 +1115,9 @@ this(JSP_PLUGIN_ACTION, attrs, null, null, start, parent); } - PlugIn(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, PLUGIN_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + PlugIn(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, PLUGIN_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1243,11 +1149,9 @@ private JspAttribute[] jspAttrs; - UninterpretedTag(String qName, String localName, - Attributes attrs, Attributes nonTaglibXmlnsAttrs, + UninterpretedTag(String qName, String localName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { - super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1277,11 +1181,9 @@ this(JSP_ELEMENT_ACTION, attrs, null, null, start, parent); } - JspElement(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, ELEMENT_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + JspElement(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, ELEMENT_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1317,11 +1219,9 @@ */ public static class JspOutput extends Node { - JspOutput(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, - Mark start, Node parent) { - super(qName, OUTPUT_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + JspOutput(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, + Node parent) { + super(qName, OUTPUT_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1331,8 +1231,8 @@ } /** - * Collected information about child elements. Used by nodes like CustomTag, - * JspBody, and NamedAttribute. The information is set in the Collector. + * Collected information about child elements. Used by nodes like CustomTag, JspBody, and NamedAttribute. The + * information is set in the Collector. */ public static class ChildInfo { private boolean scriptless; // true if the tag and its body @@ -1402,9 +1302,8 @@ private final ChildInfo childInfo = new ChildInfo(); - ChildInfoBase(String qName, String localName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, - Node parent) { + ChildInfoBase(String qName, String localName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, + Attributes taglibAttrs, Mark start, Node parent) { super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @@ -1466,10 +1365,9 @@ private TagPluginContext tagPluginContext; /** - * The following two fields are used for holding the Java scriptlets - * that the tag plugins may generate. Meaningful only if useTagPlugin is - * true; Could move them into TagPluginContextImpl, but we'll need to - * cast tagPluginContext to TagPluginContextImpl all the time... + * The following two fields are used for holding the Java scriptlets that the tag plugins may generate. + * Meaningful only if useTagPlugin is true; Could move them into TagPluginContextImpl, but we'll need to cast + * tagPluginContext to TagPluginContextImpl all the time... */ private Nodes atSTag; @@ -1478,22 +1376,18 @@ /* * Constructor for custom action implemented by tag handler. */ - CustomTag(String qName, String prefix, String localName, - String uri, Attributes attrs, Mark start, Node parent, + CustomTag(String qName, String prefix, String localName, String uri, Attributes attrs, Mark start, Node parent, TagInfo tagInfo, Class tagHandlerClass) { - this(qName, prefix, localName, uri, attrs, null, null, start, - parent, tagInfo, tagHandlerClass); + this(qName, prefix, localName, uri, attrs, null, null, start, parent, tagInfo, tagHandlerClass); } /* * Constructor for custom action implemented by tag handler. */ - CustomTag(String qName, String prefix, String localName, - String uri, Attributes attrs, Attributes nonTaglibXmlnsAttrs, - Attributes taglibAttrs, Mark start, Node parent, - TagInfo tagInfo, Class tagHandlerClass) { - super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + CustomTag(String qName, String prefix, String localName, String uri, Attributes attrs, + Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent, TagInfo tagInfo, + Class tagHandlerClass) { + super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); this.uri = uri; this.prefix = prefix; @@ -1502,40 +1396,30 @@ this.tagHandlerClass = tagHandlerClass; this.customNestingLevel = makeCustomNestingLevel(); - this.implementsIterationTag = IterationTag.class - .isAssignableFrom(tagHandlerClass); - this.implementsBodyTag = BodyTag.class - .isAssignableFrom(tagHandlerClass); - this.implementsTryCatchFinally = TryCatchFinally.class - .isAssignableFrom(tagHandlerClass); - this.implementsSimpleTag = SimpleTag.class - .isAssignableFrom(tagHandlerClass); - this.implementsDynamicAttributes = DynamicAttributes.class - .isAssignableFrom(tagHandlerClass); - this.implementsJspIdConsumer = JspIdConsumer.class - .isAssignableFrom(tagHandlerClass); + this.implementsIterationTag = IterationTag.class.isAssignableFrom(tagHandlerClass); + this.implementsBodyTag = BodyTag.class.isAssignableFrom(tagHandlerClass); + this.implementsTryCatchFinally = TryCatchFinally.class.isAssignableFrom(tagHandlerClass); + this.implementsSimpleTag = SimpleTag.class.isAssignableFrom(tagHandlerClass); + this.implementsDynamicAttributes = DynamicAttributes.class.isAssignableFrom(tagHandlerClass); + this.implementsJspIdConsumer = JspIdConsumer.class.isAssignableFrom(tagHandlerClass); } /* * Constructor for custom action implemented by tag file. */ - CustomTag(String qName, String prefix, String localName, - String uri, Attributes attrs, Mark start, Node parent, + CustomTag(String qName, String prefix, String localName, String uri, Attributes attrs, Mark start, Node parent, TagFileInfo tagFileInfo) { - this(qName, prefix, localName, uri, attrs, null, null, start, - parent, tagFileInfo); + this(qName, prefix, localName, uri, attrs, null, null, start, parent, tagFileInfo); } /* * Constructor for custom action implemented by tag file. */ - CustomTag(String qName, String prefix, String localName, - String uri, Attributes attrs, Attributes nonTaglibXmlnsAttrs, - Attributes taglibAttrs, Mark start, Node parent, + CustomTag(String qName, String prefix, String localName, String uri, Attributes attrs, + Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent, TagFileInfo tagFileInfo) { - super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + super(qName, localName, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); this.uri = uri; this.prefix = prefix; @@ -1607,8 +1491,7 @@ } /* - * @return true if this custom action is supported by a tag file, false - * otherwise + * @return true if this custom action is supported by a tag file, false otherwise */ public boolean isTagFile() { return tagFileInfo != null; @@ -1672,57 +1555,55 @@ public void setScriptingVars(List vec, int scope) { switch (scope) { - case VariableInfo.AT_BEGIN: - this.atBeginScriptingVars = vec; - break; - case VariableInfo.AT_END: - this.atEndScriptingVars = vec; - break; - case VariableInfo.NESTED: - this.nestedScriptingVars = vec; - break; - default: - throw new IllegalArgumentException( - Localizer.getMessage("jsp.error.page.invalid.varscope", Integer.valueOf(scope))); + case VariableInfo.AT_BEGIN: + this.atBeginScriptingVars = vec; + break; + case VariableInfo.AT_END: + this.atEndScriptingVars = vec; + break; + case VariableInfo.NESTED: + this.nestedScriptingVars = vec; + break; + default: + throw new IllegalArgumentException( + Localizer.getMessage("jsp.error.page.invalid.varscope", Integer.valueOf(scope))); } } /* - * Gets the scripting variables for the given scope that need to be - * declared. + * Gets the scripting variables for the given scope that need to be declared. */ public List getScriptingVars(int scope) { List vec = null; switch (scope) { - case VariableInfo.AT_BEGIN: - vec = this.atBeginScriptingVars; - break; - case VariableInfo.AT_END: - vec = this.atEndScriptingVars; - break; - case VariableInfo.NESTED: - vec = this.nestedScriptingVars; - break; - default: - throw new IllegalArgumentException( - Localizer.getMessage("jsp.error.page.invalid.varscope", Integer.valueOf(scope))); + case VariableInfo.AT_BEGIN: + vec = this.atBeginScriptingVars; + break; + case VariableInfo.AT_END: + vec = this.atEndScriptingVars; + break; + case VariableInfo.NESTED: + vec = this.nestedScriptingVars; + break; + default: + throw new IllegalArgumentException( + Localizer.getMessage("jsp.error.page.invalid.varscope", Integer.valueOf(scope))); } return vec; } /* - * Gets this custom tag's custom nesting level, which is given as the - * number of times this custom tag is nested inside itself. + * Gets this custom tag's custom nesting level, which is given as the number of times this custom tag is nested + * inside itself. */ public int getCustomNestingLevel() { return customNestingLevel; } /** - * Checks to see if the attribute of the given name is of type - * JspFragment. + * Checks to see if the attribute of the given name is of type JspFragment. * * @param name The attribute to check * @@ -1733,8 +1614,7 @@ TagAttributeInfo[] attributes = tagInfo.getAttributes(); for (TagAttributeInfo attribute : attributes) { - if (attribute.getName().equals(name) - && attribute.isFragment()) { + if (attribute.getName().equals(name) && attribute.isFragment()) { result = true; break; } @@ -1776,14 +1656,13 @@ } /* - * Computes this custom tag's custom nesting level, which corresponds to - * the number of times this custom tag is nested inside itself. + * Computes this custom tag's custom nesting level, which corresponds to the number of times this custom tag is + * nested inside itself. * * Example: * - * -- nesting level 0 -- nesting level 1 - * -- nesting level 2 -- nesting level 1 - * + * -- nesting level 0 -- nesting level 1 -- nesting level 2 + * -- nesting level 1 * * @return Custom tag's nesting level */ @@ -1791,8 +1670,7 @@ int n = 0; Node p = parent; while (p != null) { - if ((p instanceof Node.CustomTag) - && qName.equals(((Node.CustomTag) p).qName)) { + if ((p instanceof Node.CustomTag) && qName.equals(p.qName)) { n++; } p = p.parent; @@ -1801,12 +1679,14 @@ } /** - * A custom action is considered to have an empty body if the following - * holds true: - getBody() returns null, or - all immediate children are - * jsp:attribute actions, or - the action's jsp:body is empty. + * A custom action is considered to have an empty body if any of the following hold true: + *
            + *
          • getBody() returns null
          • + *
          • all immediate children are jsp:attribute actions
          • + *
          • the action's jsp:body is empty
          • + *
          * - * @return {@code true} if this custom action has an empty body, and - * {@code false} otherwise. + * @return {@code true} if this custom action has an empty body, and {@code false} otherwise. */ public boolean hasEmptyBody() { boolean hasEmptyBody = true; @@ -1831,13 +1711,13 @@ } /** - * Used as a placeholder for the evaluation code of a custom action - * attribute (used by the tag plugin machinery only). + * Used as a placeholder for the evaluation code of a custom action attribute (used by the tag plugin machinery + * only). */ public static class AttributeGenerator extends Node { - private String name; // name of the attribute + private final String name; // name of the attribute - private CustomTag tag; // The tag this attribute belongs to + private final CustomTag tag; // The tag this attribute belongs to AttributeGenerator(Mark start, String name, CustomTag tag) { super(start, null); @@ -1864,10 +1744,8 @@ */ public static class JspText extends Node { - JspText(String qName, Attributes nonTaglibXmlnsAttrs, - Attributes taglibAttrs, Mark start, Node parent) { - super(qName, TEXT_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + JspText(String qName, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { + super(qName, TEXT_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -1901,12 +1779,10 @@ this(JSP_ATTRIBUTE_ACTION, attrs, null, null, start, parent); } - NamedAttribute(String qName, Attributes attrs, - Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, + NamedAttribute(String qName, Attributes attrs, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { - super(qName, ATTRIBUTE_ACTION, attrs, nonTaglibXmlnsAttrs, - taglibAttrs, start, parent); + super(qName, ATTRIBUTE_ACTION, attrs, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); if ("false".equals(this.getAttributeValue("trim"))) { // (if null or true, leave default of true) trim = false; @@ -1954,8 +1830,8 @@ } /** - * @return A unique temporary variable name to store the result in. - * (this probably could go elsewhere, but it's convenient here) + * @return A unique temporary variable name to store the result in. (this probably could go elsewhere, but it's + * convenient here) */ public String getTemporaryVariableName() { if (temporaryVariableName == null) { @@ -1965,9 +1841,8 @@ } /* - * Get the attribute value from this named attribute (). - * Since this method is only for attributes that are not rtexpr, we can - * assume the body of the jsp:attribute is a template text. + * Get the attribute value from this named attribute (). Since this method is only for attributes + * that are not rtexpr, we can assume the body of the jsp:attribute is a template text. */ @Override public String getText() { @@ -1994,6 +1869,7 @@ try { getBody().visit(attributeVisitor); } catch (JasperException e) { + // Ignore } text = attributeVisitor.getAttrValue(); } @@ -2011,10 +1887,8 @@ this(JSP_BODY_ACTION, null, null, start, parent); } - JspBody(String qName, Attributes nonTaglibXmlnsAttrs, - Attributes taglibAttrs, Mark start, Node parent) { - super(qName, BODY_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs, - start, parent); + JspBody(String qName, Attributes nonTaglibXmlnsAttrs, Attributes taglibAttrs, Mark start, Node parent) { + super(qName, BODY_ACTION, null, nonTaglibXmlnsAttrs, taglibAttrs, start, parent); } @Override @@ -2082,10 +1956,8 @@ /** * Add a source to Java line mapping * - * @param srcLine - * The position of the source line, relative to the line at - * the start of this node. The corresponding java line is - * assumed to be consecutive, i.e. one more than the last. + * @param srcLine The position of the source line, relative to the line at the start of this node. The + * corresponding java line is assumed to be consecutive, i.e. one more than the last. */ public void addSmap(int srcLine) { if (extraSmap == null) { @@ -2100,11 +1972,9 @@ } /** - * Represents attributes that can be request time expressions. - * - * Can either be a plain attribute, an attribute that represents a request - * time expression value, or a named attribute (specified using the - * jsp:attribute standard action). + * Represents attributes that can be request time expressions. Can either be a plain attribute, an attribute that + * represents a request time expression value, or a named attribute (specified using the jsp:attribute standard + * action). */ public static class JspAttribute { @@ -2131,9 +2001,8 @@ // The node in the parse tree for the NamedAttribute private final NamedAttribute namedAttributeNode; - JspAttribute(TagAttributeInfo tai, String qName, String uri, - String localName, String value, boolean expr, ELNode.Nodes el, - boolean dyn) { + JspAttribute(TagAttributeInfo tai, String qName, String uri, String localName, String value, boolean expr, + ELNode.Nodes el, boolean dyn) { this.qName = qName; this.uri = uri; this.localName = localName; @@ -2149,13 +2018,12 @@ /** * Allow node to validate itself. * - * @param ef The expression factory to use to evaluate any EL + * @param ef The expression factory to use to evaluate any EL * @param ctx The context to use to evaluate any EL * * @throws ELException If validation fails */ - public void validateEL(ExpressionFactory ef, ELContext ctx) - throws ELException { + public void validateEL(ExpressionFactory ef, ELContext ctx) throws ELException { if (this.el != null) { // determine exact type ef.createValueExpression(ctx, this.value, String.class); @@ -2163,9 +2031,8 @@ } /** - * Use this constructor if the JspAttribute represents a named - * attribute. In this case, we have to store the nodes of the body of - * the attribute. + * Use this constructor if the JspAttribute represents a named attribute. In this case, we have to store the + * nodes of the body of the attribute. */ JspAttribute(NamedAttribute na, TagAttributeInfo tai, boolean dyn) { this.qName = na.getName(); @@ -2195,8 +2062,7 @@ } /** - * @return The namespace of the attribute, or null if in the default - * namespace + * @return The namespace of the attribute, or null if in the default namespace */ public String getURI() { return uri; @@ -2207,19 +2073,17 @@ } /** - * @return return true if there's TagAttributeInfo meaning we need to - * assign a ValueExpression + * @return return true if there's TagAttributeInfo meaning we need to assign a ValueExpression */ public boolean isDeferredInput() { - return (this.tai != null) ? this.tai.isDeferredValue() : false; + return this.tai != null && this.tai.isDeferredValue(); } /** - * @return return true if there's TagAttributeInfo meaning we need to - * assign a MethodExpression + * @return return true if there's TagAttributeInfo meaning we need to assign a MethodExpression */ public boolean isDeferredMethodInput() { - return (this.tai != null) ? this.tai.isDeferredMethod() : false; + return this.tai != null && this.tai.isDeferredMethod(); } public String getExpectedTypeName() { @@ -2247,7 +2111,7 @@ m = m.trim(); m = m.substring(m.indexOf('(') + 1); m = m.substring(0, m.length() - 1); - if (m.trim().length() > 0) { + if (!m.trim().isEmpty()) { String[] p = m.split(","); for (int i = 0; i < p.length; i++) { p[i] = p[i].trim(); @@ -2263,9 +2127,8 @@ /** * Only makes sense if namedAttribute is false. * - * @return the value for the attribute, or the expression string - * (stripped of "<%=", "%>", "%=", or "%" but containing "${" - * and "}" for EL expressions) + * @return the value for the attribute, or the expression string (stripped of "<%=", "%>", "%=", or "%" but + * containing "${" and "}" for EL expressions) */ public String getValue() { return value; @@ -2295,28 +2158,23 @@ } /** - * @return true if the value represents an expression that should be fed - * to the expression interpreter - * false for string literals or rtexprvalues that should not be - * interpreted or reevaluated + * @return true if the value represents an expression that should be fed to the expression interpreter false for + * string literals or rtexprvalues that should not be interpreted or reevaluated */ public boolean isELInterpreterInput() { - return el != null || this.isDeferredInput() - || this.isDeferredMethodInput(); + return el != null || this.isDeferredInput() || this.isDeferredMethodInput(); } /** - * @return true if the value is a string literal known at translation - * time. + * @return true if the value is a string literal known at translation time. */ public boolean isLiteral() { return !expression && (el == null) && !namedAttribute; } /** - * @return {@code true} if the attribute is a "dynamic" attribute of a - * custom tag that implements DynamicAttributes interface. That is, - * a random extra attribute that is not declared by the tag. + * @return {@code true} if the attribute is a "dynamic" attribute of a custom tag that implements + * DynamicAttributes interface. That is, a random extra attribute that is not declared by the tag. */ public boolean isDynamic() { return dynamic; @@ -2328,8 +2186,7 @@ } /** - * An ordered list of Node, used to represent the body of an element, or a - * jsp page of jsp document. + * An ordered list of Node, used to represent the body of an element, or a jsp page of jsp document. */ public static class Nodes { @@ -2352,8 +2209,7 @@ /** * Appends a node to the list * - * @param n - * The node to add + * @param n The node to add */ public void add(Node n) { list.add(n); @@ -2363,8 +2219,7 @@ /** * Removes the given node from the list. * - * @param n - * The node to be removed + * @param n The node to be removed */ public void remove(Node n) { list.remove(n); @@ -2373,8 +2228,7 @@ /** * Visit the nodes in the list with the supplied visitor * - * @param v - * The visitor used + * @param v The visitor used * * @throws JasperException if an error occurs while visiting a node */ @@ -2393,6 +2247,7 @@ try { n = list.get(index); } catch (ArrayIndexOutOfBoundsException e) { + // Ignore } return n; } @@ -2411,16 +2266,15 @@ } /** - * A visitor class for visiting the node. This class also provides the - * default action (i.e. nop) for each of the child class of the Node. An - * actual visitor should extend this class and supply the visit method for - * the nodes that it cares. + * A visitor class for visiting the node. This class also provides the default action (i.e. nop) for each of the + * child class of the Node. An actual visitor should extend this class and supply the visit method for the nodes + * that it cares. */ public static class Visitor { /** - * This method provides a place to put actions that are common to all - * nodes. Override this in the child visitor class if need to. + * This method provides a place to put actions that are common to all nodes. Override this in the child visitor + * class if needed. * * @param n The node to visit */ diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/PageDataImpl.java tomcat10-10.1.52/java/org/apache/jasper/compiler/PageDataImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/PageDataImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/PageDataImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,24 +29,13 @@ import org.xml.sax.helpers.AttributesImpl; /** - * An implementation of jakarta.servlet.jsp.tagext.PageData which - * builds the XML view of a given page. - * - * The XML view is built in two passes: - * - * During the first pass, the FirstPassVisitor collects the attributes of the - * top-level jsp:root and those of the jsp:root elements of any included - * pages, and adds them to the jsp:root element of the XML view. - * In addition, any taglib directives are converted into xmlns: attributes and - * added to the jsp:root element of the XML view. - * This pass ignores any nodes other than JspRoot and TaglibDirective. - * - * During the second pass, the SecondPassVisitor produces the XML view, using - * the combined jsp:root attributes determined in the first pass and any - * remaining pages nodes (this pass ignores any JspRoot and TaglibDirective - * nodes). - * - * @author Jan Luehe + * An implementation of jakarta.servlet.jsp.tagext.PageData which builds the XML view of a given page. The + * XML view is built in two passes: During the first pass, the FirstPassVisitor collects the attributes of the top-level + * jsp:root and those of the jsp:root elements of any included pages, and adds them to the jsp:root element of the XML + * view. In addition, any taglib directives are converted into xmlns: attributes and added to the jsp:root element of + * the XML view. This pass ignores any nodes other than JspRoot and TaglibDirective. During the second pass, the + * SecondPassVisitor produces the XML view, using the combined jsp:root attributes determined in the first pass and any + * remaining pages nodes (this pass ignores any JspRoot and TaglibDirective nodes). */ class PageDataImpl extends PageData implements TagConstants { @@ -58,24 +47,20 @@ private final StringBuilder buf; /** - * @param page the page nodes from which to generate the XML view + * @param page the page nodes from which to generate the XML view * @param compiler The compiler for this page * * @throws JasperException If an error occurs */ - PageDataImpl(Node.Nodes page, Compiler compiler) - throws JasperException { + PageDataImpl(Node.Nodes page, Compiler compiler) throws JasperException { // First pass - FirstPassVisitor firstPass = new FirstPassVisitor(page.getRoot(), - compiler.getPageInfo()); + FirstPassVisitor firstPass = new FirstPassVisitor(page.getRoot(), compiler.getPageInfo()); page.visit(firstPass); // Second pass buf = new StringBuilder(); - SecondPassVisitor secondPass - = new SecondPassVisitor(page.getRoot(), buf, compiler, - firstPass.getJspIdPrefix()); + SecondPassVisitor secondPass = new SecondPassVisitor(page.getRoot(), buf, compiler, firstPass.getJspIdPrefix()); page.visit(secondPass); } @@ -86,22 +71,18 @@ */ @Override public InputStream getInputStream() { - return new ByteArrayInputStream( - buf.toString().getBytes(StandardCharsets.UTF_8)); + return new ByteArrayInputStream(buf.toString().getBytes(StandardCharsets.UTF_8)); } /* - * First-pass Visitor for JspRoot nodes (representing jsp:root elements) - * and TablibDirective nodes, ignoring any other nodes. + * First-pass Visitor for JspRoot nodes (representing jsp:root elements) and TaglibDirective nodes, ignoring any + * other nodes. * - * The purpose of this Visitor is to collect the attributes of the - * top-level jsp:root and those of the jsp:root elements of any included - * pages, and add them to the jsp:root element of the XML view. - * In addition, this Visitor converts any taglib directives into xmlns: - * attributes and adds them to the jsp:root element of the XML view. + * The purpose of this Visitor is to collect the attributes of the top-level jsp:root and those of the jsp:root + * elements of any included pages, and add them to the jsp:root element of the XML view. In addition, this Visitor + * converts any taglib directives into xmlns: attributes and adds them to the jsp:root element of the XML view. */ - private static class FirstPassVisitor - extends Node.Visitor implements TagConstants { + private static class FirstPassVisitor extends Node.Visitor implements TagConstants { private final Node.Root root; private final AttributesImpl rootAttrs; @@ -117,8 +98,7 @@ this.root = root; this.pageInfo = pageInfo; this.rootAttrs = new AttributesImpl(); - this.rootAttrs.addAttribute("", "", "version", "CDATA", - JSP_VERSION); + this.rootAttrs.addAttribute("", "", "version", "CDATA", JSP_VERSION); this.jspIdPrefix = "jsp"; } @@ -129,30 +109,23 @@ /* * Top-level page. * - * Add - * xmlns:jsp="http://java.sun.com/JSP/Page" - * attribute only if not already present. + * Add xmlns:jsp="http://java.sun.com/JSP/Page" attribute only if not already present. */ if (!JSP_URI.equals(rootAttrs.getValue("xmlns:jsp"))) { - rootAttrs.addAttribute("", "", "xmlns:jsp", "CDATA", - JSP_URI); + rootAttrs.addAttribute("", "", "xmlns:jsp", "CDATA", JSP_URI); } if (pageInfo.isJspPrefixHijacked()) { /* - * 'jsp' prefix has been hijacked, that is, bound to a - * namespace other than the JSP namespace. This means that - * when adding an 'id' attribute to each element, we can't - * use the 'jsp' prefix. Therefore, create a new prefix - * (one that is unique across the translation unit) for use - * by the 'id' attribute, and bind it to the JSP namespace + * 'jsp' prefix has been hijacked, that is, bound to a namespace other than the JSP namespace. This + * means that when adding an 'id' attribute to each element, we can't use the 'jsp' prefix. + * Therefore, create a new prefix (one that is unique across the translation unit) for use by the + * 'id' attribute, and bind it to the JSP namespace */ - jspIdPrefix += "jsp"; - while (pageInfo.containsPrefix(jspIdPrefix)) { + do { jspIdPrefix += "jsp"; - } - rootAttrs.addAttribute("", "", "xmlns:" + jspIdPrefix, - "CDATA", JSP_URI); + } while (pageInfo.containsPrefix(jspIdPrefix)); + rootAttrs.addAttribute("", "", "xmlns:" + jspIdPrefix, "CDATA", JSP_URI); } root.setAttributes(rootAttrs); @@ -169,8 +142,7 @@ } /* - * Converts taglib directive into "xmlns:..." attribute of jsp:root - * element. + * Converts taglib directive into "xmlns:..." attribute of jsp:root element. */ @Override public void visit(Node.TaglibDirective n) throws JasperException { @@ -178,10 +150,9 @@ if (attrs != null) { String qName = "xmlns:" + attrs.getValue("prefix"); /* - * According to javadocs of org.xml.sax.helpers.AttributesImpl, - * the addAttribute method does not check to see if the - * specified attribute is already contained in the list: This - * is the application's responsibility! + * According to javadocs of org.xml.sax.helpers.AttributesImpl, the addAttribute method does not check + * to see if the specified attribute is already contained in the list: This is the application's + * responsibility! */ if (rootAttrs.getIndex(qName) == -1) { String location = attrs.getValue("uri"); @@ -189,12 +160,10 @@ if (location.startsWith("/")) { location = URN_JSPTLD + location; } - rootAttrs.addAttribute("", "", qName, "CDATA", - location); + rootAttrs.addAttribute("", "", qName, "CDATA", location); } else { location = attrs.getValue("tagdir"); - rootAttrs.addAttribute("", "", qName, "CDATA", - URN_JSPTAGDIR + location); + rootAttrs.addAttribute("", "", qName, "CDATA", URN_JSPTAGDIR + location); } } } @@ -208,19 +177,16 @@ if (attrs != null) { int len = attrs.getLength(); - for (int i=0; i\n"); if (ROOT_ACTION.equals(n.getLocalName())) { if (compiler.getCompilationContext().isTagFile()) { @@ -486,35 +446,30 @@ } else { appendText(text, false); } - buf.append("\n"); + buf.append("\n"); } else { buf.append("/>\n"); } } /* - * Appends the page directive with the given attributes to the XML - * view. + * Appends the page directive with the given attributes to the XML view. * - * Since the import attribute of the page directive is the only page - * attribute that is allowed to appear multiple times within the same - * document, and since XML allows only single-value attributes, - * the values of multiple import attributes must be combined into one, - * separated by comma. + * Since the import attribute of the page directive is the only page attribute that is allowed to appear + * multiple times within the same document, and since XML allows only single-value attributes, the values of + * multiple import attributes must be combined into one, separated by comma. * - * If the given page directive contains just 'contentType' and/or - * 'pageEncoding' attributes, we ignore it, as we've already appended - * a page directive containing just these two attributes. + * If the given page directive contains just 'contentType' and/or 'pageEncoding' attributes, we ignore it, as + * we've already appended a page directive containing just these two attributes. */ private void appendPageDirective(Node.PageDirective n) { boolean append = false; Attributes attrs = n.getAttributes(); int len = (attrs == null) ? 0 : attrs.getLength(); - for (int i=0; i 0) { + if (!n.getImports().isEmpty()) { // Concatenate names of imported classes/packages boolean first = true; for (String i : n.getImports()) { @@ -567,13 +519,11 @@ } /* - * Appends a page directive with 'pageEncoding' and 'contentType' - * attributes. + * Appends a page directive with 'pageEncoding' and 'contentType' attributes. * - * The value of the 'pageEncoding' attribute is hard-coded - * to UTF-8, whereas the value of the 'contentType' attribute, which - * is identical to what the container will pass to - * ServletResponse.setContentType(), is derived from the pageInfo. + * The value of the 'pageEncoding' attribute is hard-coded to UTF-8, whereas the value of the 'contentType' + * attribute, which is identical to what the container will pass to ServletResponse.setContentType(), is derived + * from the pageInfo. */ private void appendPageDirective() { buf.append('<').append(JSP_PAGE_DIRECTIVE_ACTION); @@ -589,21 +539,18 @@ } /* - * Appends the tag directive with the given attributes to the XML - * view. + * Appends the tag directive with the given attributes to the XML view. * - * If the given tag directive contains just a 'pageEncoding' - * attributes, we ignore it, as we've already appended + * If the given tag directive contains just a 'pageEncoding' attributes, we ignore it, as we've already appended * a tag directive containing just this attributes. */ - private void appendTagDirective(Node.TagDirective n) - throws JasperException { + private void appendTagDirective(Node.TagDirective n) throws JasperException { boolean append = false; Attributes attrs = n.getAttributes(); int len = (attrs == null) ? 0 : attrs.getLength(); - for (int i=0; i" (by replacing them with "]]>") - * within the given text, so it can be included in a CDATA section. + * Escapes any occurrences of "]]>" (by replacing them with "]]>") within the given text, so it can be + * included in a CDATA section. */ private String escapeCDATA(String text) { - if( text==null ) { + if (text == null) { return ""; } int len = text.length(); CharArrayWriter result = new CharArrayWriter(len); - for (int i=0; i')) { + for (int i = 0; i < len; i++) { + if (((i + 2) < len) && (text.charAt(i) == ']') && (text.charAt(i + 1) == ']') && + (text.charAt(i + 2) == '>')) { // match found result.write(']'); result.write(']'); @@ -700,8 +643,8 @@ */ Attributes attrs = n.getTaglibAttributes(); int len = (attrs == null) ? 0 : attrs.getLength(); - for (int i=0; i imports; private final Map dependants; @@ -47,9 +44,9 @@ private final BeanRepository beanRepository; private final Set varInfoNames; private final HashMap taglibsMap; - private final HashMap jspPrefixMapper; - private final HashMap> xmlPrefixMapper; - private final HashMap nonCustomTagPrefixMap; + private final HashMap jspPrefixMapper; + private final HashMap> xmlPrefixMapper; + private final HashMap nonCustomTagPrefixMap; private final String jspFile; private static final String defaultLanguage = "java"; private String language; @@ -59,7 +56,7 @@ private String session; private boolean isSession = true; private String bufferValue; - private int buffer = 8*1024; + private int buffer = 8 * 1024; private String autoFlush; private boolean isAutoFlush = true; private String isThreadSafeValue; @@ -78,8 +75,7 @@ // JSP 2.1 private String deferredSyntaxAllowedAsLiteralValue; private boolean deferredSyntaxAllowedAsLiteral = false; - private final ExpressionFactory expressionFactory = - ExpressionFactory.newInstance(); + private final ExpressionFactory expressionFactory = ExpressionFactory.newInstance(); private String trimDirectiveWhitespacesValue; private boolean trimDirectiveWhitespaces = false; @@ -96,7 +92,7 @@ private boolean hasJspRoot = false; private Collection includePrelude; private Collection includeCoda; - private final List pluginDcls; // Id's for tagplugin declarations + private final List pluginDcls; // Id's for tagplugin declarations // JSP 2.2 private boolean errorOnUndeclaredNamespace = false; @@ -132,8 +128,7 @@ } /** - * Check if the plugin ID has been previously declared. Make a note - * that this Id is now declared. + * Check if the plugin ID has been previously declared. Make a note that this Id is now declared. * * @param id The plugin ID to check * @@ -273,8 +268,7 @@ * * @param prefix The prefix to check * - * @return true if this translation unit contains the given prefix, false - * otherwise + * @return true if this translation unit contains the given prefix, false otherwise */ public boolean containsPrefix(String prefix) { return prefixes.contains(prefix); @@ -284,6 +278,7 @@ * Maps the given URI to the given tag library. * * @param uri The URI to map + * * @param info The tag library to be associated with the given URI */ public void addTaglib(String uri, TagLibraryInfo info) { @@ -313,8 +308,7 @@ * * @param uri The URI to map * - * @return true if the given URI is mapped to a tag library, false - * otherwise + * @return true if the given URI is mapped to a tag library, false otherwise */ public boolean hasTaglib(String uri) { return taglibsMap.containsKey(uri); @@ -324,6 +318,7 @@ * Maps the given prefix to the given URI. * * @param prefix The prefix to map + * * @param uri The URI to be associated with the given prefix */ public void addPrefixMapping(String prefix, String uri) { @@ -331,10 +326,10 @@ } /* - * Pushes the given URI onto the stack of URIs to which the given prefix - * is mapped. + * Pushes the given URI onto the stack of URIs to which the given prefix is mapped. * * @param prefix The prefix whose stack of URIs is to be pushed + * * @param uri The URI to be pushed onto the stack */ public void pushPrefixMapping(String prefix, String uri) { @@ -343,8 +338,7 @@ } /* - * Removes the URI at the top of the stack of URIs to which the given - * prefix is mapped. + * Removes the URI at the top of the stack of URIs to which the given prefix is mapped. * * @param prefix The prefix whose stack of URIs is to be popped */ @@ -362,10 +356,10 @@ */ public String getURI(String prefix) { - String uri = null; + String uri; Deque stack = xmlPrefixMapper.get(prefix); - if (stack == null || stack.size() == 0) { + if (stack == null || stack.isEmpty()) { uri = jspPrefixMapper.get(prefix); } else { uri = stack.getFirst(); @@ -380,9 +374,7 @@ /* * language */ - public void setLanguage(String value, Node n, ErrorDispatcher err, - boolean pagedir) - throws JasperException { + public void setLanguage(String value, Node n, ErrorDispatcher err, boolean pagedir) throws JasperException { if (!"java".equalsIgnoreCase(value)) { if (pagedir) { @@ -409,13 +401,11 @@ /** * Gets the value of the 'extends' page directive attribute. * - * @param useDefault TRUE if the default - * (org.apache.jasper.runtime.HttpJspBase) should be returned if this - * attribute has not been set, FALSE otherwise - * - * @return The value of the 'extends' page directive attribute, or the - * default (org.apache.jasper.runtime.HttpJspBase) if this attribute has - * not been set and useDefault is TRUE + * @param useDefault TRUE if the default (org.apache.jasper.runtime.HttpJspBase) should be returned if this + * attribute has not been set, FALSE otherwise + * + * @return The value of the 'extends' page directive attribute, or the default + * (org.apache.jasper.runtime.HttpJspBase) if this attribute has not been set and useDefault is TRUE */ public String getExtends(boolean useDefault) { return (xtends == null && useDefault ? defaultExtends : xtends); @@ -424,9 +414,8 @@ /** * Gets the value of the 'extends' page directive attribute. * - * @return The value of the 'extends' page directive attribute, or the - * default (org.apache.jasper.runtime.HttpJspBase) if this attribute has - * not been set + * @return The value of the 'extends' page directive attribute, or the default + * (org.apache.jasper.runtime.HttpJspBase) if this attribute has not been set */ public String getExtends() { return getExtends(true); @@ -448,8 +437,7 @@ /* * buffer */ - public void setBufferValue(String value, Node n, ErrorDispatcher err) - throws JasperException { + public void setBufferValue(String value, Node n, ErrorDispatcher err) throws JasperException { if ("none".equalsIgnoreCase(value)) { buffer = 0; @@ -463,7 +451,7 @@ } try { @SuppressWarnings("null") // value can't be null here - int k = Integer.parseInt(value.substring(0, value.length()-2)); + int k = Integer.parseInt(value.substring(0, value.length() - 2)); buffer = k * 1024; } catch (NumberFormatException e) { if (n == null) { @@ -489,8 +477,7 @@ /* * session */ - public void setSession(String value, Node n, ErrorDispatcher err) - throws JasperException { + public void setSession(String value, Node n, ErrorDispatcher err) throws JasperException { if ("true".equalsIgnoreCase(value)) { isSession = true; @@ -515,8 +502,7 @@ /* * autoFlush */ - public void setAutoFlush(String value, Node n, ErrorDispatcher err) - throws JasperException { + public void setAutoFlush(String value, Node n, ErrorDispatcher err) throws JasperException { if ("true".equalsIgnoreCase(value)) { isAutoFlush = true; @@ -541,8 +527,7 @@ /* * isThreadSafe */ - public void setIsThreadSafe(String value, Node n, ErrorDispatcher err) - throws JasperException { + public void setIsThreadSafe(String value, Node n, ErrorDispatcher err) throws JasperException { if ("true".equalsIgnoreCase(value)) { isThreadSafe = true; @@ -591,8 +576,7 @@ /* * isErrorPage */ - public void setIsErrorPage(String value, Node n, ErrorDispatcher err) - throws JasperException { + public void setIsErrorPage(String value, Node n, ErrorDispatcher err) throws JasperException { if ("true".equalsIgnoreCase(value)) { isErrorPage = true; @@ -617,9 +601,7 @@ /* * isELIgnored */ - public void setIsELIgnored(String value, Node n, ErrorDispatcher err, - boolean pagedir) - throws JasperException { + public void setIsELIgnored(String value, Node n, ErrorDispatcher err, boolean pagedir) throws JasperException { if ("true".equalsIgnoreCase(value)) { isELIgnored = true; @@ -640,9 +622,8 @@ /* * errorOnELNotFound */ - public void setErrorOnELNotFound(String value, Node n, ErrorDispatcher err, - boolean pagedir) - throws JasperException { + public void setErrorOnELNotFound(String value, Node n, ErrorDispatcher err, boolean pagedir) + throws JasperException { if ("true".equalsIgnoreCase(value)) { errorOnELNotFound = true; @@ -663,9 +644,8 @@ /* * deferredSyntaxAllowedAsLiteral */ - public void setDeferredSyntaxAllowedAsLiteral(String value, Node n, ErrorDispatcher err, - boolean pagedir) - throws JasperException { + public void setDeferredSyntaxAllowedAsLiteral(String value, Node n, ErrorDispatcher err, boolean pagedir) + throws JasperException { if ("true".equalsIgnoreCase(value)) { deferredSyntaxAllowedAsLiteral = true; @@ -685,9 +665,8 @@ /* * trimDirectiveWhitespaces */ - public void setTrimDirectiveWhitespaces(String value, Node n, ErrorDispatcher err, - boolean pagedir) - throws JasperException { + public void setTrimDirectiveWhitespaces(String value, Node n, ErrorDispatcher err, boolean pagedir) + throws JasperException { if ("true".equalsIgnoreCase(value)) { trimDirectiveWhitespaces = true; @@ -772,8 +751,7 @@ return errorOnUndeclaredNamespace; } - public void setErrorOnUndeclaredNamespace( - boolean errorOnUndeclaredNamespace) { + public void setErrorOnUndeclaredNamespace(boolean errorOnUndeclaredNamespace) { this.errorOnUndeclaredNamespace = errorOnUndeclaredNamespace; } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/Parser.java tomcat10-10.1.52/java/org/apache/jasper/compiler/Parser.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/Parser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/Parser.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,15 +34,9 @@ import org.xml.sax.helpers.AttributesImpl; /** - * This class implements a parser for a JSP page (non-xml view). JSP page - * grammar is included here for reference. The token '#' that appears in the - * production indicates the current input token location in the production. - * - * @author Kin-man Chung - * @author Shawn Bayern - * @author Mark Roth + * This class implements a parser for a JSP page (non-xml view). JSP page grammar is included here for reference. The + * token '#' that appears in the production indicates the current input token location in the production. */ - class Parser implements TagConstants { private final ParserController parserController; @@ -67,20 +61,16 @@ // Virtual body content types, to make parsing a little easier. // These are not accessible from outside the parser. - private static final String JAVAX_BODY_CONTENT_PARAM = - "JAVAX_BODY_CONTENT_PARAM"; + private static final String JAVAX_BODY_CONTENT_PARAM = "JAVAX_BODY_CONTENT_PARAM"; - private static final String JAVAX_BODY_CONTENT_PLUGIN = - "JAVAX_BODY_CONTENT_PLUGIN"; + private static final String JAVAX_BODY_CONTENT_PLUGIN = "JAVAX_BODY_CONTENT_PLUGIN"; - private static final String JAVAX_BODY_CONTENT_TEMPLATE_TEXT = - "JAVAX_BODY_CONTENT_TEMPLATE_TEXT"; + private static final String JAVAX_BODY_CONTENT_TEMPLATE_TEXT = "JAVAX_BODY_CONTENT_TEMPLATE_TEXT"; /** * The constructor */ - private Parser(ParserController pc, JspReader reader, boolean isTagFile, - boolean directivesOnly, Jar jar) { + private Parser(ParserController pc, JspReader reader, boolean isTagFile, boolean directivesOnly, Jar jar) { this.parserController = pc; this.ctxt = pc.getJspCompilationContext(); this.pageInfo = pc.getCompiler().getPageInfo(); @@ -96,26 +86,25 @@ /** * The main entry for Parser * - * @param pc The ParseController, use for getting other objects in compiler - * and for parsing included pages - * @param reader To read the page - * @param parent The parent node to this page, null for top level page - * @param isTagFile Is the page being parsed a tag file? - * @param directivesOnly Should only directives be parsed? - * @param jar JAR, if any, that this page was loaded from - * @param pageEnc The encoding of the source - * @param jspConfigPageEnc The encoding for the page + * @param pc The ParseController, use for getting other objects in compiler and for parsing + * included pages + * @param reader To read the page + * @param parent The parent node to this page, null for top level page + * @param isTagFile Is the page being parsed a tag file? + * @param directivesOnly Should only directives be parsed? + * @param jar JAR, if any, that this page was loaded from + * @param pageEnc The encoding of the source + * @param jspConfigPageEnc The encoding for the page * @param isDefaultPageEncoding Is the page encoding the default? - * @param isBomPresent Is a BOM present in the source + * @param isBomPresent Is a BOM present in the source + * * @return list of nodes representing the parsed page * * @throws JasperException If an error occurs during parsing */ - public static Node.Nodes parse(ParserController pc, JspReader reader, - Node parent, boolean isTagFile, boolean directivesOnly, - Jar jar, String pageEnc, String jspConfigPageEnc, - boolean isDefaultPageEncoding, boolean isBomPresent) - throws JasperException { + public static Node.Nodes parse(ParserController pc, JspReader reader, Node parent, boolean isTagFile, + boolean directivesOnly, Jar jar, String pageEnc, String jspConfigPageEnc, boolean isDefaultPageEncoding, + boolean isBomPresent) throws JasperException { Parser parser = new Parser(pc, reader, isTagFile, directivesOnly, jar); @@ -142,8 +131,7 @@ parser.addInclude(root, pageInfo.getIncludeCoda()); } - Node.Nodes page = new Node.Nodes(root); - return page; + return new Node.Nodes(root); } /** @@ -152,6 +140,7 @@ Attributes parseAttributes() throws JasperException { return parseAttributes(false); } + Attributes parseAttributes(boolean pageDirective) throws JasperException { UniqueAttributesImpl attrs = new UniqueAttributesImpl(pageDirective); @@ -161,8 +150,7 @@ try { while (parseAttribute(attrs)) { if (ws == 0 && ctxt.getOptions().getStrictWhitespace()) { - err.jspError(reader.mark(), - "jsp.error.attribute.nowhitespace"); + err.jspError(reader.mark(), "jsp.error.attribute.nowhitespace"); } ws = reader.skipSpaces(); } @@ -177,28 +165,26 @@ /** * Parse Attributes for a reader, provided for external use * - * @param pc The parser + * @param pc The parser * @param reader The source * * @return The parsed attributes * * @throws JasperException If an error occurs during parsing */ - public static Attributes parseAttributes(ParserController pc, - JspReader reader) throws JasperException { + public static Attributes parseAttributes(ParserController pc, JspReader reader) throws JasperException { Parser tmpParser = new Parser(pc, reader, false, false, null); return tmpParser.parseAttributes(true); } /** - * Attribute ::= Name S? Eq S? ( '"<%=' RTAttributeValueDouble | '"' - * AttributeValueDouble | "'<%=" RTAttributeValueSingle | "'" - * AttributeValueSingle } Note: JSP and XML spec does not allow while spaces - * around Eq. It is added to be backward compatible with Tomcat, and with - * other xml parsers. + * Attribute ::= Name S? Eq S? ( '"<%=' RTAttributeValueDouble | '"' * AttributeValueDouble | "'<%=" + * RTAttributeValueSingle | "'" AttributeValueSingle } + *

          + * Note: JSP and XML spec does not allow while spaces around Eq. It is added to be backward compatible with Tomcat, + * and with other xml parsers. */ - private boolean parseAttribute(AttributesImpl attrs) - throws JasperException { + private boolean parseAttribute(AttributesImpl attrs) throws JasperException { // Get the qualified name String qName = parseName(); @@ -216,8 +202,7 @@ String prefix = qName.substring(0, index); uri = pageInfo.getURI(prefix); if (uri == null) { - err.jspError(reader.mark(), - "jsp.error.attribute.invalidPrefix", prefix); + err.jspError(reader.mark(), "jsp.error.attribute.invalidPrefix", prefix); } localName = qName.substring(index + 1); } @@ -253,24 +238,20 @@ char ch = (char) reader.peekChar(); if (Character.isLetter(ch) || ch == '_' || ch == ':') { StringBuilder buf = new StringBuilder(); - buf.append(ch); - reader.nextChar(); - ch = (char) reader.peekChar(); - while (Character.isLetter(ch) || Character.isDigit(ch) || ch == '.' - || ch == '_' || ch == '-' || ch == ':') { + do { buf.append(ch); reader.nextChar(); ch = (char) reader.peekChar(); - } + } while (Character.isLetter(ch) || Character.isDigit(ch) || ch == '.' || ch == '_' || ch == '-' || + ch == ':'); return buf.toString(); } return null; } /** - * AttributeValueDouble ::= (QuotedChar - '"')* ('"' | <TRANSLATION_ERROR>) - * RTAttributeValueDouble ::= ((QuotedChar - '"')* - ((QuotedChar-'"')'%>"') - * ('%>"' | TRANSLATION_ERROR) + * AttributeValueDouble ::= (QuotedChar - '"')* ('"' | <TRANSLATION_ERROR>) RTAttributeValueDouble ::= + * ((QuotedChar - '"')* - ((QuotedChar-'"')'%>"') ('%>"' | TRANSLATION_ERROR) */ private String parseAttributeValue(String qName, String watch, boolean ignoreEL) throws JasperException { boolean quoteAttributeEL = ctxt.getOptions().getQuoteAttributeEL(); @@ -288,13 +269,10 @@ // If watch is longer than 1 character this is a scripting // expression and EL is always ignored - boolean isElIgnored = - pageInfo.isELIgnored() || watch.length() > 1; + boolean isElIgnored = pageInfo.isELIgnored() || watch.length() > 1; - ret = AttributeParser.getUnquoted(reader.getText(start, stop), - quote, isElIgnored, - pageInfo.isDeferredSyntaxAllowedAsLiteral(), - ctxt.getOptions().getStrictQuoteEscaping(), + ret = AttributeParser.getUnquoted(reader.getText(start, stop), quote, isElIgnored, + pageInfo.isDeferredSyntaxAllowedAsLiteral(), ctxt.getOptions().getStrictQuoteEscaping(), quoteAttributeEL); } catch (IllegalArgumentException iae) { err.jspError(start, iae.getMessage()); @@ -314,8 +292,7 @@ int i = 0; while (i < size) { char ch = tx.charAt(i); - if (i + 2 < size && ch == '%' && tx.charAt(i + 1) == '\\' - && tx.charAt(i + 2) == '>') { + if (i + 2 < size && ch == '%' && tx.charAt(i + 1) == '\\' && tx.charAt(i + 2) == '>') { cw.write('%'); cw.write('>'); i += 3; @@ -331,8 +308,7 @@ /* * Invokes parserController to parse the included page */ - private void processIncludeDirective(String file, Node parent) - throws JasperException { + private void processIncludeDirective(String file, Node parent) throws JasperException { if (file == null) { return; } @@ -349,23 +325,21 @@ } } catch (FileNotFoundException ex) { err.jspError(start, "jsp.error.file.not.found", file); - } catch (Exception ex) { - err.jspError(start, ex.getMessage()); + } catch (Exception e) { + err.jspError(start, e.getMessage()); } } /* - * Parses a page directive with the following syntax: PageDirective ::= ( S - * Attribute)* + * Parses a page directive with the following syntax: PageDirective ::= ( S Attribute)* */ private void parsePageDirective(Node parent) throws JasperException { Attributes attrs = parseAttributes(true); Node.PageDirective n = new Node.PageDirective(attrs, start, parent); /* - * A page directive may contain multiple 'import' attributes, each of - * which consists of a comma-separated list of package names. Store each - * list with the node, where it is parsed. + * A page directive may contain multiple 'import' attributes, each of which consists of a comma-separated list + * of package names. Store each list with the node, where it is parsed. */ for (int i = 0; i < attrs.getLength(); i++) { if ("import".equals(attrs.getQName(i))) { @@ -375,8 +349,7 @@ } /* - * Parses an include directive with the following syntax: IncludeDirective - * ::= ( S Attribute)* + * Parses an include directive with the following syntax: IncludeDirective ::= ( S Attribute)* */ private void parseIncludeDirective(Node parent) throws JasperException { Attributes attrs = parseAttributes(); @@ -387,8 +360,8 @@ } /** - * Add a list of files. This is used for implementing include-prelude and - * include-coda of jsp-config element in web.xml + * Add a list of files. This is used for implementing include-prelude and include-coda of jsp-config element in + * web.xml */ private void addInclude(Node parent, Collection files) throws JasperException { if (files != null) { @@ -397,16 +370,14 @@ attrs.addAttribute("", "file", "file", "CDATA", file); // Create a dummy Include directive node - Node includeNode = new Node.IncludeDirective(attrs, reader - .mark(), parent); + Node includeNode = new Node.IncludeDirective(attrs, reader.mark(), parent); processIncludeDirective(file, includeNode); } } } /* - * Parses a taglib directive with the following syntax: Directive ::= ( S - * Attribute)* + * Parses a taglib directive with the following syntax: Directive ::= ( S Attribute)* */ private void parseTaglibDirective(Node parent) throws JasperException { @@ -416,26 +387,23 @@ if (prefix != null) { Mark prevMark = pageInfo.getNonCustomTagPrefix(prefix); if (prevMark != null) { - err.jspError(reader.mark(), "jsp.error.prefix.use_before_dcl", - prefix, prevMark.getFile(), "" - + prevMark.getLineNumber()); + err.jspError(reader.mark(), "jsp.error.prefix.use_before_dcl", prefix, prevMark.getFile(), + "" + prevMark.getLineNumber()); } if (uri != null) { String uriPrev = pageInfo.getURI(prefix); if (uriPrev != null && !uriPrev.equals(uri)) { - err.jspError(reader.mark(), "jsp.error.prefix.refined", - prefix, uri, uriPrev); + err.jspError(reader.mark(), "jsp.error.prefix.refined", prefix, uri, uriPrev); } if (pageInfo.getTaglib(uri) == null) { TagLibraryInfoImpl impl = null; if (ctxt.getOptions().isCaching()) { - impl = (TagLibraryInfoImpl) ctxt.getOptions() - .getCache().get(uri); + impl = (TagLibraryInfoImpl) ctxt.getOptions().getCache().get(uri); } if (impl == null) { TldResourcePath tldResourcePath = ctxt.getTldResourcePath(uri); - impl = new TagLibraryInfoImpl(ctxt, parserController, - pageInfo, prefix, uri, tldResourcePath, err); + impl = new TagLibraryInfoImpl(ctxt, parserController, pageInfo, prefix, uri, tldResourcePath, + err); if (ctxt.getOptions().isCaching()) { ctxt.getOptions().getCache().put(uri, impl); } @@ -449,9 +417,7 @@ String urnTagdir = URN_JSPTAGDIR + tagdir; if (pageInfo.getTaglib(urnTagdir) == null) { pageInfo.addTaglib(urnTagdir, - new ImplicitTagLibraryInfo(ctxt, - parserController, pageInfo, prefix, - tagdir, err)); + new ImplicitTagLibraryInfo(ctxt, parserController, pageInfo, prefix, tagdir, err)); } pageInfo.addPrefixMapping(prefix, urnTagdir); } @@ -463,13 +429,11 @@ } /* - * Parses a directive with the following syntax: Directive ::= S? ( 'page' - * PageDirective | 'include' IncludeDirective | 'taglib' TagLibDirective) S? - * '%>' - * - * TagDirective ::= S? ('tag' PageDirective | 'include' IncludeDirective | - * 'taglib' TagLibDirective) | 'attribute AttributeDirective | 'variable - * VariableDirective S? '%>' + * Parses a directive with the following syntax: Directive ::= S? ( 'page' PageDirective | 'include' + * IncludeDirective | 'taglib' TagLibDirective) S? '%>' + * + * TagDirective ::= S? ('tag' PageDirective | 'include' IncludeDirective | 'taglib' TagLibDirective) | 'attribute + * AttributeDirective | 'variable VariableDirective S? '%>' */ private void parseDirective(Node parent) throws JasperException { reader.skipSpaces(); @@ -478,8 +442,7 @@ if (reader.matches("page")) { directive = "<%@ page"; if (isTagFile) { - err.jspError(reader.mark(), "jsp.error.directive.istagfile", - directive); + err.jspError(reader.mark(), "jsp.error.directive.istagfile", directive); } parsePageDirective(parent); } else if (reader.matches("include")) { @@ -496,22 +459,19 @@ } else if (reader.matches("tag")) { directive = "<%@ tag"; if (!isTagFile) { - err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", - directive); + err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", directive); } parseTagDirective(parent); } else if (reader.matches("attribute")) { directive = "<%@ attribute"; if (!isTagFile) { - err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", - directive); + err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", directive); } parseAttributeDirective(parent); } else if (reader.matches("variable")) { directive = "<%@ variable"; if (!isTagFile) { - err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", - directive); + err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", directive); } parseVariableDirective(parent); } else { @@ -527,15 +487,12 @@ /* * Parses a directive with the following syntax: * - * XMLJSPDirectiveBody ::= S? ( ( 'page' PageDirectiveAttrList S? ( '/>' | ( - * '>' S? ETag ) ) | ( 'include' IncludeDirectiveAttrList S? ( '/>' | ( '>' - * S? ETag ) ) | - * - * XMLTagDefDirectiveBody ::= ( ( 'tag' TagDirectiveAttrList S? ( '/>' | ( - * '>' S? ETag ) ) | ( 'include' IncludeDirectiveAttrList S? ( '/>' | ( '>' - * S? ETag ) ) | ( 'attribute' AttributeDirectiveAttrList S? ( '/>' | ( '>' - * S? ETag ) ) | ( 'variable' VariableDirectiveAttrList S? ( '/>' | ( '>' S? - * ETag ) ) ) | + * XMLJSPDirectiveBody ::= S? ( ( 'page' PageDirectiveAttrList S? ( '/>' | ( '>' S? ETag ) ) | ( 'include' + * IncludeDirectiveAttrList S? ( '/>' | ( '>' S? ETag ) ) | + * + * XMLTagDefDirectiveBody ::= ( ( 'tag' TagDirectiveAttrList S? ( '/>' | ( '>' S? ETag ) ) | ( 'include' + * IncludeDirectiveAttrList S? ( '/>' | ( '>' S? ETag ) ) | ( 'attribute' AttributeDirectiveAttrList S? ( '/>' | ( + * '>' S? ETag ) ) | ( 'variable' VariableDirectiveAttrList S? ( '/>' | ( '>' S? ETag ) ) ) | */ private void parseXMLDirective(Node parent) throws JasperException { reader.skipSpaces(); @@ -544,8 +501,7 @@ if (reader.matches("page")) { eTag = "jsp:directive.page"; if (isTagFile) { - err.jspError(reader.mark(), "jsp.error.directive.istagfile", - "<" + eTag); + err.jspError(reader.mark(), "jsp.error.directive.istagfile", "<" + eTag); } parsePageDirective(parent); } else if (reader.matches("include")) { @@ -554,22 +510,19 @@ } else if (reader.matches("tag")) { eTag = "jsp:directive.tag"; if (!isTagFile) { - err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", - "<" + eTag); + err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", "<" + eTag); } parseTagDirective(parent); } else if (reader.matches("attribute")) { eTag = "jsp:directive.attribute"; if (!isTagFile) { - err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", - "<" + eTag); + err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", "<" + eTag); } parseAttributeDirective(parent); } else if (reader.matches("variable")) { eTag = "jsp:directive.variable"; if (!isTagFile) { - err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", - "<" + eTag); + err.jspError(reader.mark(), "jsp.error.directive.isnottagfile", "<" + eTag); } parseVariableDirective(parent); } else { @@ -588,17 +541,15 @@ } /* - * Parses a tag directive with the following syntax: PageDirective ::= ( S - * Attribute)* + * Parses a tag directive with the following syntax: PageDirective ::= ( S Attribute)* */ private void parseTagDirective(Node parent) throws JasperException { Attributes attrs = parseAttributes(true); Node.TagDirective n = new Node.TagDirective(attrs, start, parent); /* - * A page directive may contain multiple 'import' attributes, each of - * which consists of a comma-separated list of package names. Store each - * list with the node, where it is parsed. + * A page directive may contain multiple 'import' attributes, each of which consists of a comma-separated list + * of package names. Store each list with the node, where it is parsed. */ for (int i = 0; i < attrs.getLength(); i++) { if ("import".equals(attrs.getQName(i))) { @@ -608,8 +559,7 @@ } /* - * Parses a attribute directive with the following syntax: - * AttributeDirective ::= ( S Attribute)* + * Parses an attribute directive with the following syntax: AttributeDirective ::= ( S Attribute)* */ private void parseAttributeDirective(Node parent) throws JasperException { Attributes attrs = parseAttributes(); @@ -618,8 +568,7 @@ } /* - * Parses a variable directive with the following syntax: - * PageDirective ::= ( S Attribute)* + * Parses a variable directive with the following syntax: PageDirective ::= ( S Attribute)* */ private void parseVariableDirective(Node parent) throws JasperException { Attributes attrs = parseAttributes(); @@ -638,8 +587,7 @@ } @SuppressWarnings("unused") - Node unused = - new Node.Comment(reader.getText(start, stop), start, parent); + Node unused = new Node.Comment(reader.getText(start, stop), start, parent); } /* @@ -653,22 +601,18 @@ } @SuppressWarnings("unused") - Node unused = new Node.Declaration( - parseScriptText(reader.getText(start, stop)), start, parent); + Node unused = new Node.Declaration(parseScriptText(reader.getText(start, stop)), start, parent); } /* - * XMLDeclarationBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<')) - * CDSect?)* ETag | CDSect ::= CDStart CData CDEnd - * CDStart ::= '' Char*)) CDEnd - * ::= ']]>' + * XMLDeclarationBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<')) CDSect?)* ETag | CDSect + * ::= CDStart CData CDEnd CDStart ::= '' Char*)) CDEnd ::= ']]>' */ private void parseXMLDeclaration(Node parent) throws JasperException { reader.skipSpaces(); if (!reader.matches("/>")) { if (!reader.matches(">")) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:declaration>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:declaration>"); } Mark stop; String text; @@ -676,8 +620,7 @@ start = reader.mark(); stop = reader.skipUntil("<"); if (stop == null) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:declaration>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:declaration>"); } text = parseScriptText(reader.getText(start, stop)); @SuppressWarnings("unused") @@ -697,8 +640,7 @@ } if (!reader.matchesETagWithoutLessThan("jsp:declaration")) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:declaration>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:declaration>"); } } } @@ -714,20 +656,17 @@ } @SuppressWarnings("unused") - Node unused = new Node.Expression( - parseScriptText(reader.getText(start, stop)), start, parent); + Node unused = new Node.Expression(parseScriptText(reader.getText(start, stop)), start, parent); } /* - * XMLExpressionBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<')) - * CDSect?)* ETag ) | + * XMLExpressionBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<')) CDSect?)* ETag ) | */ private void parseXMLExpression(Node parent) throws JasperException { reader.skipSpaces(); if (!reader.matches("/>")) { if (!reader.matches(">")) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:expression>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:expression>"); } Mark stop; String text; @@ -735,8 +674,7 @@ start = reader.mark(); stop = reader.skipUntil("<"); if (stop == null) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:expression>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:expression>"); } text = parseScriptText(reader.getText(start, stop)); @SuppressWarnings("unused") @@ -755,18 +693,15 @@ } } if (!reader.matchesETagWithoutLessThan("jsp:expression")) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:expression>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:expression>"); } } } /* - * ELExpressionBody. Starts with "#{" or "${". Ends with "}". - * See JspReader.skipELExpression(). + * ELExpressionBody. Starts with "#{" or "${". Ends with "}". See JspReader.skipELExpression(). */ - private void parseELExpression(Node parent, char type) - throws JasperException { + private void parseELExpression(Node parent, char type) throws JasperException { start = reader.mark(); Mark last = reader.skipELExpression(); if (last == null) { @@ -774,8 +709,7 @@ } @SuppressWarnings("unused") - Node unused = new Node.ELExpression(type, reader.getText(start, last), - start, parent); + Node unused = new Node.ELExpression(type, reader.getText(start, last), start, parent); } /* @@ -789,20 +723,17 @@ } @SuppressWarnings("unused") - Node unused = new Node.Scriptlet( - parseScriptText(reader.getText(start, stop)), start, parent); + Node unused = new Node.Scriptlet(parseScriptText(reader.getText(start, stop)), start, parent); } /* - * XMLScriptletBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<')) - * CDSect?)* ETag ) | + * XMLScriptletBody ::= ( S? '/>' ) | ( S? '>' (Char* - (char* '<')) CDSect?)* ETag ) | */ private void parseXMLScriptlet(Node parent) throws JasperException { reader.skipSpaces(); if (!reader.matches("/>")) { if (!reader.matches(">")) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:scriptlet>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:scriptlet>"); } Mark stop; String text; @@ -810,8 +741,7 @@ start = reader.mark(); stop = reader.skipUntil("<"); if (stop == null) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:scriptlet>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:scriptlet>"); } text = parseScriptText(reader.getText(start, stop)); @SuppressWarnings("unused") @@ -831,8 +761,7 @@ } if (!reader.matchesETagWithoutLessThan("jsp:scriptlet")) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:scriptlet>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:scriptlet>"); } } } @@ -857,11 +786,10 @@ /* * For Include: StdActionContent ::= Attributes ParamBody * - * ParamBody ::= EmptyBody | ( '>' S? ( ' ) S? ETag ) | ( '>' S? Param* ETag ) + * ParamBody ::= EmptyBody | ( '>' S? ( ' ) S? ETag ) | ( '>' S? Param* ETag ) * - * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '' ETag ) | ( '>' S? '' Param* '' */ @@ -922,8 +850,7 @@ Node getPropertyNode = new Node.GetProperty(attrs, start, parent); - parseOptionalBody(getPropertyNode, "jsp:getProperty", - TagInfo.BODY_CONTENT_EMPTY); + parseOptionalBody(getPropertyNode, "jsp:getProperty", TagInfo.BODY_CONTENT_EMPTY); } /* @@ -935,13 +862,11 @@ Node setPropertyNode = new Node.SetProperty(attrs, start, parent); - parseOptionalBody(setPropertyNode, "jsp:setProperty", - TagInfo.BODY_CONTENT_EMPTY); + parseOptionalBody(setPropertyNode, "jsp:setProperty", TagInfo.BODY_CONTENT_EMPTY); } /* - * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '' ETag ) | ( '>' S? '")) { @@ -954,12 +879,10 @@ parseNamedAttributes(parent); if (!reader.matchesETag(tag)) { // Body not allowed - err.jspError(reader.mark(), - "jsp.error.jspbody.emptybody.only", "<" + tag); + err.jspError(reader.mark(), "jsp.error.jspbody.emptybody.only", "<" + tag); } } else { - err.jspError(reader.mark(), "jsp.error.jspbody.emptybody.only", - "<" + tag); + err.jspError(reader.mark(), "jsp.error.jspbody.emptybody.only", "<" + tag); } } else { err.jspError(reader.mark(), "jsp.error.unterminated", "<" + tag); @@ -979,10 +902,8 @@ } /* - * Parses OptionalBody, but also reused to parse bodies for plugin and param - * since the syntax is identical (the only thing that differs substantially - * is how to process the body, and thus we accept the body type as a - * parameter). + * Parses OptionalBody, but also reused to parse bodies for plugin and param since the syntax is identical (the only + * thing that differs substantially is how to process the body, and thus we accept the body type as a parameter). * * OptionalBody ::= EmptyBody | ActionBody * @@ -990,19 +911,16 @@ * * TagDependentOptionalBody ::= EmptyBody | TagDependentActionBody * - * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '' ETag ) | ( '>' S? '' Body ETag ) * * ScriptlessActionBody ::= JspAttributeAndBody | ( '>' ScriptlessBody ETag ) * - * TagDependentActionBody ::= JspAttributeAndBody | ( '>' TagDependentBody - * ETag ) + * TagDependentActionBody ::= JspAttributeAndBody | ( '>' TagDependentBody ETag ) * */ - private void parseOptionalBody(Node parent, String tag, String bodyType) - throws JasperException { + private void parseOptionalBody(Node parent, String tag, String bodyType) throws JasperException { if (reader.matches("/>")) { // EmptyBody return; @@ -1024,19 +942,17 @@ } /** - * Attempts to parse 'JspAttributeAndBody' production. Returns true if it - * matched, or false if not. Assumes EmptyBody is okay as well. - * - * JspAttributeAndBody ::= ( '>' # S? ( '<jsp:attribute' NamedAttributes )? '<jsp:body' ( - * JspBodyBody | <TRANSLATION_ERROR> ) S? ETag ) + * Attempts to parse 'JspAttributeAndBody' production. Returns true if it matched, or false if not. Assumes + * EmptyBody is okay as well. + *

          + * JspAttributeAndBody ::= ( '>' # S? ( '<jsp:attribute' NamedAttributes )? '<jsp:body' ( JspBodyBody | + * <TRANSLATION_ERROR> ) S? ETag ) */ - private boolean parseJspAttributeAndBody(Node parent, String tag, - String bodyType) throws JasperException { + private boolean parseJspAttributeAndBody(Node parent, String tag, String bodyType) throws JasperException { boolean result = false; if (reader.matchesOptionalSpacesFollowedBy(" elements: parseNamedAttributes(parent); @@ -1049,24 +965,22 @@ parseJspBody(parent, bodyType); reader.skipSpaces(); if (!reader.matchesETag(tag)) { - err.jspError(reader.mark(), "jsp.error.unterminated", "<" - + tag); + err.jspError(reader.mark(), "jsp.error.unterminated", "<" + tag); } result = true; } else if (result && !reader.matchesETag(tag)) { // If we have but something other than // or the end tag, translation error. - err.jspError(reader.mark(), "jsp.error.jspbody.required", "<" - + tag); + err.jspError(reader.mark(), "jsp.error.jspbody.required", "<" + tag); } return result; } /* - * Params ::= `>' S? ( ( `' ( ( S? Param+ S? `' ) | - * ) ) | Param+ ) '' + * Params ::= `>' S? ( ( `' ( ( S? Param+ S? `' ) | ) ) | Param+ ) + * '' */ private void parseJspParams(Node parent) throws JasperException { Node jspParamsNode = new Node.ParamsAction(start, parent); @@ -1074,25 +988,21 @@ } /* - * Fallback ::= '/>' | ( `>' S? `' ( ( S? ( Char* - ( Char* `' ) ) `' - * S? ) | ) `' ) | ( '>' ( Char* - ( - * Char* '' ) ) '' ) + * Fallback ::= '/>' | ( `>' S? `' ( ( S? ( Char* - ( Char* `' ) ) `' S? ) | + * ) `' ) | ( '>' ( Char* - ( Char* '' ) ) '' ) */ private void parseFallBack(Node parent) throws JasperException { Node fallBackNode = new Node.FallBackAction(start, parent); - parseOptionalBody(fallBackNode, "jsp:fallback", - JAVAX_BODY_CONTENT_TEMPLATE_TEXT); + parseOptionalBody(fallBackNode, "jsp:fallback", JAVAX_BODY_CONTENT_TEMPLATE_TEXT); } /* * For Plugin: StdActionContent ::= Attributes PluginBody * - * PluginBody ::= EmptyBody | ( '>' S? ( ' ) S? ETag ) | ( '>' S? PluginTags - * ETag ) + * PluginBody ::= EmptyBody | ( '>' S? ( ' ) S? ETag ) | ( '>' S? PluginTags ETag ) * - * EmptyBody ::= '/>' | ( '>' ETag ) | ( '>' S? '' ETag ) | ( '>' S? '>>>>>> 76d2ad0a5d (Code clean-up. Formatting. No functional change.) */ private void parseStandardAction(Node parent) throws JasperException { Mark start = reader.mark(); @@ -1138,14 +1048,12 @@ parseForward(parent); } else if (reader.matches(INVOKE_ACTION)) { if (!isTagFile) { - err.jspError(reader.mark(), "jsp.error.action.isnottagfile", - "<jsp:invoke"); + err.jspError(reader.mark(), "jsp.error.action.isnottagfile", "<jsp:invoke"); } parseInvoke(parent); } else if (reader.matches(DOBODY_ACTION)) { if (!isTagFile) { - err.jspError(reader.mark(), "jsp.error.action.isnottagfile", - "<jsp:doBody"); + err.jspError(reader.mark(), "jsp.error.action.isnottagfile", "<jsp:doBody"); } parseDoBody(parent); } else if (reader.matches(GET_PROPERTY_ACTION)) { @@ -1188,8 +1096,7 @@ * * Attributes ::= ( S Attribute )* S? * - * CustomActionEnd ::= CustomActionTagDependent | CustomActionJSPContent | - * CustomActionScriptlessContent + * CustomActionEnd ::= CustomActionTagDependent | CustomActionJSPContent | CustomActionScriptlessContent * * CustomActionTagDependent ::= TagDependentOptionalBody * @@ -1241,11 +1148,9 @@ // tag files will be loaded later, in TagFileProcessor String handlerClassName = tagInfo.getTagClassName(); try { - tagHandlerClass = ctxt.getClassLoader().loadClass( - handlerClassName); + tagHandlerClass = ctxt.getClassLoader().loadClass(handlerClassName); } catch (Exception e) { - err.jspError(start, "jsp.error.loadclass.taghandler", - handlerClassName, tagName); + err.jspError(start, "jsp.error.loadclass.taghandler", handlerClassName, tagName); } } @@ -1261,12 +1166,11 @@ if (reader.matches("/>")) { if (tagInfo != null) { @SuppressWarnings("unused") - Node unused = new Node.CustomTag(tagName, prefix, shortTagName, - uri, attrs, start, parent, tagInfo, tagHandlerClass); + Node unused = new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs, start, parent, tagInfo, + tagHandlerClass); } else { @SuppressWarnings("unused") - Node unused = new Node.CustomTag(tagName, prefix, shortTagName, - uri, attrs, start, parent, tagFileInfo); + Node unused = new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs, start, parent, tagFileInfo); } return true; } @@ -1275,7 +1179,7 @@ // 'CustomActionJSPContent', or 'CustomActionScriptlessContent'. // depending on body-content in TLD. - // Looking for a body, it still can be empty; but if there is a + // Looking for a body, it still can be empty; but if there is // a tag body, its syntax would be dependent on the type of // body content declared in the TLD. String bc; @@ -1285,13 +1189,12 @@ bc = tagFileInfo.getTagInfo().getBodyContent(); } - Node tagNode = null; + Node tagNode; if (tagInfo != null) { - tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri, - attrs, start, parent, tagInfo, tagHandlerClass); + tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs, start, parent, tagInfo, + tagHandlerClass); } else { - tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri, - attrs, start, parent, tagFileInfo); + tagNode = new Node.CustomTag(tagName, prefix, shortTagName, uri, attrs, start, parent, tagFileInfo); } parseOptionalBody(tagNode, tagName, bc); @@ -1300,12 +1203,11 @@ } /* - * Parse for a template text string until '<' or "${" or "#{" is encountered, - * recognizing escape sequences "<\%", "\$", and "\#". + * Parse for a template text string until '<' or "${" or "#{" is encountered, recognizing escape sequences "<\%", + * "\$", and "\#". * - * Note: JSP uses '\$' as an escape for '$' and '\#' for '#' whereas EL uses - * '\${' for '${' and '\#{' for '#{'. We are processing JSP template - * test here so the JSP escapes apply. + * Note: JSP uses '\$' as an escape for '$' and '\#' for '#' whereas EL uses '\${' for '${' and '\#{' for '#{'. We + * are processing JSP template test here so the JSP escapes apply. */ private void parseTemplateText(Node parent) { @@ -1358,16 +1260,14 @@ } /* - * XMLTemplateText ::= ( S? '/>' ) | ( S? '>' ( ( Char* - ( Char* ( '<' | - * '${' ) ) ) ( '${' ELExpressionBody )? CDSect? )* ETag ) | - * + * XMLTemplateText ::= ( S? '/>' ) | ( S? '>' ( ( Char* - ( Char* ( '<' | '${' ) ) ) ( '${' ELExpressionBody )? + * CDSect? )* ETag ) | */ private void parseXMLTemplateText(Node parent) throws JasperException { reader.skipSpaces(); if (!reader.matches("/>")) { if (!reader.matches(">")) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:text>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:text>"); } CharArrayWriter ttext = new CharArrayWriter(); int ch = reader.nextChar(); @@ -1386,7 +1286,7 @@ ttext.write(text, 0, text.length()); } else if (ch == '\\') { int next = reader.peekChar(0); - if (next == '$' || next =='#') { + if (next == '$' || next == '#') { ttext.write(reader.nextChar()); } else { ttext.write('\\'); @@ -1398,8 +1298,7 @@ // Create a template text node @SuppressWarnings("unused") - Node unused = new Node.TemplateText( - ttext.toString(), start, parent); + Node unused = new Node.TemplateText(ttext.toString(), start, parent); // Mark and parse the EL expression and create its node: parseELExpression(parent, (char) ch); @@ -1416,12 +1315,10 @@ } @SuppressWarnings("unused") - Node unused = - new Node.TemplateText(ttext.toString(), start, parent); + Node unused = new Node.TemplateText(ttext.toString(), start, parent); if (!reader.hasMoreInput()) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:text>"); + err.jspError(start, "jsp.error.unterminated", "<jsp:text>"); } else if (!reader.matchesETagWithoutLessThan("jsp:text")) { err.jspError(start, "jsp.error.jsptext.badcontent"); } @@ -1429,12 +1326,11 @@ } /* - * AllBody ::= ( '<%--' JSPCommentBody ) | ( '<%@' DirectiveBody ) | ( ' 0) { @@ -1468,9 +1364,7 @@ parseXMLTemplateText(parent); } else if (!pageInfo.isELIgnored() && reader.matches("${")) { parseELExpression(parent, '$'); - } else if (!pageInfo.isELIgnored() - && !pageInfo.isDeferredSyntaxAllowedAsLiteral() - && reader.matches("#{")) { + } else if (!pageInfo.isELIgnored() && !pageInfo.isDeferredSyntaxAllowedAsLiteral() && reader.matches("#{")) { parseELExpression(parent, '#'); } else if (reader.matches(" ) | ( ' ) | ( '<%=' ) | ( ' ) | ( '<%' ) | ( ' ) | ( ' ) | ( ' ) | ( '<%=' ) | ( + * ' ) | ( '<%' ) | ( ' ) + * | ( ' ) | ( ' ) | ( '<%=' ) | ( ' ) | ( '<%' ) | ( ' ) | ( ' ) | ( '${' - * ) | ( ' ) | TemplateText + * TemplateTextBody ::= ( '<%--' JSPCommentBody ) | ( '<%@' DirectiveBody ) | ( ' ) | ( ' ) | ( '<%=' ) | ( + * ' ) | ( '<%' ) | ( ' ) + * | ( ' ) | ( '${' ) | ( ' ) | + * TemplateText */ private void parseElementsTemplateText(Node parent) throws JasperException { start = reader.mark(); @@ -1548,40 +1437,27 @@ } else if (reader.matches("")) { if (!reader.matches(">")) { - err.jspError(start, "jsp.error.unterminated", - "<jsp:attribute"); + err.jspError(start, "jsp.error.unterminated", "<jsp:attribute"); } if (namedAttributeNode.isTrim()) { reader.skipSpaces(); } - parseBody(namedAttributeNode, "jsp:attribute", - getAttributeBodyType(parent, attrs.getValue("name"))); + parseBody(namedAttributeNode, "jsp:attribute", getAttributeBodyType(parent, attrs.getValue("name"))); if (namedAttributeNode.isTrim()) { Node.Nodes subElems = namedAttributeNode.getBody(); if (subElems != null) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/ParserController.java tomcat10-10.1.52/java/org/apache/jasper/compiler/ParserController.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/ParserController.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/ParserController.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,15 +33,11 @@ /** * Controller for the parsing of a JSP page. *

          - * The same ParserController instance is used for a JSP page and any JSP - * segments included by it (via an include directive), where each segment may - * be provided in standard or XML syntax. This class selects and invokes the + * The same ParserController instance is used for a JSP page and any JSP segments included by it (via an include + * directive), where each segment may be provided in standard or XML syntax. This class selects and invokes the * appropriate parser for the JSP page and its included segments. - * - * @author Pierre Delisle - * @author Jan Luehe */ -class ParserController implements TagConstants { +public class ParserController implements TagConstants { private static final String CHARSET = "charset="; private static final String TAGS_IN_JAR_LOCATION = "/META-INF/tags/"; @@ -56,8 +52,7 @@ private boolean isXml; /* - * A stack to keep track of the 'current base directory' - * for include directives that refer to relative paths. + * A stack to keep track of the 'current base directory' for include directives that refer to relative paths. */ private final Deque baseDirStack = new ArrayDeque<>(); @@ -80,11 +75,11 @@ this.err = compiler.getErrorDispatcher(); } - public JspCompilationContext getJspCompilationContext () { + public JspCompilationContext getJspCompilationContext() { return ctxt; } - public Compiler getCompiler () { + public Compiler getCompiler() { return compiler; } @@ -96,8 +91,7 @@ * @return The parsed nodes * * @throws JasperException If an error occurs during parsing - * @throws IOException If an I/O error occurs such as the file not being - * found + * @throws IOException If an I/O error occurs such as the file not being found */ public Node.Nodes parse(String inFileName) throws JasperException, IOException { // If we're parsing a packaged tag file or a resource included by it @@ -110,16 +104,14 @@ } /** - * Parses the directives of a JSP page or tag file. This is invoked by the - * compiler. + * Parses the directives of a JSP page or tag file. This is invoked by the compiler. * * @param inFileName The path to the JSP page or tag file to be parsed. * * @return The parsed directive nodes * * @throws JasperException If an error occurs during parsing - * @throws IOException If an I/O error occurs such as the file not being - * found + * @throws IOException If an I/O error occurs such as the file not being found */ public Node.Nodes parseDirectives(String inFileName) throws JasperException, IOException { // If we're parsing a packaged tag file or a resource included by it @@ -136,39 +128,33 @@ * Processes an include directive with the given path. * * @param inFileName The path to the resource to be included. - * @param parent The parent node of the include directive. - * @param jar The JAR file from which to read the included resource, - * or null of the included resource is to be read from the filesystem + * @param parent The parent node of the include directive. + * @param jar The JAR file from which to read the included resource, or null of the included resource is to + * be read from the filesystem * * @return The parsed nodes * * @throws JasperException If an error occurs during parsing - * @throws IOException If an I/O error occurs such as the file not being - * found + * @throws IOException If an I/O error occurs such as the file not being found */ - public Node.Nodes parse(String inFileName, Node parent, Jar jar) - throws JasperException, IOException { + public Node.Nodes parse(String inFileName, Node parent, Jar jar) throws JasperException, IOException { // For files that are statically included, isTagfile and directiveOnly // remain unchanged. return doParse(inFileName, parent, jar); } /** - * Extracts tag file directive information from the given tag file. - * - * This is invoked by the compiler + * Extracts tag file directive information from the given tag file. This is invoked by the compiler * - * @param inFileName The name of the tag file to be parsed. - * @param jar The location of the tag file. + * @param inFileName The name of the tag file to be parsed. + * @param jar The location of the tag file. * * @return The parsed tag file nodes * * @throws JasperException If an error occurs during parsing - * @throws IOException If an I/O error occurs such as the file not being - * found + * @throws IOException If an I/O error occurs such as the file not being found */ - public Node.Nodes parseTagFileDirectives(String inFileName, Jar jar) - throws JasperException, IOException { + public Node.Nodes parseTagFileDirectives(String inFileName, Jar jar) throws JasperException, IOException { boolean isTagFileSave = isTagFile; boolean directiveOnlySave = directiveOnly; isTagFile = true; @@ -183,16 +169,15 @@ * Parses the JSP page or tag file with the given path name. * * @param inFileName The name of the JSP page or tag file to be parsed. - * @param parent The parent node (non-null when processing an include - * directive) - * @param jar The JAR file from which to read the JSP page or tag file, - * or null if the JSP page or tag file is to be read from the filesystem + * @param parent The parent node (non-null when processing an include directive) + * @param jar The JAR file from which to read the JSP page or tag file, or null if the JSP page or tag file + * is to be read from the filesystem */ @SuppressWarnings("null") // jar can't be null if processingTagInJar is true private Node.Nodes doParse(String inFileName, Node parent, Jar jar) throws FileNotFoundException, JasperException, IOException { - Node.Nodes parsedPage = null; + Node.Nodes parsedPage; isEncodingSpecifiedInProlog = false; isBomPresent = false; isDefaultPageEncoding = false; @@ -209,7 +194,7 @@ /* * An included file is being parsed that was included from the standard location for tag files in JAR but * tries to escape that location to either somewhere in the JAR not under the standard location or outside - * of the JAR. Neither of these are permitted. + * the JAR. Neither of these are permitted. */ err.jspError("jsp.error.invalid.includeInTagFileJar", inFileName, jar.getJarFileURL().toString()); } @@ -222,28 +207,22 @@ if (parent != null) { // Included resource, add to dependent list if (jar == null) { - compiler.getPageInfo().addDependant(absFileName, - ctxt.getLastModified(absFileName)); + compiler.getPageInfo().addDependant(absFileName, ctxt.getLastModified(absFileName)); } else { String entry = absFileName.substring(1); - compiler.getPageInfo().addDependant(jar.getURL(entry), - Long.valueOf(jar.getLastModified(entry))); + compiler.getPageInfo().addDependant(jar.getURL(entry), Long.valueOf(jar.getLastModified(entry))); } } if ((isXml && isEncodingSpecifiedInProlog) || isBomPresent) { /* - * Make sure the encoding explicitly specified in the XML - * prolog (if any) matches that in the JSP config element - * (if any), treating "UTF-16", "UTF-16BE", and "UTF-16LE" as - * identical. + * Make sure the encoding explicitly specified in the XML prolog (if any) matches that in the JSP config + * element (if any), treating "UTF-16", "UTF-16BE", and "UTF-16LE" as identical. */ - if (jspConfigPageEnc != null && !jspConfigPageEnc.equals(sourceEnc) - && (!jspConfigPageEnc.startsWith("UTF-16") - || !sourceEnc.startsWith("UTF-16"))) { - err.jspError("jsp.error.prolog_config_encoding_mismatch", - sourceEnc, jspConfigPageEnc); + if (jspConfigPageEnc != null && !jspConfigPageEnc.equals(sourceEnc) && + (!jspConfigPageEnc.startsWith("UTF-16") || !sourceEnc.startsWith("UTF-16"))) { + err.jspError("jsp.error.prolog_config_encoding_mismatch", sourceEnc, jspConfigPageEnc); } } @@ -252,18 +231,14 @@ // JSP document (XML syntax) // InputStream for jspx page is created and properly closed in // JspDocumentParser. - parsedPage = JspDocumentParser.parse(this, absFileName, jar, parent, - isTagFile, directiveOnly, sourceEnc, jspConfigPageEnc, - isEncodingSpecifiedInProlog, isBomPresent); + parsedPage = JspDocumentParser.parse(this, absFileName, jar, parent, isTagFile, directiveOnly, sourceEnc, + jspConfigPageEnc, isEncodingSpecifiedInProlog, isBomPresent); } else { // Standard syntax - try (InputStreamReader inStreamReader = JspUtil.getReader( - absFileName, sourceEnc, jar, ctxt, err, skip)) { - JspReader jspReader = new JspReader(ctxt, absFileName, - inStreamReader, err); - parsedPage = Parser.parse(this, jspReader, parent, isTagFile, - directiveOnly, jar, sourceEnc, jspConfigPageEnc, - isDefaultPageEncoding, isBomPresent); + try (InputStreamReader inStreamReader = JspUtil.getReader(absFileName, sourceEnc, jar, ctxt, err, skip)) { + JspReader jspReader = new JspReader(ctxt, absFileName, inStreamReader, err); + parsedPage = Parser.parse(this, jspReader, parent, isTagFile, directiveOnly, jar, sourceEnc, + jspConfigPageEnc, isDefaultPageEncoding, isBomPresent); } } @@ -273,56 +248,47 @@ } /* - * Checks to see if the given URI is matched by a URL pattern specified in - * a jsp-property-group in web.xml, and if so, returns the value of the - * element. + * Checks to see if the given URI is matched by a URL pattern specified in a jsp-property-group in web.xml, and if + * so, returns the value of the element. * * @param absFileName The URI to match * - * @return The value of the attribute of the - * jsp-property-group with matching URL pattern + * @return The value of the attribute of the jsp-property-group with matching URL pattern */ private String getJspConfigPageEncoding(String absFileName) { JspConfig jspConfig = ctxt.getOptions().getJspConfig(); - JspConfig.JspProperty jspProperty - = jspConfig.findJspProperty(absFileName); + JspConfig.JspProperty jspProperty = jspConfig.findJspProperty(absFileName); return jspProperty.getPageEncoding(); } /** - * Determines the syntax (standard or XML) and page encoding properties - * for the given file, and stores them in the 'isXml' and 'sourceEnc' - * instance variables, respectively. - */ - private void determineSyntaxAndEncoding(String absFileName, Jar jar, - String jspConfigPageEnc) - throws JasperException, IOException { + * Determines the syntax (standard or XML) and page encoding properties for the given file, and stores them in the + * 'isXml' and 'sourceEnc' instance variables, respectively. + */ + private void determineSyntaxAndEncoding(String absFileName, Jar jar, String jspConfigPageEnc) + throws JasperException, IOException { isXml = false; /* - * 'true' if the syntax (XML or standard) of the file is given - * from external information: either via a JSP configuration element, - * the ".jspx" suffix, or the enclosing file (for included resources) + * 'true' if the syntax (XML or standard) of the file is given from external information: either via a JSP + * configuration element, the ".jspx" suffix, or the enclosing file (for included resources) */ boolean isExternal = false; /* - * Indicates whether we need to revert from temporary usage of - * "ISO-8859-1" back to "UTF-8" + * Indicates whether we need to revert from temporary usage of "ISO-8859-1" back to "UTF-8" */ boolean revert = false; JspConfig jspConfig = ctxt.getOptions().getJspConfig(); - JspConfig.JspProperty jspProperty = jspConfig.findJspProperty( - absFileName); + JspConfig.JspProperty jspProperty = jspConfig.findJspProperty(absFileName); if (jspProperty.isXml() != null) { // If is specified in a , it is used. isXml = JspUtil.booleanValue(jspProperty.isXml()); isExternal = true; - } else if (absFileName.endsWith(".jspx") - || absFileName.endsWith(".tagx")) { + } else if (absFileName.endsWith(".jspx") || absFileName.endsWith(".tagx")) { isXml = true; isExternal = true; } @@ -350,20 +316,15 @@ if (!isXml && sourceEnc.equals("UTF-8")) { /* - * We don't know if we're dealing with XML or standard syntax. - * Therefore, we need to check to see if the page contains - * a element. + * We don't know if we're dealing with XML or standard syntax. Therefore, we need to check to see if the + * page contains a element. * - * We need to be careful, because the page may be encoded in - * ISO-8859-1 (or something entirely different), and may - * contain byte sequences that will cause a UTF-8 converter to - * throw exceptions. + * We need to be careful, because the page may be encoded in ISO-8859-1 (or something entirely + * different), and may contain byte sequences that will cause a UTF-8 converter to throw exceptions. * - * It is safe to use a source encoding of ISO-8859-1 in this - * case, as there are no invalid byte sequences in ISO-8859-1, - * and the byte/character sequences we're looking for (i.e., - * ) are identical in either encoding (both UTF-8 - * and ISO-8859-1 are extensions of ASCII). + * It is safe to use a source encoding of ISO-8859-1 in this case, as there are no invalid byte + * sequences in ISO-8859-1, and the byte/character sequences we're looking for (i.e., ) are + * identical in either encoding (both UTF-8 and ISO-8859-1 are extensions of ASCII). */ sourceEnc = "ISO-8859-1"; revert = true; @@ -378,15 +339,12 @@ } /* - * At this point, 'isExternal' or 'isXml' is FALSE. - * Search for jsp:root action, in order to determine if we're dealing - * with XML or standard syntax (unless we already know what we're - * dealing with, i.e., when 'isExternal' is TRUE and 'isXml' is FALSE). - * No check for XML prolog, since nothing prevents a page from - * outputting XML and still using JSP syntax (in this case, the - * XML prolog is treated as template text). + * At this point, 'isExternal' or 'isXml' is FALSE. Search for jsp:root action, in order to determine if we're + * dealing with XML or standard syntax (unless we already know what we're dealing with, i.e., when 'isExternal' + * is TRUE and 'isXml' is FALSE). No check for XML prolog, since nothing prevents a page from outputting XML and + * still using JSP syntax (in this case, the XML prolog is treated as template text). */ - JspReader jspReader = null; + JspReader jspReader; try { jspReader = new JspReader(ctxt, absFileName, sourceEnc, jar, err); } catch (FileNotFoundException ex) { @@ -410,10 +368,8 @@ } /* - * At this point, we know we're dealing with JSP syntax. - * If an XML prolog is provided, it's treated as template text. - * Determine the page encoding from the page directive, unless it's - * specified via JSP config. + * At this point, we know we're dealing with JSP syntax. If an XML prolog is provided, it's treated as template + * text. Determine the page encoding from the page directive, unless it's specified via JSP config. */ if (!isBomPresent) { sourceEnc = jspConfigPageEnc; @@ -430,16 +386,12 @@ } /* - * Determines page source encoding for page or tag file in JSP syntax, - * by reading (in this order) the value of the 'pageEncoding' page - * directive attribute, or the charset value of the 'contentType' page - * directive attribute. + * Determines page source encoding for page or tag file in JSP syntax, by reading (in this order) the value of the + * 'pageEncoding' page directive attribute, or the charset value of the 'contentType' page directive attribute. * * @return The page encoding, or null if not found */ - private String getPageEncodingForJspSyntax(JspReader jspReader, - Mark startMark) - throws JasperException { + private String getPageEncodingForJspSyntax(JspReader jspReader, Mark startMark) throws JasperException { String encoding = null; String saveEncoding = null; @@ -447,8 +399,8 @@ jspReader.reset(startMark); /* - * Determine page encoding from directive of the form <%@ page %>, - * <%@ tag %>, or . + * Determine page encoding from directive of the form <%@ page %>, <%@ tag %>, or + * . */ while (true) { if (jspReader.skipUntil("<") == null) { @@ -496,33 +448,29 @@ } /* - * Scans the given attributes for the attribute with the given name, - * which is either 'pageEncoding' or 'contentType', and returns the - * specified page encoding. + * Scans the given attributes for the attribute with the given name, which is either 'pageEncoding' or + * 'contentType', and returns the specified page encoding. * - * In the case of 'contentType', the page encoding is taken from the - * content type's 'charset' component. + * In the case of 'contentType', the page encoding is taken from the content type's 'charset' component. * * @param attrs The page directive attributes - * @param attrName The name of the attribute to search for (either - * 'pageEncoding' or 'contentType') + * + * @param attrName The name of the attribute to search for (either 'pageEncoding' or 'contentType') * * @return The page encoding, or null */ - private String getPageEncodingFromDirective(Attributes attrs, - String attrName) { + private String getPageEncodingFromDirective(Attributes attrs, String attrName) { String value = attrs.getValue(attrName); if (attrName.equals("pageEncoding")) { return value; } // attrName = contentType - String contentType = value; String encoding = null; - if (contentType != null) { - int loc = contentType.indexOf(CHARSET); + if (value != null) { + int loc = value.indexOf(CHARSET); if (loc != -1) { - encoding = contentType.substring(loc + CHARSET.length()); + encoding = value.substring(loc + CHARSET.length()); } } @@ -530,10 +478,9 @@ } /* - * Resolve the name of the file and update baseDirStack() to keep track of - * the current base directory for each included file. - * The 'root' file is always an 'absolute' path, so no need to put an - * initial value in the baseDirStack. + * Resolve the name of the file and update baseDirStack() to keep track of the current base directory for each + * included file. The 'root' file is always an 'absolute' path, so no need to put an initial value in the + * baseDirStack. */ private String resolveFileName(String inFileName) throws URISyntaxException { String fileName = inFileName.replace('\\', '/'); @@ -548,22 +495,19 @@ } /* - * Checks to see if the given page contains, as its first element, a - * element whose prefix is bound to the JSP namespace, as in: + * Checks to see if the given page contains, as its first element, a element whose prefix is bound to the JSP + * namespace, as in: * - * - * ... - * + * ... * * @param reader The reader for this page * - * @return true if this page contains a root element whose prefix is bound - * to the JSP namespace, and false otherwise + * @return true if this page contains a root element whose prefix is bound to the JSP namespace, and false otherwise */ private boolean hasJspRoot(JspReader reader) { // :root must be the first element - Mark start = null; + Mark start; while ((start = reader.skipUntil("<")) != null) { int c = reader.nextChar(); if (c != '!' && c != '?') { @@ -594,22 +538,16 @@ return false; } index += xmlnsDecl.length(); - while (index < root.length() - && Character.isWhitespace(root.charAt(index))) { + while (index < root.length() && Character.isWhitespace(root.charAt(index))) { index++; } if (index < root.length() && root.charAt(index) == '=') { - index++; - while (index < root.length() - && Character.isWhitespace(root.charAt(index))) { + do { index++; - } - if (index < root.length() - && (root.charAt(index) == '"' || root.charAt(index) == '\'')) { + } while (index < root.length() && Character.isWhitespace(root.charAt(index))); + if (index < root.length() && (root.charAt(index) == '"' || root.charAt(index) == '\'')) { index++; - if (root.regionMatches(index, JSP_URI, 0, JSP_URI.length())) { - return true; - } + return root.regionMatches(index, JSP_URI, 0, JSP_URI.length()); } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/ScriptingVariabler.java tomcat10-10.1.52/java/org/apache/jasper/compiler/ScriptingVariabler.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/ScriptingVariabler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/ScriptingVariabler.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,19 +27,15 @@ import org.apache.jasper.JasperException; /** - * Class responsible for determining the scripting variables that every - * custom action needs to declare. - * - * @author Jan Luehe + * Class responsible for determining the scripting variables that every custom action needs to declare. */ class ScriptingVariabler { private static final Integer MAX_SCOPE = Integer.valueOf(Integer.MAX_VALUE); /* - * Assigns an identifier (of type integer) to every custom tag, in order - * to help identify, for every custom tag, the scripting variables that it - * needs to declare. + * Assigns an identifier (of type integer) to every custom tag, in order to help identify, for every custom tag, the + * scripting variables that it needs to declare. */ private static class CustomTagCounter extends Node.Visitor { @@ -58,13 +54,12 @@ } /* - * For every custom tag, determines the scripting variables it needs to - * declare. + * For every custom tag, determines the scripting variables it needs to declare. */ private static class ScriptingVariableVisitor extends Node.Visitor { private final ErrorDispatcher err; - private final Map scriptVars; + private final Map scriptVars; ScriptingVariableVisitor(ErrorDispatcher err) { this.err = err; @@ -79,8 +74,7 @@ setScriptingVars(n, VariableInfo.AT_END); } - private void setScriptingVars(Node.CustomTag n, int scope) - throws JasperException { + private void setScriptingVars(Node.CustomTag n, int scope) throws JasperException { TagVariableInfo[] tagVarInfos = n.getTagVariableInfos(); VariableInfo[] varInfos = n.getVariableInfos(); @@ -90,10 +84,9 @@ List vec = new ArrayList<>(); - Integer ownRange = null; + Integer ownRange; Node.CustomTag parent = n.getCustomTagParent(); - if (scope == VariableInfo.AT_BEGIN - || scope == VariableInfo.AT_END) { + if (scope == VariableInfo.AT_BEGIN || scope == VariableInfo.AT_END) { if (parent == null) { ownRange = MAX_SCOPE; } else { @@ -106,39 +99,33 @@ if (varInfos.length > 0) { for (VariableInfo varInfo : varInfos) { - if (varInfo.getScope() != scope - || !varInfo.getDeclare()) { + if (varInfo.getScope() != scope || !varInfo.getDeclare()) { continue; } String varName = varInfo.getVarName(); Integer currentRange = scriptVars.get(varName); - if (currentRange == null || - ownRange.compareTo(currentRange) > 0) { + if (currentRange == null || ownRange.compareTo(currentRange) > 0) { scriptVars.put(varName, ownRange); vec.add(varInfo); } } } else { for (TagVariableInfo tagVarInfo : tagVarInfos) { - if (tagVarInfo.getScope() != scope - || !tagVarInfo.getDeclare()) { + if (tagVarInfo.getScope() != scope || !tagVarInfo.getDeclare()) { continue; } String varName = tagVarInfo.getNameGiven(); if (varName == null) { - varName = n.getTagData().getAttributeString( - tagVarInfo.getNameFromAttribute()); + varName = n.getTagData().getAttributeString(tagVarInfo.getNameFromAttribute()); if (varName == null) { - err.jspError(n, - "jsp.error.scripting.variable.missing_name", + err.jspError(n, "jsp.error.scripting.variable.missing_name", tagVarInfo.getNameFromAttribute()); } } Integer currentRange = scriptVars.get(varName); - if (currentRange == null || - ownRange.compareTo(currentRange) > 0) { + if (currentRange == null || ownRange.compareTo(currentRange) > 0) { scriptVars.put(varName, ownRange); vec.add(tagVarInfo); } @@ -149,8 +136,7 @@ } } - public static void set(Node.Nodes page, ErrorDispatcher err) - throws JasperException { + public static void set(Node.Nodes page, ErrorDispatcher err) throws JasperException { page.visit(new CustomTagCounter()); page.visit(new ScriptingVariableVisitor(err)); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/ServletWriter.java tomcat10-10.1.52/java/org/apache/jasper/compiler/ServletWriter.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/ServletWriter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/ServletWriter.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,9 +20,6 @@ /** * This is what is used to generate servlets. - * - * @author Anil K. Vijendran - * @author Kin-man Chung */ public class ServletWriter implements AutoCloseable { @@ -81,6 +78,7 @@ /** * Prints the given string followed by '\n' + * * @param s The string */ public void println(String s) { @@ -105,6 +103,7 @@ /** * Prints the current indentation, followed by the given string + * * @param s The string */ public void printin(String s) { @@ -114,6 +113,7 @@ /** * Prints the current indentation, and then the string, and a '\n'. + * * @param s The string */ public void printil(String s) { @@ -123,9 +123,8 @@ } /** - * Prints the given char. + * Prints the given char. Use println() to print a '\n'. * - * Use println() to print a '\n'. * @param c The char */ public void print(char c) { @@ -134,6 +133,7 @@ /** * Prints the given int. + * * @param i The int */ public void print(int i) { @@ -141,10 +141,8 @@ } /** - * Prints the given string. + * Prints the given string. The string must not contain any '\n', otherwise the line count will be off. * - * The string must not contain any '\n', otherwise the line count will be - * off. * @param s The string */ public void print(String s) { @@ -152,17 +150,15 @@ } /** - * Prints the given string. + * Prints the given string. If the string spans multiple lines, the line count will be adjusted accordingly. * - * If the string spans multiple lines, the line count will be adjusted - * accordingly. * @param s The string */ public void printMultiLn(String s) { int index = 0; // look for hidden newlines inside strings - while ((index=s.indexOf('\n',index)) > -1 ) { + while ((index = s.indexOf('\n', index)) > -1) { javaLine++; index++; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/SmapStratum.java tomcat10-10.1.52/java/org/apache/jasper/compiler/SmapStratum.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/SmapStratum.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/SmapStratum.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,22 +20,14 @@ import java.util.List; /** - * Represents the line and file mappings associated with a JSR-045 - * "stratum". - * - * @author Jayson Falkner - * @author Shawn Bayern + * Represents the line and file mappings associated with a JSR-045 "stratum". */ public class SmapStratum { - //********************************************************************* - // Class for storing LineInfo data - /** - * Represents a single LineSection in an SMAP, associated with - * a particular stratum. + * Represents a single LineSection in an SMAP, associated with a particular stratum. */ - static class LineInfo { + public static class LineInfo { private int inputStartLine = -1; private int outputStartLine = -1; private int lineFileID = 0; @@ -45,32 +37,31 @@ public void setInputStartLine(int inputStartLine) { if (inputStartLine < 0) { - throw new IllegalArgumentException(Localizer.getMessage( - "jsp.error.negativeParameter", Integer.valueOf(inputStartLine))); + throw new IllegalArgumentException( + Localizer.getMessage("jsp.error.negativeParameter", Integer.valueOf(inputStartLine))); } this.inputStartLine = inputStartLine; } public void setOutputStartLine(int outputStartLine) { if (outputStartLine < 0) { - throw new IllegalArgumentException(Localizer.getMessage( - "jsp.error.negativeParameter", Integer.valueOf(outputStartLine))); + throw new IllegalArgumentException( + Localizer.getMessage("jsp.error.negativeParameter", Integer.valueOf(outputStartLine))); } this.outputStartLine = outputStartLine; } /** - * Sets lineFileID. Should be called only when different from - * that of prior LineInfo object (in any given context) or 0 - * if the current LineInfo has no (logical) predecessor. - * LineInfo will print this file number no matter what. + * Sets lineFileID. Should be called only when different from that of prior LineInfo object (in any given + * context) or 0 if the current LineInfo has no (logical) predecessor. LineInfo will print this + * file number no matter what. * * @param lineFileID The new line file ID */ public void setLineFileID(int lineFileID) { if (lineFileID < 0) { - throw new IllegalArgumentException(Localizer.getMessage( - "jsp.error.negativeParameter", Integer.valueOf(lineFileID))); + throw new IllegalArgumentException( + Localizer.getMessage("jsp.error.negativeParameter", Integer.valueOf(lineFileID))); } this.lineFileID = lineFileID; this.lineFileIDSet = true; @@ -78,16 +69,16 @@ public void setInputLineCount(int inputLineCount) { if (inputLineCount < 0) { - throw new IllegalArgumentException(Localizer.getMessage( - "jsp.error.negativeParameter", Integer.valueOf(inputLineCount))); + throw new IllegalArgumentException( + Localizer.getMessage("jsp.error.negativeParameter", Integer.valueOf(inputLineCount))); } this.inputLineCount = inputLineCount; } public void setOutputLineIncrement(int outputLineIncrement) { if (outputLineIncrement < 0) { - throw new IllegalArgumentException(Localizer.getMessage( - "jsp.error.negativeParameter", Integer.valueOf(outputLineIncrement))); + throw new IllegalArgumentException( + Localizer.getMessage("jsp.error.negativeParameter", Integer.valueOf(outputLineIncrement))); } this.outputLineIncrement = outputLineIncrement; } @@ -97,9 +88,8 @@ } /** - * @return the current LineInfo as a String, print all values only when - * appropriate (but LineInfoID if and only if it's been - * specified, as its necessity is sensitive to context). + * @return the current LineInfo as a String, print all values only when appropriate (but LineInfoID if and only + * if it's been specified, as its necessity is sensitive to context). */ public String getString() { if (inputStartLine == -1 || outputStartLine == -1) { @@ -108,14 +98,14 @@ StringBuilder out = new StringBuilder(); out.append(inputStartLine); if (lineFileIDSet) { - out.append("#" + lineFileID); + out.append("#").append(lineFileID); } if (inputLineCount != 1) { - out.append("," + inputLineCount); + out.append(",").append(inputLineCount); } - out.append(":" + outputStartLine); + out.append(":").append(outputStartLine); if (outputLineIncrement != 1) { - out.append("," + outputLineIncrement); + out.append(",").append(outputLineIncrement); } out.append('\n'); return out.toString(); @@ -127,8 +117,6 @@ } } - //********************************************************************* - // Private state private final List fileNameList = new ArrayList<>(); private final List filePathList = new ArrayList<>(); @@ -139,8 +127,6 @@ // .class file private String classFileName; - //********************************************************************* - // Methods to add mapping information /** * Adds record of a new file, by filename. @@ -152,12 +138,10 @@ } /** - * Adds record of a new file, by filename and path. The path - * may be relative to a source compilation path. + * Adds record of a new file, by filename and path. The path may be relative to a source compilation path. * * @param filename the filename to add, unqualified by path - * @param filePath the path for the filename, potentially relative - * to a source compilation path + * @param filePath the path for the filename, potentially relative to a source compilation path */ public void addFile(String filename, String filePath) { int pathIndex = filePathList.indexOf(filePath); @@ -172,41 +156,29 @@ */ public void optimizeLineSection() { - //Incorporate each LineInfo into the previous LineInfo's - //outputLineIncrement, if possible + // Incorporate each LineInfo into the previous LineInfo's outputLineIncrement, if possible int i = 0; while (i < lineData.size() - 1) { LineInfo li = lineData.get(i); LineInfo liNext = lineData.get(i + 1); - if (!liNext.lineFileIDSet - && liNext.inputStartLine == li.inputStartLine - && liNext.inputLineCount == 1 - && li.inputLineCount == 1 - && liNext.outputStartLine - == li.outputStartLine - + li.inputLineCount * li.outputLineIncrement) { - li.setOutputLineIncrement( - liNext.outputStartLine - - li.outputStartLine - + liNext.outputLineIncrement); + if (!liNext.lineFileIDSet && liNext.inputStartLine == li.inputStartLine && liNext.inputLineCount == 1 && + li.inputLineCount == 1 && + liNext.outputStartLine == li.outputStartLine + li.inputLineCount * li.outputLineIncrement) { + li.setOutputLineIncrement(liNext.outputStartLine - li.outputStartLine + liNext.outputLineIncrement); lineData.remove(i + 1); } else { i++; } } - //Incorporate each LineInfo into the previous LineInfo's - //inputLineCount, if possible + // Incorporate each LineInfo into the previous LineInfo's inputLineCount, if possible i = 0; while (i < lineData.size() - 1) { LineInfo li = lineData.get(i); LineInfo liNext = lineData.get(i + 1); - if (!liNext.lineFileIDSet - && liNext.inputStartLine == li.inputStartLine + li.inputLineCount - && liNext.outputLineIncrement == li.outputLineIncrement - && liNext.outputStartLine - == li.outputStartLine - + li.inputLineCount * li.outputLineIncrement) { + if (!liNext.lineFileIDSet && liNext.inputStartLine == li.inputStartLine + li.inputLineCount && + liNext.outputLineIncrement == li.outputLineIncrement && + liNext.outputStartLine == li.outputStartLine + li.inputLineCount * li.outputLineIncrement) { li.setInputLineCount(li.inputLineCount + liNext.inputLineCount); lineData.remove(i + 1); } else { @@ -216,46 +188,35 @@ } /** - * Adds complete information about a simple line mapping. Specify - * all the fields in this method; the back-end machinery takes care - * of printing only those that are necessary in the final SMAP. - * (My view is that fields are optional primarily for spatial efficiency, - * not for programmer convenience. Could always add utility methods + * Adds complete information about a simple line mapping. Specify all the fields in this method; the back-end + * machinery takes care of printing only those that are necessary in the final SMAP. (My view is that fields are + * optional primarily for spatial efficiency, not for programmer convenience. Could always add utility methods * later.) * - * @param inputStartLine starting line in the source file - * (SMAP InputStartLine) - * @param inputFileName the filepath (or name) from which the input comes - * (yields SMAP LineFileID) Use unqualified names - * carefully, and only when they uniquely identify a file. - * @param inputLineCount the number of lines in the input to map - * (SMAP LineFileCount) - * @param outputStartLine starting line in the output file - * (SMAP OutputStartLine) - * @param outputLineIncrement number of output lines to map to each - * input line (SMAP OutputLineIncrement). Given the - * fact that the name starts with "output", I continuously have - * the subconscious urge to call this field - * OutputLineExcrement. + * @param inputStartLine starting line in the source file (SMAP InputStartLine) + * @param inputFileName the filepath (or name) from which the input comes (yields SMAP + * LineFileID) Use unqualified names carefully, and only when they + * uniquely identify a file. + * @param inputLineCount the number of lines in the input to map (SMAP LineFileCount) + * @param outputStartLine starting line in the output file (SMAP OutputStartLine) + * @param outputLineIncrement number of output lines to map to each input line (SMAP + * OutputLineIncrement). Given the fact that the name starts with + * "output", I continuously have the subconscious urge to call this field + * OutputLineExcrement. */ - public void addLineData( - int inputStartLine, - String inputFileName, - int inputLineCount, - int outputStartLine, - int outputLineIncrement) { + public void addLineData(int inputStartLine, String inputFileName, int inputLineCount, int outputStartLine, + int outputLineIncrement) { // check the input - what are you doing here?? int fileIndex = filePathList.indexOf(inputFileName); if (fileIndex == -1) { - throw new IllegalArgumentException( - "inputFileName: " + inputFileName); + throw new IllegalArgumentException("inputFileName: " + inputFileName); } - //Jasper incorrectly SMAPs certain Nodes, giving them an - //outputStartLine of 0. This can cause a fatal error in - //optimizeLineSection, making it impossible for Jasper to - //compile the JSP. Until we can fix the underlying - //SMAPping problem, we simply ignore the flawed SMAP entries. + /* + * Jasper incorrectly SMAPs certain Nodes, giving them an outputStartLine of 0. This can cause a fatal error in + * optimizeLineSection, making it impossible for Jasper to compile the JSP. Until we can fix the underlying + * SMAPping problem, we simply ignore the flawed SMAP entries. + */ if (outputStartLine == 0) { return; } @@ -296,9 +257,6 @@ } - //********************************************************************* - // Methods to retrieve information - @Override public String toString() { return getSmapStringInternal(); @@ -320,7 +278,7 @@ // start the SMAP out.append("SMAP\n"); - out.append(outputFileName + '\n'); + out.append(outputFileName).append('\n'); out.append("JSP\n"); // print StratumSection @@ -331,16 +289,16 @@ int bound = fileNameList.size(); for (int i = 0; i < bound; i++) { if (filePathList.get(i) != null) { - out.append("+ " + i + " " + fileNameList.get(i) + "\n"); + out.append("+ ").append(i).append(" ").append(fileNameList.get(i)).append("\n"); // Source paths must be relative, not absolute, so we // remove the leading "/", if one exists. String filePath = filePathList.get(i); if (filePath.startsWith("/")) { filePath = filePath.substring(1); } - out.append(filePath + "\n"); + out.append(filePath).append("\n"); } else { - out.append(i + " " + fileNameList.get(i) + "\n"); + out.append(i).append(" ").append(fileNameList.get(i)).append("\n"); } } @@ -380,8 +338,7 @@ } // This is the match - int inputOffset = - (outputLineNumber - lineInfo.outputStartLine) / lineInfo.outputLineIncrement; + int inputOffset = (outputLineNumber - lineInfo.outputStartLine) / lineInfo.outputLineIncrement; inputLineNumber = lineInfo.inputStartLine + inputOffset; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/SmapUtil.java tomcat10-10.1.52/java/org/apache/jasper/compiler/SmapUtil.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/SmapUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/SmapUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,36 +38,25 @@ import org.apache.juli.logging.LogFactory; /** - * Contains static utilities for generating SMAP data based on the - * current version of Jasper. - * - * @author Jayson Falkner - * @author Shawn Bayern - * @author Robert Field (inner SDEInstaller class) - * @author Mark Roth - * @author Kin-man Chung + * Contains static utilities for generating SMAP data based on the current version of Jasper. */ public class SmapUtil { - //********************************************************************* - // Constants - private static final Charset SMAP_ENCODING = StandardCharsets.UTF_8; - //********************************************************************* - // Public entry points /** - * Generates an appropriate SMAP representing the current compilation - * context. (JSR-045.) + * Generates an appropriate SMAP representing the current compilation context. (JSR-045.) * - * @param ctxt Current compilation context + * @param ctxt Current compilation context * @param pageNodes The current JSP page + * * @return a SMAP for the page + * * @throws IOException Error writing SMAP */ - public static Map generateSmap(JspCompilationContext ctxt, - Node.Nodes pageNodes) throws IOException { + public static Map generateSmap(JspCompilationContext ctxt, Node.Nodes pageNodes) + throws IOException { Map smapInfo = new HashMap<>(); @@ -76,8 +65,9 @@ try { pageNodes.visit(psVisitor); } catch (JasperException ex) { + // Ignore } - HashMap map = psVisitor.getMap(); + HashMap map = psVisitor.getMap(); // Assemble info about our own stratum (JSP) using JspLineMap SmapStratum s = new SmapStratum(); @@ -94,34 +84,25 @@ if (ctxt.getOptions().isSmapDumped()) { File outSmap = new File(classFileName + ".smap"); - PrintWriter so = - new PrintWriter( - new OutputStreamWriter( - new FileOutputStream(outSmap), - SMAP_ENCODING)); + PrintWriter so = new PrintWriter(new OutputStreamWriter(new FileOutputStream(outSmap), SMAP_ENCODING)); so.print(s.getSmapString()); so.close(); } - for (Map.Entry entry : map.entrySet()) { + for (Map.Entry entry : map.entrySet()) { String innerClass = entry.getKey(); s = entry.getValue(); s.optimizeLineSection(); s.setOutputFileName(unqualify(ctxt.getServletJavaFileName())); String innerClassFileName = - classFileName.substring(0, classFileName.indexOf(".class")) + - '$' + innerClass + ".class"; + classFileName.substring(0, classFileName.indexOf(".class")) + '$' + innerClass + ".class"; s.setClassFileName(innerClassFileName); smapInfo.put(ctxt.getFQCN() + "." + innerClass, s); if (ctxt.getOptions().isSmapDumped()) { File outSmap = new File(innerClassFileName + ".smap"); - PrintWriter so = - new PrintWriter( - new OutputStreamWriter( - new FileOutputStream(outSmap), - SMAP_ENCODING)); + PrintWriter so = new PrintWriter(new OutputStreamWriter(new FileOutputStream(outSmap), SMAP_ENCODING)); so.print(s.getSmapString()); so.close(); } @@ -130,21 +111,17 @@ return smapInfo; } - public static void installSmap(Map smapInfo) - throws IOException { + public static void installSmap(Map smapInfo) throws IOException { if (smapInfo == null) { return; } for (Map.Entry entry : smapInfo.entrySet()) { File outServlet = new File(entry.getValue().getClassFileName()); - SDEInstaller.install(outServlet, - entry.getValue().getSmapString().getBytes(StandardCharsets.ISO_8859_1)); + SDEInstaller.install(outServlet, entry.getValue().getSmapString().getBytes(StandardCharsets.ISO_8859_1)); } } - //********************************************************************* - // Private utilities /** * Returns an unqualified version of the given file path. @@ -154,8 +131,7 @@ return path.substring(path.lastIndexOf('/') + 1); } - //********************************************************************* - // Installation logic (from Robert Field, JSR-045 spec lead) + private static class SDEInstaller { private final Log log = LogFactory.getLog(SDEInstaller.class); // must not be static @@ -176,8 +152,8 @@ SDEInstaller installer = new SDEInstaller(classFile, smap); installer.install(tmpFile); if (!classFile.delete()) { - throw new IOException(Localizer.getMessage("jsp.error.unable.deleteClassFile", - classFile.getAbsolutePath())); + throw new IOException( + Localizer.getMessage("jsp.error.unable.deleteClassFile", classFile.getAbsolutePath())); } if (!tmpFile.renameTo(classFile)) { throw new IOException(Localizer.getMessage("jsp.error.unable.renameClassFile", @@ -185,8 +161,7 @@ } } - SDEInstaller(File inClassFile, byte[] sdeAttr) - throws IOException { + SDEInstaller(File inClassFile, byte[] sdeAttr) throws IOException { if (!inClassFile.exists()) { throw new FileNotFoundException(Localizer.getMessage("jsp.error.noFile", inClassFile)); } @@ -208,12 +183,11 @@ } static byte[] readWhole(File input) throws IOException { - int len = (int)input.length(); + int len = (int) input.length(); byte[] bytes = new byte[len]; try (FileInputStream inStream = new FileInputStream(input)) { if (inStream.read(bytes, 0, len) != len) { - throw new IOException(Localizer.getMessage( - "jsp.error.readContent", Integer.valueOf(len))); + throw new IOException(Localizer.getMessage("jsp.error.readContent", Integer.valueOf(len))); } } return bytes; @@ -344,7 +318,7 @@ } void writeU1(int val) { - gen[genPos++] = (byte)val; + gen[genPos++] = (byte) val; } void writeU2(int val) { @@ -377,53 +351,52 @@ } } - int copyConstantPool(int constantPoolCount) - throws UnsupportedEncodingException, IOException { + int copyConstantPool(int constantPoolCount) throws UnsupportedEncodingException, IOException { int sdeIndex = -1; // copy const pool index zero not in class file for (int i = 1; i < constantPoolCount; ++i) { int tag = readU1(); writeU1(tag); switch (tag) { - case 7 : // Class - case 8 : // String - case 16 : // MethodType + case 7: // Class + case 8: // String + case 16: // MethodType if (log.isTraceEnabled()) { log.trace(i + " copying 2 bytes"); } copy(2); break; - case 15 : // MethodHandle + case 15: // MethodHandle if (log.isTraceEnabled()) { log.trace(i + " copying 3 bytes"); } copy(3); break; - case 9 : // Field - case 10 : // Method - case 11 : // InterfaceMethod - case 3 : // Integer - case 4 : // Float - case 12 : // NameAndType - case 18 : // InvokeDynamic + case 9: // Field + case 10: // Method + case 11: // InterfaceMethod + case 3: // Integer + case 4: // Float + case 12: // NameAndType + case 18: // InvokeDynamic if (log.isTraceEnabled()) { log.trace(i + " copying 4 bytes"); } copy(4); break; - case 5 : // Long - case 6 : // Double + case 5: // Long + case 6: // Double if (log.isTraceEnabled()) { log.trace(i + " copying 8 bytes"); } copy(8); i++; break; - case 1 : // Utf8 + case 1: // Utf8 int len = readU2(); writeU2(len); byte[] utf8 = readBytes(len); - String str = new String(utf8, "UTF-8"); + String str = new String(utf8, StandardCharsets.UTF_8); if (log.isTraceEnabled()) { log.trace(i + " read class attr -- '" + str + "'"); } @@ -432,9 +405,8 @@ } writeBytes(utf8); break; - default : - throw new IOException(Localizer.getMessage( - "jsp.error.unexpectedTag", Integer.valueOf(tag))); + default: + throw new IOException(Localizer.getMessage("jsp.error.unexpectedTag", Integer.valueOf(tag))); } } return sdeIndex; @@ -450,14 +422,12 @@ } } - public static void evaluateNodes( - Node.Nodes nodes, - SmapStratum s, - HashMap innerClassMap, - boolean breakAtLF) { + public static void evaluateNodes(Node.Nodes nodes, SmapStratum s, HashMap innerClassMap, + boolean breakAtLF) { try { nodes.visit(new SmapGenVisitor(s, breakAtLF, innerClassMap)); } catch (JasperException ex) { + // Ignore } } @@ -465,9 +435,9 @@ private SmapStratum smap; private final boolean breakAtLF; - private final HashMap innerClassMap; + private final HashMap innerClassMap; - SmapGenVisitor(SmapStratum s, boolean breakAtLF, HashMap map) { + SmapGenVisitor(SmapStratum s, boolean breakAtLF, HashMap map) { this.smap = s; this.breakAtLF = breakAtLF; this.innerClassMap = map; @@ -594,16 +564,15 @@ return; } - //Add the file information + // Add the file information String fileName = mark.getFile(); smap.addFile(unqualify(fileName), fileName); - //Add a LineInfo that corresponds to the beginning of this node + // Add a LineInfo that corresponds to the beginning of this node int iInputStartLine = mark.getLineNumber(); int iOutputStartLine = n.getBeginJavaLine(); - int iOutputLineIncrement = breakAtLF? 1: 0; - smap.addLineData(iInputStartLine, fileName, 1, iOutputStartLine, - iOutputLineIncrement); + int iOutputLineIncrement = breakAtLF ? 1 : 0; + smap.addLineData(iInputStartLine, fileName, 1, iOutputStartLine, iOutputLineIncrement); // Output additional mappings in the text java.util.ArrayList extraSmap = n.getExtraSmap(); @@ -611,21 +580,13 @@ if (extraSmap != null) { for (Integer integer : extraSmap) { iOutputStartLine += iOutputLineIncrement; - smap.addLineData( - iInputStartLine + integer.intValue(), - fileName, - 1, - iOutputStartLine, + smap.addLineData(iInputStartLine + integer.intValue(), fileName, 1, iOutputStartLine, iOutputLineIncrement); } } } - private void doSmap( - Node n, - int inLineCount, - int outIncrement, - int skippedLines) { + private void doSmap(Node n, int inLineCount, int outIncrement, int skippedLines) { Mark mark = n.getStart(); if (mark == null) { return; @@ -633,12 +594,8 @@ String unqualifiedName = unqualify(mark.getFile()); smap.addFile(unqualifiedName, mark.getFile()); - smap.addLineData( - mark.getLineNumber() + skippedLines, - mark.getFile(), - inLineCount - skippedLines, - n.getBeginJavaLine() + skippedLines, - outIncrement); + smap.addLineData(mark.getLineNumber() + skippedLines, mark.getFile(), inLineCount - skippedLines, + n.getBeginJavaLine() + skippedLines, outIncrement); } private void doSmap(Node n) { @@ -648,7 +605,7 @@ private void doSmapText(Node n) { String text = n.getText(); int index = 0; - int next = 0; + int next; int lineCount = 1; int skippedLines = 0; boolean slashStarSeen = false; @@ -674,7 +631,7 @@ beginning = false; } } - } else if (line.length() == 0 || line.startsWith("//")) { + } else if (line.isEmpty() || line.startsWith("//")) { skippedLines++; } else { beginning = false; @@ -690,7 +647,7 @@ private static class PreScanVisitor extends Node.Visitor { - HashMap map = new HashMap<>(); + HashMap map = new HashMap<>(); @Override public void doVisit(Node n) { @@ -700,7 +657,7 @@ } } - HashMap getMap() { + HashMap getMap() { return map; } } @@ -776,12 +733,12 @@ InputStream is = null; try { - is = cl.getResourceAsStream(className.replace(".","/") + ".smap"); + is = cl.getResourceAsStream(className.replace(".", "/") + ".smap"); if (is != null) { encoding = SMAP_ENCODING; found = true; } else { - is = cl.getResourceAsStream(className.replace(".","/") + ".class"); + is = cl.getResourceAsStream(className.replace(".", "/") + ".class"); // Alternative approach would be to read the class file as per the // JLS. That would require duplicating a lot of BCEL functionality. int b = is.read(); @@ -810,11 +767,11 @@ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); byte[] buf = new byte[1024]; int numRead; - while ( (numRead = is.read(buf) ) >= 0) { + while ((numRead = is.read(buf)) >= 0) { baos.write(buf, 0, numRead); } - smap = new String(baos.toByteArray(), encoding); + smap = baos.toString(encoding); } } catch (IOException ioe) { Log log = LogFactory.getLog(SmapUtil.class); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/StringInterpreter.java tomcat10-10.1.52/java/org/apache/jasper/compiler/StringInterpreter.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/StringInterpreter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/StringInterpreter.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,33 +17,24 @@ package org.apache.jasper.compiler; /** - * Defines the interface for the String interpreter. This allows users to - * provide custom String interpreter implementations that can optimise - * String processing for an application by performing code generation for - * a sub-set of Strings. + * Defines the interface for the String interpreter. This allows users to provide custom String interpreter + * implementations that can optimise String processing for an application by performing code generation for a sub-set of + * Strings. */ public interface StringInterpreter { /** - * Generates the source code that represents the conversion of the string - * value to the appropriate type. + * Generates the source code that represents the conversion of the string value to the appropriate type. * - * @param c - * The target class to which to coerce the given string - * @param s - * The string value - * @param attrName - * The name of the attribute whose value is being supplied - * @param propEditorClass - * The property editor for the given attribute - * @param isNamedAttribute - * true if the given attribute is a named attribute (that - * is, specified using the jsp:attribute standard action), - * and false otherwise + * @param c The target class to which to coerce the given string + * @param s The string value + * @param attrName The name of the attribute whose value is being supplied + * @param propEditorClass The property editor for the given attribute + * @param isNamedAttribute true if the given attribute is a named attribute (that is, specified using the + * jsp:attribute standard action), and false otherwise * - * @return the string representing the code that will be inserted into the - * source code for the Servlet generated for the JSP. + * @return the string representing the code that will be inserted into the source code for the Servlet generated for + * the JSP. */ - String convertString(Class c, String s, String attrName, - Class propEditorClass, boolean isNamedAttribute); + String convertString(Class c, String s, String attrName, Class propEditorClass, boolean isNamedAttribute); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/StringInterpreterFactory.java tomcat10-10.1.52/java/org/apache/jasper/compiler/StringInterpreterFactory.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/StringInterpreterFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/StringInterpreterFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,14 +19,10 @@ import jakarta.servlet.ServletContext; /** - * Provides {@link StringInterpreter} instances for JSP compilation. - * - * The search order is as follows: + * Provides {@link StringInterpreter} instances for JSP compilation. The search order is as follows: *
            - *
          1. StringInterpreter instance or implementation class name provided as a - * ServletContext attribute
          2. - *
          3. Implementation class named in a ServletContext initialisation parameter - *
          4. + *
          5. StringInterpreter instance or implementation class name provided as a ServletContext attribute
          6. + *
          7. Implementation class named in a ServletContext initialisation parameter
          8. *
          9. Default implementation
          10. *
          */ @@ -39,18 +35,20 @@ /** * Obtain the correct String Interpreter for the given web application. + * * @param context The Servlet context + * * @return the String interpreter + * * @throws Exception If an error occurs creating the interpreter */ - public static StringInterpreter getStringInterpreter(ServletContext context) - throws Exception { + public static StringInterpreter getStringInterpreter(ServletContext context) throws Exception { StringInterpreter result = null; // Search for an implementation // 1. ServletContext attribute (set by application or cached by a - // previous call to this method). + // previous call to this method). Object attribute = context.getAttribute(STRING_INTERPRETER_CLASS_NAME); if (attribute instanceof StringInterpreter) { return (StringInterpreter) attribute; @@ -77,10 +75,8 @@ } - private static StringInterpreter createInstance(ServletContext context, - String className) throws Exception { - return (StringInterpreter) context.getClassLoader().loadClass( - className).getConstructor().newInstance(); + private static StringInterpreter createInstance(ServletContext context, String className) throws Exception { + return (StringInterpreter) context.getClassLoader().loadClass(className).getConstructor().newInstance(); } @@ -92,8 +88,8 @@ public static class DefaultStringInterpreter implements StringInterpreter { @Override - public String convertString(Class c, String s, String attrName, - Class propEditorClass, boolean isNamedAttribute) { + public String convertString(Class c, String s, String attrName, Class propEditorClass, + boolean isNamedAttribute) { String quoted = s; if (!isNamedAttribute) { @@ -102,11 +98,10 @@ if (propEditorClass != null) { String className = c.getCanonicalName(); - return "(" - + className - + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromBeanInfoPropertyEditor(" - + className + ".class, \"" + attrName + "\", " + quoted - + ", " + propEditorClass.getCanonicalName() + ".class)"; + return "(" + className + + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromBeanInfoPropertyEditor(" + className + + ".class, \"" + attrName + "\", " + quoted + ", " + propEditorClass.getCanonicalName() + + ".class)"; } else if (c == String.class) { return quoted; } else if (c == boolean.class) { @@ -152,22 +147,18 @@ } String className = c.getCanonicalName(); - return "(" - + className - + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromPropertyEditorManager(" - + className + ".class, \"" + attrName + "\", " + quoted - + ")"; + return "(" + className + ")org.apache.jasper.runtime.JspRuntimeLibrary.getValueFromPropertyEditorManager(" + + className + ".class, \"" + attrName + "\", " + quoted + ")"; } /** - * Intended to be used by sub-classes that don't need/want to - * re-implement the logic in + * Intended to be used by subclasses that don't need/want to re-implement the logic in * {@link #convertString(Class, String, String, Class, boolean)}. * - * @param c unused - * @param s unused - * @param isNamedAttribute unused + * @param c unused + * @param s unused + * @param isNamedAttribute unused * * @return Always {@code null} */ diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/TagFileProcessor.java tomcat10-10.1.52/java/org/apache/jasper/compiler/TagFileProcessor.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/TagFileProcessor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/TagFileProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,13 +41,9 @@ import org.apache.tomcat.util.descriptor.tld.TldResourcePath; /** - * 1. Processes and extracts the directive info in a tag file. 2. Compiles and - * loads tag files used in a JSP file. - * - * @author Kin-man Chung + * 1. Processes and extracts the directive info in a tag file. 2. Compiles and loads tag files used in a JSP file. */ - -class TagFileProcessor { +public class TagFileProcessor { private List tempVector; @@ -56,52 +52,37 @@ */ private static class TagFileDirectiveVisitor extends Node.Visitor { - private static final JspUtil.ValidAttribute[] tagDirectiveAttrs = { - new JspUtil.ValidAttribute("display-name"), - new JspUtil.ValidAttribute("body-content"), - new JspUtil.ValidAttribute("dynamic-attributes"), - new JspUtil.ValidAttribute("small-icon"), - new JspUtil.ValidAttribute("large-icon"), - new JspUtil.ValidAttribute("description"), - new JspUtil.ValidAttribute("example"), - new JspUtil.ValidAttribute("pageEncoding"), - new JspUtil.ValidAttribute("language"), - new JspUtil.ValidAttribute("import"), - new JspUtil.ValidAttribute("deferredSyntaxAllowedAsLiteral"), // JSP 2.1 - new JspUtil.ValidAttribute("trimDirectiveWhitespaces"), // JSP 2.1 - new JspUtil.ValidAttribute("isELIgnored"), + private static final JspUtil.ValidAttribute[] tagDirectiveAttrs = { new JspUtil.ValidAttribute("display-name"), + new JspUtil.ValidAttribute("body-content"), new JspUtil.ValidAttribute("dynamic-attributes"), + new JspUtil.ValidAttribute("small-icon"), new JspUtil.ValidAttribute("large-icon"), + new JspUtil.ValidAttribute("description"), new JspUtil.ValidAttribute("example"), + new JspUtil.ValidAttribute("pageEncoding"), new JspUtil.ValidAttribute("language"), + new JspUtil.ValidAttribute("import"), new JspUtil.ValidAttribute("deferredSyntaxAllowedAsLiteral"), + new JspUtil.ValidAttribute("trimDirectiveWhitespaces"), new JspUtil.ValidAttribute("isELIgnored"), new JspUtil.ValidAttribute("errorOnELNotFound") }; private static final JspUtil.ValidAttribute[] attributeDirectiveAttrs = { - new JspUtil.ValidAttribute("name", true), - new JspUtil.ValidAttribute("required"), - new JspUtil.ValidAttribute("fragment"), - new JspUtil.ValidAttribute("rtexprvalue"), - new JspUtil.ValidAttribute("type"), - new JspUtil.ValidAttribute("deferredValue"), // JSP 2.1 - new JspUtil.ValidAttribute("deferredValueType"), // JSP 2.1 - new JspUtil.ValidAttribute("deferredMethod"), // JSP 2 - new JspUtil.ValidAttribute("deferredMethodSignature"), // JSP 21 - new JspUtil.ValidAttribute("description") }; - - private static final JspUtil.ValidAttribute[] variableDirectiveAttrs = { - new JspUtil.ValidAttribute("name-given"), - new JspUtil.ValidAttribute("name-from-attribute"), - new JspUtil.ValidAttribute("alias"), - new JspUtil.ValidAttribute("variable-class"), - new JspUtil.ValidAttribute("scope"), - new JspUtil.ValidAttribute("declare"), - new JspUtil.ValidAttribute("description") }; - - private ErrorDispatcher err; + new JspUtil.ValidAttribute("name", true), new JspUtil.ValidAttribute("required"), + new JspUtil.ValidAttribute("fragment"), new JspUtil.ValidAttribute("rtexprvalue"), + new JspUtil.ValidAttribute("type"), new JspUtil.ValidAttribute("deferredValue"), + new JspUtil.ValidAttribute("deferredValueType"), new JspUtil.ValidAttribute("deferredMethod"), + new JspUtil.ValidAttribute("deferredMethodSignature"), new JspUtil.ValidAttribute("description") }; + + private static final JspUtil.ValidAttribute[] variableDirectiveAttrs = + { new JspUtil.ValidAttribute("name-given"), new JspUtil.ValidAttribute("name-from-attribute"), + new JspUtil.ValidAttribute("alias"), new JspUtil.ValidAttribute("variable-class"), + new JspUtil.ValidAttribute("scope"), new JspUtil.ValidAttribute("declare"), + new JspUtil.ValidAttribute("description") }; - private TagLibraryInfo tagLibInfo; + private final ErrorDispatcher err; - private String name = null; + private final TagLibraryInfo tagLibInfo; - private String path = null; + private final String name; - private String bodycontent = null; + private final String path; + + private String bodyContent = null; private String description = null; @@ -115,9 +96,9 @@ private String example = null; - private List attributeList; + private final List attributeList; - private List variableList; + private final List variableList; private static final String ATTR_NAME = "the name attribute of the attribute directive"; @@ -129,12 +110,11 @@ private static final String TAG_DYNAMIC = "the dynamic-attributes attribute of the tag directive"; - private Map nameTable = new HashMap<>(); + private final Map nameTable = new HashMap<>(); - private Map nameFromTable = new HashMap<>(); + private final Map nameFromTable = new HashMap<>(); - TagFileDirectiveVisitor(Compiler compiler, - TagLibraryInfo tagLibInfo, String name, String path) { + TagFileDirectiveVisitor(Compiler compiler, TagLibraryInfo tagLibInfo, String name, String path) { err = compiler.getErrorDispatcher(); this.tagLibInfo = tagLibInfo; this.name = name; @@ -148,19 +128,13 @@ JspUtil.checkAttributes("Tag directive", n, tagDirectiveAttrs, err); - bodycontent = checkConflict(n, bodycontent, "body-content"); - if (bodycontent != null - && !bodycontent - .equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY) - && !bodycontent - .equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT) - && !bodycontent - .equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)) { - err.jspError(n, "jsp.error.tagdirective.badbodycontent", - bodycontent); + bodyContent = checkConflict(n, bodyContent, "body-content"); + if (bodyContent != null && !bodyContent.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY) && + !bodyContent.equalsIgnoreCase(TagInfo.BODY_CONTENT_TAG_DEPENDENT) && + !bodyContent.equalsIgnoreCase(TagInfo.BODY_CONTENT_SCRIPTLESS)) { + err.jspError(n, "jsp.error.tagdirective.badbodycontent", bodyContent); } - dynamicAttrsMapName = checkConflict(n, dynamicAttrsMapName, - "dynamic-attributes"); + dynamicAttrsMapName = checkConflict(n, dynamicAttrsMapName, "dynamic-attributes"); if (dynamicAttrsMapName != null) { checkUniqueName(dynamicAttrsMapName, TAG_DYNAMIC, n); } @@ -171,15 +145,13 @@ example = checkConflict(n, example, "example"); } - private String checkConflict(Node n, String oldAttrValue, String attr) - throws JasperException { + private String checkConflict(Node n, String oldAttrValue, String attr) throws JasperException { String result = oldAttrValue; String attrValue = n.getAttributeValue(attr); if (attrValue != null) { if (oldAttrValue != null && !oldAttrValue.equals(attrValue)) { - err.jspError(n, "jsp.error.tag.conflict.attr", attr, - oldAttrValue, attrValue); + err.jspError(n, "jsp.error.tag.conflict.attr", attr, oldAttrValue, attrValue); } result = attrValue; } @@ -189,8 +161,7 @@ @Override public void visit(Node.AttributeDirective n) throws JasperException { - JspUtil.checkAttributes("Attribute directive", n, - attributeDirectiveAttrs, err); + JspUtil.checkAttributes("Attribute directive", n, attributeDirectiveAttrs, err); // JSP 2.1 Table JSP.8-3 // handle deferredValue and deferredValueType @@ -223,8 +194,7 @@ deferredMethodSpecified = true; deferredMethod = JspUtil.booleanValue(deferredMethodString); } - String deferredMethodSignature = n - .getAttributeValue("deferredMethodSignature"); + String deferredMethodSignature = n.getAttributeValue("deferredMethodSignature"); if (deferredMethodSignature != null) { if (deferredMethodSpecified && !deferredMethod) { err.jspError(n, "jsp.error.deferredmethodsignaturewithoutdeferredmethod"); @@ -240,15 +210,13 @@ } String attrName = n.getAttributeValue("name"); - boolean required = JspUtil.booleanValue(n - .getAttributeValue("required")); + boolean required = JspUtil.booleanValue(n.getAttributeValue("required")); boolean rtexprvalue = true; String rtexprvalueString = n.getAttributeValue("rtexprvalue"); if (rtexprvalueString != null) { rtexprvalue = JspUtil.booleanValue(rtexprvalueString); } - boolean fragment = JspUtil.booleanValue(n - .getAttributeValue("fragment")); + boolean fragment = JspUtil.booleanValue(n.getAttributeValue("fragment")); String type = n.getAttributeValue("type"); if (fragment) { // type is fixed to "JspFragment" and a translation error @@ -274,15 +242,13 @@ } } - if (("2.0".equals(tagLibInfo.getRequiredVersion()) || ("1.2".equals(tagLibInfo.getRequiredVersion()))) - && (deferredMethodSpecified || deferredMethod - || deferredValueSpecified || deferredValue)) { + if (("2.0".equals(tagLibInfo.getRequiredVersion()) || ("1.2".equals(tagLibInfo.getRequiredVersion()))) && + (deferredMethodSpecified || deferredMethod || deferredValueSpecified || deferredValue)) { err.jspError("jsp.error.invalid.version", path); } - TagAttributeInfo tagAttributeInfo = new TagAttributeInfo(attrName, - required, type, rtexprvalue, fragment, null, deferredValue, - deferredMethod, deferredValueType, deferredMethodSignature); + TagAttributeInfo tagAttributeInfo = new TagAttributeInfo(attrName, required, type, rtexprvalue, fragment, + null, deferredValue, deferredMethod, deferredValueType, deferredMethodSignature); attributeList.add(tagAttributeInfo); checkUniqueName(attrName, ATTR_NAME, n, tagAttributeInfo); } @@ -290,12 +256,10 @@ @Override public void visit(Node.VariableDirective n) throws JasperException { - JspUtil.checkAttributes("Variable directive", n, - variableDirectiveAttrs, err); + JspUtil.checkAttributes("Variable directive", n, variableDirectiveAttrs, err); String nameGiven = n.getAttributeValue("name-given"); - String nameFromAttribute = n - .getAttributeValue("name-from-attribute"); + String nameFromAttribute = n.getAttributeValue("name-from-attribute"); if (nameGiven == null && nameFromAttribute == null) { err.jspError("jsp.error.variable.either.name"); } @@ -305,8 +269,7 @@ } String alias = n.getAttributeValue("alias"); - if (nameFromAttribute != null && alias == null - || nameFromAttribute == null && alias != null) { + if (nameFromAttribute != null && alias == null || nameFromAttribute == null && alias != null) { err.jspError("jsp.error.variable.alias"); } @@ -335,9 +298,8 @@ if (nameFromAttribute != null) { /* - * An alias has been specified. We use 'nameGiven' to hold the - * value of the alias, and 'nameFromAttribute' to hold the name - * of the attribute whose value (at invocation-time) denotes the + * An alias has been specified. We use 'nameGiven' to hold the value of the alias, and + * 'nameFromAttribute' to hold the name of the attribute whose value (at invocation-time) denotes the * name of the variable that is being aliased */ nameGiven = alias; @@ -357,28 +319,25 @@ // XXX Get it from tag file name } - if (bodycontent == null) { - bodycontent = TagInfo.BODY_CONTENT_SCRIPTLESS; + if (bodyContent == null) { + bodyContent = TagInfo.BODY_CONTENT_SCRIPTLESS; } - String tagClassName = JspUtil.getTagHandlerClassName( - path, packageName, tagLibInfo.getReliableURN(), err); + String tagClassName = JspUtil.getTagHandlerClassName(path, packageName, tagLibInfo.getReliableURN(), err); TagVariableInfo[] tagVariableInfos = variableList.toArray(new TagVariableInfo[0]); TagAttributeInfo[] tagAttributeInfo = attributeList.toArray(new TagAttributeInfo[0]); - return new JasperTagInfo(name, tagClassName, bodycontent, - description, tagLibInfo, null, tagAttributeInfo, - displayName, smallIcon, largeIcon, tagVariableInfos, - dynamicAttrsMapName); + return new JasperTagInfo(name, tagClassName, bodyContent, description, tagLibInfo, null, tagAttributeInfo, + displayName, smallIcon, largeIcon, tagVariableInfos, dynamicAttrsMapName); } static class NameEntry { - private String type; + private final String type; - private Node node; + private final Node node; - private TagAttributeInfo attr; + private final TagAttributeInfo attr; NameEntry(String type, Node node, TagAttributeInfo attr) { this.type = type; @@ -399,36 +358,31 @@ } } - /** - * Reports a translation error if names specified in attributes of - * directives are not unique in this translation unit. + /* + * Reports a translation error if names specified in attributes of directives are not unique in this translation + * unit. * - * The value of the following attributes must be unique. 1. 'name' - * attribute of an attribute directive 2. 'name-given' attribute of a - * variable directive 3. 'alias' attribute of variable directive 4. - * 'dynamic-attributes' of a tag directive except that - * 'dynamic-attributes' can (and must) have the same value when it - * appears in multiple tag directives. + * The value of the following attributes must be unique. 1. 'name' attribute of an attribute directive 2. + * 'name-given' attribute of a variable directive 3. 'alias' attribute of variable directive 4. + * 'dynamic-attributes' of a tag directive except that 'dynamic-attributes' can (and must) have the same value + * when it appears in multiple tag directives. * - * Also, 'name-from' attribute of a variable directive cannot have the - * same value as that from another variable directive. + * Also, 'name-from' attribute of a variable directive cannot have the same value as that from another variable + * directive. */ - private void checkUniqueName(String name, String type, Node n) - throws JasperException { + private void checkUniqueName(String name, String type, Node n) throws JasperException { checkUniqueName(name, type, n, null); } - private void checkUniqueName(String name, String type, Node n, - TagAttributeInfo attr) throws JasperException { + private void checkUniqueName(String name, String type, Node n, TagAttributeInfo attr) throws JasperException { - Map table = (VAR_NAME_FROM.equals(type)) ? nameFromTable : nameTable; + Map table = (VAR_NAME_FROM.equals(type)) ? nameFromTable : nameTable; NameEntry nameEntry = table.get(name); if (nameEntry != null) { - if (!TAG_DYNAMIC.equals(type) || - !TAG_DYNAMIC.equals(nameEntry.getType())) { + if (!TAG_DYNAMIC.equals(type) || !TAG_DYNAMIC.equals(nameEntry.getType())) { int line = nameEntry.getNode().getStart().getLineNumber(); - err.jspError(n, "jsp.error.tagfile.nameNotUnique", type, - nameEntry.getType(), Integer.toString(line)); + err.jspError(n, "jsp.error.tagfile.nameNotUnique", type, nameEntry.getType(), + Integer.toString(line)); } } else { table.put(name, new NameEntry(type, n, attr)); @@ -440,24 +394,20 @@ */ void postCheck() throws JasperException { // Check that var.name-from-attributes has valid values. - for (Entry entry : nameFromTable.entrySet()) { + for (Entry entry : nameFromTable.entrySet()) { String key = entry.getKey(); NameEntry nameEntry = nameTable.get(key); NameEntry nameFromEntry = entry.getValue(); Node nameFromNode = nameFromEntry.getNode(); if (nameEntry == null) { - err.jspError(nameFromNode, - "jsp.error.tagfile.nameFrom.noAttribute", key); + err.jspError(nameFromNode, "jsp.error.tagfile.nameFrom.noAttribute", key); } else { Node node = nameEntry.getNode(); TagAttributeInfo tagAttr = nameEntry.getTagAttributeInfo(); - if (!"java.lang.String".equals(tagAttr.getTypeName()) - || !tagAttr.isRequired() - || tagAttr.canBeRequestTime()) { - err.jspError(nameFromNode, - "jsp.error.tagfile.nameFrom.badAttribute", - key, Integer.toString(node.getStart() - .getLineNumber())); + if (!"java.lang.String".equals(tagAttr.getTypeName()) || !tagAttr.isRequired() || + tagAttr.canBeRequestTime()) { + err.jspError(nameFromNode, "jsp.error.tagfile.nameFrom.badAttribute", key, + Integer.toString(node.getStart().getLineNumber())); } } } @@ -465,29 +415,22 @@ } /** - * Parses the tag file, and collects information on the directives included - * in it. The method is used to obtain the info on the tag file, when the - * handler that it represents is referenced. The tag file is not compiled - * here. + * Parses the tag file, and collects information on the directives included in it. The method is used to obtain the + * info on the tag file, when the handler that it represents is referenced. The tag file is not compiled here. + * + * @param pc the current ParserController used in this compilation + * @param name the tag name as specified in the TLD + * @param path the path for the tagfile + * @param jar the Jar resource containing the tag file + * @param tagLibInfo the TagLibraryInfo object associated with this TagInfo * - * @param pc - * the current ParserController used in this compilation - * @param name - * the tag name as specified in the TLD - * @param path - * the path for the tagfile - * @param jar - * the Jar resource containing the tag file - * @param tagLibInfo - * the TagLibraryInfo object associated with this TagInfo * @return a TagInfo object assembled from the directives in the tag file. * * @throws JasperException If an error occurs during parsing */ @SuppressWarnings("null") // page can't be null - public static TagInfo parseTagFileDirectives(ParserController pc, - String name, String path, Jar jar, TagLibraryInfo tagLibInfo) - throws JasperException { + public static TagInfo parseTagFileDirectives(ParserController pc, String name, String path, Jar jar, + TagLibraryInfo tagLibInfo) throws JasperException { ErrorDispatcher err = pc.getCompiler().getErrorDispatcher(); @@ -495,12 +438,11 @@ Node.Nodes page = null; try { page = pc.parseTagFileDirectives(path, jar); - } catch (IOException e) { + } catch (IOException ioe) { err.jspError("jsp.error.file.not.found", path); } - TagFileDirectiveVisitor tagFileVisitor = new TagFileDirectiveVisitor(pc - .getCompiler(), tagLibInfo, name, path); + TagFileDirectiveVisitor tagFileVisitor = new TagFileDirectiveVisitor(pc.getCompiler(), tagLibInfo, name, path); page.visit(tagFileVisitor); tagFileVisitor.postCheck(); @@ -510,16 +452,16 @@ /** * Compiles and loads a tagfile. */ - private Class loadTagFile(Compiler compiler, String tagFilePath, - TagInfo tagInfo, PageInfo parentPageInfo) throws JasperException { + private Class loadTagFile(Compiler compiler, String tagFilePath, TagInfo tagInfo, PageInfo parentPageInfo) + throws JasperException { Jar tagJar = null; Jar tagJarOriginal = null; try { if (tagFilePath.startsWith("/META-INF/")) { try { - tagJar = compiler.getCompilationContext().getTldResourcePath( - tagInfo.getTagLibrary().getURI()).openJar(); + tagJar = compiler.getCompilationContext().getTldResourcePath(tagInfo.getTagLibrary().getURI()) + .openJar(); } catch (IOException ioe) { throw new JasperException(ioe); } @@ -539,12 +481,10 @@ try { wrapper = rctxt.getWrapper(wrapperUri); if (wrapper == null) { - wrapper = new JspServletWrapper(ctxt.getServletContext(), ctxt - .getOptions(), tagFilePath, tagInfo, ctxt - .getRuntimeContext(), tagJar); + wrapper = new JspServletWrapper(ctxt.getServletContext(), ctxt.getOptions(), tagFilePath, + tagInfo, ctxt.getRuntimeContext(), tagJar); // Use same classloader and classpath for compiling tag files - wrapper.getJspEngineContext().setClassLoader( - ctxt.getClassLoader()); + wrapper.getJspEngineContext().setClassLoader(ctxt.getClassLoader()); wrapper.getJspEngineContext().setClassPath(ctxt.getClassPath()); rctxt.addWrapper(wrapperUri, wrapper); } else { @@ -569,17 +509,13 @@ // file is compiled in prototype mode, to avoid infinite // recursion. - JspServletWrapper tempWrapper = new JspServletWrapper(ctxt - .getServletContext(), ctxt.getOptions(), - tagFilePath, tagInfo, ctxt.getRuntimeContext(), - tagJar); + JspServletWrapper tempWrapper = new JspServletWrapper(ctxt.getServletContext(), + ctxt.getOptions(), tagFilePath, tagInfo, ctxt.getRuntimeContext(), tagJar); // Use same classloader and classpath for compiling tag files - tempWrapper.getJspEngineContext().setClassLoader( - ctxt.getClassLoader()); + tempWrapper.getJspEngineContext().setClassLoader(ctxt.getClassLoader()); tempWrapper.getJspEngineContext().setClassPath(ctxt.getClassPath()); tagClazz = tempWrapper.loadTagFilePrototype(); - tempVector.add(tempWrapper.getJspEngineContext() - .getCompiler()); + tempVector.add(tempWrapper.getJspEngineContext().getCompiler()); } else { tagClazz = wrapper.loadTagFile(); } @@ -593,10 +529,8 @@ try { Object tagIns = tagClazz.getConstructor().newInstance(); if (tagIns instanceof JspSourceDependent) { - for (Entry entry : ((JspSourceDependent) - tagIns).getDependants().entrySet()) { - parentPageInfo.addDependant(entry.getKey(), - entry.getValue()); + for (Entry entry : ((JspSourceDependent) tagIns).getDependants().entrySet()) { + parentPageInfo.addDependant(entry.getKey(), entry.getValue()); } } } catch (RuntimeException | ReflectiveOperationException e) { @@ -618,14 +552,14 @@ } /* - * Visitor which scans the page and looks for tag handlers that are tag - * files, compiling (if necessary) and loading them. + * Visitor which scans the page and looks for tag handlers that are tag files, compiling (if necessary) and loading + * them. */ private class TagFileLoaderVisitor extends Node.Visitor { - private Compiler compiler; + private final Compiler compiler; - private PageInfo pageInfo; + private final PageInfo pageInfo; TagFileLoaderVisitor(Compiler compiler) { @@ -640,32 +574,29 @@ String tagFilePath = tagFileInfo.getPath(); if (tagFilePath.startsWith("/META-INF/")) { // For tags in JARs, add the TLD and the tag as a dependency - TldResourcePath tldResourcePath = - compiler.getCompilationContext().getTldResourcePath( - tagFileInfo.getTagInfo().getTagLibrary().getURI()); + TldResourcePath tldResourcePath = compiler.getCompilationContext() + .getTldResourcePath(tagFileInfo.getTagInfo().getTagLibrary().getURI()); try (Jar jar = tldResourcePath.openJar()) { if (jar != null) { // Add TLD pageInfo.addDependant(jar.getURL(tldResourcePath.getEntryName()), - Long.valueOf(jar.getLastModified(tldResourcePath.getEntryName()))); + Long.valueOf(jar.getLastModified(tldResourcePath.getEntryName()))); // Add Tag pageInfo.addDependant(jar.getURL(tagFilePath.substring(1)), - Long.valueOf(jar.getLastModified(tagFilePath.substring(1)))); + Long.valueOf(jar.getLastModified(tagFilePath.substring(1)))); } else { pageInfo.addDependant(tagFilePath, - compiler.getCompilationContext().getLastModified(tagFilePath)); + compiler.getCompilationContext().getLastModified(tagFilePath)); } } catch (IOException ioe) { throw new JasperException(ioe); } } else { - pageInfo.addDependant(tagFilePath, - compiler.getCompilationContext().getLastModified(tagFilePath)); + pageInfo.addDependant(tagFilePath, compiler.getCompilationContext().getLastModified(tagFilePath)); } - Class c = loadTagFile(compiler, tagFilePath, n.getTagInfo(), - pageInfo); + Class c = loadTagFile(compiler, tagFilePath, n.getTagInfo(), pageInfo); n.setTagHandlerClass(c); } visitBody(n); @@ -673,29 +604,25 @@ } /** - * Implements a phase of the translation that compiles (if necessary) the - * tag files used in a JSP files. The directives in the tag files are - * assumed to have been processed and encapsulated as TagFileInfo in the - * CustomTag nodes. + * Implements a phase of the translation that compiles (if necessary) the tag files used in a JSP files. The + * directives in the tag files are assumed to have been processed and encapsulated as TagFileInfo in the CustomTag + * nodes. * * @param compiler Compiler to use to compile tag files * @param page The page from to scan for tag files to compile * * @throws JasperException If an error occurs during the scan or compilation */ - public void loadTagFiles(Compiler compiler, Node.Nodes page) - throws JasperException { + public void loadTagFiles(Compiler compiler, Node.Nodes page) throws JasperException { tempVector = new ArrayList<>(); page.visit(new TagFileLoaderVisitor(compiler)); } /** - * Removed the java and class files for the tag prototype generated from the - * current compilation. + * Removed the java and class files for the tag prototype generated from the current compilation. * - * @param classFileName - * If non-null, remove only the class file with with this name. + * @param classFileName If non-null, remove only the class file with this name. */ public void removeProtoTypeFiles(String classFileName) { for (Compiler c : tempVector) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java tomcat10-10.1.52/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/TagLibraryInfoImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -55,12 +55,6 @@ /** * Implementation of the TagLibraryInfo class from the JSP spec. - * - * @author Anil K. Vijendran - * @author Mandar Raje - * @author Pierre Delisle - * @author Kin-man Chung - * @author Jan Luehe */ class TagLibraryInfoImpl extends TagLibraryInfo implements TagConstants { @@ -108,10 +102,8 @@ } - TagLibraryInfoImpl(JspCompilationContext ctxt, ParserController pc, - PageInfo pi, String prefix, String uriIn, - TldResourcePath tldResourcePath, ErrorDispatcher err) - throws JasperException { + TagLibraryInfoImpl(JspCompilationContext ctxt, ParserController pc, PageInfo pi, String prefix, String uriIn, + TldResourcePath tldResourcePath, ErrorDispatcher err) throws JasperException { super(prefix, uriIn); this.ctxt = ctxt; @@ -143,7 +135,7 @@ if (path == null) { // JAR not in the web application so add it directly URL jarUrl = jar.getJarFileURL(); - long lastMod = -1; + long lastMod; URLConnection urlConn = null; try { urlConn = jarUrl.openConnection(); @@ -154,19 +146,17 @@ if (urlConn != null) { try { urlConn.getInputStream().close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } } - pageInfo.addDependant(jarUrl.toExternalForm(), - Long.valueOf(lastMod)); + pageInfo.addDependant(jarUrl.toExternalForm(), Long.valueOf(lastMod)); } // Add TLD within the JAR to the dependency list String entryName = tldResourcePath.getEntryName(); try { - pageInfo.addDependant(jar.getURL(entryName), - Long.valueOf(jar.getLastModified(entryName))); + pageInfo.addDependant(jar.getURL(entryName), Long.valueOf(jar.getLastModified(entryName))); } catch (IOException ioe) { throw new JasperException(ioe); } @@ -177,8 +167,7 @@ if (tldResourcePath.getUrl() == null) { err.jspError("jsp.error.tld.missing", prefix, uri); } - TaglibXml taglibXml = - ctxt.getOptions().getTldCache().getTaglibXml(tldResourcePath); + TaglibXml taglibXml = ctxt.getOptions().getTldCache().getTaglibXml(tldResourcePath); if (taglibXml == null) { err.jspError("jsp.error.tld.missing", prefix, uri); } @@ -239,12 +228,12 @@ /* * @param uri The uri of the TLD + * * @param ctxt The compilation context * * @return the location of the TLD identified by the uri */ - private TldResourcePath generateTldResourcePath(String uri, - JspCompilationContext ctxt) throws JasperException { + private TldResourcePath generateTldResourcePath(String uri, JspCompilationContext ctxt) throws JasperException { // TODO: this matches the current implementation but the URL logic looks fishy // map URI to location per JSP 7.3.6.2 @@ -274,14 +263,14 @@ throw new FileNotFoundException(); } /* - * When the TLD cache is built for a TLD contained within a JAR within a WAR, the jar form of the URL is - * used for any nested JAR. + * When the TLD cache is built for a TLD contained within a JAR within a WAR, the jar form of the URL is + * used for any nested JAR. */ if (url.getProtocol().equals("war") && uri.endsWith(".jar")) { url = UriUtil.warToJar(url); } - } catch (Exception ex) { - err.jspError("jsp.error.tld.unable_to_get_jar", uri, ex.toString()); + } catch (Exception e) { + err.jspError("jsp.error.tld.unable_to_get_jar", uri, e.toString()); } if (uri.endsWith(".jar")) { if (url == null) { @@ -289,7 +278,7 @@ } return new TldResourcePath(url, uri, "META-INF/taglib.tld"); } else if (uri.startsWith("/WEB-INF/lib/") || uri.startsWith("/WEB-INF/classes/") || - (uri.startsWith("/WEB-INF/tags/") && uri.endsWith(".tld")&& !uri.endsWith("implicit.tld"))) { + (uri.startsWith("/WEB-INF/tags/") && uri.endsWith(".tld") && !uri.endsWith("implicit.tld"))) { err.jspError("jsp.error.tld.invalid_tld_file", uri); } return new TldResourcePath(url, uri); @@ -311,18 +300,9 @@ List attributeInfos = tagXml.getAttributes(); List variableInfos = tagXml.getVariables(); - return new TagInfo(tagXml.getName(), - tagXml.getTagClass(), - tagXml.getBodyContent(), - tagXml.getInfo(), - this, - tei, - attributeInfos.toArray(new TagAttributeInfo[0]), - tagXml.getDisplayName(), - tagXml.getSmallIcon(), - tagXml.getLargeIcon(), - variableInfos.toArray(new TagVariableInfo[0]), - tagXml.hasDynamicAttributes()); + return new TagInfo(tagXml.getName(), tagXml.getTagClass(), tagXml.getBodyContent(), tagXml.getInfo(), this, tei, + attributeInfos.toArray(new TagAttributeInfo[0]), tagXml.getDisplayName(), tagXml.getSmallIcon(), + tagXml.getLargeIcon(), variableInfos.toArray(new TagVariableInfo[0]), tagXml.hasDynamicAttributes()); } @SuppressWarnings("null") // Impossible for path to be null at warning @@ -345,8 +325,7 @@ path = "/WEB-INF/classes" + path; } - TagInfo tagInfo = - TagFileProcessor.parseTagFileDirectives(parserController, name, path, jar, this); + TagInfo tagInfo = TagFileProcessor.parseTagFileDirectives(parserController, name, path, jar, this); return new TagFileInfo(name, path, tagInfo); } @@ -361,7 +340,7 @@ return null; } - Map initParams = new HashMap<>(validatorXml.getInitParams()); + Map initParams = new HashMap<>(validatorXml.getInitParams()); try { Class tlvClass = ctxt.getClassLoader().loadClass(validatorClass); @@ -387,12 +366,11 @@ } /** - * Translation-time validation of the XML document associated with the JSP - * page. This is a convenience method on the associated TagLibraryValidator - * class. + * Translation-time validation of the XML document associated with the JSP page. This is a convenience method on the + * associated TagLibraryValidator class. + * + * @param thePage The JSP page object * - * @param thePage - * The JSP page object * @return A string indicating whether the page is valid or not. */ public ValidationMessage[] validate(PageData thePage) { @@ -409,5 +387,5 @@ return tlv.validate(getPrefixString(), uri, thePage); } - private TagLibraryValidator tagLibraryValidator; + private final TagLibraryValidator tagLibraryValidator; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/TagPluginManager.java tomcat10-10.1.52/java/org/apache/jasper/compiler/TagPluginManager.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/TagPluginManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/TagPluginManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,24 +36,20 @@ /** * Manages tag plugin optimizations. - * - * @author Kin-man Chung */ public class TagPluginManager { - private static final String META_INF_JASPER_TAG_PLUGINS_XML = - "META-INF/org.apache.jasper/tagPlugins.xml"; + private static final String META_INF_JASPER_TAG_PLUGINS_XML = "META-INF/org.apache.jasper/tagPlugins.xml"; private static final String TAG_PLUGINS_XML = "/WEB-INF/tagPlugins.xml"; private final ServletContext ctxt; - private HashMap tagPlugins; + private HashMap tagPlugins; private boolean initialized = false; public TagPluginManager(ServletContext ctxt) { this.ctxt = ctxt; } - public void apply(Node.Nodes page, ErrorDispatcher err, PageInfo pageInfo) - throws JasperException { + public void apply(Node.Nodes page, ErrorDispatcher err, PageInfo pageInfo) throws JasperException { init(err); if (!tagPlugins.isEmpty()) { @@ -66,8 +62,7 @@ return; } - String blockExternalString = ctxt.getInitParameter( - Constants.XML_BLOCK_EXTERNAL_INIT_PARAM); + String blockExternalString = ctxt.getInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM); boolean blockExternal; if (blockExternalString == null) { blockExternal = true; @@ -94,8 +89,7 @@ parser = new TagPluginParser(ctxt, blockExternal); - Enumeration urls = - ctxt.getClassLoader().getResources(META_INF_JASPER_TAG_PLUGINS_XML); + Enumeration urls = ctxt.getClassLoader().getResources(META_INF_JASPER_TAG_PLUGINS_XML); while (urls.hasMoreElements()) { URL url = urls.nextElement(); parser.parse(url); @@ -116,9 +110,9 @@ } } - Map plugins = parser.getPlugins(); + Map plugins = parser.getPlugins(); tagPlugins = new HashMap<>(plugins.size()); - for (Map.Entry entry : plugins.entrySet()) { + for (Map.Entry entry : plugins.entrySet()) { try { String tagClass = entry.getKey(); String pluginName = entry.getValue(); @@ -133,8 +127,7 @@ } /** - * Invoke tag plugin for the given custom tag, if a plugin exists for - * the custom tag's tag handler. + * Invoke tag plugin for the given custom tag, if a plugin exists for the custom tag's tag handler. *

          * The given custom tag node will be manipulated by the plugin. */ @@ -168,7 +161,7 @@ private static class TagPluginContextImpl implements TagPluginContext { private final Node.CustomTag node; private final PageInfo pageInfo; - private final HashMap pluginAttributes; + private final HashMap pluginAttributes; private Node.Nodes curNodes; TagPluginContextImpl(Node.CustomTag n, PageInfo pageInfo) { @@ -249,15 +242,12 @@ @Override public void generateJavaSource(String sourceCode) { - curNodes.add(new Node.Scriptlet(sourceCode, node.getStart(), - null)); + curNodes.add(new Node.Scriptlet(sourceCode, node.getStart(), null)); } @Override public void generateAttribute(String attributeName) { - curNodes.add(new Node.AttributeGenerator(node.getStart(), - attributeName, - node)); + curNodes.add(new Node.AttributeGenerator(node.getStart(), attributeName, node)); } @Override diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/TextOptimizer.java tomcat10-10.1.52/java/org/apache/jasper/compiler/TextOptimizer.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/TextOptimizer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/TextOptimizer.java 2026-01-23 19:33:36.000000000 +0000 @@ -84,8 +84,7 @@ @Override public void visit(Node.TemplateText n) throws JasperException { if (n.isAllSpace()) { - if ((options.getTrimSpaces() == TrimSpacesOption.TRUE || - pageInfo.isTrimDirectiveWhitespaces())) { + if ((options.getTrimSpaces() == TrimSpacesOption.TRUE || pageInfo.isTrimDirectiveWhitespaces())) { n.setText(EMPTY_TEXT); return; } else if (options.getTrimSpaces() == TrimSpacesOption.SINGLE) { @@ -105,8 +104,8 @@ } /** - * This method breaks concatenation mode. As a side effect it copies - * the concatenated string to the first text node + * This method breaks concatenation mode. As a side effect it copies the concatenated string to the first text + * node */ private void collectText() { @@ -119,8 +118,7 @@ } - public static void concatenate(Compiler compiler, Node.Nodes page) - throws JasperException { + public static void concatenate(Compiler compiler, Node.Nodes page) throws JasperException { TextCatVisitor v = new TextCatVisitor(compiler); page.visit(v); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/TldCache.java tomcat10-10.1.52/java/org/apache/jasper/compiler/TldCache.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/TldCache.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/TldCache.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,48 +34,43 @@ import org.xml.sax.SAXException; /** - * This class caches parsed instances of TLD files to remove the need for the - * same TLD to be parsed for each JSP that references it. It does not protect - * against multiple threads processing the same, new TLD but it does ensure that - * each all threads will use the same TLD object after parsing. + * This class caches parsed instances of TLD files to remove the need for the same TLD to be parsed for each JSP that + * references it. It does not protect against multiple threads processing the same new TLD, but it does ensure that each + * all threads will use the same TLD object after parsing. */ public class TldCache { - public static final String SERVLET_CONTEXT_ATTRIBUTE_NAME = - TldCache.class.getName(); + public static final String SERVLET_CONTEXT_ATTRIBUTE_NAME = TldCache.class.getName(); private final ServletContext servletContext; private final Map uriTldResourcePathMap = new HashMap<>(); - private final Map tldResourcePathTaglibXmlMap = - new HashMap<>(); + private final Map tldResourcePathTaglibXmlMap = new HashMap<>(); private final TldParser tldParser; public static TldCache getInstance(ServletContext servletContext) { if (servletContext == null) { - throw new IllegalArgumentException(Localizer.getMessage( - "org.apache.jasper.compiler.TldCache.servletContextNull")); + throw new IllegalArgumentException( + Localizer.getMessage("org.apache.jasper.compiler.TldCache.servletContextNull")); } return (TldCache) servletContext.getAttribute(SERVLET_CONTEXT_ATTRIBUTE_NAME); } - public TldCache(ServletContext servletContext, - Map uriTldResourcePathMap, - Map tldResourcePathTaglibXmlMap) { + public TldCache(ServletContext servletContext, Map uriTldResourcePathMap, + Map tldResourcePathTaglibXmlMap) { this.servletContext = servletContext; this.uriTldResourcePathMap.putAll(uriTldResourcePathMap); - for (Entry entry : tldResourcePathTaglibXmlMap.entrySet()) { + for (Entry entry : tldResourcePathTaglibXmlMap.entrySet()) { TldResourcePath tldResourcePath = entry.getKey(); - long lastModified[] = getLastModified(tldResourcePath); - TaglibXmlCacheEntry cacheEntry = new TaglibXmlCacheEntry( - entry.getValue(), lastModified[0], lastModified[1]); + long[] lastModified = getLastModified(tldResourcePath); + TaglibXmlCacheEntry cacheEntry = + new TaglibXmlCacheEntry(entry.getValue(), lastModified[0], lastModified[1]); this.tldResourcePathTaglibXmlMap.put(tldResourcePath, cacheEntry); } - boolean validate = Boolean.parseBoolean( - servletContext.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM)); - String blockExternalString = servletContext.getInitParameter( - Constants.XML_BLOCK_EXTERNAL_INIT_PARAM); + boolean validate = + Boolean.parseBoolean(servletContext.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM)); + String blockExternalString = servletContext.getInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM); boolean blockExternal; if (blockExternalString == null) { blockExternal = true; @@ -96,7 +91,7 @@ if (cacheEntry == null) { return null; } - long lastModified[] = getLastModified(tldResourcePath); + long[] lastModified = getLastModified(tldResourcePath); if (lastModified[0] != cacheEntry.getWebAppPathLastModified() || lastModified[1] != cacheEntry.getEntryLastModified()) { synchronized (cacheEntry) { @@ -143,7 +138,7 @@ result[1] = jar.getLastModified(tldResourcePath.getEntryName()); } } - } catch (IOException e) { + } catch (IOException ignore) { // Ignore (shouldn't happen) } return result; @@ -154,8 +149,7 @@ private volatile long webAppPathLastModified; private volatile long entryLastModified; - TaglibXmlCacheEntry(TaglibXml taglibXml, long webAppPathLastModified, - long entryLastModified) { + TaglibXmlCacheEntry(TaglibXml taglibXml, long webAppPathLastModified, long entryLastModified) { this.taglibXml = taglibXml; this.webAppPathLastModified = webAppPathLastModified; this.entryLastModified = entryLastModified; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/Validator.java tomcat10-10.1.52/java/org/apache/jasper/compiler/Validator.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/Validator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/Validator.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,6 +24,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -47,15 +48,8 @@ import org.xml.sax.Attributes; /** - * Performs validation on the page elements. Attributes are checked for - * mandatory presence, entry value validity, and consistency. As a side effect, - * some page global value (such as those from page directives) are stored, for - * later use. - * - * @author Kin-man Chung - * @author Jan Luehe - * @author Shawn Bayern - * @author Mark Roth + * Performs validation on the page elements. Attributes are checked for mandatory presence, entry value validity, and + * consistency. As a side effect, some page global value (such as those from page directives) are stored, for later use. */ class Validator { @@ -68,24 +62,16 @@ private final ErrorDispatcher err; - private static final JspUtil.ValidAttribute[] pageDirectiveAttrs = { - new JspUtil.ValidAttribute("language"), - new JspUtil.ValidAttribute("extends"), - new JspUtil.ValidAttribute("import"), - new JspUtil.ValidAttribute("session"), - new JspUtil.ValidAttribute("buffer"), - new JspUtil.ValidAttribute("autoFlush"), - new JspUtil.ValidAttribute("isThreadSafe"), - new JspUtil.ValidAttribute("info"), - new JspUtil.ValidAttribute("errorPage"), - new JspUtil.ValidAttribute("isErrorPage"), - new JspUtil.ValidAttribute("contentType"), - new JspUtil.ValidAttribute("pageEncoding"), - new JspUtil.ValidAttribute("isELIgnored"), - new JspUtil.ValidAttribute("errorOnELNotFound"), - new JspUtil.ValidAttribute("deferredSyntaxAllowedAsLiteral"), - new JspUtil.ValidAttribute("trimDirectiveWhitespaces") - }; + private static final JspUtil.ValidAttribute[] pageDirectiveAttrs = + { new JspUtil.ValidAttribute("language"), new JspUtil.ValidAttribute("extends"), + new JspUtil.ValidAttribute("import"), new JspUtil.ValidAttribute("session"), + new JspUtil.ValidAttribute("buffer"), new JspUtil.ValidAttribute("autoFlush"), + new JspUtil.ValidAttribute("isThreadSafe"), new JspUtil.ValidAttribute("info"), + new JspUtil.ValidAttribute("errorPage"), new JspUtil.ValidAttribute("isErrorPage"), + new JspUtil.ValidAttribute("contentType"), new JspUtil.ValidAttribute("pageEncoding"), + new JspUtil.ValidAttribute("isELIgnored"), new JspUtil.ValidAttribute("errorOnELNotFound"), + new JspUtil.ValidAttribute("deferredSyntaxAllowedAsLiteral"), + new JspUtil.ValidAttribute("trimDirectiveWhitespaces") }; private boolean pageEncodingSeen = false; @@ -110,8 +96,7 @@ @Override public void visit(Node.PageDirective n) throws JasperException { - JspUtil.checkAttributes("Page directive", n, pageDirectiveAttrs, - err); + JspUtil.checkAttributes("Page directive", n, pageDirectiveAttrs, err); // JSP.2.10.1 Attributes attrs = n.getAttributes(); @@ -123,85 +108,74 @@ if (pageInfo.getLanguage(false) == null) { pageInfo.setLanguage(value, n, err, true); } else if (!pageInfo.getLanguage(false).equals(value)) { - err.jspError(n, "jsp.error.page.conflict.language", - pageInfo.getLanguage(false), value); + err.jspError(n, "jsp.error.page.conflict.language", pageInfo.getLanguage(false), value); } } else if ("extends".equals(attr)) { if (pageInfo.getExtends(false) == null) { pageInfo.setExtends(value); } else if (!pageInfo.getExtends(false).equals(value)) { - err.jspError(n, "jsp.error.page.conflict.extends", - pageInfo.getExtends(false), value); + err.jspError(n, "jsp.error.page.conflict.extends", pageInfo.getExtends(false), value); } } else if ("contentType".equals(attr)) { if (pageInfo.getContentType() == null) { pageInfo.setContentType(value); } else if (!pageInfo.getContentType().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.contenttype", - pageInfo.getContentType(), value); + err.jspError(n, "jsp.error.page.conflict.contenttype", pageInfo.getContentType(), value); } } else if ("session".equals(attr)) { if (pageInfo.getSession() == null) { pageInfo.setSession(value, n, err); } else if (!pageInfo.getSession().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.session", - pageInfo.getSession(), value); + err.jspError(n, "jsp.error.page.conflict.session", pageInfo.getSession(), value); } } else if ("buffer".equals(attr)) { if (pageInfo.getBufferValue() == null) { pageInfo.setBufferValue(value, n, err); } else if (!pageInfo.getBufferValue().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.buffer", - pageInfo.getBufferValue(), value); + err.jspError(n, "jsp.error.page.conflict.buffer", pageInfo.getBufferValue(), value); } } else if ("autoFlush".equals(attr)) { if (pageInfo.getAutoFlush() == null) { pageInfo.setAutoFlush(value, n, err); } else if (!pageInfo.getAutoFlush().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.autoflush", - pageInfo.getAutoFlush(), value); + err.jspError(n, "jsp.error.page.conflict.autoflush", pageInfo.getAutoFlush(), value); } } else if ("isThreadSafe".equals(attr)) { if (pageInfo.getIsThreadSafe() == null) { pageInfo.setIsThreadSafe(value, n, err); } else if (!pageInfo.getIsThreadSafe().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.isthreadsafe", - pageInfo.getIsThreadSafe(), value); + err.jspError(n, "jsp.error.page.conflict.isthreadsafe", pageInfo.getIsThreadSafe(), value); } } else if ("isELIgnored".equals(attr)) { if (pageInfo.getIsELIgnored() == null) { pageInfo.setIsELIgnored(value, n, err, true); } else if (!pageInfo.getIsELIgnored().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.iselignored", - pageInfo.getIsELIgnored(), value); + err.jspError(n, "jsp.error.page.conflict.iselignored", pageInfo.getIsELIgnored(), value); } } else if ("errorOnELNotFound".equals(attr)) { if (pageInfo.getErrorOnELNotFound() == null) { pageInfo.setErrorOnELNotFound(value, n, err, true); } else if (!pageInfo.getErrorOnELNotFound().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.errorOnELNotFound", - pageInfo.getErrorOnELNotFound(), value); + err.jspError(n, "jsp.error.page.conflict.errorOnELNotFound", pageInfo.getErrorOnELNotFound(), + value); } } else if ("isErrorPage".equals(attr)) { if (pageInfo.getIsErrorPage() == null) { pageInfo.setIsErrorPage(value, n, err); } else if (!pageInfo.getIsErrorPage().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.iserrorpage", - pageInfo.getIsErrorPage(), value); + err.jspError(n, "jsp.error.page.conflict.iserrorpage", pageInfo.getIsErrorPage(), value); } } else if ("errorPage".equals(attr)) { if (pageInfo.getErrorPage() == null) { pageInfo.setErrorPage(value); } else if (!pageInfo.getErrorPage().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.errorpage", - pageInfo.getErrorPage(), value); + err.jspError(n, "jsp.error.page.conflict.errorpage", pageInfo.getErrorPage(), value); } } else if ("info".equals(attr)) { if (pageInfo.getInfo() == null) { pageInfo.setInfo(value); } else if (!pageInfo.getInfo().equals(value)) { - err.jspError(n, "jsp.error.page.conflict.info", - pageInfo.getInfo(), value); + err.jspError(n, "jsp.error.page.conflict.info", pageInfo.getInfo(), value); } } else if ("pageEncoding".equals(attr)) { if (pageEncodingSeen) { @@ -213,30 +187,17 @@ n.getRoot().setPageEncoding(actual); } else if ("deferredSyntaxAllowedAsLiteral".equals(attr)) { if (pageInfo.getDeferredSyntaxAllowedAsLiteral() == null) { - pageInfo.setDeferredSyntaxAllowedAsLiteral(value, n, - err, true); - } else if (!pageInfo.getDeferredSyntaxAllowedAsLiteral() - .equals(value)) { - err - .jspError( - n, - "jsp.error.page.conflict.deferredsyntaxallowedasliteral", - pageInfo - .getDeferredSyntaxAllowedAsLiteral(), - value); + pageInfo.setDeferredSyntaxAllowedAsLiteral(value, n, err, true); + } else if (!pageInfo.getDeferredSyntaxAllowedAsLiteral().equals(value)) { + err.jspError(n, "jsp.error.page.conflict.deferredsyntaxallowedasliteral", + pageInfo.getDeferredSyntaxAllowedAsLiteral(), value); } } else if ("trimDirectiveWhitespaces".equals(attr)) { if (pageInfo.getTrimDirectiveWhitespaces() == null) { - pageInfo.setTrimDirectiveWhitespaces(value, n, err, - true); - } else if (!pageInfo.getTrimDirectiveWhitespaces().equals( - value)) { - err - .jspError( - n, - "jsp.error.page.conflict.trimdirectivewhitespaces", - pageInfo.getTrimDirectiveWhitespaces(), - value); + pageInfo.setTrimDirectiveWhitespaces(value, n, err, true); + } else if (!pageInfo.getTrimDirectiveWhitespaces().equals(value)) { + err.jspError(n, "jsp.error.page.conflict.trimdirectivewhitespaces", + pageInfo.getTrimDirectiveWhitespaces(), value); } } } @@ -268,22 +229,20 @@ if (pageInfo.getLanguage(false) == null) { pageInfo.setLanguage(value, n, err, false); } else if (!pageInfo.getLanguage(false).equals(value)) { - err.jspError(n, "jsp.error.tag.conflict.language", - pageInfo.getLanguage(false), value); + err.jspError(n, "jsp.error.tag.conflict.language", pageInfo.getLanguage(false), value); } } else if ("isELIgnored".equals(attr)) { if (pageInfo.getIsELIgnored() == null) { pageInfo.setIsELIgnored(value, n, err, false); } else if (!pageInfo.getIsELIgnored().equals(value)) { - err.jspError(n, "jsp.error.tag.conflict.iselignored", - pageInfo.getIsELIgnored(), value); + err.jspError(n, "jsp.error.tag.conflict.iselignored", pageInfo.getIsELIgnored(), value); } } else if ("errorOnELNotFound".equals(attr)) { if (pageInfo.getErrorOnELNotFound() == null) { pageInfo.setErrorOnELNotFound(value, n, err, false); } else if (!pageInfo.getErrorOnELNotFound().equals(value)) { - err.jspError(n, "jsp.error.tag.conflict.errorOnELNotFound", - pageInfo.getErrorOnELNotFound(), value); + err.jspError(n, "jsp.error.tag.conflict.errorOnELNotFound", pageInfo.getErrorOnELNotFound(), + value); } } else if ("pageEncoding".equals(attr)) { if (pageEncodingSeen) { @@ -294,30 +253,17 @@ n.getRoot().setPageEncoding(value); } else if ("deferredSyntaxAllowedAsLiteral".equals(attr)) { if (pageInfo.getDeferredSyntaxAllowedAsLiteral() == null) { - pageInfo.setDeferredSyntaxAllowedAsLiteral(value, n, - err, false); - } else if (!pageInfo.getDeferredSyntaxAllowedAsLiteral() - .equals(value)) { - err - .jspError( - n, - "jsp.error.tag.conflict.deferredsyntaxallowedasliteral", - pageInfo - .getDeferredSyntaxAllowedAsLiteral(), - value); + pageInfo.setDeferredSyntaxAllowedAsLiteral(value, n, err, false); + } else if (!pageInfo.getDeferredSyntaxAllowedAsLiteral().equals(value)) { + err.jspError(n, "jsp.error.tag.conflict.deferredsyntaxallowedasliteral", + pageInfo.getDeferredSyntaxAllowedAsLiteral(), value); } } else if ("trimDirectiveWhitespaces".equals(attr)) { if (pageInfo.getTrimDirectiveWhitespaces() == null) { - pageInfo.setTrimDirectiveWhitespaces(value, n, err, - false); - } else if (!pageInfo.getTrimDirectiveWhitespaces().equals( - value)) { - err - .jspError( - n, - "jsp.error.tag.conflict.trimdirectivewhitespaces", - pageInfo.getTrimDirectiveWhitespaces(), - value); + pageInfo.setTrimDirectiveWhitespaces(value, n, err, false); + } else if (!pageInfo.getTrimDirectiveWhitespaces().equals(value)) { + err.jspError(n, "jsp.error.tag.conflict.trimdirectivewhitespaces", + pageInfo.getTrimDirectiveWhitespaces(), value); } } } @@ -342,55 +288,43 @@ } /* - * Compares page encodings specified in various places, and throws - * exception in case of page encoding mismatch. + * Compares page encodings specified in various places, and throws exception in case of page encoding mismatch. * - * @param pageDirEnc The value of the pageEncoding attribute of the page - * directive @param pageDir The page directive node + * @param pageDirEnc The value of the pageEncoding attribute of the page directive @param pageDir The page + * directive node * * @throws JasperException in case of page encoding mismatch */ - private String comparePageEncodings(String thePageDirEnc, - Node.PageDirective pageDir) throws JasperException { + private String comparePageEncodings(String thePageDirEnc, Node.PageDirective pageDir) throws JasperException { Node.Root root = pageDir.getRoot(); String configEnc = root.getJspConfigPageEncoding(); String pageDirEnc = thePageDirEnc.toUpperCase(Locale.ENGLISH); /* - * Compare the 'pageEncoding' attribute of the page directive with - * the encoding specified in the JSP config element whose URL - * pattern matches this page. Treat "UTF-16", "UTF-16BE", and - * "UTF-16LE" as identical. + * Compare the 'pageEncoding' attribute of the page directive with the encoding specified in the JSP config + * element whose URL pattern matches this page. Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as identical. */ if (configEnc != null) { configEnc = configEnc.toUpperCase(Locale.ENGLISH); - if (!pageDirEnc.equals(configEnc) - && (!pageDirEnc.startsWith("UTF-16") || !configEnc - .startsWith("UTF-16"))) { - err.jspError(pageDir, - "jsp.error.config_pagedir_encoding_mismatch", - configEnc, pageDirEnc); + if (!pageDirEnc.equals(configEnc) && + (!pageDirEnc.startsWith("UTF-16") || !configEnc.startsWith("UTF-16"))) { + err.jspError(pageDir, "jsp.error.config_pagedir_encoding_mismatch", configEnc, pageDirEnc); } else { return configEnc; } } /* - * Compare the 'pageEncoding' attribute of the page directive with - * the encoding specified in the XML prolog (only for XML syntax, - * and only if JSP document contains XML prolog with encoding - * declaration). Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as - * identical. + * Compare the 'pageEncoding' attribute of the page directive with the encoding specified in the XML prolog + * (only for XML syntax, and only if JSP document contains XML prolog with encoding declaration). Treat + * "UTF-16", "UTF-16BE", and "UTF-16LE" as identical. */ if ((root.isXmlSyntax() && root.isEncodingSpecifiedInProlog()) || root.isBomPresent()) { String pageEnc = root.getPageEncoding().toUpperCase(Locale.ENGLISH); - if (!pageDirEnc.equals(pageEnc) - && (!pageDirEnc.startsWith("UTF-16") || !pageEnc - .startsWith("UTF-16"))) { - err.jspError(pageDir, - "jsp.error.prolog_pagedir_encoding_mismatch", - pageEnc, pageDirEnc); + if (!pageDirEnc.equals(pageEnc) && + (!pageDirEnc.startsWith("UTF-16") || !pageEnc.startsWith("UTF-16"))) { + err.jspError(pageDir, "jsp.error.prolog_pagedir_encoding_mismatch", pageEnc, pageDirEnc); } else { return pageEnc; } @@ -400,34 +334,27 @@ } /* - * Compares page encodings specified in various places, and throws - * exception in case of page encoding mismatch. + * Compares page encodings specified in various places, and throws exception in case of page encoding mismatch. * - * @param thePageDirEnc The value of the pageEncoding attribute of the page - * directive @param pageDir The page directive node + * @param thePageDirEnc The value of the pageEncoding attribute of the page directive @param pageDir The page + * directive node * * @throws JasperException in case of page encoding mismatch */ - private void compareTagEncodings(String thePageDirEnc, - Node.TagDirective pageDir) throws JasperException { + private void compareTagEncodings(String thePageDirEnc, Node.TagDirective pageDir) throws JasperException { Node.Root root = pageDir.getRoot(); String pageDirEnc = thePageDirEnc.toUpperCase(Locale.ENGLISH); /* - * Compare the 'pageEncoding' attribute of the page directive with - * the encoding specified in the XML prolog (only for XML syntax, - * and only if JSP document contains XML prolog with encoding - * declaration). Treat "UTF-16", "UTF-16BE", and "UTF-16LE" as - * identical. + * Compare the 'pageEncoding' attribute of the page directive with the encoding specified in the XML prolog + * (only for XML syntax, and only if JSP document contains XML prolog with encoding declaration). Treat + * "UTF-16", "UTF-16BE", and "UTF-16LE" as identical. */ if ((root.isXmlSyntax() && root.isEncodingSpecifiedInProlog()) || root.isBomPresent()) { String pageEnc = root.getPageEncoding().toUpperCase(Locale.ENGLISH); - if (!pageDirEnc.equals(pageEnc) - && (!pageDirEnc.startsWith("UTF-16") || !pageEnc - .startsWith("UTF-16"))) { - err.jspError(pageDir, - "jsp.error.prolog_pagedir_encoding_mismatch", - pageEnc, pageDirEnc); + if (!pageDirEnc.equals(pageEnc) && + (!pageDirEnc.startsWith("UTF-16") || !pageEnc.startsWith("UTF-16"))) { + err.jspError(pageDir, "jsp.error.prolog_pagedir_encoding_mismatch", pageEnc, pageDirEnc); } } } @@ -451,82 +378,55 @@ private final StringBuilder buf = new StringBuilder(32); - private static final JspUtil.ValidAttribute[] jspRootAttrs = { - new JspUtil.ValidAttribute("xsi:schemaLocation"), - new JspUtil.ValidAttribute("version", true) }; - - private static final JspUtil.ValidAttribute[] includeDirectiveAttrs = { new JspUtil.ValidAttribute( - "file", true) }; - - private static final JspUtil.ValidAttribute[] taglibDirectiveAttrs = { - new JspUtil.ValidAttribute("uri"), - new JspUtil.ValidAttribute("tagdir"), - new JspUtil.ValidAttribute("prefix", true) }; - - private static final JspUtil.ValidAttribute[] includeActionAttrs = { - new JspUtil.ValidAttribute("page", true), - new JspUtil.ValidAttribute("flush") }; - - private static final JspUtil.ValidAttribute[] paramActionAttrs = { - new JspUtil.ValidAttribute("name", true), - new JspUtil.ValidAttribute("value", true) }; - - private static final JspUtil.ValidAttribute[] forwardActionAttrs = { - new JspUtil.ValidAttribute("page", true) }; - - private static final JspUtil.ValidAttribute[] getPropertyAttrs = { - new JspUtil.ValidAttribute("name", true), - new JspUtil.ValidAttribute("property", true) }; - - private static final JspUtil.ValidAttribute[] setPropertyAttrs = { - new JspUtil.ValidAttribute("name", true), - new JspUtil.ValidAttribute("property", true), - new JspUtil.ValidAttribute("value", false), - new JspUtil.ValidAttribute("param") }; - - private static final JspUtil.ValidAttribute[] useBeanAttrs = { - new JspUtil.ValidAttribute("id", true), - new JspUtil.ValidAttribute("scope"), - new JspUtil.ValidAttribute("class"), - new JspUtil.ValidAttribute("type"), - new JspUtil.ValidAttribute("beanName", false) }; - - private static final JspUtil.ValidAttribute[] plugInAttrs = { - new JspUtil.ValidAttribute("type", true), - new JspUtil.ValidAttribute("code", true), - new JspUtil.ValidAttribute("codebase"), - new JspUtil.ValidAttribute("align"), - new JspUtil.ValidAttribute("archive"), - new JspUtil.ValidAttribute("height", false), - new JspUtil.ValidAttribute("hspace"), - new JspUtil.ValidAttribute("jreversion"), - new JspUtil.ValidAttribute("name"), - new JspUtil.ValidAttribute("vspace"), - new JspUtil.ValidAttribute("width", false), - new JspUtil.ValidAttribute("nspluginurl"), - new JspUtil.ValidAttribute("iepluginurl") }; - - private static final JspUtil.ValidAttribute[] attributeAttrs = { - new JspUtil.ValidAttribute("name", true), - new JspUtil.ValidAttribute("trim"), - new JspUtil.ValidAttribute("omit")}; - - private static final JspUtil.ValidAttribute[] invokeAttrs = { - new JspUtil.ValidAttribute("fragment", true), - new JspUtil.ValidAttribute("var"), - new JspUtil.ValidAttribute("varReader"), - new JspUtil.ValidAttribute("scope") }; - - private static final JspUtil.ValidAttribute[] doBodyAttrs = { - new JspUtil.ValidAttribute("var"), - new JspUtil.ValidAttribute("varReader"), - new JspUtil.ValidAttribute("scope") }; + private static final JspUtil.ValidAttribute[] jspRootAttrs = + { new JspUtil.ValidAttribute("xsi:schemaLocation"), new JspUtil.ValidAttribute("version", true) }; + + private static final JspUtil.ValidAttribute[] includeDirectiveAttrs = + { new JspUtil.ValidAttribute("file", true) }; + + private static final JspUtil.ValidAttribute[] taglibDirectiveAttrs = { new JspUtil.ValidAttribute("uri"), + new JspUtil.ValidAttribute("tagdir"), new JspUtil.ValidAttribute("prefix", true) }; + + private static final JspUtil.ValidAttribute[] includeActionAttrs = + { new JspUtil.ValidAttribute("page", true), new JspUtil.ValidAttribute("flush") }; + + private static final JspUtil.ValidAttribute[] paramActionAttrs = + { new JspUtil.ValidAttribute("name", true), new JspUtil.ValidAttribute("value", true) }; + + private static final JspUtil.ValidAttribute[] forwardActionAttrs = { new JspUtil.ValidAttribute("page", true) }; + + private static final JspUtil.ValidAttribute[] getPropertyAttrs = + { new JspUtil.ValidAttribute("name", true), new JspUtil.ValidAttribute("property", true) }; + + private static final JspUtil.ValidAttribute[] setPropertyAttrs = + { new JspUtil.ValidAttribute("name", true), new JspUtil.ValidAttribute("property", true), + new JspUtil.ValidAttribute("value", false), new JspUtil.ValidAttribute("param") }; + + private static final JspUtil.ValidAttribute[] useBeanAttrs = { new JspUtil.ValidAttribute("id", true), + new JspUtil.ValidAttribute("scope"), new JspUtil.ValidAttribute("class"), + new JspUtil.ValidAttribute("type"), new JspUtil.ValidAttribute("beanName", false) }; + + private static final JspUtil.ValidAttribute[] plugInAttrs = { new JspUtil.ValidAttribute("type", true), + new JspUtil.ValidAttribute("code", true), new JspUtil.ValidAttribute("codebase"), + new JspUtil.ValidAttribute("align"), new JspUtil.ValidAttribute("archive"), + new JspUtil.ValidAttribute("height", false), new JspUtil.ValidAttribute("hspace"), + new JspUtil.ValidAttribute("jreversion"), new JspUtil.ValidAttribute("name"), + new JspUtil.ValidAttribute("vspace"), new JspUtil.ValidAttribute("width", false), + new JspUtil.ValidAttribute("nspluginurl"), new JspUtil.ValidAttribute("iepluginurl") }; + + private static final JspUtil.ValidAttribute[] attributeAttrs = { new JspUtil.ValidAttribute("name", true), + new JspUtil.ValidAttribute("trim"), new JspUtil.ValidAttribute("omit") }; + + private static final JspUtil.ValidAttribute[] invokeAttrs = + { new JspUtil.ValidAttribute("fragment", true), new JspUtil.ValidAttribute("var"), + new JspUtil.ValidAttribute("varReader"), new JspUtil.ValidAttribute("scope") }; + + private static final JspUtil.ValidAttribute[] doBodyAttrs = { new JspUtil.ValidAttribute("var"), + new JspUtil.ValidAttribute("varReader"), new JspUtil.ValidAttribute("scope") }; private static final JspUtil.ValidAttribute[] jspOutputAttrs = { - new JspUtil.ValidAttribute("omit-xml-declaration"), - new JspUtil.ValidAttribute("doctype-root-element"), - new JspUtil.ValidAttribute("doctype-public"), - new JspUtil.ValidAttribute("doctype-system") }; + new JspUtil.ValidAttribute("omit-xml-declaration"), new JspUtil.ValidAttribute("doctype-root-element"), + new JspUtil.ValidAttribute("doctype-public"), new JspUtil.ValidAttribute("doctype-system") }; private final ExpressionFactory expressionFactory; @@ -538,20 +438,17 @@ this.err = compiler.getErrorDispatcher(); this.loader = compiler.getCompilationContext().getClassLoader(); // Get the cached EL expression factory for this context - expressionFactory = - JspFactory.getDefaultFactory().getJspApplicationContext( - compiler.getCompilationContext().getServletContext()). - getExpressionFactory(); + expressionFactory = JspFactory.getDefaultFactory() + .getJspApplicationContext(compiler.getCompilationContext().getServletContext()) + .getExpressionFactory(); } @Override public void visit(Node.JspRoot n) throws JasperException { JspUtil.checkAttributes("Jsp:root", n, jspRootAttrs, err); String version = n.getTextAttribute("version"); - if (!version.equals("1.2") && !version.equals("2.0") && - !version.equals("2.1") && !version.equals("2.2") && - !version.equals("2.3") && !version.equals("3.0") && - !version.equals("3.1")) { + if (!version.equals("1.2") && !version.equals("2.0") && !version.equals("2.1") && !version.equals("2.2") && + !version.equals("2.3") && !version.equals("3.0") && !version.equals("3.1")) { err.jspError(n, "jsp.error.jsproot.version.invalid", version); } visitBody(n); @@ -559,15 +456,13 @@ @Override public void visit(Node.IncludeDirective n) throws JasperException { - JspUtil.checkAttributes("Include directive", n, - includeDirectiveAttrs, err); + JspUtil.checkAttributes("Include directive", n, includeDirectiveAttrs, err); visitBody(n); } @Override public void visit(Node.TaglibDirective n) throws JasperException { - JspUtil.checkAttributes("Taglib directive", n, - taglibDirectiveAttrs, err); + JspUtil.checkAttributes("Taglib directive", n, taglibDirectiveAttrs, err); // Either 'uri' or 'tagdir' attribute must be specified String uri = n.getAttributeValue("uri"); String tagdir = n.getAttributeValue("tagdir"); @@ -575,9 +470,7 @@ err.jspError(n, "jsp.error.taglibDirective.missing.location"); } if (uri != null && tagdir != null) { - err - .jspError(n, - "jsp.error.taglibDirective.both_uri_and_tagdir"); + err.jspError(n, "jsp.error.taglibDirective.both_uri_and_tagdir"); } } @@ -587,8 +480,7 @@ // make sure the value of the 'name' attribute is not a // request-time expression throwErrorIfExpression(n, "name", "jsp:param"); - n.setValue(getJspAttribute(null, "value", null, null, n - .getAttributeValue("value"), n, null, false)); + n.setValue(getJspAttribute(null, "value", null, null, n.getAttributeValue("value"), n, null, false)); visitBody(n); } @@ -604,18 +496,15 @@ @Override public void visit(Node.IncludeAction n) throws JasperException { - JspUtil.checkAttributes("Include action", n, includeActionAttrs, - err); - n.setPage(getJspAttribute(null, "page", null, null, n - .getAttributeValue("page"), n, null, false)); + JspUtil.checkAttributes("Include action", n, includeActionAttrs, err); + n.setPage(getJspAttribute(null, "page", null, null, n.getAttributeValue("page"), n, null, false)); visitBody(n); } @Override public void visit(Node.ForwardAction n) throws JasperException { JspUtil.checkAttributes("Forward", n, forwardActionAttrs, err); - n.setPage(getJspAttribute(null, "page", null, null, n - .getAttributeValue("page"), n, null, false)); + n.setPage(getJspAttribute(null, "page", null, null, n.getAttributeValue("page"), n, null, false)); visitBody(n); } @@ -631,8 +520,7 @@ String param = n.getTextAttribute("param"); String value = n.getAttributeValue("value"); - n.setValue(getJspAttribute(null, "value", null, null, value, - n, null, false)); + n.setValue(getJspAttribute(null, "value", null, null, value, n, null, false)); boolean valueSpecified = n.getValue() != null; @@ -671,8 +559,8 @@ err.jspError(n, "jsp.error.usebean.noSession"); } - Node.JspAttribute jattr = getJspAttribute(null, "beanName", null, - null, n.getAttributeValue("beanName"), n, null, false); + Node.JspAttribute jattr = + getJspAttribute(null, "beanName", null, null, n.getAttributeValue("beanName"), n, null, false); n.setBeanName(jattr); if (className != null && jattr != null) { err.jspError(n, "jsp.error.usebean.notBoth"); @@ -715,12 +603,12 @@ err.jspError(n, "jsp.error.plugin.nocode"); } - Node.JspAttribute width = getJspAttribute(null, "width", null, - null, n.getAttributeValue("width"), n, null, false); + Node.JspAttribute width = + getJspAttribute(null, "width", null, null, n.getAttributeValue("width"), n, null, false); n.setWidth(width); - Node.JspAttribute height = getJspAttribute(null, "height", null, - null, n.getAttributeValue("height"), n, null, false); + Node.JspAttribute height = + getJspAttribute(null, "height", null, null, n.getAttributeValue("height"), n, null, false); n.setHeight(height); visitBody(n); @@ -729,8 +617,7 @@ @Override public void visit(Node.NamedAttribute n) throws JasperException { JspUtil.checkAttributes("Attribute", n, attributeAttrs, err); - n.setOmit(getJspAttribute(null, "omit", null, null, n - .getAttributeValue("omit"), n, null, false)); + n.setOmit(getJspAttribute(null, "omit", null, null, n.getAttributeValue("omit"), n, null, false)); visitBody(n); } @@ -778,10 +665,8 @@ // build expression StringBuilder expr = this.getBuffer(); - expr.append(n.getType()).append('{').append(n.getText()) - .append('}'); - ELNode.Nodes el = ELParser.parse(expr.toString(), pageInfo - .isDeferredSyntaxAllowedAsLiteral()); + expr.append(n.getType()).append('{').append(n.getText()).append('}'); + ELNode.Nodes el = ELParser.parse(expr.toString(), pageInfo.isDeferredSyntaxAllowedAsLiteral()); // validate/prepare expression prepareExpression(el, n, expr.toString()); @@ -808,9 +693,8 @@ err.jspError(n, "jsp.error.el.template.deferred"); } } - jspAttrs[i] = getJspAttribute(null, attrs.getQName(i), - attrs.getURI(i), attrs.getLocalName(i), value, n, - null, false); + jspAttrs[i] = getJspAttribute(null, attrs.getQName(i), attrs.getURI(i), attrs.getLocalName(i), + value, n, null, false); } n.setJspAttributes(jspAttrs); } @@ -831,12 +715,10 @@ boolean prevCharIsEscape = false; while (i < value.length()) { char c = value.charAt(i); - if (c == '#' && (i+1) < len && value.charAt(i+1) == '{' && !prevCharIsEscape) { + if (c == '#' && (i + 1) < len && value.charAt(i + 1) == '{' && !prevCharIsEscape) { return true; - } else if (c == '\\') { - prevCharIsEscape = true; } else { - prevCharIsEscape = false; + prevCharIsEscape = c == '\\'; } i++; } @@ -855,29 +737,22 @@ /* * The bodycontent of a SimpleTag cannot be JSP. */ - if (n.implementsSimpleTag() - && tagInfo.getBodyContent().equalsIgnoreCase( - TagInfo.BODY_CONTENT_JSP)) { - err.jspError(n, "jsp.error.simpletag.badbodycontent", tagInfo - .getTagClassName()); + if (n.implementsSimpleTag() && tagInfo.getBodyContent().equalsIgnoreCase(TagInfo.BODY_CONTENT_JSP)) { + err.jspError(n, "jsp.error.simpletag.badbodycontent", tagInfo.getTagClassName()); } /* - * If the tag handler declares in the TLD that it supports dynamic - * attributes, it also must implement the DynamicAttributes - * interface. + * If the tag handler declares in the TLD that it supports dynamic attributes, it also must implement the + * DynamicAttributes interface. */ - if (tagInfo.hasDynamicAttributes() - && !n.implementsDynamicAttributes()) { - err.jspError(n, "jsp.error.dynamic.attributes.not.implemented", - n.getQName()); + if (tagInfo.hasDynamicAttributes() && !n.implementsDynamicAttributes()) { + err.jspError(n, "jsp.error.dynamic.attributes.not.implemented", n.getQName()); } /* - * Make sure all required attributes are present, either as - * attributes or named attributes (). Also make sure - * that the same attribute is not specified in both attributes or - * named attributes. + * Make sure all required attributes are present, either as attributes or named attributes + * (). Also make sure that the same attribute is not specified in both attributes or named + * attributes. */ TagAttributeInfo[] tldAttrs = tagInfo.getAttributes(); String customActionUri = n.getURI(); @@ -888,20 +763,16 @@ if (attrs != null) { attr = attrs.getValue(tldAttr.getName()); if (attr == null) { - attr = attrs.getValue(customActionUri, tldAttr - .getName()); + attr = attrs.getValue(customActionUri, tldAttr.getName()); } } - Node.NamedAttribute na = n.getNamedAttributeNode(tldAttr - .getName()); + Node.NamedAttribute na = n.getNamedAttributeNode(tldAttr.getName()); if (tldAttr.isRequired() && attr == null && na == null) { - err.jspError(n, "jsp.error.missing_attribute", tldAttr - .getName(), n.getLocalName()); + err.jspError(n, "jsp.error.missing_attribute", tldAttr.getName(), n.getLocalName()); } if (attr != null && na != null) { - err.jspError(n, "jsp.error.duplicate.name.jspattribute", - tldAttr.getName()); + err.jspError(n, "jsp.error.duplicate.name.jspattribute", tldAttr.getName()); } } @@ -911,7 +782,7 @@ if (jspAttrsSize > 0) { jspAttrs = new Node.JspAttribute[jspAttrsSize]; } - Hashtable tagDataAttrs = new Hashtable<>(attrsSize); + Hashtable tagDataAttrs = new Hashtable<>(attrsSize); checkXmlAttributes(n, jspAttrs, tagDataAttrs); checkNamedAttributes(n, jspAttrs, attrsSize, tagDataAttrs); @@ -922,11 +793,9 @@ // has one or more variable subelements to have a TagExtraInfo // class that returns a non-null object. TagExtraInfo tei = tagInfo.getTagExtraInfo(); - if (tei != null && tei.getVariableInfo(tagData) != null - && tei.getVariableInfo(tagData).length > 0 - && tagInfo.getTagVariableInfos().length > 0) { - err.jspError("jsp.error.non_null_tei_and_var_subelems", n - .getQName()); + if (tei != null && tei.getVariableInfo(tagData) != null && tei.getVariableInfo(tagData).length > 0 && + tagInfo.getTagVariableInfos().length > 0) { + err.jspError("jsp.error.non_null_tei_and_var_subelems", n.getQName()); } n.setTagData(tagData); @@ -957,15 +826,12 @@ // Process XML-style attributes for (int i = 0; i < xmlAttrLen; i++) { if ("name".equals(attrs.getLocalName(i))) { - n.setNameAttribute(getJspAttribute(null, attrs.getQName(i), - attrs.getURI(i), attrs.getLocalName(i), attrs - .getValue(i), n, null, false)); + n.setNameAttribute(getJspAttribute(null, attrs.getQName(i), attrs.getURI(i), attrs.getLocalName(i), + attrs.getValue(i), n, null, false)); } else { if (jspAttrIndex < jspAttrSize) { - jspAttrs[jspAttrIndex++] = getJspAttribute(null, - attrs.getQName(i), attrs.getURI(i), - attrs.getLocalName(i), attrs.getValue(i), n, - null, false); + jspAttrs[jspAttrIndex++] = getJspAttribute(null, attrs.getQName(i), attrs.getURI(i), + attrs.getLocalName(i), attrs.getValue(i), n, null, false); } } } @@ -975,10 +841,8 @@ // Process named attributes for (int i = 0; i < namedAttrs.size(); i++) { - Node.NamedAttribute na = (Node.NamedAttribute) namedAttrs - .getNode(i); - jspAttrs[jspAttrIndex++] = new Node.JspAttribute(na, null, - false); + Node.NamedAttribute na = (Node.NamedAttribute) namedAttrs.getNode(i); + jspAttrs[jspAttrIndex++] = new Node.JspAttribute(na, null, false); } n.setJspAttributes(jspAttrs); @@ -1004,32 +868,23 @@ String doctypePublicOld = pageInfo.getDoctypePublic(); String doctypeSystemOld = pageInfo.getDoctypeSystem(); - if (omitXmlDecl != null && omitXmlDeclOld != null - && !omitXmlDecl.equals(omitXmlDeclOld)) { - err.jspError(n, "jsp.error.jspoutput.conflict", - "omit-xml-declaration", omitXmlDeclOld, omitXmlDecl); + if (omitXmlDecl != null && omitXmlDeclOld != null && !omitXmlDecl.equals(omitXmlDeclOld)) { + err.jspError(n, "jsp.error.jspoutput.conflict", "omit-xml-declaration", omitXmlDeclOld, omitXmlDecl); } - if (doctypeName != null && doctypeNameOld != null - && !doctypeName.equals(doctypeNameOld)) { - err.jspError(n, "jsp.error.jspoutput.conflict", - "doctype-root-element", doctypeNameOld, doctypeName); + if (doctypeName != null && doctypeNameOld != null && !doctypeName.equals(doctypeNameOld)) { + err.jspError(n, "jsp.error.jspoutput.conflict", "doctype-root-element", doctypeNameOld, doctypeName); } - if (doctypePublic != null && doctypePublicOld != null - && !doctypePublic.equals(doctypePublicOld)) { - err.jspError(n, "jsp.error.jspoutput.conflict", - "doctype-public", doctypePublicOld, doctypePublic); + if (doctypePublic != null && doctypePublicOld != null && !doctypePublic.equals(doctypePublicOld)) { + err.jspError(n, "jsp.error.jspoutput.conflict", "doctype-public", doctypePublicOld, doctypePublic); } - if (doctypeSystem != null && doctypeSystemOld != null - && !doctypeSystem.equals(doctypeSystemOld)) { - err.jspError(n, "jsp.error.jspoutput.conflict", - "doctype-system", doctypeSystemOld, doctypeSystem); + if (doctypeSystem != null && doctypeSystemOld != null && !doctypeSystem.equals(doctypeSystemOld)) { + err.jspError(n, "jsp.error.jspoutput.conflict", "doctype-system", doctypeSystemOld, doctypeSystem); } - if (doctypeName == null && doctypeSystem != null - || doctypeName != null && doctypeSystem == null) { + if (doctypeName == null && doctypeSystem != null || doctypeName != null && doctypeSystem == null) { err.jspError(n, "jsp.error.jspoutput.doctypenamesystem"); } @@ -1088,28 +943,23 @@ } /* - * Make sure the given custom action does not have any invalid - * attributes. + * Make sure the given custom action does not have any invalid attributes. * - * A custom action and its declared attributes always belong to the same - * namespace, which is identified by the prefix name of the custom tag - * invocation. For example, in this invocation: + * A custom action and its declared attributes always belong to the same namespace, which is identified by the + * prefix name of the custom tag invocation. For example, in this invocation: * * , the action * - * "test" and its attributes "a", "b", and "c" all belong to the - * namespace identified by the prefix "my". The above invocation would - * be equivalent to: + * "test" and its attributes "a", "b", and "c" all belong to the namespace identified by the prefix "my". The + * above invocation would be equivalent to: * * * - * An action attribute may have a prefix different from that of the - * action invocation only if the underlying tag handler supports dynamic - * attributes, in which case the attribute with the different prefix is - * considered a dynamic attribute. + * An action attribute may have a prefix different from that of the action invocation only if the underlying tag + * handler supports dynamic attributes, in which case the attribute with the different prefix is considered a + * dynamic attribute. */ - private void checkXmlAttributes(Node.CustomTag n, - Node.JspAttribute[] jspAttrs, Map tagDataAttrs) + private void checkXmlAttributes(Node.CustomTag n, Node.JspAttribute[] jspAttrs, Map tagDataAttrs) throws JasperException { TagInfo tagInfo = n.getTagInfo(); @@ -1119,36 +969,31 @@ for (int i = 0; attrs != null && i < attrs.getLength(); i++) { boolean found = false; - boolean runtimeExpression = ((n.getRoot().isXmlSyntax() && attrs.getValue(i).startsWith("%=")) - || (!n.getRoot().isXmlSyntax() && attrs.getValue(i).startsWith("<%="))); + boolean runtimeExpression = ((n.getRoot().isXmlSyntax() && attrs.getValue(i).startsWith("%=")) || + (!n.getRoot().isXmlSyntax() && attrs.getValue(i).startsWith("<%="))); boolean elExpression = false; boolean deferred = false; - double libraryVersion = Double.parseDouble( - tagInfo.getTagLibrary().getRequiredVersion()); + double libraryVersion = Double.parseDouble(tagInfo.getTagLibrary().getRequiredVersion()); boolean deferredSyntaxAllowedAsLiteral = - pageInfo.isDeferredSyntaxAllowedAsLiteral() || - libraryVersion < 2.1; + pageInfo.isDeferredSyntaxAllowedAsLiteral() || libraryVersion < 2.1; String xmlAttributeValue = attrs.getValue(i); ELNode.Nodes el = null; if (!runtimeExpression && !pageInfo.isELIgnored()) { - el = ELParser.parse(xmlAttributeValue, - deferredSyntaxAllowedAsLiteral); + el = ELParser.parse(xmlAttributeValue, deferredSyntaxAllowedAsLiteral); Iterator nodes = el.iterator(); while (nodes.hasNext()) { ELNode node = nodes.next(); if (node instanceof ELNode.Root) { if (((ELNode.Root) node).getType() == '$') { if (elExpression && deferred) { - err.jspError(n, - "jsp.error.attribute.deferredmix"); + err.jspError(n, "jsp.error.attribute.deferredmix"); } elExpression = true; } else if (((ELNode.Root) node).getType() == '#') { if (elExpression && !deferred) { - err.jspError(n, - "jsp.error.attribute.deferredmix"); + err.jspError(n, "jsp.error.attribute.deferredmix"); } elExpression = true; deferred = true; @@ -1166,8 +1011,7 @@ // Should be a single Text node Iterator it = el.iterator(); if (it.hasNext()) { - textAttributeValue = ((ELNode.Text) it.next()) - .getText(); + textAttributeValue = ((ELNode.Text) it.next()).getText(); } else { textAttributeValue = ""; } @@ -1175,14 +1019,12 @@ textAttributeValue = xmlAttributeValue; } for (int j = 0; tldAttrs != null && j < tldAttrs.length; j++) { - if (attrs.getLocalName(i).equals(tldAttrs[j].getName()) - && (attrs.getURI(i) == null - || attrs.getURI(i).length() == 0 || attrs - .getURI(i).equals(n.getURI()))) { + if (attrs.getLocalName(i).equals(tldAttrs[j].getName()) && (attrs.getURI(i) == null || + attrs.getURI(i).isEmpty() || attrs.getURI(i).equals(n.getURI()))) { TagAttributeInfo tldAttr = tldAttrs[j]; - if (tldAttr.canBeRequestTime() - || tldAttr.isDeferredMethod() || tldAttr.isDeferredValue()) { // JSP 2.1 + if (tldAttr.canBeRequestTime() || tldAttr.isDeferredMethod() || tldAttr.isDeferredValue()) { // JSP + // 2.1 if (!expression) { @@ -1204,9 +1046,7 @@ // Can't specify a literal for a // deferred method with an expected type // of void - JSP.2.3.4 - err.jspError(n, - "jsp.error.literal_with_void", - tldAttr.getName()); + err.jspError(n, "jsp.error.literal_with_void", tldAttr.getName()); } } if (tldAttr.isDeferredValue()) { @@ -1219,76 +1059,58 @@ try { expectedClass = JspUtil.toClass(expectedType, loader); } catch (ClassNotFoundException e) { - err.jspError - (n, "jsp.error.unknown_attribute_type", - tldAttr.getName(), expectedType); + err.jspError(n, "jsp.error.unknown_attribute_type", tldAttr.getName(), + expectedType); } // Check casting - not possible for all types - if (String.class.equals(expectedClass) || - expectedClass == Long.TYPE || - expectedClass == Double.TYPE || - expectedClass == Byte.TYPE || - expectedClass == Short.TYPE || - expectedClass == Integer.TYPE || + if (String.class.equals(expectedClass) || expectedClass == Long.TYPE || + expectedClass == Double.TYPE || expectedClass == Byte.TYPE || + expectedClass == Short.TYPE || expectedClass == Integer.TYPE || expectedClass == Float.TYPE || Number.class.isAssignableFrom(expectedClass) || - Character.class.equals(expectedClass) || - Character.TYPE == expectedClass || - Boolean.class.equals(expectedClass) || - Boolean.TYPE == expectedClass || + Character.class.equals(expectedClass) || Character.TYPE == expectedClass || + Boolean.class.equals(expectedClass) || Boolean.TYPE == expectedClass || expectedClass.isEnum()) { try { expressionFactory.coerceToType(textAttributeValue, expectedClass); } catch (Exception e) { - err.jspError - (n, "jsp.error.coerce_to_type", - tldAttr.getName(), expectedType, textAttributeValue); + err.jspError(n, "jsp.error.coerce_to_type", tldAttr.getName(), expectedType, + textAttributeValue); } } } - jspAttrs[i] = new Node.JspAttribute(tldAttr, - attrs.getQName(i), attrs.getURI(i), - attrs.getLocalName(i), - textAttributeValue, false, null, false); + jspAttrs[i] = new Node.JspAttribute(tldAttr, attrs.getQName(i), attrs.getURI(i), + attrs.getLocalName(i), textAttributeValue, false, null, false); } else { if (deferred && !tldAttr.isDeferredMethod() && !tldAttr.isDeferredValue()) { // No deferred expressions allowed for this attribute - err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr", - tldAttr.getName()); + err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr", tldAttr.getName()); } if (!deferred && !tldAttr.canBeRequestTime()) { // Only deferred expressions are allowed for this attribute - err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr", - tldAttr.getName()); + err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr", tldAttr.getName()); } // EL or Runtime expression - jspAttrs[i] = getJspAttribute(tldAttr, - attrs.getQName(i), attrs.getURI(i), - attrs.getLocalName(i), - xmlAttributeValue, n, el, false); + jspAttrs[i] = getJspAttribute(tldAttr, attrs.getQName(i), attrs.getURI(i), + attrs.getLocalName(i), xmlAttributeValue, n, el, false); } } else { // Attribute does not accept any expressions. // Make sure its value does not contain any. if (expression) { - err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr", - tldAttr.getName()); + err.jspError(n, "jsp.error.attribute.custom.non_rt_with_expr", tldAttr.getName()); } - jspAttrs[i] = new Node.JspAttribute(tldAttr, - attrs.getQName(i), attrs.getURI(i), - attrs.getLocalName(i), - textAttributeValue, false, null, false); + jspAttrs[i] = new Node.JspAttribute(tldAttr, attrs.getQName(i), attrs.getURI(i), + attrs.getLocalName(i), textAttributeValue, false, null, false); } if (expression) { - tagDataAttrs.put(attrs.getQName(i), - TagData.REQUEST_TIME_VALUE); + tagDataAttrs.put(attrs.getQName(i), TagData.REQUEST_TIME_VALUE); } else { - tagDataAttrs.put(attrs.getQName(i), - textAttributeValue); + tagDataAttrs.put(attrs.getQName(i), textAttributeValue); } found = true; break; @@ -1296,57 +1118,46 @@ } if (!found) { if (tagInfo.hasDynamicAttributes()) { - jspAttrs[i] = getJspAttribute(null, attrs.getQName(i), - attrs.getURI(i), attrs.getLocalName(i), + jspAttrs[i] = getJspAttribute(null, attrs.getQName(i), attrs.getURI(i), attrs.getLocalName(i), xmlAttributeValue, n, el, true); } else { - err.jspError(n, "jsp.error.bad_attribute", attrs - .getQName(i), n.getLocalName()); + err.jspError(n, "jsp.error.bad_attribute", attrs.getQName(i), n.getLocalName()); } } } } /* - * Make sure the given custom action does not have any invalid named - * attributes + * Make sure the given custom action does not have any invalid named attributes */ - private void checkNamedAttributes(Node.CustomTag n, - Node.JspAttribute[] jspAttrs, int start, - Map tagDataAttrs) - throws JasperException { + private void checkNamedAttributes(Node.CustomTag n, Node.JspAttribute[] jspAttrs, int start, + Map tagDataAttrs) throws JasperException { TagInfo tagInfo = n.getTagInfo(); TagAttributeInfo[] tldAttrs = tagInfo.getAttributes(); Node.Nodes naNodes = n.getNamedAttributeNodes(); for (int i = 0; i < naNodes.size(); i++) { - Node.NamedAttribute na = (Node.NamedAttribute) naNodes - .getNode(i); + Node.NamedAttribute na = (Node.NamedAttribute) naNodes.getNode(i); boolean found = false; for (TagAttributeInfo tldAttr : tldAttrs) { /* - * See above comment about namespace matches. For named - * attributes, we use the prefix instead of URI as the match - * criterion, because in the case of a JSP document, we'd - * have to keep track of which namespaces are in scope when - * parsing a named attribute, in order to determine the URI - * that the prefix of the named attribute's name matches to. + * See above comment about namespace matches. For named attributes, we use the prefix instead of URI + * as the match criterion, because in the case of a JSP document, we'd have to keep track of which + * namespaces are in scope when parsing a named attribute, in order to determine the URI that the + * prefix of the named attribute's name matches to. */ String attrPrefix = na.getPrefix(); - if (na.getLocalName().equals(tldAttr.getName()) - && (attrPrefix == null || attrPrefix.length() == 0 || attrPrefix - .equals(n.getPrefix()))) { - jspAttrs[start + i] = new Node.JspAttribute(na, - tldAttr, false); + if (na.getLocalName().equals(tldAttr.getName()) && + (attrPrefix == null || attrPrefix.isEmpty() || attrPrefix.equals(n.getPrefix()))) { + jspAttrs[start + i] = new Node.JspAttribute(na, tldAttr, false); NamedAttributeVisitor nav = null; if (na.getBody() != null) { nav = new NamedAttributeVisitor(); na.getBody().visit(nav); } if (nav != null && nav.hasDynamicContent()) { - tagDataAttrs.put(na.getName(), - TagData.REQUEST_TIME_VALUE); + tagDataAttrs.put(na.getName(), TagData.REQUEST_TIME_VALUE); } else { tagDataAttrs.put(na.getName(), na.getText()); } @@ -1356,31 +1167,24 @@ } if (!found) { if (tagInfo.hasDynamicAttributes()) { - jspAttrs[start + i] = new Node.JspAttribute(na, null, - true); + jspAttrs[start + i] = new Node.JspAttribute(na, null, true); } else { - err.jspError(n, "jsp.error.bad_attribute", - na.getName(), n.getLocalName()); + err.jspError(n, "jsp.error.bad_attribute", na.getName(), n.getLocalName()); } } } } /** - * Preprocess attributes that can be expressions. Expression delimiters - * are stripped. + * Preprocess attributes that can be expressions. Expression delimiters are stripped. *

          - * If value is null, checks if there are any NamedAttribute subelements - * in the tree node, and if so, constructs a JspAttribute out of a child - * NamedAttribute node. + * If value is null, checks if there are any NamedAttribute sub elements in the tree node, and if so, constructs + * a JspAttribute out of a child NamedAttribute node. * - * @param el EL expression, if already parsed by the caller (so that we - * can skip re-parsing it) + * @param el EL expression, if already parsed by the caller (so that we can skip reparsing it) */ - private Node.JspAttribute getJspAttribute(TagAttributeInfo tai, - String qName, String uri, String localName, String value, - Node n, ELNode.Nodes el, boolean dynamic) - throws JasperException { + private Node.JspAttribute getJspAttribute(TagAttributeInfo tai, String qName, String uri, String localName, + String value, Node n, ELNode.Nodes el, boolean dynamic) throws JasperException { Node.JspAttribute result = null; @@ -1390,14 +1194,11 @@ if (value != null) { if (n.getRoot().isXmlSyntax() && value.startsWith("%=")) { - result = new Node.JspAttribute(tai, qName, uri, localName, - value.substring(2, value.length() - 1), true, null, - dynamic); - } else if (!n.getRoot().isXmlSyntax() - && value.startsWith("<%=")) { - result = new Node.JspAttribute(tai, qName, uri, localName, - value.substring(3, value.length() - 2), true, null, - dynamic); + result = new Node.JspAttribute(tai, qName, uri, localName, value.substring(2, value.length() - 1), + true, null, dynamic); + } else if (!n.getRoot().isXmlSyntax() && value.startsWith("<%=")) { + result = new Node.JspAttribute(tai, qName, uri, localName, value.substring(3, value.length() - 2), + true, null, dynamic); } else { if (!pageInfo.isELIgnored()) { // The attribute can contain expressions but is not a @@ -1407,8 +1208,7 @@ // validate expression syntax if string contains // expression(s) if (el == null) { - el = ELParser.parse(value, - pageInfo.isDeferredSyntaxAllowedAsLiteral()); + el = ELParser.parse(value, pageInfo.isDeferredSyntaxAllowedAsLiteral()); } if (el.containsEL()) { @@ -1426,8 +1226,7 @@ } } - if (n instanceof Node.UninterpretedTag && - n.getRoot().isXmlSyntax()) { + if (n instanceof Node.UninterpretedTag && n.getRoot().isXmlSyntax()) { // Attribute values of uninterpreted tags will have been // XML un-escaped during parsing. Since these attributes // are part of an uninterpreted tag the value needs to @@ -1435,8 +1234,8 @@ // The wrinkle is that the output of any EL must not be // re-escaped as that must be output as is. if (el != null) { - XmlEscapeNonELVisitor v = new XmlEscapeNonELVisitor( - pageInfo.isDeferredSyntaxAllowedAsLiteral()); + XmlEscapeNonELVisitor v = + new XmlEscapeNonELVisitor(pageInfo.isDeferredSyntaxAllowedAsLiteral()); el.visit(v); value = v.getText(); } else { @@ -1444,21 +1243,16 @@ } } - result = new Node.JspAttribute(tai, qName, uri, localName, - value, false, el, dynamic); + result = new Node.JspAttribute(tai, qName, uri, localName, value, false, el, dynamic); if (el != null) { - ELContextImpl ctx = - new ELContextImpl(expressionFactory); + ELContextImpl ctx = new ELContextImpl(expressionFactory); ctx.setFunctionMapper(getFunctionMapper(el)); try { - result.validateEL(this.pageInfo - .getExpressionFactory(), ctx); + result.validateEL(this.pageInfo.getExpressionFactory(), ctx); } catch (ELException e) { - this.err.jspError(n.getStart(), - "jsp.error.invalid.expression", value, e - .toString()); + this.err.jspError(n.getStart(), "jsp.error.invalid.expression", value, e.toString()); } } } @@ -1467,11 +1261,9 @@ // that might contain the value for this attribute. // Otherwise, the attribute wasn't found so we return null. - Node.NamedAttribute namedAttributeNode = n - .getNamedAttributeNode(qName); + Node.NamedAttribute namedAttributeNode = n.getNamedAttributeNode(qName); if (namedAttributeNode != null) { - result = new Node.JspAttribute(namedAttributeNode, tai, - dynamic); + result = new Node.JspAttribute(namedAttributeNode, tai, dynamic); } } @@ -1481,16 +1273,14 @@ private static class XmlEscapeNonELVisitor extends ELParser.TextBuilder { - protected XmlEscapeNonELVisitor( - boolean isDeferredSyntaxAllowedAsLiteral) { + protected XmlEscapeNonELVisitor(boolean isDeferredSyntaxAllowedAsLiteral) { super(isDeferredSyntaxAllowedAsLiteral); } @Override public void visit(Text n) throws JasperException { - output.append(ELParser.escapeLiteralExpression( - Escape.xml(n.getText()), - isDeferredSyntaxAllowedAsLiteral)); + output.append( + ELParser.escapeLiteralExpression(Escape.xml(n.getText()), isDeferredSyntaxAllowedAsLiteral)); } } @@ -1504,26 +1294,24 @@ } /* - * Checks to see if the given attribute value represents a runtime or EL - * expression. + * Checks to see if the given attribute value represents a runtime or EL expression. */ private boolean isExpression(Node n, String value, boolean checkDeferred) { - boolean runtimeExpression = ((n.getRoot().isXmlSyntax() && value.startsWith("%=")) - || (!n.getRoot().isXmlSyntax() && value.startsWith("<%="))); + boolean runtimeExpression = ((n.getRoot().isXmlSyntax() && value.startsWith("%=")) || + (!n.getRoot().isXmlSyntax() && value.startsWith("<%="))); boolean elExpression = false; if (!runtimeExpression && !pageInfo.isELIgnored()) { - Iterator nodes = ELParser.parse(value, - pageInfo.isDeferredSyntaxAllowedAsLiteral()).iterator(); + Iterator nodes = ELParser.parse(value, pageInfo.isDeferredSyntaxAllowedAsLiteral()).iterator(); while (nodes.hasNext()) { ELNode node = nodes.next(); if (node instanceof ELNode.Root) { if (((ELNode.Root) node).getType() == '$') { elExpression = true; break; - } else if (checkDeferred && !pageInfo.isDeferredSyntaxAllowedAsLiteral() - && ((ELNode.Root) node).getType() == '#') { + } else if (checkDeferred && !pageInfo.isDeferredSyntaxAllowedAsLiteral() && + ((ELNode.Root) node).getType() == '#') { elExpression = true; break; } @@ -1536,18 +1324,13 @@ } /* - * Throws exception if the value of the attribute with the given name in - * the given node is given as an RT or EL expression, but the spec - * requires a static value. + * Throws exception if the value of the attribute with the given name in the given node is given as an RT or EL + * expression, but the spec requires a static value. */ - private void throwErrorIfExpression(Node n, String attrName, - String actionName) throws JasperException { - if (n.getAttributes() != null - && n.getAttributes().getValue(attrName) != null - && isExpression(n, n.getAttributes().getValue(attrName), true)) { - err.jspError(n, - "jsp.error.attribute.standard.non_rt_with_expr", - attrName, actionName); + private void throwErrorIfExpression(Node n, String attrName, String actionName) throws JasperException { + if (n.getAttributes() != null && n.getAttributes().getValue(attrName) != null && + isExpression(n, n.getAttributes().getValue(attrName), true)) { + err.jspError(n, "jsp.error.attribute.standard.non_rt_with_expr", attrName, actionName); } } @@ -1556,8 +1339,7 @@ @Override public void doVisit(Node n) throws JasperException { - if (!(n instanceof Node.JspText) - && !(n instanceof Node.TemplateText)) { + if (!(n instanceof Node.JspText) && !(n instanceof Node.TemplateText)) { hasDynamicContent = true; } visitBody(n); @@ -1582,8 +1364,7 @@ // prefix not specified and a default ns found return attrs.getValue(i); } - if (prefix != null && k >= 0 - && prefix.equals(name.substring(k + 1))) { + if (prefix != null && k >= 0 && prefix.equals(name.substring(k + 1))) { return attrs.getValue(i); } } @@ -1594,12 +1375,11 @@ /** * Validate functions in EL expressions */ - private void validateFunctions(ELNode.Nodes el, Node n) - throws JasperException { + private void validateFunctions(ELNode.Nodes el, Node n) throws JasperException { class FVVisitor extends ELNode.Visitor { - private Node n; + private final Node n; FVVisitor(Node n) { this.n = n; @@ -1624,8 +1404,7 @@ // longer able to be sure this is an error. return; } else { - err.jspError(n, "jsp.error.attribute.invalidPrefix", - prefix); + err.jspError(n, "jsp.error.attribute.invalidPrefix", prefix); } } TagLibraryInfo taglib = pageInfo.getTaglib(uri); @@ -1646,8 +1425,7 @@ el.visit(new FVVisitor(n)); } - private void prepareExpression(ELNode.Nodes el, Node n, String expr) - throws JasperException { + private void prepareExpression(ELNode.Nodes el, Node n, String expr) throws JasperException { validateFunctions(el, n); // test it out @@ -1661,8 +1439,7 @@ } } - private void processSignature(ELNode.Function func) - throws JasperException { + private void processSignature(ELNode.Function func) throws JasperException { func.setMethodName(getMethod(func)); func.setParameters(getParameters(func)); } @@ -1676,8 +1453,7 @@ Matcher m = METHOD_NAME_PATTERN.matcher(signature); if (!m.matches()) { - err.jspError("jsp.error.tld.fn.invalid.signature", func - .getPrefix(), func.getName()); + err.jspError("jsp.error.tld.fn.invalid.signature", func.getPrefix(), func.getName()); } return m.group(1); @@ -1688,8 +1464,7 @@ * * @return An array of parameter class names */ - private String[] getParameters(ELNode.Function func) - throws JasperException { + private String[] getParameters(ELNode.Function func) throws JasperException { FunctionInfo funcInfo = func.getFunctionInfo(); String signature = funcInfo.getFunctionSignature(); List params = new ArrayList<>(); @@ -1703,8 +1478,7 @@ if (p < 0) { p = signature.indexOf(')', start); if (p < 0) { - err.jspError("jsp.error.tld.fn.invalid.signature", func - .getPrefix(), func.getName()); + err.jspError("jsp.error.tld.fn.invalid.signature", func.getPrefix(), func.getName()); } lastArg = true; } @@ -1720,16 +1494,14 @@ return params.toArray(new String[0]); } - private FunctionMapper getFunctionMapper(ELNode.Nodes el) - throws JasperException { + private FunctionMapper getFunctionMapper(ELNode.Nodes el) throws JasperException { class ValidateFunctionMapper extends FunctionMapper { - private Map fnmap = new HashMap<>(); + private final Map fnmap = new HashMap<>(); @Override - public void mapFunction(String prefix, String localName, - Method method) { + public void mapFunction(String prefix, String localName, Method method) { fnmap.put(prefix + ":" + localName, method); } @@ -1740,7 +1512,7 @@ } class MapperELVisitor extends ELNode.Visitor { - private ValidateFunctionMapper fmapper; + private final ValidateFunctionMapper fmapper; MapperELVisitor(ValidateFunctionMapper fmapper) { this.fmapper = fmapper; @@ -1758,17 +1530,14 @@ Class c = null; Method method = null; try { - c = loader.loadClass(n.getFunctionInfo() - .getFunctionClass()); + c = loader.loadClass(n.getFunctionInfo().getFunctionClass()); } catch (ClassNotFoundException e) { - err.jspError("jsp.error.function.classnotfound", n - .getFunctionInfo().getFunctionClass(), n - .getPrefix() - + ':' + n.getName(), e.getMessage()); + err.jspError("jsp.error.function.classnotfound", n.getFunctionInfo().getFunctionClass(), + n.getPrefix() + ':' + n.getName(), e.getMessage()); } - String paramTypes[] = n.getParameters(); + String[] paramTypes = n.getParameters(); int size = paramTypes.length; - Class params[] = new Class[size]; + Class[] params = new Class[size]; int i = 0; try { for (i = 0; i < size; i++) { @@ -1776,15 +1545,12 @@ } method = c.getDeclaredMethod(n.getMethodName(), params); } catch (ClassNotFoundException e) { - err.jspError("jsp.error.signature.classnotfound", - paramTypes[i], n.getPrefix() + ':' - + n.getName(), e.getMessage()); + err.jspError("jsp.error.signature.classnotfound", paramTypes[i], + n.getPrefix() + ':' + n.getName(), e.getMessage()); } catch (NoSuchMethodException e) { - err.jspError("jsp.error.noFunctionMethod", n - .getMethodName(), n.getName(), c.getName()); + err.jspError("jsp.error.noFunctionMethod", n.getMethodName(), n.getName(), c.getName()); } - fmapper.mapFunction(n.getPrefix(), n.getName(), - method); + fmapper.mapFunction(n.getPrefix(), n.getName(), method); } } @@ -1820,8 +1586,7 @@ if (errors != null && errors.length != 0) { StringBuilder errMsg = new StringBuilder(); errMsg.append("

          "); - errMsg.append(Localizer.getMessage( - "jsp.error.tei.invalid.attributes", n.getQName())); + errMsg.append(Localizer.getMessage("jsp.error.tei.invalid.attributes", n.getQName())); errMsg.append("

          "); for (ValidationMessage error : errors) { errMsg.append("

          "); @@ -1840,13 +1605,11 @@ } } - public static void validateDirectives(Compiler compiler, Node.Nodes page) - throws JasperException { + public static void validateDirectives(Compiler compiler, Node.Nodes page) throws JasperException { page.visit(new DirectiveVisitor(compiler)); } - public static void validateExDirectives(Compiler compiler, Node.Nodes page) - throws JasperException { + public static void validateExDirectives(Compiler compiler, Node.Nodes page) throws JasperException { // Determine the default output content type PageInfo pageInfo = compiler.getPageInfo(); String contentType = pageInfo.getContentType(); @@ -1854,11 +1617,7 @@ if (contentType == null || !contentType.contains("charset=")) { boolean isXml = page.getRoot().isXmlSyntax(); String defaultType; - if (contentType == null) { - defaultType = isXml ? "text/xml" : "text/html"; - } else { - defaultType = contentType; - } + defaultType = Objects.requireNonNullElse(contentType, isXml ? "text/xml" : "text/html"); String charset = null; if (isXml) { @@ -1877,22 +1636,20 @@ } /* - * Validate all other nodes. This validation step includes checking a - * custom tag's mandatory and optional attributes against information in - * the TLD (first validation step for custom tags according to - * JSP.10.5). + * Validate all other nodes. This validation step includes checking a custom tag's mandatory and optional + * attributes against information in the TLD (first validation step for custom tags according to JSP.10.5). */ page.visit(new ValidateVisitor(compiler)); /* - * Invoke TagLibraryValidator classes of all imported tags (second - * validation step for custom tags according to JSP.10.5). + * Invoke TagLibraryValidator classes of all imported tags (second validation step for custom tags according to + * JSP.10.5). */ validateXmlView(new PageDataImpl(page, compiler), compiler); /* - * Invoke TagExtraInfo method isValid() for all imported tags (third - * validation step for custom tags according to JSP.10.5). + * Invoke TagExtraInfo method isValid() for all imported tags (third validation step for custom tags according + * to JSP.10.5). */ page.visit(new TagExtraInfoVisitor(compiler)); @@ -1902,11 +1659,9 @@ // Private (utility) methods /** - * Validate XML view against the TagLibraryValidator classes of all imported - * tag libraries. + * Validate XML view against the TagLibraryValidator classes of all imported tag libraries. */ - private static void validateXmlView(PageData xmlView, Compiler compiler) - throws JasperException { + private static void validateXmlView(PageData xmlView, Compiler compiler) throws JasperException { StringBuilder errMsg = null; ErrorDispatcher errDisp = compiler.getErrorDispatcher(); @@ -1924,8 +1679,7 @@ errMsg = new StringBuilder(); } errMsg.append("

          "); - errMsg.append(Localizer.getMessage( - "jsp.error.tlv.invalid.page", tli.getShortName(), + errMsg.append(Localizer.getMessage("jsp.error.tlv.invalid.page", tli.getShortName(), compiler.getPageInfo().getJspFile())); errMsg.append("

          "); for (ValidationMessage error : errors) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java tomcat10-10.1.52/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java --- tomcat10-10.1.34/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/compiler/tagplugin/TagPluginContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -81,8 +81,8 @@ String getConstantAttribute(String attribute); /** - * Generate codes to evaluate value of a attribute in the custom tag The codes is a Java expression. NOTE: Currently - * cannot handle attributes that are fragments. + * Generate codes to evaluate value of an attribute in the custom tag The codes is a Java expression. NOTE: + * Currently cannot handle attributes that are fragments. * * @param attribute The specified attribute */ @@ -101,10 +101,10 @@ /** * Get the PluginContext for the parent of this custom tag. NOTE: The operations available for PluginContext so - * obtained is limited to getPluginAttribute and setPluginAttribute, and queries (e.g. isScriptless(). There should + * obtained is limited to getPluginAttribute and setPluginAttribute, and queries (e.g. isScriptless()). There should * be no calls to generate*(). * - * @return The pluginContext for the parent node. null if the parent is not a custom tag, or if the pluginContext if + * @return The pluginContext for the parent node. null if the parent is not a custom tag, or if the pluginContext is * not available (because useTagPlugin is false, e.g). */ TagPluginContext getParentContext(); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/ELContextImpl.java tomcat10-10.1.52/java/org/apache/jasper/el/ELContextImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/el/ELContextImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/ELContextImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,9 +38,7 @@ import org.apache.jasper.Constants; /** - * Implementation of ELContext - * - * @author Jacob Hookom + * Implementation of ELContext. */ public class ELContextImpl extends ELContext { @@ -53,7 +51,7 @@ private static final class VariableMapperImpl extends VariableMapper { - private Map vars; + private Map vars; @Override public ValueExpression resolveVariable(String variable) { @@ -64,8 +62,7 @@ } @Override - public ValueExpression setVariable(String variable, - ValueExpression expression) { + public ValueExpression setVariable(String variable, ValueExpression expression) { if (vars == null) { vars = new HashMap<>(); } @@ -84,8 +81,7 @@ DefaultResolver = null; } else { DefaultResolver = new CompositeELResolver(); - ((CompositeELResolver) DefaultResolver).add( - ELManager.getExpressionFactory().getStreamELResolver()); + ((CompositeELResolver) DefaultResolver).add(ELManager.getExpressionFactory().getStreamELResolver()); ((CompositeELResolver) DefaultResolver).add(new StaticFieldELResolver()); ((CompositeELResolver) DefaultResolver).add(new MapELResolver()); ((CompositeELResolver) DefaultResolver).add(new ResourceBundleELResolver()); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/ELContextWrapper.java tomcat10-10.1.52/java/org/apache/jasper/el/ELContextWrapper.java --- tomcat10-10.1.34/java/org/apache/jasper/el/ELContextWrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/ELContextWrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Simple ELContextWrapper for runtime evaluation of EL w/ dynamic FunctionMappers - * - * @author jhook */ public final class ELContextWrapper extends ELContext { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/ELResolverImpl.java tomcat10-10.1.52/java/org/apache/jasper/el/ELResolverImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/el/ELResolverImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/ELResolverImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,8 +32,7 @@ private final VariableResolver variableResolver; private final ELResolver elResolver; - public ELResolverImpl(VariableResolver variableResolver, - ExpressionFactory factory) { + public ELResolverImpl(VariableResolver variableResolver, ExpressionFactory factory) { this.variableResolver = variableResolver; this.elResolver = ELContextImpl.getDefaultResolver(factory); } @@ -46,8 +45,7 @@ context.setPropertyResolved(base, property); if (property != null) { try { - return this.variableResolver.resolveVariable(property - .toString()); + return this.variableResolver.resolveVariable(property.toString()); } catch (jakarta.servlet.jsp.el.ELException e) { throw new ELException(e.getMessage(), e.getCause()); } @@ -68,8 +66,7 @@ context.setPropertyResolved(base, property); if (property != null) { try { - Object obj = this.variableResolver.resolveVariable(property - .toString()); + Object obj = this.variableResolver.resolveVariable(property.toString()); return (obj != null) ? obj.getClass() : null; } catch (jakarta.servlet.jsp.el.ELException e) { throw new ELException(e.getMessage(), e.getCause()); @@ -84,14 +81,12 @@ } @Override - public void setValue(ELContext context, Object base, Object property, - Object value) { + public void setValue(ELContext context, Object base, Object property, Object value) { Objects.requireNonNull(context); if (base == null) { context.setPropertyResolved(base, property); - throw new PropertyNotWritableException( - "Legacy VariableResolver wrapped, not writable"); + throw new PropertyNotWritableException("Legacy VariableResolver wrapped, not writable"); } if (!context.isPropertyResolved()) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/ExpressionEvaluatorImpl.java tomcat10-10.1.52/java/org/apache/jasper/el/ExpressionEvaluatorImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/el/ExpressionEvaluatorImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/ExpressionEvaluatorImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,12 +35,10 @@ } @Override - public Expression parseExpression(String expression, - @SuppressWarnings("rawtypes") Class expectedType, + public Expression parseExpression(String expression, @SuppressWarnings("rawtypes") Class expectedType, FunctionMapper fMapper) throws ELException { try { - ELContextImpl ctx = - new ELContextImpl(ELContextImpl.getDefaultResolver(factory)); + ELContextImpl ctx = new ELContextImpl(ELContextImpl.getDefaultResolver(factory)); if (fMapper != null) { ctx.setFunctionMapper(new FunctionMapperImpl(fMapper)); } @@ -52,10 +50,8 @@ } @Override - public Object evaluate(String expression, - @SuppressWarnings("rawtypes") Class expectedType, - VariableResolver vResolver, FunctionMapper fMapper) - throws ELException { + public Object evaluate(String expression, @SuppressWarnings("rawtypes") Class expectedType, + VariableResolver vResolver, FunctionMapper fMapper) throws ELException { return this.parseExpression(expression, expectedType, fMapper).evaluate(vResolver); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/ExpressionImpl.java tomcat10-10.1.52/java/org/apache/jasper/el/ExpressionImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/el/ExpressionImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/ExpressionImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,8 +37,7 @@ @Override public Object evaluate(VariableResolver vResolver) throws ELException { - ELContext ctx = - new ELContextImpl(new ELResolverImpl(vResolver, factory)); + ELContext ctx = new ELContextImpl(new ELResolverImpl(vResolver, factory)); return ve.getValue(ctx); } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/JasperELResolver.java tomcat10-10.1.52/java/org/apache/jasper/el/JasperELResolver.java --- tomcat10-10.1.34/java/org/apache/jasper/el/JasperELResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/JasperELResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,20 +43,18 @@ import org.apache.jasper.runtime.JspRuntimeLibrary; /** - * Jasper-specific CompositeELResolver that optimizes certain functions to avoid - * unnecessary resolver calls. + * Jasper-specific CompositeELResolver that optimizes certain functions to avoid unnecessary resolver calls. */ public class JasperELResolver extends CompositeELResolver { // Keep aligned with class under test private static final int STANDARD_RESOLVERS_COUNT = 11; - private AtomicInteger resolversSize = new AtomicInteger(0); + private final AtomicInteger resolversSize = new AtomicInteger(0); private volatile ELResolver[] resolvers; private final int appResolversSize; - public JasperELResolver(List appResolvers, - ELResolver streamResolver) { + public JasperELResolver(List appResolvers, ELResolver streamResolver) { appResolversSize = appResolvers.size(); resolvers = new ELResolver[appResolversSize + STANDARD_RESOLVERS_COUNT]; @@ -99,17 +97,17 @@ @Override public Object getValue(ELContext context, Object base, Object property) - throws NullPointerException, PropertyNotFoundException, ELException { + throws NullPointerException, PropertyNotFoundException, ELException { context.setPropertyResolved(false); int start; - Object result = null; + Object result; if (base == null) { // call implicit and app resolvers int index = 1 /* implicit */ + appResolversSize; for (int i = 0; i < index; i++) { - result = resolvers[i].getValue(context, base, property); + result = resolvers[i].getValue(context, null, property); if (context.isPropertyResolved()) { return result; } @@ -137,24 +135,21 @@ } @Override - public Object invoke(ELContext context, Object base, Object method, - Class[] paramTypes, Object[] params) { + public Object invoke(ELContext context, Object base, Object method, Class[] paramTypes, Object[] params) { String targetMethod = coerceToString(method); - if (targetMethod.length() == 0) { + if (targetMethod.isEmpty()) { throw new ELException(new NoSuchMethodException()); } context.setPropertyResolved(false); - Object result = null; + Object result; // skip implicit and call app resolvers, stream resolver and static // resolver - int index = 1 /* implicit */ + appResolversSize + - 2 /* stream + static */; + int index = 1 /* implicit */ + appResolversSize + 2 /* stream + static */; for (int i = 1; i < index; i++) { - result = resolvers[i].invoke( - context, base, targetMethod, paramTypes, params); + result = resolvers[i].invoke(context, base, targetMethod, paramTypes, params); if (context.isPropertyResolved()) { return result; } @@ -165,8 +160,7 @@ // call bean and the rest of resolvers int size = resolversSize.get(); for (int i = index; i < size; i++) { - result = resolvers[i].invoke( - context, base, targetMethod, paramTypes, params); + result = resolvers[i].invoke(context, base, targetMethod, paramTypes, params); if (context.isPropertyResolved()) { return result; } @@ -191,14 +185,13 @@ } /** - * Extend ELResolver for Graal to avoid bean info use if possible, - * as BeanELResolver needs manual reflection configuration. + * Extend ELResolver for Graal to avoid bean info use if possible, as BeanELResolver needs manual reflection + * configuration. */ public static class GraalBeanELResolver extends ELResolver { @Override - public Object getValue(ELContext context, Object base, - Object property) { + public Object getValue(ELContext context, Object base, Object property) { Objects.requireNonNull(context); if (base == null || property == null) { return null; @@ -210,8 +203,8 @@ try { method.setAccessible(true); value = method.invoke(base, (Object[]) null); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); } } @@ -219,8 +212,7 @@ } @Override - public void setValue(ELContext context, Object base, Object property, - Object value) { + public void setValue(ELContext context, Object base, Object property, Object value) { Objects.requireNonNull(context); if (base == null || property == null) { return; @@ -230,16 +222,15 @@ context.setPropertyResolved(base, property); try { method.invoke(base, value); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); } } } @Override - public boolean isReadOnly(ELContext context, Object base, - Object property) { + public boolean isReadOnly(ELContext context, Object base, Object property) { Objects.requireNonNull(context); if (base == null || property == null) { return false; @@ -247,11 +238,11 @@ Class beanClass = base.getClass(); String prop = property.toString(); Method readMethod = getReadMethod(beanClass, prop); - return readMethod == null || !(getWriteMethod(beanClass, prop, readMethod.getReturnType()) != null); + return readMethod == null || getWriteMethod(beanClass, prop, readMethod.getReturnType()) == null; } private static Method getReadMethod(Class beanClass, String prop) { - Method methods[] = beanClass.getMethods(); + Method[] methods = beanClass.getMethods(); String isGetter = "is" + capitalize(prop); String getter = "get" + capitalize(prop); for (Method method : methods) { @@ -268,10 +259,10 @@ private static Method getWriteMethod(Class beanClass, String prop, Class valueClass) { String setter = "set" + capitalize(prop); - Method methods[] = beanClass.getMethods(); + Method[] methods = beanClass.getMethods(); for (Method method : methods) { - if (method.getParameterCount() == 1 && setter.equals(method.getName()) - && (valueClass == null || valueClass.isAssignableFrom(method.getParameterTypes()[0]))) { + if (method.getParameterCount() == 1 && setter.equals(method.getName()) && + (valueClass == null || valueClass.isAssignableFrom(method.getParameterTypes()[0]))) { return method; } } @@ -279,23 +270,21 @@ } private static String capitalize(String name) { - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { return name; } - char chars[] = name.toCharArray(); + char[] chars = name.toCharArray(); chars[0] = Character.toUpperCase(chars[0]); return new String(chars); } @Override - public Class getType(ELContext context, Object base, - Object property) { + public Class getType(ELContext context, Object base, Object property) { return null; } @Override - public Iterator getFeatureDescriptors( - ELContext context, Object base) { + public Iterator getFeatureDescriptors(ELContext context, Object base) { return null; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/JspMethodExpression.java tomcat10-10.1.52/java/org/apache/jasper/el/JspMethodExpression.java --- tomcat10-10.1.34/java/org/apache/jasper/el/JspMethodExpression.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/JspMethodExpression.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,7 @@ import jakarta.el.MethodReference; import jakarta.el.PropertyNotFoundException; -public final class JspMethodExpression extends MethodExpression implements - Externalizable { +public final class JspMethodExpression extends MethodExpression implements Externalizable { private String mark; @@ -47,8 +46,7 @@ @Override public MethodInfo getMethodInfo(ELContext context) - throws NullPointerException, PropertyNotFoundException, - MethodNotFoundException, ELException { + throws NullPointerException, PropertyNotFoundException, MethodNotFoundException, ELException { context.notifyBeforeEvaluation(getExpressionString()); try { MethodInfo result = this.target.getMethodInfo(context); @@ -74,8 +72,7 @@ @Override public Object invoke(ELContext context, Object[] params) - throws NullPointerException, PropertyNotFoundException, - MethodNotFoundException, ELException { + throws NullPointerException, PropertyNotFoundException, MethodNotFoundException, ELException { context.notifyBeforeEvaluation(getExpressionString()); try { Object result = this.target.invoke(context, params); @@ -156,8 +153,7 @@ } @Override - public void readExternal(ObjectInput in) throws IOException, - ClassNotFoundException { + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.mark = in.readUTF(); this.target = (MethodExpression) in.readObject(); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/JspPropertyNotFoundException.java tomcat10-10.1.52/java/org/apache/jasper/el/JspPropertyNotFoundException.java --- tomcat10-10.1.34/java/org/apache/jasper/el/JspPropertyNotFoundException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/JspPropertyNotFoundException.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,7 @@ import jakarta.el.PropertyNotFoundException; -public final class JspPropertyNotFoundException extends - PropertyNotFoundException { +public final class JspPropertyNotFoundException extends PropertyNotFoundException { private static final long serialVersionUID = 1L; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/JspPropertyNotWritableException.java tomcat10-10.1.52/java/org/apache/jasper/el/JspPropertyNotWritableException.java --- tomcat10-10.1.34/java/org/apache/jasper/el/JspPropertyNotWritableException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/JspPropertyNotWritableException.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,7 @@ import jakarta.el.PropertyNotWritableException; -public class JspPropertyNotWritableException extends - PropertyNotWritableException { +public class JspPropertyNotWritableException extends PropertyNotWritableException { private static final long serialVersionUID = 1L; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/el/JspValueExpression.java tomcat10-10.1.52/java/org/apache/jasper/el/JspValueExpression.java --- tomcat10-10.1.34/java/org/apache/jasper/el/JspValueExpression.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/el/JspValueExpression.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,12 +28,9 @@ import jakarta.el.ValueExpression; /** - * Wrapper for providing context to ValueExpressions - * - * @author Jacob Hookom + * Wrapper for providing context to ValueExpressions. */ -public final class JspValueExpression extends ValueExpression implements - Externalizable { +public final class JspValueExpression extends ValueExpression implements Externalizable { private ValueExpression target; @@ -54,8 +51,7 @@ } @Override - public Class getType(ELContext context) throws NullPointerException, - PropertyNotFoundException, ELException { + public Class getType(ELContext context) throws NullPointerException, PropertyNotFoundException, ELException { context.notifyBeforeEvaluation(getExpressionString()); try { Class result = this.target.getType(context); @@ -75,8 +71,7 @@ } @Override - public boolean isReadOnly(ELContext context) throws NullPointerException, - PropertyNotFoundException, ELException { + public boolean isReadOnly(ELContext context) throws NullPointerException, PropertyNotFoundException, ELException { context.notifyBeforeEvaluation(getExpressionString()); try { boolean result = this.target.isReadOnly(context); @@ -97,8 +92,7 @@ @Override public void setValue(ELContext context, Object value) - throws NullPointerException, PropertyNotFoundException, - PropertyNotWritableException, ELException { + throws NullPointerException, PropertyNotFoundException, PropertyNotWritableException, ELException { context.notifyBeforeEvaluation(getExpressionString()); try { this.target.setValue(context, value); @@ -122,8 +116,7 @@ } @Override - public T getValue(ELContext context) throws NullPointerException, - PropertyNotFoundException, ELException { + public T getValue(ELContext context) throws NullPointerException, PropertyNotFoundException, ELException { context.notifyBeforeEvaluation(getExpressionString()); try { T result = this.target.getValue(context); @@ -169,8 +162,7 @@ } @Override - public void readExternal(ObjectInput in) throws IOException, - ClassNotFoundException { + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.mark = in.readUTF(); this.target = (ValueExpression) in.readObject(); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/optimizations/ELInterpreterTagSetters.java tomcat10-10.1.52/java/org/apache/jasper/optimizations/ELInterpreterTagSetters.java --- tomcat10-10.1.34/java/org/apache/jasper/optimizations/ELInterpreterTagSetters.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/optimizations/ELInterpreterTagSetters.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,32 +31,32 @@ import org.apache.juli.logging.LogFactory; /** - * A non-specification compliant {@link ELInterpreter} that optimizes a subset - * of setters for tag attributes. + * A non-specification compliant {@link ELInterpreter} that optimizes a subset of setters for tag attributes. *

          * The cases optimized by this implementation are: *

            *
          • expressions that are solely a literal boolean
          • - *
          • expressions that are solely a constant string used (with coercion where - * necessary) with a setter that accepts:
          • - *
            • - *
            • boolean / Boolean
            • - *
            • char / Character
            • - *
            • BigDecimal
            • - *
            • long / Long
            • - *
            • int / Integer
            • - *
            • short / Short
            • - *
            • byte / Byte
            • - *
            • double / Double
            • - *
            • float / Float
            • - *
            • BigInteger
            • - *
            • Enum
            • - *
            • String
            • - *
          • + *
          • expressions that are solely a constant string used (with coercion where necessary) with a setter that + * accepts:
          • + *
          • + *
              + *
            • boolean / Boolean
            • + *
            • char / Character
            • + *
            • BigDecimal
            • + *
            • long / Long
            • + *
            • int / Integer
            • + *
            • short / Short
            • + *
            • byte / Byte
            • + *
            • double / Double
            • + *
            • float / Float
            • + *
            • BigInteger
            • + *
            • Enum
            • + *
            • String
            • + *
            + *
          • *
          - * The specification compliance issue is that it essentially skips the first - * three {@link ELResolver}s listed in section JSP.2.9 and effectively hard - * codes the use of the 4th {@link ELResolver} in that list. + * The specification compliance issue is that it essentially skips the first three {@link ELResolver}s listed in section + * JSP.2.9 and effectively hard codes the use of the 4th {@link ELResolver} in that list. * * @see "https://bz.apache.org/bugzilla/show_bug.cgi?id=64872" */ @@ -70,8 +70,7 @@ private final Pattern PATTERN_NUMERIC = Pattern.compile("[$][{]([\"'])([+-]?\\d+(\\.\\d+)?)\\1[}]"); @Override - public String interpreterCall(JspCompilationContext context, - boolean isTagFile, String expression, + public String interpreterCall(JspCompilationContext context, boolean isTagFile, String expression, Class expectedType, String fnmapvar) { String result = null; @@ -91,7 +90,7 @@ result = "Boolean.FALSE"; } } - // Character + // Character } else if (Character.TYPE == expectedType) { Matcher m = PATTERN_STRING_CONSTANT.matcher(expression); if (m.matches()) { @@ -102,7 +101,7 @@ if (m.matches()) { return "Character.valueOf(\'" + m.group(2).charAt(0) + "\')"; } - // Numeric - BigDecimal + // Numeric - BigDecimal } else if (BigDecimal.class == expectedType) { Matcher m = PATTERN_NUMERIC.matcher(expression); if (m.matches()) { @@ -111,11 +110,13 @@ BigDecimal unused = new BigDecimal(m.group(2)); result = "new java.math.BigDecimal(\"" + m.group(2) + "\")"; } catch (NumberFormatException e) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "BigDecimal"), e); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "BigDecimal"), e); + } // Continue and resolve the value at runtime } } - // Numeric - long/Long + // Numeric - long/Long } else if (Long.TYPE == expectedType || Long.class == expectedType) { Matcher m = PATTERN_NUMERIC.matcher(expression); if (m.matches()) { @@ -129,11 +130,13 @@ result = "Long.valueOf(\"" + m.group(2) + "\")"; } } catch (NumberFormatException e) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Long"), e); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Long"), e); + } // Continue and resolve the value at runtime } } - // Numeric - int/Integer + // Numeric - int/Integer } else if (Integer.TYPE == expectedType || Integer.class == expectedType) { Matcher m = PATTERN_NUMERIC.matcher(expression); if (m.matches()) { @@ -146,11 +149,13 @@ result = "Integer.valueOf(\"" + m.group(2) + "\")"; } } catch (NumberFormatException e) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Integer"), e); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Integer"), e); + } // Continue and resolve the value at runtime } } - // Numeric - short/Short + // Numeric - short/Short } else if (Short.TYPE == expectedType || Short.class == expectedType) { Matcher m = PATTERN_NUMERIC.matcher(expression); if (m.matches()) { @@ -164,11 +169,13 @@ result = "Short.valueOf(\"" + m.group(2) + "\")"; } } catch (NumberFormatException e) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Short"), e); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Short"), e); + } // Continue and resolve the value at runtime } } - // Numeric - byte/Byte + // Numeric - byte/Byte } else if (Byte.TYPE == expectedType || Byte.class == expectedType) { Matcher m = PATTERN_NUMERIC.matcher(expression); if (m.matches()) { @@ -182,11 +189,13 @@ result = "Byte.valueOf(\"" + m.group(2) + "\")"; } } catch (NumberFormatException e) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Byte"), e); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Byte"), e); + } // Continue and resolve the value at runtime } } - // Numeric - double/Double + // Numeric - double/Double } else if (Double.TYPE == expectedType || Double.class == expectedType) { Matcher m = PATTERN_NUMERIC.matcher(expression); if (m.matches()) { @@ -199,11 +208,13 @@ result = "Double.valueOf(\"" + m.group(2) + "\")"; } } catch (NumberFormatException e) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Double"), e); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Double"), e); + } // Continue and resolve the value at runtime } } - // Numeric - float/Float + // Numeric - float/Float } else if (Float.TYPE == expectedType || Float.class == expectedType) { Matcher m = PATTERN_NUMERIC.matcher(expression); if (m.matches()) { @@ -217,11 +228,13 @@ result = "Float.valueOf(\"" + m.group(2) + "\")"; } } catch (NumberFormatException e) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Float"), e); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Float"), e); + } // Continue and resolve the value at runtime } } - // Numeric - BigInteger + // Numeric - BigInteger } else if (BigInteger.class == expectedType) { Matcher m = PATTERN_NUMERIC.matcher(expression); if (m.matches()) { @@ -230,12 +243,14 @@ BigInteger unused = new BigInteger(m.group(2)); result = "new java.math.BigInteger(\"" + m.group(2) + "\")"; } catch (NumberFormatException e) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "BigInteger"), e); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "BigInteger"), e); + } // Continue and resolve the value at runtime } } - // Enum - } else if (expectedType.isEnum()){ + // Enum + } else if (expectedType.isEnum()) { Matcher m = PATTERN_STRING_CONSTANT.matcher(expression); if (m.matches()) { try { @@ -243,11 +258,14 @@ Enum enumValue = Enum.valueOf((Class) expectedType, m.group(2)); result = expectedType.getName() + "." + enumValue.name(); } catch (IllegalArgumentException iae) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), "Enum[" + expectedType.getName() + "]"), iae); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", m.group(2), + "Enum[" + expectedType.getName() + "]"), iae); + } // Continue and resolve the value at runtime } } - // String + // String } else if (String.class == expectedType) { Matcher m = PATTERN_STRING_CONSTANT.matcher(expression); if (m.matches()) { @@ -256,12 +274,12 @@ } if (result == null) { - result = JspUtil.interpreterCall(isTagFile, expression, expectedType, - fnmapvar); + result = JspUtil.interpreterCall(isTagFile, expression, expectedType, fnmapvar); } if (log.isTraceEnabled()) { - log.trace("Expression [" + expression + "], type [" + expectedType.getName() + "], returns [" + result + "]"); + log.trace( + "Expression [" + expression + "], type [" + expectedType.getName() + "], returns [" + result + "]"); } return result; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/optimizations/StringInterpreterEnum.java tomcat10-10.1.52/java/org/apache/jasper/optimizations/StringInterpreterEnum.java --- tomcat10-10.1.34/java/org/apache/jasper/optimizations/StringInterpreterEnum.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/optimizations/StringInterpreterEnum.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,7 +37,9 @@ Enum enumValue = Enum.valueOf((Class) c, s); return c.getName() + "." + enumValue.name(); } catch (IllegalArgumentException iae) { - log.debug(Localizer.getMessage("jsp.error.typeConversion", s, "Enum[" + c.getName() + "]"), iae); + if (log.isDebugEnabled()) { + log.debug(Localizer.getMessage("jsp.error.typeConversion", s, "Enum[" + c.getName() + "]"), iae); + } // Continue and resolve the value at runtime } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/resources/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/jasper/resources/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/jasper/resources/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/resources/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -93,6 +93,7 @@ jsp.error.invalid.implicit.version=[{0}]处标记文件的隐式TLD中定义的JSP版本无效。 jsp.error.invalid.scope=非法的scope属性值:[{0}](必须是page、request、session或application中的一个) jsp.error.invalid.tagdir=标签文件目录 [{0}] 不以"/WEB-INF/tags"开头 +jsp.error.invalid.varscope=无效的变量范围[{0}] jsp.error.invalid.version=为标签 [{0}] 定义了无效的 JSP 版本号 jsp.error.ise_on_clear=当缓存大小等于0时调用clear()函数是非法的 jsp.error.java.line.number=在生成的java文件中的第:[{0}]行发生错误:[{1}] @@ -216,6 +217,7 @@ jsp.error.tag.invalid.trimdirectivewhitespaces=Tag指令:trimDirectiveWhitespaces的值无效 jsp.error.tag.language.nonjava=标记指令:无效的语言属性。 jsp.error.tag.multi.pageencoding=Tag指令不能多次出现pageencoding +jsp.error.tagHandlerPool=无法创建tag处理程序池[{0}] jsp.error.tagdirective.badbodycontent=标签指令中的无效的内容体[{0}] jsp.error.tagfile.badSuffix=在文件路径[{0}]下找不到".tag"的后缀 jsp.error.tagfile.illegalPath=非法的标记文件路径:[{0}],必须以“/WEB-INF/tags”或“/META-INF/tags”开头。 diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/BodyContentImpl.java tomcat10-10.1.52/java/org/apache/jasper/runtime/BodyContentImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/BodyContentImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/BodyContentImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,14 +27,8 @@ import org.apache.jasper.compiler.Localizer; /** - * Write text to a character-output stream, buffering characters so as - * to provide for the efficient writing of single characters, arrays, - * and strings. - * - * Provide support for discarding for the output that has been buffered. - * - * @author Rajiv Mordani - * @author Jan Luehe + * Write text to a character-output stream, buffering characters to provide efficient writing of single characters, + * arrays, and strings. Provide support for discarding the output that has been buffered. */ public class BodyContentImpl extends BodyContent { @@ -52,9 +46,10 @@ /** * Constructor. + * * @param enclosingWriter The wrapped writer - * @param limitBuffer true to discard large buffers - * @param tagBufferSize the buffer size + * @param limitBuffer true to discard large buffers + * @param tagBufferSize the buffer size */ public BodyContentImpl(JspWriter enclosingWriter, boolean limitBuffer, int tagBufferSize) { super(enclosingWriter); @@ -73,7 +68,7 @@ } else { ensureOpen(); if (nextChar >= bufferSize) { - reAllocBuff (1); + reAllocBuff(1); } cb[nextChar++] = (char) c; } @@ -86,19 +81,18 @@ } else { ensureOpen(); - if ((off < 0) || (off > cbuf.length) || (len < 0) || - ((off + len) > cbuf.length) || ((off + len) < 0)) { + if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } if (len >= bufferSize - nextChar) { - reAllocBuff (len); + reAllocBuff(len); } System.arraycopy(cbuf, off, cb, nextChar, len); - nextChar+=len; + nextChar += len; } } @@ -264,13 +258,13 @@ } @Override - public void println(double x) throws IOException{ + public void println(double x) throws IOException { print(x); println(); } @Override - public void println(char x[]) throws IOException { + public void println(char[] x) throws IOException { print(x); println(); } @@ -327,12 +321,12 @@ @Override public int getRemaining() { - return (writer == null) ? bufferSize-nextChar : 0; + return (writer == null) ? bufferSize - nextChar : 0; } @Override public Reader getReader() { - return (writer == null) ? new CharArrayReader (cb, 0, nextChar) : null; + return (writer == null) ? new CharArrayReader(cb, 0, nextChar) : null; } @Override @@ -344,7 +338,7 @@ public void writeOut(Writer out) throws IOException { if (writer == null) { out.write(cb, 0, nextChar); - // Flush not called as the writer passed could be a BodyContent and + // Flush not called as the writer passed could be a BodyContent, and // it doesn't allow to flush. } } @@ -361,13 +355,11 @@ } /** - * This method shall "reset" the internal state of a BodyContentImpl, - * releasing all internal references, and preparing it for potential - * reuse by a later invocation of {@link PageContextImpl#pushBody(Writer)}. - * - *

          Note, that BodyContentImpl instances are usually owned by a - * PageContextImpl instance, and PageContextImpl instances are recycled - * and reused. + * This method shall "reset" the internal state of a BodyContentImpl, releasing all internal references, and + * preparing it for potential reuse by a later invocation of {@link PageContextImpl#pushBody(Writer)}. + *

          + * Note, that BodyContentImpl instances are usually owned by a PageContextImpl instance, and PageContextImpl + * instances are recycled and reused. * * @see PageContextImpl#release() */ @@ -375,7 +367,7 @@ this.writer = null; try { this.clear(); - } catch (IOException ex) { + } catch (IOException ignore) { // ignore } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/ExceptionUtils.java tomcat10-10.1.52/java/org/apache/jasper/runtime/ExceptionUtils.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/ExceptionUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/ExceptionUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,8 @@ public class ExceptionUtils { /** - * Checks whether the supplied Throwable is one that needs to be - * rethrown and swallows all others. + * Checks whether the supplied Throwable is one that needs to be rethrown and swallows all others. + * * @param t the Throwable to check */ public static void handleThrowable(Throwable t) { @@ -44,11 +44,11 @@ } /** - * Checks whether the supplied Throwable is an instance of - * InvocationTargetException and returns the throwable that is - * wrapped by it, if there is any. + * Checks whether the supplied Throwable is an instance of InvocationTargetException and returns the + * throwable that is wrapped by it, if there is any. * * @param t the Throwable to check + * * @return t or t.getCause() */ public static Throwable unwrapInvocationTargetException(Throwable t) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/HttpJspBase.java tomcat10-10.1.52/java/org/apache/jasper/runtime/HttpJspBase.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/HttpJspBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/HttpJspBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,8 +30,6 @@ /** * This is the super class of all JSP-generated servlets. - * - * @author Anil K. Vijendran */ public abstract class HttpJspBase extends HttpServlet implements HttpJspPage { @@ -41,9 +39,7 @@ } @Override - public final void init(ServletConfig config) - throws ServletException - { + public final void init(ServletConfig config) throws ServletException { super.init(config); jspInit(); _jspInit(); @@ -62,8 +58,7 @@ @Override public final void service(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException - { + throws ServletException, IOException { _jspService(request, response); } @@ -82,7 +77,6 @@ } @Override - public abstract void _jspService(HttpServletRequest request, - HttpServletResponse response) - throws ServletException, IOException; + public abstract void _jspService(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/JspApplicationContextImpl.java tomcat10-10.1.52/java/org/apache/jasper/runtime/JspApplicationContextImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/JspApplicationContextImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/JspApplicationContextImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.List; -import jakarta.el.CompositeELResolver; import jakarta.el.ELContext; import jakarta.el.ELContextEvent; import jakarta.el.ELContextListener; @@ -37,16 +36,13 @@ import org.apache.jasper.el.JasperELResolver; /** - * Implementation of JspApplicationContext - * - * @author Jacob Hookom + * Implementation of JspApplicationContext. */ public class JspApplicationContextImpl implements JspApplicationContext { private static final String KEY = JspApplicationContextImpl.class.getName(); - private final ExpressionFactory expressionFactory = - ExpressionFactory.newInstance(); + private final ExpressionFactory expressionFactory = ExpressionFactory.newInstance(); private final List contextListeners = new ArrayList<>(); @@ -72,8 +68,7 @@ if (context == null) { throw new IllegalArgumentException(Localizer.getMessage("jsp.error.nullArgument")); } - JspApplicationContextImpl impl = (JspApplicationContextImpl) context - .getAttribute(KEY); + JspApplicationContextImpl impl = (JspApplicationContextImpl) context.getAttribute(KEY); if (impl == null) { impl = new JspApplicationContextImpl(); context.setAttribute(KEY, impl); @@ -112,9 +107,7 @@ private ELResolver createELResolver() { this.instantiated = true; if (this.resolver == null) { - CompositeELResolver r = new JasperELResolver(this.resolvers, - expressionFactory.getStreamELResolver()); - this.resolver = r; + this.resolver = new JasperELResolver(this.resolvers, expressionFactory.getStreamELResolver()); } return this.resolver; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/JspContextWrapper.java tomcat10-10.1.52/java/org/apache/jasper/runtime/JspContextWrapper.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/JspContextWrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/JspContextWrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -57,16 +57,9 @@ import org.apache.jasper.compiler.Localizer; /** - * Implementation of a JSP Context Wrapper. - * - * The JSP Context Wrapper is a JspContext created and maintained by a tag - * handler implementation. It wraps the Invoking JSP Context, that is, the - * JspContext instance passed to the tag handler by the invoking page via - * setJspContext(). - * - * @author Kin-man Chung - * @author Jan Luehe - * @author Jacob Hookom + * Implementation of a JSP Context Wrapper. The JSP Context Wrapper is a JspContext created and maintained by a tag + * handler implementation. It wraps the Invoking JSP Context, that is, the JspContext instance passed to the tag handler + * by the invoking page via setJspContext(). */ @SuppressWarnings("deprecation") // Have to support old JSP EL API public class JspContextWrapper extends PageContext implements VariableResolver { @@ -76,7 +69,7 @@ // Invoking JSP context private final PageContext invokingJspCtxt; - private final transient HashMap pageAttributes; + private final transient HashMap pageAttributes; // ArrayList of NESTED scripting variables private final ArrayList nestedVars; @@ -89,7 +82,7 @@ private final Map aliases; - private final HashMap originalNestedVars; + private final HashMap originalNestedVars; private ServletContext servletContext = null; @@ -97,13 +90,12 @@ private final PageContext rootJspCtxt; - public JspContextWrapper(JspTag jspTag, JspContext jspContext, - ArrayList nestedVars, ArrayList atBeginVars, - ArrayList atEndVars, Map aliases) { + public JspContextWrapper(JspTag jspTag, JspContext jspContext, ArrayList nestedVars, + ArrayList atBeginVars, ArrayList atEndVars, Map aliases) { this.jspTag = jspTag; this.invokingJspCtxt = (PageContext) jspContext; if (jspContext instanceof JspContextWrapper) { - rootJspCtxt = ((JspContextWrapper)jspContext).rootJspCtxt; + rootJspCtxt = ((JspContextWrapper) jspContext).rootJspCtxt; } else { rootJspCtxt = invokingJspCtxt; } @@ -122,8 +114,7 @@ } @Override - public void initialize(Servlet servlet, ServletRequest request, - ServletResponse response, String errorPageURL, + public void initialize(Servlet servlet, ServletRequest request, ServletResponse response, String errorPageURL, boolean needsSession, int bufferSize, boolean autoFlush) throws IOException, IllegalStateException, IllegalArgumentException { } @@ -132,8 +123,7 @@ public Object getAttribute(String name) { if (name == null) { - throw new NullPointerException(Localizer - .getMessage("jsp.error.attribute.null_name")); + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); } return pageAttributes.get(name); @@ -143,8 +133,7 @@ public Object getAttribute(String name, int scope) { if (name == null) { - throw new NullPointerException(Localizer - .getMessage("jsp.error.attribute.null_name")); + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); } if (scope == PAGE_SCOPE) { @@ -158,8 +147,7 @@ public void setAttribute(String name, Object value) { if (name == null) { - throw new NullPointerException(Localizer - .getMessage("jsp.error.attribute.null_name")); + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); } if (value != null) { @@ -173,8 +161,7 @@ public void setAttribute(String name, Object value, int scope) { if (name == null) { - throw new NullPointerException(Localizer - .getMessage("jsp.error.attribute.null_name")); + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); } if (scope == PAGE_SCOPE) { @@ -192,8 +179,7 @@ public Object findAttribute(String name) { if (name == null) { - throw new NullPointerException(Localizer - .getMessage("jsp.error.attribute.null_name")); + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); } Object o = pageAttributes.get(name); @@ -203,7 +189,7 @@ if (getSession() != null) { try { o = rootJspCtxt.getAttribute(name, SESSION_SCOPE); - } catch (IllegalStateException ise) { + } catch (IllegalStateException ignore) { // Session has been invalidated. // Ignore and fall through to application scope. } @@ -221,8 +207,7 @@ public void removeAttribute(String name) { if (name == null) { - throw new NullPointerException(Localizer - .getMessage("jsp.error.attribute.null_name")); + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); } pageAttributes.remove(name); @@ -237,8 +222,7 @@ public void removeAttribute(String name, int scope) { if (name == null) { - throw new NullPointerException(Localizer - .getMessage("jsp.error.attribute.null_name")); + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); } if (scope == PAGE_SCOPE) { @@ -252,8 +236,7 @@ public int getAttributesScope(String name) { if (name == null) { - throw new NullPointerException(Localizer - .getMessage("jsp.error.attribute.null_name")); + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); } if (pageAttributes.get(name) != null) { @@ -321,20 +304,17 @@ } @Override - public void forward(String relativeUrlPath) throws ServletException, - IOException { + public void forward(String relativeUrlPath) throws ServletException, IOException { invokingJspCtxt.forward(relativeUrlPath); } @Override - public void include(String relativeUrlPath) throws ServletException, - IOException { + public void include(String relativeUrlPath) throws ServletException, IOException { invokingJspCtxt.include(relativeUrlPath); } @Override - public void include(String relativeUrlPath, boolean flush) - throws ServletException, IOException { + public void include(String relativeUrlPath, boolean flush) throws ServletException, IOException { invokingJspCtxt.include(relativeUrlPath, false); } @@ -366,16 +346,14 @@ } @Override - public void handlePageException(Exception ex) throws IOException, - ServletException { + public void handlePageException(Exception ex) throws IOException, ServletException { // Should never be called since handleException() called with a // Throwable in the generated servlet. handlePageException((Throwable) ex); } @Override - public void handlePageException(Throwable t) throws IOException, - ServletException { + public void handlePageException(Throwable t) throws IOException, ServletException { invokingJspCtxt.handlePageException(t); } @@ -414,31 +392,30 @@ } /** - * Copies the variables of the given scope from the virtual page scope of - * this JSP context wrapper to the page scope of the invoking JSP context. + * Copies the variables of the given scope from the virtual page scope of this JSP context wrapper to the page scope + * of the invoking JSP context. * - * @param scope - * variable scope (one of NESTED, AT_BEGIN, or AT_END) + * @param scope variable scope (one of NESTED, AT_BEGIN, or AT_END) */ private void copyTagToPageScope(int scope) { Iterator iter = null; switch (scope) { - case VariableInfo.NESTED: - if (nestedVars != null) { - iter = nestedVars.iterator(); - } - break; - case VariableInfo.AT_BEGIN: - if (atBeginVars != null) { - iter = atBeginVars.iterator(); - } - break; - case VariableInfo.AT_END: - if (atEndVars != null) { - iter = atEndVars.iterator(); - } - break; + case VariableInfo.NESTED: + if (nestedVars != null) { + iter = nestedVars.iterator(); + } + break; + case VariableInfo.AT_BEGIN: + if (atBeginVars != null) { + iter = atBeginVars.iterator(); + } + break; + case VariableInfo.AT_END: + if (atEndVars != null) { + iter = atEndVars.iterator(); + } + break; } while ((iter != null) && iter.hasNext()) { @@ -454,8 +431,8 @@ } /** - * Saves the values of any NESTED variables that are present in the invoking - * JSP context, so they can later be restored. + * Saves the values of any NESTED variables that are present in the invoking JSP context, so they can later be + * restored. */ private void saveNestedVariables() { if (nestedVars != null) { @@ -487,13 +464,12 @@ } /** - * Checks to see if the given variable name is used as an alias, and if so, - * returns the variable name for which it is used as an alias. + * Checks to see if the given variable name is used as an alias, and if so, returns the variable name for which it + * is used as an alias. + * + * @param varName The variable name to check * - * @param varName - * The variable name to check - * @return The variable name for which varName is used as an alias, or - * varName if it is not being used as an alias + * @return The variable name for which varName is used as an alias, or varName if it is not being used as an alias */ private String findAlias(String varName) { @@ -646,7 +622,7 @@ } @Override - public void enterLambdaScope(Map arguments) { + public void enterLambdaScope(Map arguments) { wrapped.enterLambdaScope(arguments); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/JspFactoryImpl.java tomcat10-10.1.52/java/org/apache/jasper/runtime/JspFactoryImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/JspFactoryImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/JspFactoryImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,8 +33,6 @@ /** * Implementation of JspFactory. - * - * @author Anil K. Vijendran */ public class JspFactoryImpl extends JspFactory { @@ -42,30 +40,26 @@ private int poolSize = -1; @Override - public PageContext getPageContext(Servlet servlet, ServletRequest request, - ServletResponse response, String errorPageURL, boolean needsSession, - int bufferSize, boolean autoflush) { - - if( Constants.IS_SECURITY_ENABLED ) { - PrivilegedGetPageContext dp = new PrivilegedGetPageContext( - this, servlet, request, response, errorPageURL, + public PageContext getPageContext(Servlet servlet, ServletRequest request, ServletResponse response, + String errorPageURL, boolean needsSession, int bufferSize, boolean autoflush) { + + if (Constants.IS_SECURITY_ENABLED) { + PrivilegedGetPageContext dp = new PrivilegedGetPageContext(this, servlet, request, response, errorPageURL, needsSession, bufferSize, autoflush); return AccessController.doPrivileged(dp); } else { - return internalGetPageContext(servlet, request, response, - errorPageURL, needsSession, - bufferSize, autoflush); + return internalGetPageContext(servlet, request, response, errorPageURL, needsSession, bufferSize, + autoflush); } } @Override public void releasePageContext(PageContext pc) { - if( pc == null ) { + if (pc == null) { return; } - if( Constants.IS_SECURITY_ENABLED ) { - PrivilegedReleasePageContext dp = new PrivilegedReleasePageContext( - this,pc); + if (Constants.IS_SECURITY_ENABLED) { + PrivilegedReleasePageContext dp = new PrivilegedReleasePageContext(this, pc); AccessController.doPrivileged(dp); } else { internalReleasePageContext(pc); @@ -86,9 +80,8 @@ this.poolSize = poolSize; } - private PageContext internalGetPageContext(Servlet servlet, ServletRequest request, - ServletResponse response, String errorPageURL, boolean needsSession, - int bufferSize, boolean autoflush) { + private PageContext internalGetPageContext(Servlet servlet, ServletRequest request, ServletResponse response, + String errorPageURL, boolean needsSession, int bufferSize, boolean autoflush) { PageContext pc; if (poolSize > 0) { @@ -106,9 +99,8 @@ } try { - pc.initialize(servlet, request, response, errorPageURL, - needsSession, bufferSize, autoflush); - } catch (IOException ioe) { + pc.initialize(servlet, request, response, errorPageURL, needsSession, bufferSize, autoflush); + } catch (IOException ignore) { // Implementation never throws IOE but can't change the signature // since it is part of the JSP API } @@ -123,8 +115,7 @@ } } - private static class PrivilegedGetPageContext - implements PrivilegedAction { + private static class PrivilegedGetPageContext implements PrivilegedAction { private JspFactoryImpl factory; private Servlet servlet; @@ -135,9 +126,9 @@ private int bufferSize; private boolean autoflush; - PrivilegedGetPageContext(JspFactoryImpl factory, Servlet servlet, - ServletRequest request, ServletResponse response, String errorPageURL, - boolean needsSession, int bufferSize, boolean autoflush) { + PrivilegedGetPageContext(JspFactoryImpl factory, Servlet servlet, ServletRequest request, + ServletResponse response, String errorPageURL, boolean needsSession, int bufferSize, + boolean autoflush) { this.factory = factory; this.servlet = servlet; this.request = request; @@ -150,19 +141,17 @@ @Override public PageContext run() { - return factory.internalGetPageContext(servlet, request, response, - errorPageURL, needsSession, bufferSize, autoflush); + return factory.internalGetPageContext(servlet, request, response, errorPageURL, needsSession, bufferSize, + autoflush); } } - private static class PrivilegedReleasePageContext - implements PrivilegedAction { + private static class PrivilegedReleasePageContext implements PrivilegedAction { private JspFactoryImpl factory; private PageContext pageContext; - PrivilegedReleasePageContext(JspFactoryImpl factory, - PageContext pageContext) { + PrivilegedReleasePageContext(JspFactoryImpl factory, PageContext pageContext) { this.factory = factory; this.pageContext = pageContext; } @@ -174,7 +163,7 @@ } } - private static final class PageContextPool { + private static final class PageContextPool { private final PageContext[] pool; @@ -203,8 +192,7 @@ } @Override - public JspApplicationContext getJspApplicationContext( - final ServletContext context) { + public JspApplicationContext getJspApplicationContext(final ServletContext context) { if (Constants.IS_SECURITY_ENABLED) { return AccessController.doPrivileged( (PrivilegedAction) () -> JspApplicationContextImpl.getInstance(context)); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/JspFragmentHelper.java tomcat10-10.1.52/java/org/apache/jasper/runtime/JspFragmentHelper.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/JspFragmentHelper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/JspFragmentHelper.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,15 +22,11 @@ import jakarta.servlet.jsp.tagext.JspTag; /** - * Helper class from which all Jsp Fragment helper classes extend. - * This class allows for the emulation of numerous fragments within - * a single class, which in turn reduces the load on the class loader - * since there are potentially many JspFragments in a single page. + * Helper class from which all Jsp Fragment helper classes extend. This class allows for the emulation of numerous + * fragments within a single class, which in turn reduces the load on the class loader since there are potentially many + * JspFragments in a single page. *

          - * The class also provides various utility methods for JspFragment - * implementations. - * - * @author Mark Roth + * The class also provides various utility methods for JspFragment implementations. */ public abstract class JspFragmentHelper extends JspFragment { @@ -39,13 +35,11 @@ protected final PageContext _jspx_page_context; protected final JspTag parentTag; - public JspFragmentHelper( int discriminator, JspContext jspContext, - JspTag parentTag ) - { + public JspFragmentHelper(int discriminator, JspContext jspContext, JspTag parentTag) { this.discriminator = discriminator; this.jspContext = jspContext; - if(jspContext instanceof PageContext) { - _jspx_page_context = (PageContext)jspContext; + if (jspContext instanceof PageContext) { + _jspx_page_context = (PageContext) jspContext; } else { _jspx_page_context = null; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/JspRuntimeLibrary.java tomcat10-10.1.52/java/org/apache/jasper/runtime/JspRuntimeLibrary.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/JspRuntimeLibrary.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/JspRuntimeLibrary.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,12 +18,14 @@ import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStreamWriter; import java.lang.reflect.Method; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Enumeration; +import jakarta.el.VariableMapper; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; @@ -43,16 +45,7 @@ import org.apache.tomcat.InstanceManager; /** - * Bunch of util methods that are used by code generated for useBean, - * getProperty and setProperty. - * - * The __begin, __end stuff is there so that the JSP engine can - * actually parse this file and inline them if people don't want - * runtime dependencies on this class. However, I'm not sure if that - * works so well right now. It got forgotten at some point. -akv - * - * @author Mandar Raje - * @author Shawn Bayern + * Bunch of util methods that are used by code generated for useBean, getProperty and setProperty. */ public class JspRuntimeLibrary { @@ -73,28 +66,24 @@ } /** - * Returns the value of the jakarta.servlet.error.exception request - * attribute value, if present, otherwise the value of the - * jakarta.servlet.jsp.jspException request attribute value. + * Returns the value of the jakarta.servlet.error.exception request attribute value, if present, otherwise the value + * of the jakarta.servlet.jsp.jspException request attribute value. This method is called at the beginning of the + * generated servlet code for a JSP error page, when the "exception" implicit scripting language variable is + * initialized. * - * This method is called at the beginning of the generated servlet code - * for a JSP error page, when the "exception" implicit scripting language - * variable is initialized. * @param request The Servlet request + * * @return the throwable in the error attribute if any */ public static Throwable getThrowable(ServletRequest request) { - Throwable error = (Throwable) request.getAttribute( - RequestDispatcher.ERROR_EXCEPTION); + Throwable error = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); if (error == null) { error = (Throwable) request.getAttribute(PageContext.EXCEPTION); if (error != null) { /* - * The only place that sets JSP_EXCEPTION is - * PageContextImpl.handlePageException(). It really should set - * SERVLET_EXCEPTION, but that would interfere with the - * ErrorReportValve. Therefore, if JSP_EXCEPTION is set, we - * need to set SERVLET_EXCEPTION. + * The only place that sets JSP_EXCEPTION is PageContextImpl.handlePageException(). It really should set + * SERVLET_EXCEPTION, but that would interfere with the ErrorReportValve. Therefore, if JSP_EXCEPTION is + * set, we need to set SERVLET_EXCEPTION. */ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, error); } @@ -104,7 +93,7 @@ } public static boolean coerceToBoolean(String s) { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return false; } else { return Boolean.parseBoolean(s); @@ -112,7 +101,7 @@ } public static byte coerceToByte(String s) { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return (byte) 0; } else { return Byte.parseByte(s); @@ -120,7 +109,7 @@ } public static char coerceToChar(String s) { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return (char) 0; } else { return s.charAt(0); @@ -128,7 +117,7 @@ } public static double coerceToDouble(String s) { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return 0; } else { return Double.parseDouble(s); @@ -136,7 +125,7 @@ } public static float coerceToFloat(String s) { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return 0; } else { return Float.parseFloat(s); @@ -144,7 +133,7 @@ } public static int coerceToInt(String s) { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return 0; } else { return Integer.parseInt(s); @@ -152,7 +141,7 @@ } public static short coerceToShort(String s) { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return (short) 0; } else { return Short.parseShort(s); @@ -160,7 +149,7 @@ } public static long coerceToLong(String s) { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return 0; } else { return Long.parseLong(s); @@ -169,7 +158,7 @@ public static Object coerce(String s, Class target) { - boolean isNullOrEmpty = (s == null || s.length() == 0); + boolean isNullOrEmpty = (s == null || s.isEmpty()); if (target == Boolean.class) { if (isNullOrEmpty) { @@ -225,11 +214,9 @@ } } - // __begin convertMethod - public static Object convert(String propertyName, String s, Class t, - Class propertyEditorClass) - throws JasperException - { + + public static Object convert(String propertyName, String s, Class t, Class propertyEditorClass) + throws JasperException { try { if (s == null) { if (t.equals(Boolean.class) || t.equals(Boolean.TYPE)) { @@ -239,84 +226,76 @@ } } if (propertyEditorClass != null) { - return getValueFromBeanInfoPropertyEditor( - t, propertyName, s, propertyEditorClass); + return getValueFromBeanInfoPropertyEditor(t, propertyName, s, propertyEditorClass); } else if (t.equals(Boolean.class) || t.equals(Boolean.TYPE)) { return Boolean.valueOf(s); } else if (t.equals(Byte.class) || t.equals(Byte.TYPE)) { - if (s.length() == 0) { - return Byte.valueOf((byte)0); + if (s.isEmpty()) { + return Byte.valueOf((byte) 0); } else { return Byte.valueOf(s); } } else if (t.equals(Character.class) || t.equals(Character.TYPE)) { - if (s.length() == 0) { + if (s.isEmpty()) { return Character.valueOf((char) 0); } else { return Character.valueOf(s.charAt(0)); } } else if (t.equals(Double.class) || t.equals(Double.TYPE)) { - if (s.length() == 0) { + if (s.isEmpty()) { return Double.valueOf(0); } else { return Double.valueOf(s); } } else if (t.equals(Integer.class) || t.equals(Integer.TYPE)) { - if (s.length() == 0) { + if (s.isEmpty()) { return Integer.valueOf(0); } else { return Integer.valueOf(s); } } else if (t.equals(Float.class) || t.equals(Float.TYPE)) { - if (s.length() == 0) { + if (s.isEmpty()) { return Float.valueOf(0); } else { return Float.valueOf(s); } } else if (t.equals(Long.class) || t.equals(Long.TYPE)) { - if (s.length() == 0) { + if (s.isEmpty()) { return Long.valueOf(0); } else { return Long.valueOf(s); } } else if (t.equals(Short.class) || t.equals(Short.TYPE)) { - if (s.length() == 0) { + if (s.isEmpty()) { return Short.valueOf((short) 0); } else { return Short.valueOf(s); } - } else if ( t.equals(String.class) ) { + } else if (t.equals(String.class)) { return s; } else if (t.getName().equals("java.lang.Object")) { return new String(s); } else { - return getValueFromPropertyEditorManager( - t, propertyName, s); + return getValueFromPropertyEditorManager(t, propertyName, s); } - } catch (Exception ex) { - throw new JasperException(ex); + } catch (Exception e) { + throw new JasperException(e); } } - // __end convertMethod - // __begin introspectMethod - public static void introspect(Object bean, ServletRequest request) - throws JasperException - { + + public static void introspect(Object bean, ServletRequest request) throws JasperException { Enumeration e = request.getParameterNames(); - while ( e.hasMoreElements() ) { - String name = e.nextElement(); + while (e.hasMoreElements()) { + String name = e.nextElement(); String value = request.getParameter(name); introspecthelper(bean, name, value, request, name, true); } } - // __end introspectMethod - // __begin introspecthelperMethod - public static void introspecthelper(Object bean, String prop, - String value, ServletRequest request, - String param, boolean ignoreMethodNF) - throws JasperException { + + public static void introspecthelper(Object bean, String prop, String value, ServletRequest request, String param, + boolean ignoreMethodNF) throws JasperException { Method method = null; Class type = null; Class propertyEditorClass = null; @@ -327,11 +306,9 @@ type = method.getParameterTypes()[0]; } } else { - java.beans.BeanInfo info - = java.beans.Introspector.getBeanInfo(bean.getClass()); - if ( info != null ) { - java.beans.PropertyDescriptor pd[] - = info.getPropertyDescriptors(); + java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean.getClass()); + if (info != null) { + java.beans.PropertyDescriptor[] pd = info.getPropertyDescriptors(); for (java.beans.PropertyDescriptor propertyDescriptor : pd) { if (propertyDescriptor.getName().equals(prop)) { method = propertyDescriptor.getWriteMethod(); @@ -345,57 +322,49 @@ if (method != null && type != null) { if (type.isArray()) { if (request == null) { - throw new JasperException( - Localizer.getMessage("jsp.error.beans.setproperty.noindexset")); + throw new JasperException(Localizer.getMessage("jsp.error.beans.setproperty.noindexset")); } Class t = type.getComponentType(); String[] values = request.getParameterValues(param); - //XXX Please check. - if(values == null) { + // XXX Please check. + if (values == null) { return; } - if(t.equals(String.class)) { + if (t.equals(String.class)) { method.invoke(bean, new Object[] { values }); } else { - createTypedArray (prop, bean, method, values, t, - propertyEditorClass); + createTypedArray(prop, bean, method, values, t, propertyEditorClass); } } else { - if(value == null || (param != null && value.equals(""))) { + if (value == null || (param != null && value.isEmpty())) { return; } Object oval = convert(prop, value, type, propertyEditorClass); - if ( oval != null ) { - method.invoke(bean, new Object[] { oval }); + if (oval != null) { + method.invoke(bean, oval); } } } - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } if (!ignoreMethodNF && (method == null)) { if (type == null) { throw new JasperException( - Localizer.getMessage("jsp.error.beans.noproperty", - prop, - bean.getClass().getName())); + Localizer.getMessage("jsp.error.beans.noproperty", prop, bean.getClass().getName())); } else { - throw new JasperException( - Localizer.getMessage("jsp.error.beans.nomethod.setproperty", - prop, - type.getName(), - bean.getClass().getName())); + throw new JasperException(Localizer.getMessage("jsp.error.beans.nomethod.setproperty", prop, + type.getName(), bean.getClass().getName())); } } } - // __end introspecthelperMethod - //------------------------------------------------------------------- + + // ------------------------------------------------------------------- // functions to convert builtin Java data types to string. - //------------------------------------------------------------------- - // __begin toStringMethod + // ------------------------------------------------------------------- public static String toString(Object o) { return String.valueOf(o); } @@ -431,152 +400,146 @@ public static String toString(char c) { return Character.toString(c); } - // __end toStringMethod /** - * Create a typed array. - * This is a special case where params are passed through - * the request and the property is indexed. - * @param propertyName The property name - * @param bean The bean - * @param method The method - * @param values Array values - * @param t The class + * Create a typed array. This is a special case where params are passed through the request and the property is + * indexed. + * + * @param propertyName The property name + * @param bean The bean + * @param method The method + * @param values Array values + * @param t The class * @param propertyEditorClass The editor for the property + * * @throws JasperException An error occurred */ - public static void createTypedArray(String propertyName, - Object bean, - Method method, - String[] values, - Class t, - Class propertyEditorClass) - throws JasperException { + public static void createTypedArray(String propertyName, Object bean, Method method, String[] values, Class t, + Class propertyEditorClass) throws JasperException { try { if (propertyEditorClass != null) { Object[] tmpval = new Integer[values.length]; - for (int i=0; i with EL expression for 'value' attribute - public static void handleSetPropertyExpression(Object bean, - String prop, String expression, PageContext pageContext, - ProtectedFunctionMapper functionMapper ) - throws JasperException - { + public static void handleSetPropertyExpression(Object bean, String prop, String expression, PageContext pageContext, + ProtectedFunctionMapper functionMapper) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { - PageContextImpl.proprietaryEvaluate( - expression, - method.getParameterTypes()[0], - pageContext, - functionMapper) - }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, PageContextImpl.proprietaryEvaluate(expression, method.getParameterTypes()[0], + pageContext, functionMapper)); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } - public static void handleSetProperty(Object bean, String prop, - Object value) - throws JasperException - { + public static void handleSetProperty(Object bean, String prop, Object value) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { value }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, value); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } - public static void handleSetProperty(Object bean, String prop, - int value) - throws JasperException - { + public static void handleSetProperty(Object bean, String prop, int value) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { Integer.valueOf(value) }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, Integer.valueOf(value)); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } - public static void handleSetProperty(Object bean, String prop, - short value) - throws JasperException - { + public static void handleSetProperty(Object bean, String prop, short value) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { Short.valueOf(value) }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, Short.valueOf(value)); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } - public static void handleSetProperty(Object bean, String prop, - long value) - throws JasperException - { + public static void handleSetProperty(Object bean, String prop, long value) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { Long.valueOf(value) }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, Long.valueOf(value)); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } - public static void handleSetProperty(Object bean, String prop, - double value) - throws JasperException - { + public static void handleSetProperty(Object bean, String prop, double value) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { Double.valueOf(value) }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, Double.valueOf(value)); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } - public static void handleSetProperty(Object bean, String prop, - float value) - throws JasperException - { + public static void handleSetProperty(Object bean, String prop, float value) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { Float.valueOf(value) }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, Float.valueOf(value)); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } - public static void handleSetProperty(Object bean, String prop, - char value) - throws JasperException - { + public static void handleSetProperty(Object bean, String prop, char value) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { Character.valueOf(value) }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, Character.valueOf(value)); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } - public static void handleSetProperty(Object bean, String prop, - byte value) - throws JasperException - { + public static void handleSetProperty(Object bean, String prop, byte value) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { Byte.valueOf(value) }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, Byte.valueOf(value)); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } - public static void handleSetProperty(Object bean, String prop, - boolean value) - throws JasperException - { + public static void handleSetProperty(Object bean, String prop, boolean value) throws JasperException { try { Method method = getWriteMethod(bean.getClass(), prop); - method.invoke(bean, new Object[] { Boolean.valueOf(value) }); - } catch (Exception ex) { - Throwable thr = ExceptionUtils.unwrapInvocationTargetException(ex); + method.invoke(bean, Boolean.valueOf(value)); + } catch (Exception e) { + Throwable thr = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(thr); - throw new JasperException(ex); + throw new JasperException(e); } } /** * Reverse of Introspector.decapitalize. + * * @param name The name + * * @return the capitalized string */ public static String capitalize(String name) { - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { return name; } - char chars[] = name.toCharArray(); + char[] chars = name.toCharArray(); chars[0] = Character.toUpperCase(chars[0]); return new String(chars); } - public static Method getWriteMethod(Class beanClass, String prop) - throws JasperException { + public static Method getWriteMethod(Class beanClass, String prop) throws JasperException { Method result = null; Class type = null; if (GRAAL) { String setter = "set" + capitalize(prop); - Method methods[] = beanClass.getMethods(); + Method[] methods = beanClass.getMethods(); for (Method method : methods) { if (setter.equals(method.getName())) { return method; @@ -796,7 +722,7 @@ } else { try { java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(beanClass); - java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors(); + java.beans.PropertyDescriptor[] pd = info.getPropertyDescriptors(); for (java.beans.PropertyDescriptor propertyDescriptor : pd) { if (propertyDescriptor.getName().equals(prop)) { result = propertyDescriptor.getWriteMethod(); @@ -804,30 +730,28 @@ break; } } - } catch (Exception ex) { - throw new JasperException (ex); + } catch (Exception e) { + throw new JasperException(e); } } if (result == null) { if (type == null) { - throw new JasperException(Localizer.getMessage( - "jsp.error.beans.noproperty", prop, beanClass.getName())); + throw new JasperException( + Localizer.getMessage("jsp.error.beans.noproperty", prop, beanClass.getName())); } else { - throw new JasperException(Localizer.getMessage( - "jsp.error.beans.nomethod.setproperty", - prop, type.getName(), beanClass.getName())); + throw new JasperException(Localizer.getMessage("jsp.error.beans.nomethod.setproperty", prop, + type.getName(), beanClass.getName())); } } return result; } - public static Method getReadMethod(Class beanClass, String prop) - throws JasperException { + public static Method getReadMethod(Class beanClass, String prop) throws JasperException { Method result = null; Class type = null; if (GRAAL) { String setter = "get" + capitalize(prop); - Method methods[] = beanClass.getMethods(); + Method[] methods = beanClass.getMethods(); for (Method method : methods) { if (setter.equals(method.getName())) { return method; @@ -836,7 +760,7 @@ } else { try { java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(beanClass); - java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors(); + java.beans.PropertyDescriptor[] pd = info.getPropertyDescriptors(); for (java.beans.PropertyDescriptor propertyDescriptor : pd) { if (propertyDescriptor.getName().equals(prop)) { result = propertyDescriptor.getReadMethod(); @@ -844,70 +768,59 @@ break; } } - } catch (Exception ex) { - throw new JasperException (ex); + } catch (Exception e) { + throw new JasperException(e); } } if (result == null) { if (type == null) { - throw new JasperException(Localizer.getMessage( - "jsp.error.beans.noproperty", prop, beanClass.getName())); + throw new JasperException( + Localizer.getMessage("jsp.error.beans.noproperty", prop, beanClass.getName())); } else { - throw new JasperException(Localizer.getMessage( - "jsp.error.beans.nomethod", prop, beanClass.getName())); + throw new JasperException(Localizer.getMessage("jsp.error.beans.nomethod", prop, beanClass.getName())); } } return result; } - //********************************************************************* + // ********************************************************************* // PropertyEditor Support - public static Object getValueFromBeanInfoPropertyEditor( - Class attrClass, String attrName, String attrValue, - Class propertyEditorClass) - throws JasperException - { + public static Object getValueFromBeanInfoPropertyEditor(Class attrClass, String attrName, String attrValue, + Class propertyEditorClass) throws JasperException { try { - PropertyEditor pe = (PropertyEditor)propertyEditorClass.getConstructor().newInstance(); + PropertyEditor pe = (PropertyEditor) propertyEditorClass.getConstructor().newInstance(); pe.setAsText(attrValue); return pe.getValue(); - } catch (Exception ex) { - if (attrValue.length() == 0) { + } catch (Exception e) { + if (attrValue.isEmpty()) { return null; } else { - throw new JasperException( - Localizer.getMessage("jsp.error.beans.property.conversion", - attrValue, attrClass.getName(), attrName, - ex.getMessage())); + throw new JasperException(Localizer.getMessage("jsp.error.beans.property.conversion", attrValue, + attrClass.getName(), attrName, e.getMessage())); } } } - public static Object getValueFromPropertyEditorManager( - Class attrClass, String attrName, String attrValue) - throws JasperException - { + public static Object getValueFromPropertyEditorManager(Class attrClass, String attrName, String attrValue) + throws JasperException { try { - PropertyEditor propEditor = - PropertyEditorManager.findEditor(attrClass); + PropertyEditor propEditor = PropertyEditorManager.findEditor(attrClass); if (propEditor != null) { propEditor.setAsText(attrValue); return propEditor.getValue(); - } else if (attrValue.length() == 0) { + } else if (attrValue.isEmpty()) { return null; } else { throw new IllegalArgumentException( - Localizer.getMessage("jsp.error.beans.propertyeditor.notregistered")); + Localizer.getMessage("jsp.error.beans.propertyeditor.notregistered")); } } catch (IllegalArgumentException ex) { - if (attrValue.length() == 0) { + if (attrValue.isEmpty()) { return null; } else { - throw new JasperException( - Localizer.getMessage("jsp.error.beans.property.conversion", - attrValue, attrClass.getName(), attrName, - ex.getMessage())); + throw new JasperException(Localizer.getMessage("jsp.error.beans.property.conversion", attrValue, + attrClass.getName(), attrName, ex.getMessage())); } } } @@ -919,15 +832,14 @@ /** - * Convert a possibly relative resource path into a context-relative - * resource path that starts with a '/'. + * Convert a possibly relative resource path into a context-relative resource path that starts with a '/'. * - * @param request The servlet request we are processing + * @param request The servlet request we are processing * @param relativePath The possibly relative resource path + * * @return an absolute path */ - public static String getContextRelativePath(ServletRequest request, - String relativePath) { + public static String getContextRelativePath(ServletRequest request, String relativePath) { if (relativePath.startsWith("/")) { return relativePath; @@ -936,11 +848,9 @@ return relativePath; } HttpServletRequest hrequest = (HttpServletRequest) request; - String uri = (String) request.getAttribute( - RequestDispatcher.INCLUDE_SERVLET_PATH); + String uri = (String) request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); if (uri != null) { - String pathInfo = (String) - request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO); + String pathInfo = (String) request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO); if (pathInfo == null) { if (uri.lastIndexOf('/') >= 0) { uri = uri.substring(0, uri.lastIndexOf('/')); @@ -958,24 +868,19 @@ /** - * Perform a RequestDispatcher.include() operation, with optional flushing - * of the response beforehand. + * Perform a RequestDispatcher.include() operation, with optional flushing of the response beforehand. * - * @param request The servlet request we are processing - * @param response The servlet response we are processing + * @param request The servlet request we are processing + * @param response The servlet response we are processing * @param relativePath The relative path of the resource to be included - * @param out The Writer to whom we are currently writing - * @param flush Should we flush before the include is processed? + * @param out The Writer to whom we are currently writing + * @param flush Should we flush before the include is processed? * - * @exception IOException if thrown by the included servlet + * @exception IOException if thrown by the included servlet * @exception ServletException if thrown by the included servlet */ - public static void include(ServletRequest request, - ServletResponse response, - String relativePath, - JspWriter out, - boolean flush) - throws IOException, ServletException { + public static void include(ServletRequest request, ServletResponse response, String relativePath, JspWriter out, + boolean flush) throws IOException, ServletException { if (flush && !(out instanceof BodyContent)) { out.flush(); @@ -984,99 +889,47 @@ // FIXME - It is tempting to use request.getRequestDispatcher() to // resolve a relative path directly, but Catalina currently does not // take into account whether the caller is inside a RequestDispatcher - // include or not. Whether Catalina *should* take that into account - // is a spec issue currently under review. In the mean time, + // include or not. Whether Catalina *should* take that into account + // is a spec issue currently under review. In the mean time, // replicate Jasper's previous behavior String resourcePath = getContextRelativePath(request, relativePath); RequestDispatcher rd = request.getRequestDispatcher(resourcePath); if (rd != null) { - rd.include(request, - new ServletResponseWrapperInclude(response, out)); + rd.include(request, new ServletResponseWrapperInclude(response, out)); } else { - throw new JasperException( - Localizer.getMessage("jsp.error.include.exception", resourcePath)); + throw new JasperException(Localizer.getMessage("jsp.error.include.exception", resourcePath)); } } /** * URL encodes a string, based on the supplied character encoding. - * This performs the same function as java.next.URLEncode.encode - * in J2SDK1.4, and should be removed if the only platform supported - * is 1.4 or higher. - * @param s The String to be URL encoded. + * + * @param s The String to be URL encoded. * @param enc The character encoding + * * @return The URL encoded String */ public static String URLEncode(String s, String enc) { - if (s == null) { return "null"; } - if (enc == null) { - enc = "ISO-8859-1"; // The default request encoding + enc = "ISO-8859-1"; // The default request encoding } - - StringBuilder out = new StringBuilder(s.length()); - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - OutputStreamWriter writer = null; + Charset cs; try { - writer = new OutputStreamWriter(buf, enc); - } catch (java.io.UnsupportedEncodingException ex) { - // Use the default encoding? - writer = new OutputStreamWriter(buf); - } - - for (int i = 0; i < s.length(); i++) { - int c = s.charAt(i); - if (c == ' ') { - out.append('+'); - } else if (isSafeChar(c)) { - out.append((char)c); - } else { - // convert to external encoding before hex conversion - try { - writer.write(c); - writer.flush(); - } catch(IOException e) { - buf.reset(); - continue; - } - byte[] ba = buf.toByteArray(); - for (byte b : ba) { - out.append('%'); - // Converting each byte in the buffer - out.append(Character.forDigit((b >> 4) & 0xf, 16)); - out.append(Character.forDigit(b & 0xf, 16)); - } - buf.reset(); - } - } - return out.toString(); - } - - private static boolean isSafeChar(int c) { - if (c >= 'a' && c <= 'z') { - return true; - } - if (c >= 'A' && c <= 'Z') { - return true; - } - if (c >= '0' && c <= '9') { - return true; - } - if (c == '-' || c == '_' || c == '.' || c == '!' || - c == '~' || c == '*' || c == '\'' || c == '(' || c == ')') { - return true; + cs = Charset.forName(enc); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + cs = StandardCharsets.ISO_8859_1; } - return false; + return URLEncoder.encode(s, cs); } - public static JspWriter startBufferedBody(PageContext pageContext, BodyTag tag) - throws JspException { + public static JspWriter startBufferedBody(PageContext pageContext, BodyTag tag) throws JspException { BodyContent out = pageContext.pushBody(); tag.setBodyContent(out); tag.doInitBody(); @@ -1109,4 +962,30 @@ } } + + /** + * This method parallels the logic of {@code SetSupport.doEndTag()}. + * + * @param pageContext pageContext + * @param var name of the variable + * @param value value to store + * @param scope scope + */ + public static void nonstandardSetTag(jakarta.servlet.jsp.PageContext pageContext, String var, Object value, + int scope) { + if (value == null) { + // matches SetTag and removes the key from the specified scope + pageContext.removeAttribute(var, scope); + } else { + if (scope == PageContext.PAGE_SCOPE) { + // matches SetTag and cleans up the VariableMapper + VariableMapper vm = pageContext.getELContext().getVariableMapper(); + if (vm != null) { + vm.setVariable(var, null); + } + } + // does the all-important set of the correct scope + pageContext.setAttribute(var, value, scope); + } + } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/JspSourceDependent.java tomcat10-10.1.52/java/org/apache/jasper/runtime/JspSourceDependent.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/JspSourceDependent.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/JspSourceDependent.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,21 +19,22 @@ import java.util.Map; /** - * Interface for tracking the source files dependencies, for the purpose - * of compiling out of date pages. This is used for - * 1) files that are included by page directives - * 2) files that are included by include-prelude and include-coda in jsp:config - * 3) files that are tag files and referenced - * 4) TLDs referenced + * Interface for tracking the source files dependencies, for the purpose of compiling out of date pages. This is used + * for: + *

            + *
          • files that are included by page directives
          • + *
          • files that are included by include-prelude and include-coda in jsp:config
          • + *
          • files that are tag files and referenced
          • + *
          • TLDs referenced
          • + *
          */ public interface JspSourceDependent { - /** - * Returns a map of file names and last modified time where the current page - * has a source dependency on the file. - * @return the map of dependent resources - */ + /** + * Returns a map of file names and last modified time where the current page has a source dependency on the file. + * + * @return the map of dependent resources + */ Map getDependants(); - -} +} \ No newline at end of file diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/JspSourceDirectives.java tomcat10-10.1.52/java/org/apache/jasper/runtime/JspSourceDirectives.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/JspSourceDirectives.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/JspSourceDirectives.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,9 +17,8 @@ package org.apache.jasper.runtime; /** - * Provides runtime access to selected compile time directives. Page directives - * are not added to this interface until there is a requirement to access them - * at runtime. + * Provides runtime access to selected compile time directives. Page directives are not added to this interface until + * there is a requirement to access them at runtime. */ public interface JspSourceDirectives { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/JspSourceImports.java tomcat10-10.1.52/java/org/apache/jasper/runtime/JspSourceImports.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/JspSourceImports.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/JspSourceImports.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,12 +19,12 @@ import java.util.Set; /** - * The EL engine needs access to the imports used in the JSP page to configure - * the ELContext. The imports are available at compile time but the ELContext - * is created lazily per page. This interface exposes the imports at runtime so - * that they may be added to the ELContext when it is created. + * The EL engine needs access to the imports used in the JSP page to configure the ELContext. The imports are available + * at compile time but the ELContext is created lazily per page. This interface exposes the imports at runtime so that + * they may be added to the ELContext when it is created. */ public interface JspSourceImports { Set getPackageImports(); + Set getClassImports(); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/JspWriterImpl.java tomcat10-10.1.52/java/org/apache/jasper/runtime/JspWriterImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/JspWriterImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/JspWriterImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,43 +27,34 @@ import org.apache.jasper.compiler.Localizer; /** - * Write text to a character-output stream, buffering characters so as - * to provide for the efficient writing of single characters, arrays, - * and strings. - * - * Provide support for discarding for the output that has been - * buffered. - * - * This needs revisiting when the buffering problems in the JSP spec - * are fixed -akv - * - * @author Anil K. Vijendran + * Write text to a character-output stream, buffering characters to provide efficient writing of single characters, + * arrays, and strings. Provide support for discarding the output that has been buffered. This needs revisiting when the + * buffering problems in the JSP spec are fixed -akv. */ public class JspWriterImpl extends JspWriter { private Writer out; private ServletResponse response; - private char cb[]; + private char[] cb; private int nextChar; private boolean flushed = false; private boolean closed = false; public JspWriterImpl() { - super( Constants.DEFAULT_BUFFER_SIZE, true ); + super(Constants.DEFAULT_BUFFER_SIZE, true); } /** - * Create a new buffered character-output stream that uses an output - * buffer of the given size. + * Create a new buffered character-output stream that uses an output buffer of the given size. * - * @param response A Servlet Response - * @param sz Output-buffer size, a positive integer - * @param autoFlush true to automatically flush on buffer - * full, false to throw an overflow exception in that case - * @exception IllegalArgumentException If sz is <= 0 + * @param response A Servlet Response + * @param sz Output-buffer size, a positive integer + * @param autoFlush true to automatically flush on buffer full, false to throw an overflow + * exception in that case + * + * @exception IllegalArgumentException If sz is < 0 */ - public JspWriterImpl(ServletResponse response, int sz, - boolean autoFlush) { + public JspWriterImpl(ServletResponse response, int sz, boolean autoFlush) { super(sz, autoFlush); if (sz < 0) { throw new IllegalArgumentException(Localizer.getMessage("jsp.error.negativeBufferSize")); @@ -73,14 +64,14 @@ nextChar = 0; } - void init( ServletResponse response, int sz, boolean autoFlush ) { - this.response= response; - if( sz > 0 && ( cb == null || sz > cb.length ) ) { - cb=new char[sz]; + void init(ServletResponse response, int sz, boolean autoFlush) { + this.response = response; + if (sz > 0 && (cb == null || sz > cb.length)) { + cb = new char[sz]; } nextChar = 0; - this.autoFlush=autoFlush; - this.bufferSize=sz; + this.autoFlush = autoFlush; + this.bufferSize = sz; } /** @@ -95,9 +86,9 @@ } /** - * Flush the output buffer to the underlying character stream, without - * flushing the stream itself. This method is non-private only so that it - * may be invoked by PrintStream. + * Flush the output buffer to the underlying character stream, without flushing the stream itself. This method is + * non-private only so that it may be invoked by PrintStream. + * * @throws IOException Error writing buffered data */ protected final void flushBuffer() throws IOException { @@ -133,12 +124,10 @@ public final void clear() throws IOException { if ((bufferSize == 0) && (out != null)) { // clear() is illegal after any unbuffered output (JSP.5.5) - throw new IllegalStateException( - Localizer.getMessage("jsp.error.ise_on_clear")); + throw new IllegalStateException(Localizer.getMessage("jsp.error.ise_on_clear")); } if (flushed) { - throw new IOException( - Localizer.getMessage("jsp.error.attempt_to_clear_flushed_buffer")); + throw new IOException(Localizer.getMessage("jsp.error.attempt_to_clear_flushed_buffer")); } ensureOpen(); nextChar = 0; @@ -147,8 +136,7 @@ @Override public void clearBuffer() throws IOException { if (bufferSize == 0) { - throw new IllegalStateException( - Localizer.getMessage("jsp.error.ise_on_clear")); + throw new IllegalStateException(Localizer.getMessage("jsp.error.ise_on_clear")); } ensureOpen(); nextChar = 0; @@ -159,7 +147,7 @@ } @Override - public void flush() throws IOException { + public void flush() throws IOException { flushBuffer(); if (out != null) { out.flush(); @@ -212,18 +200,15 @@ } /** - * Our own little min method, to avoid loading java.lang.Math if we've run - * out of file descriptors and we're trying to print a stack trace. + * Our own little min method, to avoid loading java.lang.Math if we've run out of file descriptors, and we're trying + * to print a stack trace. */ private static int min(int a, int b) { - if (a < b) { - return a; - } - return b; + return Math.min(a, b); } @Override - public void write(char cbuf[], int off, int len) throws IOException { + public void write(char[] cbuf, int off, int len) throws IOException { ensureOpen(); if (bufferSize == 0) { @@ -232,17 +217,17 @@ return; } - if ((off < 0) || (off > cbuf.length) || (len < 0) || - ((off + len) > cbuf.length) || ((off + len) < 0)) { + if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } if (len >= bufferSize) { - /* If the request length exceeds the size of the output buffer, - flush the buffer and then write the data directly. In this - way buffered streams will cascade harmlessly. */ + /* + * If the request length exceeds the size of the output buffer, flush the buffer and then write the data + * directly. In this way buffered streams will cascade harmlessly. + */ if (autoFlush) { flushBuffer(); } else { @@ -271,7 +256,7 @@ } @Override - public void write(char buf[]) throws IOException { + public void write(char[] buf) throws IOException { write(buf, 0, buf.length); } @@ -339,7 +324,7 @@ } @Override - public void print(char s[]) throws IOException { + public void print(char[] s) throws IOException { write(s); } @@ -400,7 +385,7 @@ } @Override - public void println(char x[]) throws IOException { + public void println(char[] x) throws IOException { print(x); println(); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/PageContextImpl.java tomcat10-10.1.52/java/org/apache/jasper/runtime/PageContextImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/PageContextImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/PageContextImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -52,16 +52,7 @@ import org.apache.jasper.runtime.JspContextWrapper.ELContextWrapper; /** - * Implementation of the PageContext class from the JSP spec. Also doubles as a - * VariableResolver for the EL. - * - * @author Anil K. Vijendran - * @author Larry Cable - * @author Hans Bergsten - * @author Pierre Delisle - * @author Mark Roth - * @author Jan Luehe - * @author Jacob Hookom + * Implementation of the PageContext class from the JSP spec. Also doubles as a VariableResolver for the EL. */ public class PageContextImpl extends PageContext { @@ -89,7 +80,7 @@ private int bodyContentTagBufferSize = Constants.DEFAULT_TAG_BUFFER_SIZE; // page-scope attributes - private final transient HashMap attributes; + private final transient HashMap attributes; // per-request state private transient ServletRequest request; @@ -113,10 +104,8 @@ } @Override - public void initialize(Servlet servlet, ServletRequest request, - ServletResponse response, String errorPageURL, - boolean needsSession, int bufferSize, boolean autoFlush) - throws IOException { + public void initialize(Servlet servlet, ServletRequest request, ServletResponse response, String errorPageURL, + boolean needsSession, int bufferSize, boolean autoFlush) throws IOException { // initialize state this.servlet = servlet; @@ -175,9 +164,8 @@ out = baseOut; try { ((JspWriterImpl) out).flushBuffer(); - } catch (IOException ex) { - IllegalStateException ise = new IllegalStateException(Localizer.getMessage("jsp.error.flush"), ex); - throw ise; + } catch (IOException ioe) { + throw new IllegalStateException(Localizer.getMessage("jsp.error.flush"), ioe); } finally { servlet = null; config = null; @@ -191,7 +179,7 @@ baseOut.recycle(); session = null; attributes.clear(); - for (BodyContentImpl body: outs) { + for (BodyContentImpl body : outs) { body.recycle(); } } @@ -210,23 +198,23 @@ } switch (scope) { - case PAGE_SCOPE: - return attributes.get(name); + case PAGE_SCOPE: + return attributes.get(name); - case REQUEST_SCOPE: - return request.getAttribute(name); + case REQUEST_SCOPE: + return request.getAttribute(name); - case SESSION_SCOPE: - if (session == null) { - throw new IllegalStateException(Localizer.getMessage("jsp.error.page.noSession")); - } - return session.getAttribute(name); + case SESSION_SCOPE: + if (session == null) { + throw new IllegalStateException(Localizer.getMessage("jsp.error.page.noSession")); + } + return session.getAttribute(name); - case APPLICATION_SCOPE: - return context.getAttribute(name); + case APPLICATION_SCOPE: + return context.getAttribute(name); - default: - throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); + default: + throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); } } @@ -246,61 +234,60 @@ removeAttribute(name, scope); } else { switch (scope) { + case PAGE_SCOPE: + attributes.put(name, o); + break; + + case REQUEST_SCOPE: + request.setAttribute(name, o); + break; + + case SESSION_SCOPE: + if (session == null) { + throw new IllegalStateException(Localizer.getMessage("jsp.error.page.noSession")); + } + session.setAttribute(name, o); + break; + + case APPLICATION_SCOPE: + context.setAttribute(name, o); + break; + + default: + throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); + } + } + } + + @Override + public void removeAttribute(final String name, final int scope) { + + if (name == null) { + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); + } + + switch (scope) { case PAGE_SCOPE: - attributes.put(name, o); + attributes.remove(name); break; case REQUEST_SCOPE: - request.setAttribute(name, o); + request.removeAttribute(name); break; case SESSION_SCOPE: if (session == null) { - throw new IllegalStateException(Localizer - .getMessage("jsp.error.page.noSession")); + throw new IllegalStateException(Localizer.getMessage("jsp.error.page.noSession")); } - session.setAttribute(name, o); + session.removeAttribute(name); break; case APPLICATION_SCOPE: - context.setAttribute(name, o); + context.removeAttribute(name); break; default: throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); - } - } - } - - @Override - public void removeAttribute(final String name, final int scope) { - - if (name == null) { - throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); - } - - switch (scope) { - case PAGE_SCOPE: - attributes.remove(name); - break; - - case REQUEST_SCOPE: - request.removeAttribute(name); - break; - - case SESSION_SCOPE: - if (session == null) { - throw new IllegalStateException(Localizer.getMessage("jsp.error.page.noSession")); - } - session.removeAttribute(name); - break; - - case APPLICATION_SCOPE: - context.removeAttribute(name); - break; - - default: - throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); } } @@ -324,7 +311,7 @@ if (session.getAttribute(name) != null) { return SESSION_SCOPE; } - } catch(IllegalStateException ise) { + } catch (IllegalStateException ignore) { // Session has been invalidated. // Ignore and fall through to application scope. } @@ -356,7 +343,7 @@ if (session != null) { try { o = session.getAttribute(name); - } catch(IllegalStateException ise) { + } catch (IllegalStateException ignore) { // Session has been invalidated. // Ignore and fall through to application scope. } @@ -371,23 +358,23 @@ @Override public Enumeration getAttributeNamesInScope(final int scope) { switch (scope) { - case PAGE_SCOPE: - return Collections.enumeration(attributes.keySet()); + case PAGE_SCOPE: + return Collections.enumeration(attributes.keySet()); - case REQUEST_SCOPE: - return request.getAttributeNames(); + case REQUEST_SCOPE: + return request.getAttributeNames(); - case SESSION_SCOPE: - if (session == null) { - throw new IllegalStateException(Localizer.getMessage("jsp.error.page.noSession")); - } - return session.getAttributeNames(); + case SESSION_SCOPE: + if (session == null) { + throw new IllegalStateException(Localizer.getMessage("jsp.error.page.noSession")); + } + return session.getAttributeNames(); - case APPLICATION_SCOPE: - return context.getAttributeNames(); + case APPLICATION_SCOPE: + return context.getAttributeNames(); - default: - throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); + default: + throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); } } @@ -400,10 +387,10 @@ removeAttribute(name, PAGE_SCOPE); removeAttribute(name, REQUEST_SCOPE); - if( session != null ) { + if (session != null) { try { removeAttribute(name, SESSION_SCOPE); - } catch(IllegalStateException ise) { + } catch (IllegalStateException ignore) { // Session has been invalidated. // Ignore and fall throw to application scope. } @@ -444,8 +431,7 @@ /** * Returns the exception associated with this page context, if any. *

          - * Added wrapping for Throwables to avoid ClassCastException: see Bugzilla - * 31171 for details. + * Added wrapping for Throwables to avoid ClassCastException: see Bugzilla 31171 for details. * * @return The Exception associated with this page context, if any. */ @@ -470,8 +456,7 @@ String path = relativeUrlPath; if (!path.startsWith("/")) { - String uri = (String) request.getAttribute( - RequestDispatcher.INCLUDE_SERVLET_PATH); + String uri = (String) request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); if (uri == null) { uri = ((HttpServletRequest) request).getServletPath(); } @@ -483,23 +468,19 @@ } @Override - public void include(String relativeUrlPath) throws ServletException, - IOException { - JspRuntimeLibrary - .include(request, response, relativeUrlPath, out, true); + public void include(String relativeUrlPath) throws ServletException, IOException { + JspRuntimeLibrary.include(request, response, relativeUrlPath, out, true); } @Override - public void include(final String relativeUrlPath, final boolean flush) - throws ServletException, IOException { + public void include(final String relativeUrlPath, final boolean flush) throws ServletException, IOException { JspRuntimeLibrary.include(request, response, relativeUrlPath, out, flush); } @Override @Deprecated public jakarta.servlet.jsp.el.VariableResolver getVariableResolver() { - return new org.apache.jasper.el.VariableResolverImpl( - this.getELContext()); + return new org.apache.jasper.el.VariableResolverImpl(this.getELContext()); } @Override @@ -508,9 +489,8 @@ try { out.clear(); baseOut.clear(); - } catch (IOException ex) { - throw new IllegalStateException(Localizer.getMessage( - "jsp.error.attempt_to_clear_flushed_buffer"), ex); + } catch (IOException ioe) { + throw new IllegalStateException(Localizer.getMessage("jsp.error.attempt_to_clear_flushed_buffer"), ioe); } // Make sure that the response object is not the wrapper for include @@ -574,20 +554,17 @@ } /** - * Provides programmatic access to the ExpressionEvaluator. The JSP - * Container must return a valid instance of an ExpressionEvaluator that can - * parse EL expressions. + * Provides programmatic access to the ExpressionEvaluator. The JSP Container must return a valid instance of an + * ExpressionEvaluator that can parse EL expressions. */ @Override @Deprecated public jakarta.servlet.jsp.el.ExpressionEvaluator getExpressionEvaluator() { - return new org.apache.jasper.el.ExpressionEvaluatorImpl( - this.applicationContext.getExpressionFactory()); + return new org.apache.jasper.el.ExpressionEvaluatorImpl(this.applicationContext.getExpressionFactory()); } @Override - public void handlePageException(Exception ex) throws IOException, - ServletException { + public void handlePageException(Exception ex) throws IOException, ServletException { // Should never be called since handleException() called with a // Throwable in the generated servlet. handlePageException((Throwable) ex); @@ -600,16 +577,13 @@ throw new NullPointerException(Localizer.getMessage("jsp.error.page.nullThrowable")); } - if (errorPageURL != null && !errorPageURL.equals("")) { + if (errorPageURL != null && !errorPageURL.isEmpty()) { /* - * Set request attributes. Do not set the - * jakarta.servlet.error.exception attribute here (instead, set in the - * generated servlet code for the error page) in order to prevent - * the ErrorReportValve, which is invoked as part of forwarding the - * request to the error page, from throwing it if the response has - * not been committed (the response will have been committed if the - * error page is a JSP page). + * Set request attributes. Do not set the jakarta.servlet.error.exception attribute here (instead, set in + * the generated servlet code for the error page) in order to prevent the ErrorReportValve, which is invoked + * as part of forwarding the request to the error page, from throwing it if the response has not been + * committed (the response will have been committed if the error page is a JSP page). */ request.setAttribute(EXCEPTION, t); request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, @@ -647,47 +621,39 @@ if (t instanceof ServletException) { throw (ServletException) t; } - if (t instanceof RuntimeException) { - throw (RuntimeException) t; - } - Throwable rootCause = null; if (t instanceof JspException || t instanceof ELException || t instanceof jakarta.servlet.jsp.el.ELException) { rootCause = t.getCause(); } - if (rootCause != null) { - throw new ServletException( - t.getClass().getName() + ": " + t.getMessage(), rootCause); + throw new ServletException(t.getClass().getName() + ": " + t.getMessage(), rootCause); + } + // ELException is a runtime exception + if (t instanceof RuntimeException) { + throw (RuntimeException) t; } - throw new ServletException(t); } } /** - * Proprietary method to evaluate EL expressions. XXX - This method should - * go away once the EL interpreter moves out of JSTL and into its own - * project. For now, this is necessary because the standard machinery is too - * slow. + * Proprietary method to evaluate EL expressions. XXX - This method should go away once the EL interpreter moves out + * of JSTL and into its own project. For now, this is necessary because the standard machinery is too slow. + * + * @param expression The expression to be evaluated + * @param expectedType The expected resulting type + * @param pageContext The page context + * @param functionMap Maps prefix and name to Method * - * @param expression - * The expression to be evaluated - * @param expectedType - * The expected resulting type - * @param pageContext - * The page context - * @param functionMap - * Maps prefix and name to Method * @return The result of the evaluation + * * @throws ELException If an error occurs during the evaluation */ - public static Object proprietaryEvaluate(final String expression, - final Class expectedType, final PageContext pageContext, - final ProtectedFunctionMapper functionMap) - throws ELException { - final ExpressionFactory exprFactory = jspf.getJspApplicationContext(pageContext.getServletContext()).getExpressionFactory(); + public static Object proprietaryEvaluate(final String expression, final Class expectedType, + final PageContext pageContext, final ProtectedFunctionMapper functionMap) throws ELException { + final ExpressionFactory exprFactory = + jspf.getJspApplicationContext(pageContext.getServletContext()).getExpressionFactory(); ELContext ctx = pageContext.getELContext(); ELContextImpl ctxImpl; if (ctx instanceof ELContextWrapper) { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/ProtectedFunctionMapper.java tomcat10-10.1.52/java/org/apache/jasper/runtime/ProtectedFunctionMapper.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/ProtectedFunctionMapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/ProtectedFunctionMapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,15 +22,11 @@ import jakarta.servlet.jsp.el.FunctionMapper; /** - * Maps EL functions to their Java method counterparts. Keeps the actual Method - * objects protected so that JSP pages can't indirectly do reflection. - * - * @author Mark Roth - * @author Kin-man Chung + * Maps EL functions to their Java method counterparts. Keeps the actual Method objects protected so that JSP pages + * can't indirectly do reflection. */ @SuppressWarnings("deprecation") // Have to support old JSP EL API -public final class ProtectedFunctionMapper extends jakarta.el.FunctionMapper - implements FunctionMapper { +public final class ProtectedFunctionMapper extends jakarta.el.FunctionMapper implements FunctionMapper { /** * Maps "prefix:name" to java.lang.Method objects. @@ -49,8 +45,8 @@ } /** - * Generated Servlet and Tag Handler implementations call this method to - * retrieve an instance of the ProtectedFunctionMapper. + * Generated Servlet and Tag Handler implementations call this method to retrieve an instance of the + * ProtectedFunctionMapper. * * @return A new protected function mapper. */ @@ -61,22 +57,16 @@ } /** - * Stores a mapping from the given EL function prefix and name to the given - * Java method. + * Stores a mapping from the given EL function prefix and name to the given Java method. * - * @param fnQName - * The EL function qualified name (including prefix) - * @param c - * The class containing the Java method - * @param methodName - * The name of the Java method - * @param args - * The arguments of the Java method - * @throws RuntimeException - * if no method with the given signature could be found. + * @param fnQName The EL function qualified name (including prefix) + * @param c The class containing the Java method + * @param methodName The name of the Java method + * @param args The arguments of the Java method + * + * @throws RuntimeException if no method with the given signature could be found. */ - public void mapFunction(String fnQName, final Class c, - final String methodName, final Class[] args) { + public void mapFunction(String fnQName, final Class c, final String methodName, final Class[] args) { // Skip if null values were passed in. They indicate a function // added via a lambda or ImportHandler; nether of which need to be // placed in the Map. @@ -87,33 +77,27 @@ try { method = c.getMethod(methodName, args); } catch (NoSuchMethodException e) { - throw new RuntimeException( - "Invalid function mapping - no such method: " - + e.getMessage()); + throw new RuntimeException("Invalid function mapping - no such method: " + e.getMessage()); } this.fnmap.put(fnQName, method); } /** - * Creates an instance for this class, and stores the Method for the given - * EL function prefix and name. This method is used for the case when there - * is only one function in the EL expression. - * - * @param fnQName - * The EL function qualified name (including prefix) - * @param c - * The class containing the Java method - * @param methodName - * The name of the Java method - * @param args - * The arguments of the Java method - * @throws RuntimeException - * if no method with the given signature could be found. + * Creates an instance for this class, and stores the Method for the given EL function prefix and name. This method + * is used for the case when there is only one function in the EL expression. + * + * @param fnQName The EL function qualified name (including prefix) + * @param c The class containing the Java method + * @param methodName The name of the Java method + * @param args The arguments of the Java method + * + * @throws RuntimeException if no method with the given signature could be found. + * * @return the mapped function */ - public static ProtectedFunctionMapper getMapForFunction(String fnQName, - final Class c, final String methodName, final Class[] args) { + public static ProtectedFunctionMapper getMapForFunction(String fnQName, final Class c, final String methodName, + final Class[] args) { Method method = null; ProtectedFunctionMapper funcMapper = new ProtectedFunctionMapper(); // Skip if null values were passed in. They indicate a function @@ -123,9 +107,7 @@ try { method = c.getMethod(methodName, args); } catch (NoSuchMethodException e) { - throw new RuntimeException( - "Invalid function mapping - no such method: " - + e.getMessage()); + throw new RuntimeException("Invalid function mapping - no such method: " + e.getMessage()); } } funcMapper.theMethod = method; @@ -133,13 +115,12 @@ } /** - * Resolves the specified local name and prefix into a Java.lang.Method. - * Returns null if the prefix and local name are not found. + * Resolves the specified local name and prefix into a Java.lang.Method. Returns null if the prefix and local name + * are not found. + * + * @param prefix the prefix of the function + * @param localName the short name of the function * - * @param prefix - * the prefix of the function - * @param localName - * the short name of the function * @return the result of the method mapping. Null means no entry found. */ @Override diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/ServletResponseWrapperInclude.java tomcat10-10.1.52/java/org/apache/jasper/runtime/ServletResponseWrapperInclude.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/ServletResponseWrapperInclude.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/ServletResponseWrapperInclude.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,15 +26,9 @@ import jakarta.servlet.jsp.JspWriter; /** - * ServletResponseWrapper used by the JSP 'include' action. - * - * This wrapper response object is passed to RequestDispatcher.include(), so - * that the output of the included resource is appended to that of the - * including page. - * - * @author Pierre Delisle + * ServletResponseWrapper used by the JSP 'include' action. This wrapper response object is passed to + * RequestDispatcher.include(), so that the output of the included resource is appended to that of the including page. */ - public class ServletResponseWrapperInclude extends HttpServletResponseWrapper { /** @@ -44,9 +38,8 @@ private final JspWriter jspWriter; - public ServletResponseWrapperInclude(ServletResponse response, - JspWriter jspWriter) { - super((HttpServletResponse)response); + public ServletResponseWrapperInclude(ServletResponse response, JspWriter jspWriter) { + super((HttpServletResponse) response); this.printWriter = new PrintWriter(jspWriter); this.jspWriter = jspWriter; } @@ -65,14 +58,14 @@ } /** - * Clears the output buffer of the JspWriter associated with the including - * page. + * Clears the output buffer of the JspWriter associated with the including page. */ @Override public void resetBuffer() { try { jspWriter.clearBuffer(); } catch (IOException ioe) { + // Ignore } } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/runtime/TagHandlerPool.java tomcat10-10.1.52/java/org/apache/jasper/runtime/TagHandlerPool.java --- tomcat10-10.1.34/java/org/apache/jasper/runtime/TagHandlerPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/runtime/TagHandlerPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,6 @@ /** * Pool of tag handlers that can be reused. - * - * @author Jan Luehe */ public class TagHandlerPool { @@ -53,7 +51,6 @@ result = (TagHandlerPool) c.getConstructor().newInstance(); } catch (Exception e) { LogFactory.getLog(TagHandlerPool.class).info(Localizer.getMessage("jsp.error.tagHandlerPool"), e); - result = null; } } if (result == null) { @@ -70,8 +67,8 @@ if (maxSizeS != null) { try { maxSize = Integer.parseInt(maxSizeS); - } catch (Exception ex) { - maxSize = -1; + } catch (Exception e) { + // Ignore } } if (maxSize < 0) { @@ -93,14 +90,14 @@ } /** - * Gets the next available tag handler from this tag handler pool, - * instantiating one if this tag handler pool is empty. + * Gets the next available tag handler from this tag handler pool, instantiating one if this tag handler pool is + * empty. + * + * @param handlerClass Tag handler class * - * @param handlerClass - * Tag handler class * @return Reused or newly instantiated tag handler - * @throws JspException - * if a tag handler cannot be instantiated + * + * @throws JspException if a tag handler cannot be instantiated */ public Tag get(Class handlerClass) throws JspException { Tag handler; @@ -115,8 +112,7 @@ // wait for us to construct a tag for this thread. try { if (useInstanceManagerForTags) { - return (Tag) instanceManager.newInstance( - handlerClass.getName(), handlerClass.getClassLoader()); + return (Tag) instanceManager.newInstance(handlerClass.getName(), handlerClass.getClassLoader()); } else { Tag instance = handlerClass.getConstructor().newInstance(); instanceManager.newInstance(instance); @@ -130,12 +126,10 @@ } /** - * Adds the given tag handler to this tag handler pool, unless this tag - * handler pool has already reached its capacity, in which case the tag - * handler's release() method is called. + * Adds the given tag handler to this tag handler pool, unless this tag handler pool has already reached its + * capacity, in which case the tag handler's release() method is called. * - * @param handler - * Tag handler to add to this tag handler pool + * @param handler Tag handler to add to this tag handler pool */ public void reuse(Tag handler) { synchronized (this) { @@ -149,8 +143,7 @@ } /** - * Calls the release() method of all available tag handlers in this tag - * handler pool. + * Calls the release() method of all available tag handlers in this tag handler pool. */ public synchronized void release() { for (int i = current; i >= 0; i--) { @@ -159,8 +152,7 @@ } - protected static String getOption(ServletConfig config, String name, - String defaultV) { + protected static String getOption(ServletConfig config, String name, String defaultV) { if (config == null) { return defaultV; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/security/SecurityClassLoad.java tomcat10-10.1.52/java/org/apache/jasper/security/SecurityClassLoad.java --- tomcat10-10.1.34/java/org/apache/jasper/security/SecurityClassLoad.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/security/SecurityClassLoad.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,7 @@ import org.apache.juli.logging.LogFactory; /** - * Static class used to preload java classes when using the - * Java SecurityManager so that the defineClassInPackage + * Static class used to preload java classes when using the Java SecurityManager so that the defineClassInPackage * RuntimePermission does not trigger an AccessControlException. */ public final class SecurityClassLoad { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/security/SecurityUtil.java tomcat10-10.1.52/java/org/apache/jasper/security/SecurityUtil.java --- tomcat10-10.1.34/java/org/apache/jasper/security/SecurityUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/security/SecurityUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,18 +22,18 @@ * Util class for Security related operations. */ -public final class SecurityUtil{ +public final class SecurityUtil { private static final boolean packageDefinitionEnabled = - System.getProperty("package.definition") == null ? false : true; + System.getProperty("package.definition") == null ? false : true; /** - * Return the SecurityManager only if Security is enabled AND - * package protection mechanism is enabled. + * Return the SecurityManager only if Security is enabled AND package protection mechanism is enabled. + * * @return true if package protection is enabled */ - public static boolean isPackageProtectionEnabled(){ - if (packageDefinitionEnabled && Constants.IS_SECURITY_ENABLED){ + public static boolean isPackageProtectionEnabled() { + if (packageDefinitionEnabled && Constants.IS_SECURITY_ENABLED) { return true; } return false; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/servlet/JasperInitializer.java tomcat10-10.1.52/java/org/apache/jasper/servlet/JasperInitializer.java --- tomcat10-10.1.34/java/org/apache/jasper/servlet/JasperInitializer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/servlet/JasperInitializer.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,8 +44,8 @@ private final Log log = LogFactory.getLog(JasperInitializer.class); // must not be static /* - * Preload classes required at runtime by a JSP servlet so that - * we don't get a defineClassInPackage security exception. + * Preload classes required at runtime by a JSP servlet so that we don't get a defineClassInPackage security + * exception. */ static { JspFactoryImpl factory = new JspFactoryImpl(); @@ -61,15 +61,13 @@ log.debug(Localizer.getMessage(MSG + ".onStartup", context.getServletContextName())); } - // Setup a simple default Instance Manager - if (context.getAttribute(InstanceManager.class.getName())==null) { + // Set up a simple default Instance Manager + if (context.getAttribute(InstanceManager.class.getName()) == null) { context.setAttribute(InstanceManager.class.getName(), new SimpleInstanceManager()); } - boolean validate = Boolean.parseBoolean( - context.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM)); - String blockExternalString = context.getInitParameter( - Constants.XML_BLOCK_EXTERNAL_INIT_PARAM); + boolean validate = Boolean.parseBoolean(context.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM)); + String blockExternalString = context.getInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM); boolean blockExternal; if (blockExternalString == null) { blockExternal = true; @@ -91,8 +89,7 @@ } context.setAttribute(TldCache.SERVLET_CONTEXT_ATTRIBUTE_NAME, - new TldCache(context, scanner.getUriTldResourcePathMap(), - scanner.getTldResourcePathTaglibXmlMap())); + new TldCache(context, scanner.getUriTldResourcePathMap(), scanner.getTldResourcePathTaglibXmlMap())); String poolSizeValue = context.getInitParameter(Constants.JSP_FACTORY_POOL_SIZE_INIT_PARAM); int poolSize = 8; @@ -110,8 +107,8 @@ } - protected TldScanner newTldScanner(ServletContext context, boolean namespaceAware, - boolean validate, boolean blockExternal) { + protected TldScanner newTldScanner(ServletContext context, boolean namespaceAware, boolean validate, + boolean blockExternal) { return new TldScanner(context, namespaceAware, validate, blockExternal); } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/servlet/JasperLoader.java tomcat10-10.1.52/java/org/apache/jasper/servlet/JasperLoader.java --- tomcat10-10.1.34/java/org/apache/jasper/servlet/JasperLoader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/servlet/JasperLoader.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,11 +24,8 @@ import java.security.PermissionCollection; /** - * Class loader for loading servlet class files (corresponding to JSP files) - * and tag handler class files (corresponding to tag files). - * - * @author Anil K. Vijendran - * @author Harish Prabandham + * Class loader for loading servlet class files (corresponding to JSP files) and tag handler class files (corresponding + * to tag files). */ public class JasperLoader extends URLClassLoader { @@ -36,8 +33,7 @@ private final SecurityManager securityManager; private final String packageName; - public JasperLoader(URL[] urls, ClassLoader parent, - String packageName, PermissionCollection permissionCollection) { + public JasperLoader(URL[] urls, ClassLoader parent, String packageName, PermissionCollection permissionCollection) { super(urls, parent); this.permissionCollection = permissionCollection; this.securityManager = System.getSecurityManager(); @@ -45,9 +41,8 @@ } /** - * Load the class with the specified name. This method searches for - * classes in the same manner as loadClass(String, boolean) - * with false as the second argument. + * Load the class with the specified name. This method searches for classes in the same manner as + * loadClass(String, boolean) with false as the second argument. * * @param name Name of the class to be loaded * @@ -59,35 +54,28 @@ } /** - * Load the class with the specified name, searching using the following - * algorithm until it finds and returns the class. If the class cannot - * be found, returns ClassNotFoundException. + * Load the class with the specified name, searching using the following algorithm until it finds and returns the + * class. If the class cannot be found, returns ClassNotFoundException. *

            - *
          • Call findLoadedClass(String) to check if the - * class has already been loaded. If it has, the same - * Class object is returned.
          • - *
          • If the delegate property is set to true, - * call the loadClass() method of the parent class - * loader, if any.
          • - *
          • Call findClass() to find this class in our locally - * defined repositories.
          • - *
          • Call the loadClass() method of our parent - * class loader, if any.
          • + *
          • Call findLoadedClass(String) to check if the class has already been loaded. If it has, the same + * Class object is returned.
          • + *
          • If the delegate property is set to true, call the loadClass() method + * of the parent class loader, if any.
          • + *
          • Call findClass() to find this class in our locally defined repositories.
          • + *
          • Call the loadClass() method of our parent class loader, if any.
          • *
          - * If the class was found using the above steps, and the - * resolve flag is true, this method will then - * call resolveClass(Class) on the resulting Class object. + * If the class was found using the above steps, and the resolve flag is true, this method + * will then call resolveClass(Class) on the resulting Class object. * - * @param name Name of the class to be loaded + * @param name Name of the class to be loaded * @param resolve If true then resolve the class * * @exception ClassNotFoundException if the class was not found */ @Override - public synchronized Class loadClass(final String name, boolean resolve) - throws ClassNotFoundException { + public synchronized Class loadClass(final String name, boolean resolve) throws ClassNotFoundException { - Class clazz = null; + Class clazz; // (0) Check our previously loaded class cache clazz = findLoadedClass(name); @@ -104,23 +92,22 @@ if (dot >= 0) { try { // Do not call the security manager since by default, we grant that package. - if (!"org.apache.jasper.runtime".equalsIgnoreCase(name.substring(0,dot))){ - securityManager.checkPackageAccess(name.substring(0,dot)); + if (!"org.apache.jasper.runtime".equalsIgnoreCase(name.substring(0, dot))) { + securityManager.checkPackageAccess(name.substring(0, dot)); } } catch (SecurityException se) { - String error = "Security Violation, attempt to use " + - "Restricted Class: " + name; + String error = "Security Violation, attempt to use " + "Restricted Class: " + name; se.printStackTrace(); throw new ClassNotFoundException(error); } } } - if( !name.startsWith(packageName + '.') ) { + if (!name.startsWith(packageName + '.')) { // Class is not in org.apache.jsp, therefore, have our // parent load it clazz = getParent().loadClass(name); - if( resolve ) { + if (resolve) { resolveClass(clazz); } return clazz; @@ -143,7 +130,7 @@ if (url != null) { try { is = url.openStream(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } @@ -153,13 +140,11 @@ /** - * Get the Permissions for a CodeSource. - * - * Since this ClassLoader is only used for a JSP page in - * a web application context, we just return our preset - * PermissionCollection for the web app context. + * Get the Permissions for a CodeSource. Since this ClassLoader is only used for a JSP page in a web application + * context, we just return our preset PermissionCollection for the web app context. * * @param codeSource Code source where the code was loaded from + * * @return PermissionCollection for CodeSource */ @Override diff -Nru tomcat10-10.1.34/java/org/apache/jasper/servlet/JspCServletContext.java tomcat10-10.1.52/java/org/apache/jasper/servlet/JspCServletContext.java --- tomcat10-10.1.34/java/org/apache/jasper/servlet/JspCServletContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/servlet/JspCServletContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -65,12 +65,8 @@ /** - * Simple ServletContext implementation without - * HTTP-specific methods. - * - * @author Peter Rossbach (pr@webapp.de) + * Simple ServletContext implementation without HTTP-specific methods. */ - public class JspCServletContext implements ServletContext { @@ -104,13 +100,13 @@ /** * Merged web.xml for the application. */ - private WebXml webXml; + private final WebXml webXml; private List resourceJARs; - private JspConfigDescriptor jspConfigDescriptor; + private final JspConfigDescriptor jspConfigDescriptor; /** @@ -124,21 +120,19 @@ /** * Create a new instance of this ServletContext implementation. * - * @param aLogWriter PrintWriter which is used for log() calls + * @param aLogWriter PrintWriter which is used for log() calls * @param aResourceBaseURL Resource base URL - * @param classLoader Class loader for this {@link ServletContext} - * @param validate Should a validating parser be used to parse web.xml? - * @param blockExternal Should external entities be blocked when parsing - * web.xml? + * @param classLoader Class loader for this {@link ServletContext} + * @param validate Should a validating parser be used to parse web.xml? + * @param blockExternal Should external entities be blocked when parsing web.xml? + * * @throws JasperException An error occurred building the merged web.xml */ - public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL, - ClassLoader classLoader, boolean validate, boolean blockExternal) - throws JasperException { + public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL, ClassLoader classLoader, boolean validate, + boolean blockExternal) throws JasperException { myAttributes = new HashMap<>(); - myParameters.put(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM, - String.valueOf(blockExternal)); + myParameters.put(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM, String.valueOf(blockExternal)); myLogWriter = aLogWriter; myResourceBaseURL = aResourceBaseURL; this.loader = classLoader; @@ -146,21 +140,19 @@ jspConfigDescriptor = webXml.getJspConfigDescriptor(); } - private WebXml buildMergedWebXml(boolean validate, boolean blockExternal) - throws JasperException { + private WebXml buildMergedWebXml(boolean validate, boolean blockExternal) throws JasperException { WebXml webXml = new WebXml(); WebXmlParser webXmlParser = new WebXmlParser(validate, validate, blockExternal); // Use this class's classloader as Ant will have set the TCCL to its own webXmlParser.setClassLoader(getClass().getClassLoader()); try { - URL url = getResource( - org.apache.tomcat.util.descriptor.web.Constants.WEB_XML_LOCATION); + URL url = getResource(org.apache.tomcat.util.descriptor.web.Constants.WEB_XML_LOCATION); if (!webXmlParser.parseWebXml(url, webXml, false)) { throw new JasperException(Localizer.getMessage("jspc.error.invalidWebXml")); } - } catch (IOException e) { - throw new JasperException(e); + } catch (IOException ioe) { + throw new JasperException(ioe); } // if the application is metadata-complete then we can skip fragment processing @@ -175,7 +167,7 @@ return webXml; } - Map fragments = scanForFragments(webXmlParser); + Map fragments = scanForFragments(webXmlParser); Set orderedFragments = WebXml.orderWebFragments(webXml, fragments, this); // Find resource JARs @@ -192,11 +184,7 @@ List resourceJars = new ArrayList<>(); // Build list of potential resource JARs. Use same ordering as ContextConfig Set resourceFragments = new LinkedHashSet<>(orderedFragments); - for (WebXml fragment : fragments) { - if (!resourceFragments.contains(fragment)) { - resourceFragments.add(fragment); - } - } + resourceFragments.addAll(fragments); for (WebXml resourceFragment : resourceFragments) { try (Jar jar = JarFactory.newInstance(resourceFragment.getURL())) { @@ -213,15 +201,14 @@ } - private Map scanForFragments(WebXmlParser webXmlParser) throws JasperException { + private Map scanForFragments(WebXmlParser webXmlParser) throws JasperException { StandardJarScanner scanner = new StandardJarScanner(); // TODO - enabling this means initializing the classloader first in JspC scanner.setScanClassPath(false); // TODO - configure filter rules from Ant rather then system properties scanner.setJarScanFilter(new StandardJarScanFilter()); - FragmentJarScannerCallback callback = - new FragmentJarScannerCallback(webXmlParser, false, true); + FragmentJarScannerCallback callback = new FragmentJarScannerCallback(webXmlParser, false, true); scanner.scan(JarScanType.PLUGGABILITY, this, callback); if (!callback.isOk()) { throw new JasperException(Localizer.getMessage("jspc.error.invalidFragment")); @@ -330,7 +317,7 @@ // Strip leading '/' path = path.substring(1); - URL url = null; + URL url; try { URI uri = new URI(myResourceBaseURL.toExternalForm() + path); url = uri.toURL(); @@ -385,7 +372,7 @@ if (basePath != null) { File theBaseDir = new File(basePath); if (theBaseDir.isDirectory()) { - String theFiles[] = theBaseDir.list(); + String[] theFiles = theBaseDir.list(); if (theFiles != null) { for (String theFile : theFiles) { File testFile = new File(basePath + File.separator + theFile); @@ -406,11 +393,9 @@ for (URL jarUrl : resourceJARs) { try (Jar jar = JarFactory.newInstance(jarUrl)) { jar.nextEntry(); - for (String entryName = jar.getEntryName(); - entryName != null; - jar.nextEntry(), entryName = jar.getEntryName()) { - if (entryName.startsWith(jarPath) && - entryName.length() > jarPath.length()) { + for (String entryName = jar.getEntryName(); entryName != null; jar.nextEntry(), entryName = + jar.getEntryName()) { + if (entryName.startsWith(jarPath) && entryName.length() > jarPath.length()) { // Let the Set implementation handle duplicates int sep = entryName.indexOf('/', jarPath.length()); if (sep < 0) { @@ -422,8 +407,8 @@ } } } - } catch (IOException e) { - log(e.getMessage(), e); + } catch (IOException ioe) { + log(ioe.getMessage(), ioe); } } } @@ -470,15 +455,13 @@ @Override - public FilterRegistration.Dynamic addFilter(String filterName, - String className) { + public FilterRegistration.Dynamic addFilter(String filterName, String className) { return null; } @Override - public ServletRegistration.Dynamic addServlet(String servletName, - String className) { + public ServletRegistration.Dynamic addServlet(String servletName, String className) { return null; } @@ -502,8 +485,7 @@ @Override - public void setSessionTrackingModes( - Set sessionTrackingModes) { + public void setSessionTrackingModes(Set sessionTrackingModes) { // Do nothing } @@ -515,22 +497,19 @@ @Override - public Dynamic addFilter(String filterName, - Class filterClass) { + public Dynamic addFilter(String filterName, Class filterClass) { return null; } @Override - public ServletRegistration.Dynamic addServlet(String servletName, - Servlet servlet) { + public ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { return null; } @Override - public ServletRegistration.Dynamic addServlet(String servletName, - Class servletClass) { + public ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { return null; } @@ -542,15 +521,13 @@ @Override - public T createFilter(Class c) - throws ServletException { + public T createFilter(Class c) throws ServletException { return null; } @Override - public T createServlet(Class c) - throws ServletException { + public T createServlet(Class c) throws ServletException { return null; } @@ -592,8 +569,7 @@ @Override - public T createListener(Class c) - throws ServletException { + public T createListener(Class c) throws ServletException { return null; } @@ -623,7 +599,7 @@ @Override - public Map getFilterRegistrations() { + public Map getFilterRegistrations() { return null; } @@ -635,7 +611,7 @@ @Override - public Map getServletRegistrations() { + public Map getServletRegistrations() { return null; } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/servlet/JspServlet.java tomcat10-10.1.52/java/org/apache/jasper/servlet/JspServlet.java --- tomcat10-10.1.34/java/org/apache/jasper/servlet/JspServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/servlet/JspServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,20 +45,9 @@ import org.apache.tomcat.util.security.Escape; /** - * The JSP engine (a.k.a Jasper). - * - * The servlet container is responsible for providing a - * URLClassLoader for the web application context Jasper - * is being used in. Jasper will try get the Tomcat - * ServletContext attribute for its ServletContext class - * loader, if that fails, it uses the parent class loader. - * In either case, it must be a URLClassLoader. - * - * @author Anil K. Vijendran - * @author Harish Prabandham - * @author Remy Maucherat - * @author Kin-man Chung - * @author Glenn Nielsen + * The Jasper JSP engine. The servlet container is responsible for providing a URLClassLoader for the web application + * context Jasper is being used in. Jasper will try to get the Tomcat ServletContext attribute for its ServletContext + * class loader, if that fails, it uses the parent class loader. In either case, it must be a URLClassLoader. */ public class JspServlet extends HttpServlet implements PeriodicEventListener { @@ -87,8 +76,7 @@ // Check for a custom Options implementation String engineOptionsName = config.getInitParameter("engineOptionsClass"); if (Constants.IS_SECURITY_ENABLED && engineOptionsName != null) { - log.info(Localizer.getMessage( - "jsp.info.ignoreSetting", "engineOptionsClass", engineOptionsName)); + log.info(Localizer.getMessage("jsp.info.ignoreSetting", "engineOptionsClass", engineOptionsName)); engineOptionsName = null; } if (engineOptionsName != null) { @@ -100,11 +88,11 @@ Constructor ctor = engineOptionsClass.getConstructor(ctorSig); Object[] args = { config, context }; options = (Options) ctor.newInstance(args); - } catch (Throwable e) { - e = ExceptionUtils.unwrapInvocationTargetException(e); - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); // Need to localize this. - log.warn(Localizer.getMessage("jsp.warning.engineOptionsClass", engineOptionsName), e); + log.warn(Localizer.getMessage("jsp.warning.engineOptionsClass", engineOptionsName), throwable); // Use the default Options implementation options = new EmbeddedServletOptions(config, context); } @@ -123,42 +111,39 @@ throw new ServletException(Localizer.getMessage("jsp.error.no.jsp", jspFile), e); } try { - if (SecurityUtil.isPackageProtectionEnabled()){ - AccessController.doPrivileged((PrivilegedExceptionAction) () -> { - serviceJspFile(null, null, jspFile, true); - return null; - }); + if (SecurityUtil.isPackageProtectionEnabled()) { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + serviceJspFile(null, null, jspFile, true); + return null; + }); } else { serviceJspFile(null, null, jspFile, true); } - } catch (IOException e) { - throw new ServletException(Localizer.getMessage("jsp.error.precompilation", jspFile), e); + } catch (IOException ioe) { + throw new ServletException(Localizer.getMessage("jsp.error.precompilation", jspFile), ioe); } catch (PrivilegedActionException e) { Throwable t = e.getCause(); if (t instanceof ServletException) { - throw (ServletException)t; + throw (ServletException) t; } throw new ServletException(Localizer.getMessage("jsp.error.precompilation", jspFile), e); } } if (log.isDebugEnabled()) { - log.debug(Localizer.getMessage("jsp.message.scratch.dir.is", - options.getScratchDir().toString())); + log.debug(Localizer.getMessage("jsp.message.scratch.dir.is", options.getScratchDir().toString())); log.debug(Localizer.getMessage("jsp.message.dont.modify.servlets")); } } /** - * Returns the number of JSPs for which JspServletWrappers exist, i.e., - * the number of JSPs that have been loaded into the webapp with which - * this JspServlet is associated. - * - *

          This info may be used for monitoring purposes. + * Returns the number of JSPs for which JspServletWrappers exist, i.e., the number of JSPs that have been loaded + * into the webapp with which this JspServlet is associated. + *

          + * This info may be used for monitoring purposes. * - * @return The number of JSPs that have been loaded into the webapp with - * which this JspServlet is associated + * @return The number of JSPs that have been loaded into the webapp with which this JspServlet is associated */ public int getJspCount() { return this.rctxt.getJspCount(); @@ -177,11 +162,10 @@ /** * Gets the number of JSPs that have been reloaded. + *

          + * This info may be used for monitoring purposes. * - *

          This info may be used for monitoring purposes. - * - * @return The number of JSPs (in the webapp with which this JspServlet is - * associated) that have been reloaded + * @return The number of JSPs (in the webapp with which this JspServlet is associated) that have been reloaded */ public int getJspReloadCount() { return this.rctxt.getJspReloadCount(); @@ -190,11 +174,11 @@ /** * Gets the number of JSPs that are in the JSP limiter queue + *

          + * This info may be used for monitoring purposes. * - *

          This info may be used for monitoring purposes. - * - * @return The number of JSPs (in the webapp with which this JspServlet is - * associated) that are in the JSP limiter queue + * @return The number of JSPs (in the webapp with which this JspServlet is associated) that are in the JSP limiter + * queue */ public int getJspQueueLength() { return this.rctxt.getJspQueueLength(); @@ -203,11 +187,10 @@ /** * Gets the number of JSPs that have been unloaded. + *

          + * This info may be used for monitoring purposes. * - *

          This info may be used for monitoring purposes. - * - * @return The number of JSPs (in the webapp with which this JspServlet is - * associated) that have been unloaded + * @return The number of JSPs (in the webapp with which this JspServlet is associated) that have been unloaded */ public int getJspUnloadCount() { return this.rctxt.getJspUnloadCount(); @@ -215,17 +198,17 @@ /** - *

          Look for a precompilation request as described in - * Section 8.4.2 of the JSP 1.2 Specification. WARNING - - * we cannot use request.getParameter() for this, because - * that will trigger parsing all of the request parameters, and not give - * a servlet the opportunity to call - * request.setCharacterEncoding() first.

          + *

          + * Look for a precompilation request as described in Section 8.4.2 of the JSP 1.2 Specification. + * WARNING - we cannot use request.getParameter() for this, because that will trigger + * parsing all of the request parameters, and not give a servlet the opportunity to call + * request.setCharacterEncoding() first. + *

          * * @param request The servlet request we are processing * - * @exception ServletException if an invalid parameter value for the - * jsp_precompile parameter name is specified + * @exception ServletException if an invalid parameter value for the jsp_precompile parameter name is + * specified */ boolean preCompile(HttpServletRequest request) throws ServletException { @@ -238,16 +221,15 @@ if (start < 0) { return false; } - queryString = - queryString.substring(start + precompileParameter.length()); - if (queryString.length() == 0) { - return true; // ?jsp_precompile + queryString = queryString.substring(start + precompileParameter.length()); + if (queryString.isEmpty()) { + return true; // ?jsp_precompile } if (queryString.startsWith("&")) { - return true; // ?jsp_precompile&foo=bar... + return true; // ?jsp_precompile&foo=bar... } if (!queryString.startsWith("=")) { - return false; // part of some other name or value + return false; // part of some other name or value } int limit = queryString.length(); int ampersand = queryString.indexOf('&'); @@ -256,51 +238,45 @@ } String value = queryString.substring(1, limit); if (value.equals("true")) { - return true; // ?jsp_precompile=true + return true; // ?jsp_precompile=true } else if (value.equals("false")) { // Spec says if jsp_precompile=false, the request should not // be delivered to the JSP page; the easiest way to implement // this is to set the flag to true, and precompile the page anyway. // This still conforms to the spec, since it says the // precompilation request can be ignored. - return true; // ?jsp_precompile=false + return true; // ?jsp_precompile=false } else { - throw new ServletException(Localizer.getMessage("jsp.error.precompilation.parameter", - precompileParameter, value)); + throw new ServletException( + Localizer.getMessage("jsp.error.precompilation.parameter", precompileParameter, value)); } } @Override - public void service (HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // jspFile may be configured as an init-param for this servlet instance String jspUri = jspFile; if (jspUri == null) { /* - * Check to see if the requested JSP has been the target of a - * RequestDispatcher.include() + * Check to see if the requested JSP has been the target of a RequestDispatcher.include() */ - jspUri = (String) request.getAttribute( - RequestDispatcher.INCLUDE_SERVLET_PATH); + jspUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); if (jspUri != null) { /* - * Requested JSP has been target of - * RequestDispatcher.include(). Its path is assembled from the - * relevant jakarta.servlet.include.* request attributes + * Requested JSP has been target of RequestDispatcher.include(). Its path is assembled from the relevant + * jakarta.servlet.include.* request attributes */ - String pathInfo = (String) request.getAttribute( - RequestDispatcher.INCLUDE_PATH_INFO); + String pathInfo = (String) request.getAttribute(RequestDispatcher.INCLUDE_PATH_INFO); if (pathInfo != null) { jspUri += pathInfo; } } else { /* - * Requested JSP has not been the target of a - * RequestDispatcher.include(). Reconstruct its path from the + * Requested JSP has not been the target of a RequestDispatcher.include(). Reconstruct its path from the * request's getServletPath() and getPathInfo() */ jspUri = request.getServletPath(); @@ -325,9 +301,9 @@ serviceJspFile(request, response, jspUri, precompile); } catch (RuntimeException | IOException | ServletException e) { throw e; - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - throw new ServletException(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + throw new ServletException(t); } } @@ -350,14 +326,12 @@ // -------------------------------------------------------- Private Methods - private void serviceJspFile(HttpServletRequest request, - HttpServletResponse response, String jspUri, - boolean precompile) - throws ServletException, IOException { + private void serviceJspFile(HttpServletRequest request, HttpServletResponse response, String jspUri, + boolean precompile) throws ServletException, IOException { JspServletWrapper wrapper = rctxt.getWrapper(jspUri); if (wrapper == null) { - synchronized(this) { + synchronized (this) { wrapper = rctxt.getWrapper(jspUri); if (wrapper == null) { // Check if the requested JSP page exists, to avoid @@ -366,9 +340,8 @@ handleMissingResource(request, response, jspUri); return; } - wrapper = new JspServletWrapper(config, options, jspUri, - rctxt); - rctxt.addWrapper(jspUri,wrapper); + wrapper = new JspServletWrapper(config, options, jspUri, rctxt); + rctxt.addWrapper(jspUri, wrapper); } } } @@ -382,14 +355,12 @@ } - private void handleMissingResource(HttpServletRequest request, - HttpServletResponse response, String jspUri) + private void handleMissingResource(HttpServletRequest request, HttpServletResponse response, String jspUri) throws ServletException, IOException { - String includeRequestUri = - (String)request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); + String includeRequestUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); - String msg = Localizer.getMessage("jsp.error.file.not.found",jspUri); + String msg = Localizer.getMessage("jsp.error.file.not.found", jspUri); if (includeRequestUri != null) { // This file was included. Throw an exception as // a response.sendError() will be ignored diff -Nru tomcat10-10.1.34/java/org/apache/jasper/servlet/JspServletWrapper.java tomcat10-10.1.52/java/org/apache/jasper/servlet/JspServletWrapper.java --- tomcat10-10.1.34/java/org/apache/jasper/servlet/JspServletWrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/servlet/JspServletWrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,26 +49,13 @@ import org.apache.tomcat.Jar; /** - * The JSP engine (a.k.a Jasper). - * - * The servlet container is responsible for providing a - * URLClassLoader for the web application context Jasper - * is being used in. Jasper will try get the Tomcat - * ServletContext attribute for its ServletContext class - * loader, if that fails, it uses the parent class loader. - * In either case, it must be a URLClassLoader. - * - * @author Anil K. Vijendran - * @author Harish Prabandham - * @author Remy Maucherat - * @author Kin-man Chung - * @author Glenn Nielsen - * @author Tim Fennell + * The Jasper JSP engine. The servlet container is responsible for providing a URLClassLoader for the web application + * context Jasper is being used in. Jasper will try to get the Tomcat ServletContext attribute for its ServletContext + * class loader, if that fails, it uses the parent class loader. In either case, it must be a URLClassLoader. */ public class JspServletWrapper { - private static final Map ALWAYS_OUTDATED_DEPENDENCIES = - new HashMap<>(); + private static final Map ALWAYS_OUTDATED_DEPENDENCIES = new HashMap<>(); static { // If this is missing, @@ -86,11 +73,9 @@ private final ServletConfig config; private final Options options; /* - * The servlet / tag file needs a compilation check on first access. Use a - * separate flag (rather then theServlet == null / tagHandlerClass == null - * as it avoids the potentially expensive isOutDated() calls in - * ctxt.compile() if there are multiple concurrent requests for the servlet - * / tag before the class has been loaded. + * The servlet / tag file needs a compilation check on first access. Use a separate flag (rather than theServlet == + * null / tagHandlerClass == null) as it avoids the potentially expensive isOutDated() calls in ctxt.compile() if + * there are multiple concurrent requests for the servlet / tag before the class has been loaded. */ private volatile boolean mustCompile = true; /* Whether the servlet/tag file needs reloading on next access */ @@ -110,42 +95,33 @@ /* * JspServletWrapper for JSP pages. */ - public JspServletWrapper(ServletConfig config, Options options, - String jspUri, JspRuntimeContext rctxt) { + public JspServletWrapper(ServletConfig config, Options options, String jspUri, JspRuntimeContext rctxt) { this.isTagFile = false; this.config = config; this.options = options; this.jspUri = jspUri; - unloadByCount = options.getMaxLoadedJsps() > 0 ? true : false; - unloadByIdle = options.getJspIdleTimeout() > 0 ? true : false; - unloadAllowed = unloadByCount || unloadByIdle ? true : false; - ctxt = new JspCompilationContext(jspUri, options, - config.getServletContext(), - this, rctxt); + unloadByCount = options.getMaxLoadedJsps() > 0; + unloadByIdle = options.getJspIdleTimeout() > 0; + unloadAllowed = unloadByCount || unloadByIdle; + ctxt = new JspCompilationContext(jspUri, options, config.getServletContext(), this, rctxt); } /* * JspServletWrapper for tag files. */ - public JspServletWrapper(ServletContext servletContext, - Options options, - String tagFilePath, - TagInfo tagInfo, - JspRuntimeContext rctxt, - Jar tagJar) { + public JspServletWrapper(ServletContext servletContext, Options options, String tagFilePath, TagInfo tagInfo, + JspRuntimeContext rctxt, Jar tagJar) { this.isTagFile = true; - this.config = null; // not used + this.config = null; // not used this.options = options; this.jspUri = tagFilePath; this.tripCount = 0; - unloadByCount = options.getMaxLoadedJsps() > 0 ? true : false; - unloadByIdle = options.getJspIdleTimeout() > 0 ? true : false; - unloadAllowed = unloadByCount || unloadByIdle ? true : false; - ctxt = new JspCompilationContext(jspUri, tagInfo, options, - servletContext, this, rctxt, - tagJar); + unloadByCount = options.getMaxLoadedJsps() > 0; + unloadByIdle = options.getJspIdleTimeout() > 0; + unloadAllowed = unloadByCount || unloadByIdle; + ctxt = new JspCompilationContext(jspUri, tagInfo, options, servletContext, this, rctxt, tagJar); } public JspCompilationContext getJspEngineContext() { @@ -166,13 +142,11 @@ public Servlet getServlet() throws ServletException { /* - * DCL on 'reload' requires that 'reload' be volatile - * (this also forces a read memory barrier, ensuring the new servlet - * object is read consistently). + * DCL on 'reload' requires that 'reload' be volatile (this also forces a read memory barrier, ensuring the new + * servlet object is read consistently). * - * When running in non development mode with a checkInterval it is - * possible (see BZ 62603) for a race condition to cause failures - * if a Servlet or tag is reloaded while a compile check is running + * When running in non development mode with a checkInterval it is possible (see BZ 62603) for a race condition + * to cause failures if a Servlet or tag is reloaded while a compile check is running */ if (getReloadInternal() || theServlet == null) { synchronized (this) { @@ -188,8 +162,7 @@ InstanceManager instanceManager = InstanceManagerFactory.getInstanceManager(config); servlet = (Servlet) instanceManager.newInstance(ctxt.getFQCN(), ctxt.getJspLoader()); } catch (Exception e) { - Throwable t = ExceptionUtils - .unwrapInvocationTargetException(e); + Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(t); throw new JasperException(t); } @@ -223,8 +196,7 @@ } /** - * Sets the last-modified time of the servlet class file associated with - * this JspServletWrapper. + * Sets the last-modified time of the servlet class file associated with this JspServletWrapper. * * @param lastModified Last-modified time of servlet class */ @@ -242,7 +214,7 @@ // a new loader will be created which will load the new // class. // TODO Are there inefficiencies between reload and the - // isOutDated() check? + // isOutDated() check? ctxt.clearJspLoader(); } } @@ -251,7 +223,9 @@ /** * Compile (if needed) and load a tag file. + * * @return the loaded class + * * @throws JasperException Error compiling or loading tag file */ public Class loadTagFile() throws JasperException { @@ -290,11 +264,11 @@ } /** - * Compile and load a prototype for the Tag file. This is needed - * when compiling tag files with circular dependencies. A prototype - * (skeleton) with no dependencies on other other tag files is - * generated and compiled. + * Compile and load a prototype for the Tag file. This is needed when compiling tag files with circular + * dependencies. A prototype (skeleton) with no dependencies on other tag files is generated and compiled. + * * @return the loaded class + * * @throws JasperException Error compiling or loading tag file */ public Class loadTagFilePrototype() throws JasperException { @@ -309,6 +283,7 @@ /** * Get a list of files that the current page has source dependency on. + * * @return the map of dependent resources */ public Map getDependants() { @@ -360,9 +335,7 @@ return unloadHandle; } - public void service(HttpServletRequest request, - HttpServletResponse response, - boolean precompile) + public void service(HttpServletRequest request, HttpServletResponse response, boolean precompile) throws ServletException, IOException, FileNotFoundException { Servlet servlet; @@ -376,9 +349,8 @@ if ((available > 0L) && (available < Long.MAX_VALUE)) { if (available > System.currentTimeMillis()) { response.setDateHeader("Retry-After", available); - response.sendError - (HttpServletResponse.SC_SERVICE_UNAVAILABLE, - Localizer.getMessage("jsp.error.unavailable")); + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, + Localizer.getMessage("jsp.error.unavailable")); return; } @@ -422,11 +394,11 @@ throw handleJspException(ex); } throw ex; - } catch (Exception ex) { + } catch (Exception e) { if (options.getDevelopment()) { - throw handleJspException(ex); + throw handleJspException(e); } - throw new JasperException(ex); + throw new JasperException(e); } try { @@ -434,7 +406,7 @@ * (3) Handle limitation of number of loaded Jsps */ if (unloadAllowed) { - synchronized(this) { + synchronized (this) { if (unloadByCount) { if (unloadHandle == null) { unloadHandle = ctxt.getRuntimeContext().push(this); @@ -455,8 +427,7 @@ */ servlet.service(request, response); } catch (UnavailableException ex) { - String includeRequestUri = (String) - request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); + String includeRequestUri = (String) request.getAttribute(RequestDispatcher.INCLUDE_REQUEST_URI); if (includeRequestUri != null) { // This file was included. Throw an exception as // a response.sendError() will be ignored by the @@ -466,28 +437,25 @@ int unavailableSeconds = ex.getUnavailableSeconds(); if (unavailableSeconds <= 0) { - unavailableSeconds = 60; // Arbitrary default + unavailableSeconds = 60; // Arbitrary default } - available = System.currentTimeMillis() + - (unavailableSeconds * 1000L); - response.sendError - (HttpServletResponse.SC_SERVICE_UNAVAILABLE, - ex.getMessage()); + available = System.currentTimeMillis() + (unavailableSeconds * 1000L); + response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, ex.getMessage()); } catch (ServletException | IllegalStateException ex) { - if(options.getDevelopment()) { + if (options.getDevelopment()) { throw handleJspException(ex); } throw ex; - } catch (IOException ex) { + } catch (IOException ioe) { if (options.getDevelopment()) { - throw new IOException(handleJspException(ex).getMessage(), ex); + throw new IOException(handleJspException(ioe).getMessage(), ioe); } - throw ex; - } catch (Exception ex) { - if(options.getDevelopment()) { - throw handleJspException(ex); + throw ioe; + } catch (Exception e) { + if (options.getDevelopment()) { + throw handleJspException(e); } - throw new JasperException(ex); + throw new JasperException(e); } } @@ -506,8 +474,7 @@ Throwable t = ExceptionUtils.unwrapInvocationTargetException(e); ExceptionUtils.handleThrowable(t); // Log any exception, since it can't be passed along - log.error(Localizer.getMessage("jsp.error.file.not.found", - e.getMessage()), t); + log.error(Localizer.getMessage("jsp.error.file.not.found", e.getMessage()), t); } } } @@ -518,6 +485,7 @@ public long getLastModificationTest() { return lastModificationTest; } + /** * @param lastModificationTest The lastModificationTest to set. */ @@ -533,23 +501,27 @@ } /** - *

          Attempts to construct a JasperException that contains helpful information - * about what went wrong. Uses the JSP compiler system to translate the line - * number in the generated servlet that originated the exception to a line - * number in the JSP. Then constructs an exception containing that - * information, and a snippet of the JSP to help debugging. - * Please see https://bz.apache.org/bugzilla/show_bug.cgi?id=37062 and - * http://www.tfenne.com/jasper/ for more details. + *

          + * Attempts to construct a JasperException that contains helpful information about what went wrong. Uses the JSP + * compiler system to translate the line number in the generated servlet that originated the exception to a line + * number in the JSP. Then constructs an exception containing that information, and a snippet of the JSP to help + * debugging. Please see BZ 37062 for more + * details. *

          * * @param ex the exception that was the cause of the problem. + * * @return a JasperException with more detailed information */ protected JasperException handleJspException(Exception ex) { try { Throwable realException = ex; + // Unwrap Servlet exception once if (ex instanceof ServletException) { - realException = ((ServletException) ex).getRootCause(); + Throwable rootCause = ((ServletException) ex).getRootCause(); + if (rootCause != null) { + realException = rootCause; + } } // Find the first stack frame that represents code generated by @@ -573,7 +545,7 @@ if (smap == null) { // If we couldn't find a frame in the stack trace corresponding - // to the generated servlet class or we don't have a copy of the + // to the generated servlet class, or we don't have a copy of the // smap to hand, we can't really add anything return new JasperException(ex); } @@ -592,19 +564,17 @@ source.getFileName(), source.getLineNumber(), null, ctxt); if (options.getDisplaySourceFragment()) { - return new JasperException(Localizer.getMessage - ("jsp.exception", detail.getJspFileName(), - "" + source.getLineNumber()) + System.lineSeparator() + - System.lineSeparator() + detail.getJspExtract() + - System.lineSeparator() + System.lineSeparator() + - "Stacktrace:", ex); + return new JasperException( + Localizer.getMessage("jsp.exception", detail.getJspFileName(), "" + source.getLineNumber()) + + System.lineSeparator() + System.lineSeparator() + detail.getJspExtract() + + System.lineSeparator() + System.lineSeparator() + "Stacktrace:", + ex); } - return new JasperException(Localizer.getMessage - ("jsp.exception", detail.getJspFileName(), - "" + source.getLineNumber()), ex); - } catch (Exception je) { + return new JasperException( + Localizer.getMessage("jsp.exception", detail.getJspFileName(), "" + source.getLineNumber()), ex); + } catch (Exception e) { // If anything goes wrong, just revert to the original behaviour if (ex instanceof JasperException) { return (JasperException) ex; diff -Nru tomcat10-10.1.34/java/org/apache/jasper/servlet/TldPreScanned.java tomcat10-10.1.52/java/org/apache/jasper/servlet/TldPreScanned.java --- tomcat10-10.1.34/java/org/apache/jasper/servlet/TldPreScanned.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/servlet/TldPreScanned.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,19 +29,19 @@ private final Collection preScannedURLs; - public TldPreScanned (ServletContext context, boolean namespaceAware, boolean validation, - boolean blockExternal, Collection preScannedTlds) { + public TldPreScanned(ServletContext context, boolean namespaceAware, boolean validation, boolean blockExternal, + Collection preScannedTlds) { super(context, namespaceAware, validation, blockExternal); preScannedURLs = preScannedTlds; } @Override public void scanJars() { - for (URL url : preScannedURLs){ + for (URL url : preScannedURLs) { String str = url.toExternalForm(); int a = str.indexOf("jar:"); int b = str.indexOf("!/"); - if (a >= 0 && b> 0) { + if (a >= 0 && b > 0) { String fileUrl = str.substring(a + 4, b); String path = str.substring(b + 2); try { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/servlet/TldScanner.java tomcat10-10.1.52/java/org/apache/jasper/servlet/TldScanner.java --- tomcat10-10.1.34/java/org/apache/jasper/servlet/TldScanner.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/servlet/TldScanner.java 2026-01-23 19:33:36.000000000 +0000 @@ -59,26 +59,20 @@ private static final String WEB_INF = "/WEB-INF/"; private final ServletContext context; private final TldParser tldParser; - private final Map uriTldResourcePathMap = new HashMap<>(); - private final Map tldResourcePathTaglibXmlMap = new HashMap<>(); + private final Map uriTldResourcePathMap = new HashMap<>(); + private final Map tldResourcePathTaglibXmlMap = new HashMap<>(); private final List listeners = new ArrayList<>(); /** * Initialise with the application's ServletContext. * * @param context the application's servletContext - * @param namespaceAware should the XML parser used to parse TLD files be - * configured to be name space aware - * @param validation should the XML parser used to parse TLD files be - * configured to use validation - * @param blockExternal should the XML parser used to parse TLD files be - * configured to be block references to external - * entities + * @param namespaceAware should the XML parser used to parse TLD files be configured to be name space aware + * @param validation should the XML parser used to parse TLD files be configured to use validation + * @param blockExternal should the XML parser used to parse TLD files be configured to be block references to + * external entities */ - public TldScanner(ServletContext context, - boolean namespaceAware, - boolean validation, - boolean blockExternal) { + public TldScanner(ServletContext context, boolean namespaceAware, boolean validation, boolean blockExternal) { this.context = context; this.tldParser = new TldParser(namespaceAware, validation, blockExternal); @@ -109,13 +103,12 @@ * * @return the map of URI to TldResourcePath */ - public Map getUriTldResourcePathMap() { + public Map getUriTldResourcePathMap() { return uriTldResourcePathMap; } /** - * Returns the map of TldResourcePath to parsed XML files built by this - * scanner. + * Returns the map of TldResourcePath to parsed XML files built by this scanner. * * @return the map of TldResourcePath to parsed XML files */ @@ -133,11 +126,10 @@ } /** - * Set the class loader used by the digester to create objects as a result - * of this scan. Normally this only needs to be set when using JspC. + * Set the class loader used by the digester to create objects as a result of this scan. Normally this only needs to + * be set when using JspC. * - * @param classLoader Class loader to use when creating new objects while - * parsing TLDs + * @param classLoader Class loader to use when creating new objects while parsing TLDs */ public void setClassLoader(ClassLoader classLoader) { tldParser.setClassLoader(classLoader); @@ -151,7 +143,8 @@ /** * Scan for TLDs defined in <jsp-config>. - * @throws IOException Error reading resources + * + * @throws IOException Error reading resources * @throws SAXException XML parsing error */ protected void scanJspConfig() throws IOException, SAXException { @@ -172,16 +165,12 @@ resourcePath = WEB_INF + resourcePath; } if (uriTldResourcePathMap.containsKey(taglibURI)) { - log.warn(Localizer.getMessage(MSG + ".webxmlSkip", - resourcePath, - taglibURI)); + log.warn(Localizer.getMessage(MSG + ".webxmlSkip", resourcePath, taglibURI)); continue; } if (log.isTraceEnabled()) { - log.trace(Localizer.getMessage(MSG + ".webxmlAdd", - resourcePath, - taglibURI)); + log.trace(Localizer.getMessage(MSG + ".webxmlAdd", resourcePath, taglibURI)); } URL url = context.getResource(resourcePath); @@ -202,10 +191,7 @@ listeners.addAll(tld.getListeners()); } } else { - log.warn(Localizer.getMessage(MSG + ".webxmlFailPathDoesNotExist", - resourcePath, - taglibURI)); - continue; + log.warn(Localizer.getMessage(MSG + ".webxmlFailPathDoesNotExist", resourcePath, taglibURI)); } } } @@ -214,11 +200,11 @@ * Scan web application resources for TLDs, recursively. * * @param startPath the directory resource to scan + * * @throws IOException if there was a problem scanning for or loading a TLD * @throws SAXException if there was a problem parsing a TLD */ - protected void scanResourcePaths(String startPath) - throws IOException, SAXException { + protected void scanResourcePaths(String startPath) throws IOException, SAXException { boolean found = false; Set dirList = context.getResourcePaths(startPath); @@ -266,8 +252,7 @@ } protected void parseTld(String resourcePath) throws IOException, SAXException { - TldResourcePath tldResourcePath = - new TldResourcePath(context.getResource(resourcePath), resourcePath); + TldResourcePath tldResourcePath = new TldResourcePath(context.getResource(resourcePath), resourcePath); parseTld(tldResourcePath); } @@ -301,16 +286,13 @@ boolean found = false; URL jarFileUrl = jar.getJarFileURL(); jar.nextEntry(); - for (String entryName = jar.getEntryName(); - entryName != null; - jar.nextEntry(), entryName = jar.getEntryName()) { - if (!(entryName.startsWith("META-INF/") && - entryName.endsWith(TLD_EXT))) { + for (String entryName = jar.getEntryName(); entryName != null; jar.nextEntry(), entryName = + jar.getEntryName()) { + if (!(entryName.startsWith("META-INF/") && entryName.endsWith(TLD_EXT))) { continue; } found = true; - TldResourcePath tldResourcePath = - new TldResourcePath(jarFileUrl, webappPath, entryName); + TldResourcePath tldResourcePath = new TldResourcePath(jarFileUrl, webappPath, entryName); try { parseTld(tldResourcePath); } catch (SAXException e) { @@ -324,29 +306,24 @@ } else { foundJarWithoutTld = true; if (log.isDebugEnabled()) { - log.debug(Localizer.getMessage( - "jsp.tldCache.noTldInJar", jarFileUrl.toString())); + log.debug(Localizer.getMessage("jsp.tldCache.noTldInJar", jarFileUrl.toString())); } } } @Override - public void scan(File file, final String webappPath, boolean isWebapp) - throws IOException { + public void scan(File file, final String webappPath, boolean isWebapp) throws IOException { File metaInf = new File(file, "META-INF"); if (!metaInf.isDirectory()) { return; } foundFileWithoutTld = false; final Path filePath = file.toPath(); - Files.walkFileTree(metaInf.toPath(), new SimpleFileVisitor() { + Files.walkFileTree(metaInf.toPath(), new SimpleFileVisitor<>() { @Override - public FileVisitResult visitFile(Path file, - BasicFileAttributes attrs) - throws IOException { + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { Path fileName = file.getFileName(); - if (fileName == null || !fileName.toString().toLowerCase( - Locale.ENGLISH).endsWith(TLD_EXT)) { + if (fileName == null || !fileName.toString().toLowerCase(Locale.ENGLISH).endsWith(TLD_EXT)) { return FileVisitResult.CONTINUE; } @@ -355,8 +332,7 @@ if (webappPath == null) { resourcePath = null; } else { - String subPath = file.subpath( - filePath.getNameCount(), file.getNameCount()).toString(); + String subPath = file.subpath(filePath.getNameCount(), file.getNameCount()).toString(); if ('/' != File.separatorChar) { subPath = subPath.replace(File.separatorChar, '/'); } @@ -375,13 +351,11 @@ }); if (foundFileWithoutTld) { if (log.isDebugEnabled()) { - log.debug(Localizer.getMessage("jsp.tldCache.tldInDir", - file.getAbsolutePath())); + log.debug(Localizer.getMessage("jsp.tldCache.tldInDir", file.getAbsolutePath())); } } else { if (log.isDebugEnabled()) { - log.debug(Localizer.getMessage("jsp.tldCache.noTldInDir", - file.getAbsolutePath())); + log.debug(Localizer.getMessage("jsp.tldCache.noTldInDir", file.getAbsolutePath())); } } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/Util.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/Util.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/Util.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/Util.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,9 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.util.Locale; +import java.util.Objects; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.WriteListener; @@ -35,22 +37,18 @@ import org.apache.jasper.compiler.Localizer; /** - * Util contains some often used consts, static methods and embedded class - * to support the JSTL tag plugin. + * Util contains some often used consts, static methods and embedded class to support the JSTL tag plugin. */ public class Util { - private static final String VALID_SCHEME_CHAR = - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-"; + private static final String VALID_SCHEME_CHAR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+.-"; - public static final String DEFAULT_ENCODING = - "ISO-8859-1"; + public static final String DEFAULT_ENCODING = "ISO-8859-1"; private static final int HIGHEST_SPECIAL = '>'; - private static final char[][] specialCharactersRepresentation = - new char[HIGHEST_SPECIAL + 1][]; + private static final char[][] specialCharactersRepresentation = new char[HIGHEST_SPECIAL + 1][]; static { specialCharactersRepresentation['&'] = "&".toCharArray(); @@ -61,26 +59,22 @@ } /** - * Converts the given string description of a scope to the corresponding - * PageContext constant. - * - * The validity of the given scope has already been checked by the - * appropriate TLV. + * Converts the given string description of a scope to the corresponding PageContext constant. The validity of the + * given scope has already been checked by the appropriate TLV. * * @param scope String description of scope * - * @return PageContext constant corresponding to given scope description - * - * taken from org.apache.taglibs.standard.tag.common.core.Util + * @return PageContext constant corresponding to given scope description taken from + * org.apache.taglibs.standard.tag.common.core.Util */ - public static int getScope(String scope){ + public static int getScope(String scope) { int ret = PageContext.PAGE_SCOPE; - if("request".equalsIgnoreCase(scope)){ + if ("request".equalsIgnoreCase(scope)) { ret = PageContext.REQUEST_SCOPE; - }else if("session".equalsIgnoreCase(scope)){ + } else if ("session".equalsIgnoreCase(scope)) { ret = PageContext.SESSION_SCOPE; - }else if("application".equalsIgnoreCase(scope)){ + } else if ("application".equalsIgnoreCase(scope)) { ret = PageContext.APPLICATION_SCOPE; } @@ -88,24 +82,25 @@ } /** - * Returns true if our current URL is absolute, - * false otherwise. - * taken from org.apache.taglibs.standard.tag.common.core.ImportSupport + * Returns true if our current URL is absolute, false otherwise. taken from + * org.apache.taglibs.standard.tag.common.core.ImportSupport + * * @param url The URL + * * @return true if the URL is absolute */ - public static boolean isAbsoluteUrl(String url){ - if(url == null){ + public static boolean isAbsoluteUrl(String url) { + if (url == null) { return false; } int colonPos = url.indexOf(':'); - if(colonPos == -1){ + if (colonPos == -1) { return false; } - for(int i=0;i + *
        • {@code &} -> {@code &}
        • + *
        • {@code <} -> {@code <}
        • + *
        • {@code >} -> {@code >}
        • + *
        • {@code "} -> {@code "}
        • + *
        • {@code '} -> {@code '}
        • + * + * See also OutSupport.writeEscapedXml(). taken from org.apache.taglibs.standard.tag.common.core.Util * - * See also OutSupport.writeEscapedXml(). - * - * taken from org.apache.taglibs.standard.tag.common.core.Util * @param buffer Data to escape + * * @return escaped data */ public static String escapeXml(String buffer) { String result = escapeXml(buffer.toCharArray(), buffer.length()); - if (result == null) { - return buffer; - } else { - return result; - } + return Objects.requireNonNullElse(result, buffer); } @SuppressWarnings("null") // escapedBuffer cannot be null @@ -197,7 +188,7 @@ } // add unescaped portion if (start < i) { - escapedBuffer.append(arrayBuffer,start,i-start); + escapedBuffer.append(arrayBuffer, start, i - start); } start = i + 1; // add escaped xml @@ -211,31 +202,30 @@ } // add rest of unescaped portion if (start < length) { - escapedBuffer.append(arrayBuffer,start,length-start); + escapedBuffer.append(arrayBuffer, start, length - start); } return escapedBuffer.toString(); } /** - * Utility methods - * taken from org.apache.taglibs.standard.tag.common.core.UrlSupport - * @param url The URL - * @param context The context + * Utility methods taken from org.apache.taglibs.standard.tag.common.core.UrlSupport + * + * @param url The URL + * @param context The context * @param pageContext The page context + * * @return the absolute URL + * * @throws JspException If the URL doesn't start with '/' */ - public static String resolveUrl( - String url, String context, PageContext pageContext) - throws JspException { + public static String resolveUrl(String url, String context, PageContext pageContext) throws JspException { // don't touch absolute URLs if (isAbsoluteUrl(url)) { return url; } // normalize relative URLs against a context root - HttpServletRequest request = - (HttpServletRequest) pageContext.getRequest(); + HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); if (context == null) { if (url.startsWith("/")) { return request.getContextPath() + url; @@ -258,10 +248,10 @@ } /** - * Wraps responses to allow us to retrieve results as Strings. Mainly taken - * from org.apache.taglibs.standard.tag.common.core.importSupport. + * Wraps responses to allow us to retrieve results as Strings. Mainly taken from + * org.apache.taglibs.standard.tag.common.core.importSupport. */ - public static class ImportResponseWrapper extends HttpServletResponseWrapper{ + public static class ImportResponseWrapper extends HttpServletResponseWrapper { private final StringWriter sw = new StringWriter(); private final ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -284,7 +274,6 @@ } - }; private boolean isWriterUsed; private boolean isStreamUsed; @@ -333,11 +322,11 @@ return status; } - public String getCharEncoding(){ + public String getCharEncoding() { return this.charEncoding; } - public void setCharEncoding(String ce){ + public void setCharEncoding(String ce) { this.charEncoding = ce; } @@ -345,13 +334,12 @@ if (isWriterUsed) { return sw.toString(); } else if (isStreamUsed) { - if (this.charEncoding != null && !this.charEncoding.equals("")) { + if (this.charEncoding != null && !this.charEncoding.isEmpty()) { return bos.toString(charEncoding); } else { - return bos.toString("ISO-8859-1"); + return bos.toString(StandardCharsets.ISO_8859_1); } - } - else { + } else { return ""; // target didn't write anything } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Catch.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Catch.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Catch.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Catch.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,41 +24,41 @@ @Override public void doTag(TagPluginContext ctxt) { - //flag for the existence of the var attribute + // flag for the existence of the var attribute boolean hasVar = ctxt.isAttributeSpecified("var"); - //temp name for exception and caught + // temp name for exception and caught String exceptionName = ctxt.getTemporaryVariableName(); String caughtName = ctxt.getTemporaryVariableName(); - //main part to generate code + // main part to generate code ctxt.generateJavaSource("boolean " + caughtName + " = false;"); ctxt.generateJavaSource("try{"); ctxt.generateBody(); ctxt.generateJavaSource("}"); - //do catch + // do catch ctxt.generateJavaSource("catch(Throwable " + exceptionName + "){"); - //if the var specified, the exception object should - //be set to the attribute "var" defines in page scope - if(hasVar){ + // if the var specified, the exception object should + // be set to the attribute "var" defines in page scope + if (hasVar) { String strVar = ctxt.getConstantAttribute("var"); - ctxt.generateJavaSource(" pageContext.setAttribute(\"" + strVar + "\", " - + exceptionName + ", PageContext.PAGE_SCOPE);"); + ctxt.generateJavaSource( + " pageContext.setAttribute(\"" + strVar + "\", " + exceptionName + ", PageContext.PAGE_SCOPE);"); } - //whenever there's exception caught, - //the flag caught should be set true; + // whenever there's exception caught, + // the flag caught should be set true; ctxt.generateJavaSource(" " + caughtName + " = true;"); ctxt.generateJavaSource("}"); - //do finally + // do finally ctxt.generateJavaSource("finally{"); - //if var specified, the attribute it defines - //in page scope should be removed - if(hasVar){ + // if var specified, the attribute it defines + // in page scope should be removed + if (hasVar) { String strVar = ctxt.getConstantAttribute("var"); ctxt.generateJavaSource(" if(!" + caughtName + "){"); ctxt.generateJavaSource(" pageContext.removeAttribute(\"" + strVar + "\", PageContext.PAGE_SCOPE);"); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/ForEach.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/ForEach.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/ForEach.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/ForEach.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,7 +26,7 @@ @Override public void doTag(TagPluginContext ctxt) { - String index = null; + String index; boolean hasVarStatus = ctxt.isAttributeSpecified("varStatus"); if (hasVarStatus) { @@ -71,8 +71,7 @@ } /** - * Generate codes for Collections - * The pseudo code is: + * Generate codes for Collections The pseudocode is: */ private void doCollection(TagPluginContext ctxt) { @@ -84,7 +83,7 @@ ctxt.generateAttribute("items"); ctxt.generateJavaSource(";"); - String indexV=null, beginV=null, endV=null, stepV=null; + String indexV = null, beginV = null, endV = null, stepV = null; if (hasBegin) { beginV = ctxt.getTemporaryVariableName(); ctxt.generateJavaSource("int " + beginV + " = "); @@ -161,9 +160,8 @@ if (hasBegin) { String tV = ctxt.getTemporaryVariableName(); - ctxt.generateJavaSource("for (int " + tV + "=" + beginV + ";" + - tV + ">0 && " + iterV + ".hasNext(); " + - tV + "--)"); + ctxt.generateJavaSource( + "for (int " + tV + "=" + beginV + ";" + tV + ">0 && " + iterV + ".hasNext(); " + tV + "--)"); ctxt.generateJavaSource(iterV + ".next();"); } @@ -178,9 +176,8 @@ if (hasStep) { String tV = ctxt.getTemporaryVariableName(); - ctxt.generateJavaSource("for (int " + tV + "=" + stepV + "-1;" + - tV + ">0 && " + iterV + ".hasNext(); " + - tV + "--)"); + ctxt.generateJavaSource( + "for (int " + tV + "=" + stepV + "-1;" + tV + ">0 && " + iterV + ".hasNext(); " + tV + "--)"); ctxt.generateJavaSource(iterV + ".next();"); } if (hasEnd) { @@ -190,8 +187,7 @@ ctxt.generateJavaSource(indexV + "++;"); } if (hasBegin) { - ctxt.generateJavaSource("if(" + beginV + "+" + indexV + - ">"+ endV + ")"); + ctxt.generateJavaSource("if(" + beginV + "+" + indexV + ">" + endV + ")"); } else { ctxt.generateJavaSource("if(" + indexV + ">" + endV + ")"); } @@ -205,7 +201,7 @@ * Generate iterators for data types supported in items */ private void generateIterators(TagPluginContext ctxt) { - + //@formatter:off // Object[] ctxt.generateDeclaration("ObjectArrayIterator", "private Iterator toIterator(final Object[] a){\n" + @@ -344,6 +340,6 @@ " });\n" + "}" ); - + //@formatter:on } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/ForTokens.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/ForTokens.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/ForTokens.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/ForTokens.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,60 +25,61 @@ public void doTag(TagPluginContext ctxt) { boolean hasVar, hasVarStatus, hasBegin, hasEnd, hasStep; - //init the flags + // init the flags hasVar = ctxt.isAttributeSpecified("var"); hasVarStatus = ctxt.isAttributeSpecified("varStatus"); hasBegin = ctxt.isAttributeSpecified("begin"); hasEnd = ctxt.isAttributeSpecified("end"); hasStep = ctxt.isAttributeSpecified("step"); - if(hasVarStatus){ + if (hasVarStatus) { ctxt.dontUseTagPlugin(); return; } - //define all the temp variables' names + // define all the temp variables' names String itemsName = ctxt.getTemporaryVariableName(); String delimsName = ctxt.getTemporaryVariableName(); String stName = ctxt.getTemporaryVariableName(); String beginName = ctxt.getTemporaryVariableName(); - String endName = ctxt.getTemporaryVariableName(); + String endName = ctxt.getTemporaryVariableName(); String stepName = ctxt.getTemporaryVariableName(); String index = ctxt.getTemporaryVariableName(); - String temp = ctxt.getTemporaryVariableName(); + String temp = ctxt.getTemporaryVariableName(); String tokensCountName = ctxt.getTemporaryVariableName(); - //get the value of the "items" attribute + // get the value of the "items" attribute ctxt.generateJavaSource("String " + itemsName + " = (String)"); ctxt.generateAttribute("items"); ctxt.generateJavaSource(";"); - //get the value of the "delim" attribute + // get the value of the "delim" attribute ctxt.generateJavaSource("String " + delimsName + " = (String)"); ctxt.generateAttribute("delims"); ctxt.generateJavaSource(";"); - //new a StringTokenizer Object according to the "items" and the "delim" - ctxt.generateJavaSource("java.util.StringTokenizer " + stName + " = " + - "new java.util.StringTokenizer(" + itemsName + ", " + delimsName + ");"); + // new a StringTokenizer Object according to the "items" and the "delim" + ctxt.generateJavaSource("java.util.StringTokenizer " + stName + " = " + "new java.util.StringTokenizer(" + + itemsName + ", " + delimsName + ");"); - //if "begin" specified, move the token to the "begin" place - //and record the begin index. default begin place is 0. + // if "begin" specified, move the token to the "begin" place + // and record the begin index. default begin place is 0. ctxt.generateJavaSource("int " + tokensCountName + " = " + stName + ".countTokens();"); - if(hasBegin){ - ctxt.generateJavaSource("int " + beginName + " = " ); + if (hasBegin) { + ctxt.generateJavaSource("int " + beginName + " = "); ctxt.generateAttribute("begin"); ctxt.generateJavaSource(";"); - ctxt.generateJavaSource("for(int " + index + " = 0; " + index + " < " + beginName + " && " + stName + ".hasMoreTokens(); " + index + "++, " + stName + ".nextToken()){}"); - }else{ + ctxt.generateJavaSource("for(int " + index + " = 0; " + index + " < " + beginName + " && " + stName + + ".hasMoreTokens(); " + index + "++, " + stName + ".nextToken()){}"); + } else { ctxt.generateJavaSource("int " + beginName + " = 0;"); } - //when "end" is specified, if the "end" is more than the last index, - //record the end place as the last index, otherwise, record it as "end"; - //default end place is the last index - if(hasEnd){ - ctxt.generateJavaSource("int " + endName + " = 0;" ); + // when "end" is specified, if the "end" is more than the last index, + // record the end place as the last index, otherwise, record it as "end"; + // default end place is the last index + if (hasEnd) { + ctxt.generateJavaSource("int " + endName + " = 0;"); ctxt.generateJavaSource("if((" + tokensCountName + " - 1) < "); ctxt.generateAttribute("end"); ctxt.generateJavaSource("){"); @@ -87,26 +88,27 @@ ctxt.generateJavaSource(" " + endName + " = "); ctxt.generateAttribute("end"); ctxt.generateJavaSource(";}"); - }else{ + } else { ctxt.generateJavaSource("int " + endName + " = " + tokensCountName + " - 1;"); } - //get the step value from "step" if specified. - //default step value is 1. - if(hasStep){ - ctxt.generateJavaSource("int " + stepName + " = " ); + // get the step value from "step" if specified. + // default step value is 1. + if (hasStep) { + ctxt.generateJavaSource("int " + stepName + " = "); ctxt.generateAttribute("step"); ctxt.generateJavaSource(";"); - }else{ + } else { ctxt.generateJavaSource("int " + stepName + " = 1;"); } - //the loop - ctxt.generateJavaSource("for(int " + index + " = " + beginName + "; " + index + " <= " + endName + "; " + index + "++){"); + // the loop + ctxt.generateJavaSource( + "for(int " + index + " = " + beginName + "; " + index + " <= " + endName + "; " + index + "++){"); ctxt.generateJavaSource(" String " + temp + " = " + stName + ".nextToken();"); ctxt.generateJavaSource(" if(((" + index + " - " + beginName + ") % " + stepName + ") == 0){"); - //if var specified, put the current token into the attribute "var" defines. - if(hasVar){ + // if var specified, put the current token into the attribute "var" defines. + if (hasVar) { String strVar = ctxt.getConstantAttribute("var"); ctxt.generateJavaSource(" pageContext.setAttribute(\"" + strVar + "\", " + temp + ");"); } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Import.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Import.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Import.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Import.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,19 +26,19 @@ public void doTag(TagPluginContext ctxt) { boolean hasContext, hasVar, hasScope, hasVarReader, hasCharEncoding; - //flags - hasContext = ctxt.isAttributeSpecified("context"); + // flags + hasContext = ctxt.isAttributeSpecified("context"); hasVar = ctxt.isAttributeSpecified("var"); hasScope = ctxt.isAttributeSpecified("scope"); hasVarReader = ctxt.isAttributeSpecified("varReader"); hasCharEncoding = ctxt.isAttributeSpecified("charEncoding"); - //variables' names + // variables' names String urlName = ctxt.getTemporaryVariableName(); String contextName = ctxt.getTemporaryVariableName(); - String iauName = ctxt.getTemporaryVariableName(); // is absolute url - String urlObjName = ctxt.getTemporaryVariableName(); //URL object - String ucName = ctxt.getTemporaryVariableName(); //URLConnection + String iauName = ctxt.getTemporaryVariableName(); // is absolute url + String urlObjName = ctxt.getTemporaryVariableName(); // URL object + String ucName = ctxt.getTemporaryVariableName(); // URLConnection String inputStreamName = ctxt.getTemporaryVariableName(); String tempReaderName = ctxt.getTemporaryVariableName(); String tempReaderName2 = ctxt.getTemporaryVariableName(); @@ -49,172 +49,187 @@ String servletContextName = ctxt.getTemporaryVariableName(); String servletPathName = ctxt.getTemporaryVariableName(); String requestDispatcherName = ctxt.getTemporaryVariableName(); - String irwName = ctxt.getTemporaryVariableName(); //ImportResponseWrapper name - String brName = ctxt.getTemporaryVariableName(); //BufferedReader name - String sbName = ctxt.getTemporaryVariableName(); //StringBuilder name + String irwName = ctxt.getTemporaryVariableName(); // ImportResponseWrapper name + String brName = ctxt.getTemporaryVariableName(); // BufferedReader name + String sbName = ctxt.getTemporaryVariableName(); // StringBuilder name String tempStringName = ctxt.getTemporaryVariableName(); - //is absolute url + // is absolute url ctxt.generateJavaSource("boolean " + iauName + ";"); - //get the url value + // get the url value ctxt.generateJavaSource("String " + urlName + " = "); ctxt.generateAttribute("url"); ctxt.generateJavaSource(";"); - //validate the url + // validate the url ctxt.generateJavaSource("if(" + urlName + " == null || " + urlName + ".equals(\"\")){"); ctxt.generateJavaSource(" throw new JspTagException(\"The \\\"url\\\" attribute " + - "illegally evaluated to \\\"null\\\" or \\\"\\\" in <import>\");"); + "illegally evaluated to \\\"null\\\" or \\\"\\\" in <import>\");"); ctxt.generateJavaSource("}"); - //initialize the is_absolute_url - ctxt.generateJavaSource(iauName + " = " + - "org.apache.jasper.tagplugins.jstl.Util.isAbsoluteUrl(" + urlName + ");"); + // initialize the is_absolute_url + ctxt.generateJavaSource( + iauName + " = " + "org.apache.jasper.tagplugins.jstl.Util.isAbsoluteUrl(" + urlName + ");"); - //validate the context - if(hasContext){ + // validate the context + if (hasContext) { ctxt.generateJavaSource("String " + contextName + " = "); ctxt.generateAttribute("context"); ctxt.generateJavaSource(";"); - ctxt.generateJavaSource("if((!" + contextName + ".startsWith(\"/\")) " + - "|| (!" + urlName + ".startsWith(\"/\"))){"); + ctxt.generateJavaSource( + "if((!" + contextName + ".startsWith(\"/\")) " + "|| (!" + urlName + ".startsWith(\"/\"))){"); ctxt.generateJavaSource(" throw new JspTagException" + "(\"In URL tags, when the \\\"context\\\" attribute is specified, " + - "values of both \\\"context\\\" and \\\"url\\\" must start with \\\"/\\\".\");"); + "values of both \\\"context\\\" and \\\"url\\\" must start with \\\"/\\\".\");"); ctxt.generateJavaSource("}"); } - //define charset + // define charset ctxt.generateJavaSource("String " + charSetName + " = null;"); - //if the charEncoding attribute is specified - if(hasCharEncoding){ + // if the charEncoding attribute is specified + if (hasCharEncoding) { - //initialize the charEncoding + // initialize the charEncoding ctxt.generateJavaSource("String " + charEncodingName + " = "); ctxt.generateAttribute("charEncoding"); ctxt.generateJavaSource(";"); - //assign appropriate value to the charset - ctxt.generateJavaSource("if(null != " + charEncodingName + " " + - "&& !" + charEncodingName + ".equals(\"\")){"); - ctxt.generateJavaSource(" " + charSetName + " = " - + charEncodingName + ";"); + // assign appropriate value to the charset + ctxt.generateJavaSource( + "if(null != " + charEncodingName + " " + "&& !" + charEncodingName + ".equals(\"\")){"); + ctxt.generateJavaSource(" " + charSetName + " = " + charEncodingName + ";"); ctxt.generateJavaSource("}"); } - //reshape the url string - ctxt.generateJavaSource("if(!"+iauName+"){"); + // reshape the url string + ctxt.generateJavaSource("if(!" + iauName + "){"); ctxt.generateJavaSource(" if(!" + urlName + ".startsWith(\"/\")){"); ctxt.generateJavaSource(" String " + servletPathName + " = " + - "((HttpServletRequest)pageContext.getRequest()).getServletPath();"); - ctxt.generateJavaSource(" " + urlName + " = " - + servletPathName + ".substring(0," + servletPathName + ".lastIndexOf('/')) + '/' + " + urlName + ";"); + "((HttpServletRequest)pageContext.getRequest()).getServletPath();"); + ctxt.generateJavaSource(" " + urlName + " = " + servletPathName + ".substring(0," + servletPathName + + ".lastIndexOf('/')) + '/' + " + urlName + ";"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource("}"); - //if the varReader attribute specified - if(hasVarReader){ + // if the varReader attribute specified + if (hasVarReader) { - //get the String value of varReader + // get the String value of varReader ctxt.generateJavaSource("String " + varReaderName + " = "); ctxt.generateAttribute("varReader"); ctxt.generateJavaSource(";"); - //if the url is absolute url + // if the url is absolute url ctxt.generateJavaSource("if(" + iauName + "){"); - //get the content of the target + // get the content of the target ctxt.generateJavaSource(" java.net.URL " + urlObjName + " = new java.net.URL(" + urlName + ");"); - ctxt.generateJavaSource(" java.net.URLConnection " + ucName + " = " - + urlObjName + ".openConnection();"); - ctxt.generateJavaSource(" java.io.InputStream " + inputStreamName + " = " - + ucName + ".getInputStream();"); + ctxt.generateJavaSource(" java.net.URLConnection " + ucName + " = " + urlObjName + ".openConnection();"); + ctxt.generateJavaSource( + " java.io.InputStream " + inputStreamName + " = " + ucName + ".getInputStream();"); ctxt.generateJavaSource(" if(" + charSetName + " == null){"); - ctxt.generateJavaSource(" String " + contentTypeName + " = " - + ucName + ".getContentType();"); + ctxt.generateJavaSource(" String " + contentTypeName + " = " + ucName + ".getContentType();"); ctxt.generateJavaSource(" if(null != " + contentTypeName + "){"); ctxt.generateJavaSource(" " + charSetName + " = " + - "org.apache.jasper.tagplugins.jstl.Util.getContentTypeAttribute(" + contentTypeName + ", \"charset\");"); - ctxt.generateJavaSource(" if(" + charSetName + " == null) " - + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); + "org.apache.jasper.tagplugins.jstl.Util.getContentTypeAttribute(" + contentTypeName + + ", \"charset\");"); + ctxt.generateJavaSource(" if(" + charSetName + " == null) " + charSetName + + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); ctxt.generateJavaSource(" }else{"); - ctxt.generateJavaSource(" " + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); + ctxt.generateJavaSource( + " " + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" }"); - if(!hasCharEncoding){ + if (!hasCharEncoding) { ctxt.generateJavaSource(" String " + contentTypeName + " = " + ucName + ".getContentType();"); } - //define the Reader + // define the Reader ctxt.generateJavaSource(" java.io.Reader " + tempReaderName + " = null;"); - //initialize the Reader object + // initialize the Reader object ctxt.generateJavaSource(" try{"); - ctxt.generateJavaSource(" " + tempReaderName + " = new java.io.InputStreamReader(" + inputStreamName + ", " + charSetName + ");"); - ctxt.generateJavaSource(" }catch(Exception ex){"); - ctxt.generateJavaSource(" " + tempReaderName + " = new java.io.InputStreamReader(" + inputStreamName + ", org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING);"); + ctxt.generateJavaSource(" " + tempReaderName + " = new java.io.InputStreamReader(" + + inputStreamName + ", " + charSetName + ");"); + ctxt.generateJavaSource(" }catch(Exception e){"); + ctxt.generateJavaSource(" " + tempReaderName + " = new java.io.InputStreamReader(" + + inputStreamName + ", org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING);"); ctxt.generateJavaSource(" }"); - //validate the response + // validate the response ctxt.generateJavaSource(" if(" + ucName + " instanceof java.net.HttpURLConnection){"); - ctxt.generateJavaSource(" int status = ((java.net.HttpURLConnection) " + ucName + ").getResponseCode();"); + ctxt.generateJavaSource( + " int status = ((java.net.HttpURLConnection) " + ucName + ").getResponseCode();"); ctxt.generateJavaSource(" if(status < 200 || status > 299){"); ctxt.generateJavaSource(" throw new JspTagException(status + \" \" + " + urlName + ");"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" }"); - //set attribute in the page context scope + // set attribute in the page context scope ctxt.generateJavaSource(" pageContext.setAttribute(" + varReaderName + ", " + tempReaderName + ");"); - //if the url is relative + // if the url is relative ctxt.generateJavaSource("}else{"); - //if the url is relative, http request is needed + // if the url is relative, http request is needed ctxt.generateJavaSource(" if (!(pageContext.getRequest() instanceof HttpServletRequest " + - "&& pageContext.getResponse() instanceof HttpServletResponse)){"); - ctxt.generateJavaSource(" throw new JspTagException(\"Relative <import> from non-HTTP request not allowed\");"); + "&& pageContext.getResponse() instanceof HttpServletResponse)){"); + ctxt.generateJavaSource( + " throw new JspTagException(\"Relative <import> from non-HTTP request not allowed\");"); ctxt.generateJavaSource(" }"); - //get the servlet context of the context defined in the context attribute + // get the servlet context of the context defined in the context attribute ctxt.generateJavaSource(" ServletContext " + servletContextName + " = null;"); - if(hasContext){ + if (hasContext) { ctxt.generateJavaSource(" if(null != " + contextName + "){"); - ctxt.generateJavaSource(" " + servletContextName + " = pageContext.getServletContext().getContext(" + contextName + ");" ); + ctxt.generateJavaSource(" " + servletContextName + + " = pageContext.getServletContext().getContext(" + contextName + ");"); ctxt.generateJavaSource(" }else{"); ctxt.generateJavaSource(" " + servletContextName + " = pageContext.getServletContext();"); ctxt.generateJavaSource(" }"); - }else{ + } else { ctxt.generateJavaSource(" " + servletContextName + " = pageContext.getServletContext();"); } // ctxt.generateJavaSource(" if(" + servletContextName + " == null){"); - if(hasContext){ - ctxt.generateJavaSource(" throw new JspTagException(\"Unable to get RequestDispatcher for Context: \\\" \"+" + contextName + "+\" \\\" and URL: \\\" \" +" + urlName + "+ \" \\\". Verify values and/or enable cross context access.\");"); - }else{ - ctxt.generateJavaSource(" throw new JspTagException(\"Unable to get RequestDispatcher for URL: \\\" \" +" + urlName + "+ \" \\\". Verify values and/or enable cross context access.\");"); + if (hasContext) { + ctxt.generateJavaSource( + " throw new JspTagException(\"Unable to get RequestDispatcher for Context: \\\" \"+" + + contextName + "+\" \\\" and URL: \\\" \" +" + urlName + + "+ \" \\\". Verify values and/or enable cross context access.\");"); + } else { + ctxt.generateJavaSource( + " throw new JspTagException(\"Unable to get RequestDispatcher for URL: \\\" \" +" + + urlName + "+ \" \\\". Verify values and/or enable cross context access.\");"); } ctxt.generateJavaSource(" }"); - //get the request dispatcher - ctxt.generateJavaSource(" RequestDispatcher " + requestDispatcherName + " = " + servletContextName + ".getRequestDispatcher(org.apache.jasper.tagplugins.jstl.Util.stripSession("+urlName+"));"); - ctxt.generateJavaSource(" if(" + requestDispatcherName + " == null) throw new JspTagException(org.apache.jasper.tagplugins.jstl.Util.stripSession("+urlName+"));"); - - //initialize a ImportResponseWrapper to include the resource - ctxt.generateJavaSource(" org.apache.jasper.tagplugins.jstl.Util.ImportResponseWrapper " + irwName + " = new org.apache.jasper.tagplugins.jstl.Util.ImportResponseWrapper((HttpServletResponse) pageContext.getResponse());"); + // get the request dispatcher + ctxt.generateJavaSource(" RequestDispatcher " + requestDispatcherName + " = " + servletContextName + + ".getRequestDispatcher(org.apache.jasper.tagplugins.jstl.Util.stripSession(" + urlName + "));"); + ctxt.generateJavaSource(" if(" + requestDispatcherName + + " == null) throw new JspTagException(org.apache.jasper.tagplugins.jstl.Util.stripSession(" + + urlName + "));"); + + // initialize a ImportResponseWrapper to include the resource + ctxt.generateJavaSource(" org.apache.jasper.tagplugins.jstl.Util.ImportResponseWrapper " + irwName + + " = new org.apache.jasper.tagplugins.jstl.Util.ImportResponseWrapper((HttpServletResponse) pageContext.getResponse());"); ctxt.generateJavaSource(" if(" + charSetName + " == null){"); - ctxt.generateJavaSource(" " + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); + ctxt.generateJavaSource( + " " + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" " + irwName + ".setCharEncoding(" + charSetName + ");"); ctxt.generateJavaSource(" try{"); - ctxt.generateJavaSource(" " + requestDispatcherName + ".include(pageContext.getRequest(), " + irwName + ");"); + ctxt.generateJavaSource( + " " + requestDispatcherName + ".include(pageContext.getRequest(), " + irwName + ");"); ctxt.generateJavaSource(" }catch(java.io.IOException ex){"); ctxt.generateJavaSource(" throw new JspException(ex);"); ctxt.generateJavaSource(" }catch(RuntimeException ex){"); @@ -227,35 +242,38 @@ ctxt.generateJavaSource(" throw new JspException(rc);"); ctxt.generateJavaSource(" }"); - //validate the response status + // validate the response status ctxt.generateJavaSource(" if(" + irwName + ".getStatus() < 200 || " + irwName + ".getStatus() > 299){"); - ctxt.generateJavaSource(" throw new JspTagException(" + irwName + ".getStatus()+\" \" + org.apache.jasper.tagplugins.jstl.Util.stripSession(" + urlName + "));"); + ctxt.generateJavaSource(" throw new JspTagException(" + irwName + + ".getStatus()+\" \" + org.apache.jasper.tagplugins.jstl.Util.stripSession(" + urlName + "));"); ctxt.generateJavaSource(" }"); - //push in the page context - ctxt.generateJavaSource(" java.io.Reader " + tempReaderName + " = new java.io.StringReader(" + irwName + ".getString());"); + // push in the page context + ctxt.generateJavaSource(" java.io.Reader " + tempReaderName + " = new java.io.StringReader(" + irwName + + ".getString());"); ctxt.generateJavaSource(" pageContext.setAttribute(" + varReaderName + ", " + tempReaderName + ");"); ctxt.generateJavaSource("}"); - //execute the body action + // execute the body action ctxt.generateBody(); - //close the reader - ctxt.generateJavaSource("java.io.Reader " + tempReaderName2 + " = (java.io.Reader)pageContext.getAttribute(" + varReaderName + ");"); + // close the reader + ctxt.generateJavaSource("java.io.Reader " + tempReaderName2 + + " = (java.io.Reader)pageContext.getAttribute(" + varReaderName + ");"); ctxt.generateJavaSource("if(" + tempReaderName2 + " != null) " + tempReaderName2 + ".close();"); ctxt.generateJavaSource("pageContext.removeAttribute(" + varReaderName + ",1);"); } - //if the varReader is not specified - else{ + // if the varReader is not specified + else { ctxt.generateJavaSource("pageContext.setAttribute(\"url_without_param\"," + urlName + ");"); ctxt.generateBody(); ctxt.generateJavaSource(urlName + " = (String)pageContext.getAttribute(\"url_without_param\");"); ctxt.generateJavaSource("pageContext.removeAttribute(\"url_without_param\");"); String strScope = "page"; - if(hasScope){ + if (hasScope) { strScope = ctxt.getConstantAttribute("scope"); } int iScope = Util.getScope(strScope); @@ -264,88 +282,107 @@ ctxt.generateJavaSource("if(" + iauName + "){"); - //get the content of the target + // get the content of the target ctxt.generateJavaSource(" java.net.URL " + urlObjName + " = new java.net.URL(" + urlName + ");"); ctxt.generateJavaSource(" java.net.URLConnection " + ucName + " = " + urlObjName + ".openConnection();"); - ctxt.generateJavaSource(" java.io.InputStream " + inputStreamName + " = " + ucName + ".getInputStream();"); + ctxt.generateJavaSource( + " java.io.InputStream " + inputStreamName + " = " + ucName + ".getInputStream();"); ctxt.generateJavaSource(" java.io.Reader " + tempReaderName + " = null;"); ctxt.generateJavaSource(" if(" + charSetName + " == null){"); - ctxt.generateJavaSource(" String " + contentTypeName + " = " - + ucName + ".getContentType();"); + ctxt.generateJavaSource(" String " + contentTypeName + " = " + ucName + ".getContentType();"); ctxt.generateJavaSource(" if(null != " + contentTypeName + "){"); ctxt.generateJavaSource(" " + charSetName + " = " + - "org.apache.jasper.tagplugins.jstl.Util.getContentTypeAttribute(" + contentTypeName + ", \"charset\");"); - ctxt.generateJavaSource(" if(" + charSetName + " == null) " - + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); + "org.apache.jasper.tagplugins.jstl.Util.getContentTypeAttribute(" + contentTypeName + + ", \"charset\");"); + ctxt.generateJavaSource(" if(" + charSetName + " == null) " + charSetName + + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); ctxt.generateJavaSource(" }else{"); - ctxt.generateJavaSource(" " + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); + ctxt.generateJavaSource( + " " + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" try{"); - ctxt.generateJavaSource(" " + tempReaderName + " = new java.io.InputStreamReader(" + inputStreamName + "," + charSetName + ");"); - ctxt.generateJavaSource(" }catch(Exception ex){"); - //ctxt.generateJavaSource(" throw new JspTagException(ex.toString());"); - ctxt.generateJavaSource(" " + tempReaderName + " = new java.io.InputStreamReader(" + inputStreamName + ",org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING);"); + ctxt.generateJavaSource(" " + tempReaderName + " = new java.io.InputStreamReader(" + + inputStreamName + "," + charSetName + ");"); + ctxt.generateJavaSource(" }catch(Exception e){"); + ctxt.generateJavaSource(" " + tempReaderName + " = new java.io.InputStreamReader(" + + inputStreamName + ",org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING);"); ctxt.generateJavaSource(" }"); - //validate the response + // validate the response ctxt.generateJavaSource(" if(" + ucName + " instanceof java.net.HttpURLConnection){"); - ctxt.generateJavaSource(" int status = ((java.net.HttpURLConnection) " + ucName + ").getResponseCode();"); + ctxt.generateJavaSource( + " int status = ((java.net.HttpURLConnection) " + ucName + ").getResponseCode();"); ctxt.generateJavaSource(" if(status < 200 || status > 299){"); ctxt.generateJavaSource(" throw new JspTagException(status + \" \" + " + urlName + ");"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" }"); - ctxt.generateJavaSource(" java.io.BufferedReader " + brName + " = new java.io.BufferedReader(" + tempReaderName + ");"); + ctxt.generateJavaSource( + " java.io.BufferedReader " + brName + " = new java.io.BufferedReader(" + tempReaderName + ");"); ctxt.generateJavaSource(" StringBuilder " + sbName + " = new StringBuilder();"); String index = ctxt.getTemporaryVariableName(); ctxt.generateJavaSource(" int " + index + ";"); - ctxt.generateJavaSource(" while(("+index+" = "+brName+".read()) != -1) "+sbName+".append((char)"+index+");"); - ctxt.generateJavaSource(" " + tempStringName + " = " +sbName + ".toString();"); + ctxt.generateJavaSource(" while((" + index + " = " + brName + ".read()) != -1) " + sbName + + ".append((char)" + index + ");"); + ctxt.generateJavaSource(" " + tempStringName + " = " + sbName + ".toString();"); ctxt.generateJavaSource("}else{"); - //if the url is relative, http request is needed. + // if the url is relative, http request is needed. ctxt.generateJavaSource(" if (!(pageContext.getRequest() instanceof HttpServletRequest " + - "&& pageContext.getResponse() instanceof HttpServletResponse)){"); - ctxt.generateJavaSource(" throw new JspTagException(\"Relative <import> from non-HTTP request not allowed\");"); + "&& pageContext.getResponse() instanceof HttpServletResponse)){"); + ctxt.generateJavaSource( + " throw new JspTagException(\"Relative <import> from non-HTTP request not allowed\");"); ctxt.generateJavaSource(" }"); - //get the servlet context of the context defined in the context attribute + // get the servlet context of the context defined in the context attribute ctxt.generateJavaSource(" ServletContext " + servletContextName + " = null;"); - if(hasContext){ + if (hasContext) { ctxt.generateJavaSource(" if(null != " + contextName + "){"); - ctxt.generateJavaSource(" " + servletContextName + " = pageContext.getServletContext().getContext(" + contextName + ");" ); + ctxt.generateJavaSource(" " + servletContextName + + " = pageContext.getServletContext().getContext(" + contextName + ");"); ctxt.generateJavaSource(" }else{"); ctxt.generateJavaSource(" " + servletContextName + " = pageContext.getServletContext();"); ctxt.generateJavaSource(" }"); - }else{ + } else { ctxt.generateJavaSource(" " + servletContextName + " = pageContext.getServletContext();"); } // ctxt.generateJavaSource(" if(" + servletContextName + " == null){"); - if(hasContext){ - ctxt.generateJavaSource(" throw new JspTagException(\"Unable to get RequestDispatcher for Context: \\\" \" +" + contextName + "+ \" \\\" and URL: \\\" \" +" + urlName + "+ \" \\\". Verify values and/or enable cross context access.\");"); - }else{ - ctxt.generateJavaSource(" throw new JspTagException(\"Unable to get RequestDispatcher for URL: \\\" \" +" + urlName + "+ \" \\\". Verify values and/or enable cross context access.\");"); + if (hasContext) { + ctxt.generateJavaSource( + " throw new JspTagException(\"Unable to get RequestDispatcher for Context: \\\" \" +" + + contextName + "+ \" \\\" and URL: \\\" \" +" + urlName + + "+ \" \\\". Verify values and/or enable cross context access.\");"); + } else { + ctxt.generateJavaSource( + " throw new JspTagException(\"Unable to get RequestDispatcher for URL: \\\" \" +" + + urlName + "+ \" \\\". Verify values and/or enable cross context access.\");"); } ctxt.generateJavaSource(" }"); - //get the request dispatcher - ctxt.generateJavaSource(" RequestDispatcher " + requestDispatcherName + " = " + servletContextName + ".getRequestDispatcher(org.apache.jasper.tagplugins.jstl.Util.stripSession("+urlName+"));"); - ctxt.generateJavaSource(" if(" + requestDispatcherName + " == null) throw new JspTagException(org.apache.jasper.tagplugins.jstl.Util.stripSession("+urlName+"));"); - - //initialize a ImportResponseWrapper to include the resource - ctxt.generateJavaSource(" org.apache.jasper.tagplugins.jstl.Util.ImportResponseWrapper " + irwName + " = new org.apache.jasper.tagplugins.jstl.Util.ImportResponseWrapper((HttpServletResponse) pageContext.getResponse());"); + // get the request dispatcher + ctxt.generateJavaSource(" RequestDispatcher " + requestDispatcherName + " = " + servletContextName + + ".getRequestDispatcher(org.apache.jasper.tagplugins.jstl.Util.stripSession(" + urlName + "));"); + ctxt.generateJavaSource(" if(" + requestDispatcherName + + " == null) throw new JspTagException(org.apache.jasper.tagplugins.jstl.Util.stripSession(" + + urlName + "));"); + + // initialize a ImportResponseWrapper to include the resource + ctxt.generateJavaSource(" org.apache.jasper.tagplugins.jstl.Util.ImportResponseWrapper " + irwName + + " = new org.apache.jasper.tagplugins.jstl.Util.ImportResponseWrapper((HttpServletResponse) pageContext.getResponse());"); ctxt.generateJavaSource(" if(" + charSetName + " == null){"); - ctxt.generateJavaSource(" " + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); + ctxt.generateJavaSource( + " " + charSetName + " = org.apache.jasper.tagplugins.jstl.Util.DEFAULT_ENCODING;"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" " + irwName + ".setCharEncoding(" + charSetName + ");"); ctxt.generateJavaSource(" try{"); - ctxt.generateJavaSource(" " + requestDispatcherName + ".include(pageContext.getRequest(), " + irwName + ");"); + ctxt.generateJavaSource( + " " + requestDispatcherName + ".include(pageContext.getRequest(), " + irwName + ");"); ctxt.generateJavaSource(" }catch(java.io.IOException ex){"); ctxt.generateJavaSource(" throw new JspException(ex);"); ctxt.generateJavaSource(" }catch(RuntimeException ex){"); @@ -358,19 +395,21 @@ ctxt.generateJavaSource(" throw new JspException(rc);"); ctxt.generateJavaSource(" }"); - //validate the response status + // validate the response status ctxt.generateJavaSource(" if(" + irwName + ".getStatus() < 200 || " + irwName + ".getStatus() > 299){"); - ctxt.generateJavaSource(" throw new JspTagException(" + irwName + ".getStatus()+\" \" + org.apache.jasper.tagplugins.jstl.Util.stripSession(" + urlName + "));"); + ctxt.generateJavaSource(" throw new JspTagException(" + irwName + + ".getStatus()+\" \" + org.apache.jasper.tagplugins.jstl.Util.stripSession(" + urlName + "));"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" " + tempStringName + " = " + irwName + ".getString();"); ctxt.generateJavaSource("}"); - if(hasVar){ + if (hasVar) { String strVar = ctxt.getConstantAttribute("var"); - ctxt.generateJavaSource("pageContext.setAttribute(\""+strVar+"\"," + tempStringName + "," + iScope + ");"); - }else{ + ctxt.generateJavaSource( + "pageContext.setAttribute(\"" + strVar + "\"," + tempStringName + "," + iScope + ");"); + } else { ctxt.generateJavaSource("pageContext.getOut().print(" + tempStringName + ");"); } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Out.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Out.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Out.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Out.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,36 +31,33 @@ @Override public void doTag(TagPluginContext ctxt) { - //these two data member are to indicate - //whether the corresponding attribute is specified - boolean hasDefault=false, hasEscapeXml=false; - hasDefault = ctxt.isAttributeSpecified("default"); - hasEscapeXml = ctxt.isAttributeSpecified("escapeXml"); + // these two data member are to indicate + // whether the corresponding attribute is specified + boolean hasDefault = ctxt.isAttributeSpecified("default"); + boolean hasEscapeXml = ctxt.isAttributeSpecified("escapeXml"); - //strValName, strEscapeXmlName & strDefName are two variables' name - //standing for value, escapeXml and default attribute + // strValName, strEscapeXmlName & strDefName are two variables' name + // standing for value, escapeXml and default attribute String strObjectName = ctxt.getTemporaryVariableName(); String strValName = ctxt.getTemporaryVariableName(); String strDefName = ctxt.getTemporaryVariableName(); String strEscapeXmlName = ctxt.getTemporaryVariableName(); String strSkipBodyName = ctxt.getTemporaryVariableName(); - //according to the tag file, the value attribute is mandatory. + // according to the tag file, the value attribute is mandatory. ctxt.generateImport("java.io.Reader"); ctxt.generateJavaSource("Object " + strObjectName + "="); ctxt.generateAttribute("value"); ctxt.generateJavaSource(";"); ctxt.generateJavaSource("String " + strValName + "=null;"); - ctxt.generateJavaSource("if(!(" + strObjectName + - " instanceof Reader) && "+ strObjectName + " != null){"); - ctxt.generateJavaSource( - strValName + " = " + strObjectName + ".toString();"); + ctxt.generateJavaSource("if(!(" + strObjectName + " instanceof Reader) && " + strObjectName + " != null){"); + ctxt.generateJavaSource(strValName + " = " + strObjectName + ".toString();"); ctxt.generateJavaSource("}"); - //initiate the strDefName with null. - //if the default has been specified, then assign the value to it; + // initiate the strDefName with null. + // if the default has been specified, then assign the value to it; ctxt.generateJavaSource("String " + strDefName + " = null;"); - if(hasDefault){ + if (hasDefault) { ctxt.generateJavaSource("if("); ctxt.generateAttribute("default"); ctxt.generateJavaSource(" != null){"); @@ -70,28 +67,26 @@ ctxt.generateJavaSource("}"); } - //initiate the strEscapeXmlName with true; - //if the escapeXml is specified, assign the value to it; + // initiate the strEscapeXmlName with true; + // if the escapeXml is specified, assign the value to it; ctxt.generateJavaSource("boolean " + strEscapeXmlName + " = true;"); - if(hasEscapeXml){ + if (hasEscapeXml) { ctxt.generateJavaSource(strEscapeXmlName + " = "); ctxt.generateAttribute("escapeXml"); ctxt.generateJavaSource(";"); } - //main part. + // main part. ctxt.generateJavaSource( - "boolean " + strSkipBodyName + " = " + - "org.apache.jasper.tagplugins.jstl.core.Out.output(out, " + - strObjectName + ", " + strValName + ", " + strDefName + ", " + - strEscapeXmlName + ");"); + "boolean " + strSkipBodyName + " = " + "org.apache.jasper.tagplugins.jstl.core.Out.output(out, " + + strObjectName + ", " + strValName + ", " + strDefName + ", " + strEscapeXmlName + ");"); ctxt.generateJavaSource("if(!" + strSkipBodyName + ") {"); ctxt.generateBody(); ctxt.generateJavaSource("}"); } - public static boolean output(JspWriter out, Object input, String value, - String defaultValue, boolean escapeXml) throws IOException { + public static boolean output(JspWriter out, Object input, String value, String defaultValue, boolean escapeXml) + throws IOException { if (input instanceof Reader) { char[] buffer = new char[8096]; int read = 0; @@ -114,7 +109,7 @@ } else { String v = value != null ? value : defaultValue; if (v != null) { - if(escapeXml){ + if (escapeXml) { v = Util.escapeXml(v); } out.write(v); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Param.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Param.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Param.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Param.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,51 +24,55 @@ @Override public void doTag(TagPluginContext ctxt) { - //don't support the body content + // don't support the body content - //define names of all the temp variables + // define names of all the temp variables String nameName = ctxt.getTemporaryVariableName(); String valueName = ctxt.getTemporaryVariableName(); String urlName = ctxt.getTemporaryVariableName(); String encName = ctxt.getTemporaryVariableName(); String index = ctxt.getTemporaryVariableName(); - //if the param tag has no parents, throw a exception + // if the param tag has no parents, throw an exception TagPluginContext parent = ctxt.getParentContext(); - if(parent == null){ - ctxt.generateJavaSource(" throw new JspTagException" + - "(\"<param> outside <import> or <urlEncode>\");"); + if (parent == null) { + ctxt.generateJavaSource( + " throw new JspTagException" + "(\"<param> outside <import> or <urlEncode>\");"); return; } - //get the url string before adding this param - ctxt.generateJavaSource("String " + urlName + " = " + - "(String)pageContext.getAttribute(\"url_without_param\");"); + // get the url string before adding this param + ctxt.generateJavaSource( + "String " + urlName + " = " + "(String)pageContext.getAttribute(\"url_without_param\");"); - //get the value of "name" + // get the value of "name" ctxt.generateJavaSource("String " + nameName + " = "); ctxt.generateAttribute("name"); ctxt.generateJavaSource(";"); - //if the "name" is null then do nothing. - //else add such string "name=value" to the url. - //and the url should be encoded + // if the "name" is null then do nothing. + // else add such string "name=value" to the url. + // and the url should be encoded ctxt.generateJavaSource("if(" + nameName + " != null && !" + nameName + ".equals(\"\")){"); ctxt.generateJavaSource(" String " + valueName + " = "); ctxt.generateAttribute("value"); ctxt.generateJavaSource(";"); ctxt.generateJavaSource(" if(" + valueName + " == null) " + valueName + " = \"\";"); ctxt.generateJavaSource(" String " + encName + " = pageContext.getResponse().getCharacterEncoding();"); - ctxt.generateJavaSource(" " + nameName + " = java.net.URLEncoder.encode(" + nameName + ", " + encName + ");"); - ctxt.generateJavaSource(" " + valueName + " = java.net.URLEncoder.encode(" + valueName + ", " + encName + ");"); + ctxt.generateJavaSource( + " " + nameName + " = java.net.URLEncoder.encode(" + nameName + ", " + encName + ");"); + ctxt.generateJavaSource( + " " + valueName + " = java.net.URLEncoder.encode(" + valueName + ", " + encName + ");"); ctxt.generateJavaSource(" int " + index + ";"); ctxt.generateJavaSource(" " + index + " = " + urlName + ".indexOf(\'?\');"); - //if the current param is the first one, add a "?" ahead of it - //else add a "&" ahead of it + // if the current param is the first one, add a "?" ahead of it + // else add a "&" ahead of it ctxt.generateJavaSource(" if(" + index + " == -1){"); - ctxt.generateJavaSource(" " + urlName + " = " + urlName + " + \"?\" + " + nameName + " + \"=\" + " + valueName + ";"); + ctxt.generateJavaSource( + " " + urlName + " = " + urlName + " + \"?\" + " + nameName + " + \"=\" + " + valueName + ";"); ctxt.generateJavaSource(" }else{"); - ctxt.generateJavaSource(" " + urlName + " = " + urlName + " + \"&\" + " + nameName + " + \"=\" + " + valueName + ";"); + ctxt.generateJavaSource( + " " + urlName + " = " + urlName + " + \"&\" + " + nameName + " + \"=\" + " + valueName + ";"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" pageContext.setAttribute(\"url_without_param\"," + urlName + ");"); ctxt.generateJavaSource("}"); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Redirect.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Redirect.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Redirect.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Redirect.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,54 +24,51 @@ @Override public void doTag(TagPluginContext ctxt) { - //flag for the existence of the "context" + // flag for the existence of the "context" boolean hasContext = ctxt.isAttributeSpecified("context"); - //names of the temp variables + // names of the temp variables String urlName = ctxt.getTemporaryVariableName(); String contextName = ctxt.getTemporaryVariableName(); String baseUrlName = ctxt.getTemporaryVariableName(); String resultName = ctxt.getTemporaryVariableName(); String responseName = ctxt.getTemporaryVariableName(); - //get context + // get context ctxt.generateJavaSource("String " + contextName + " = null;"); - if(hasContext){ + if (hasContext) { ctxt.generateJavaSource(contextName + " = "); ctxt.generateAttribute("context"); ctxt.generateJavaSource(";"); } - //get the url + // get the url ctxt.generateJavaSource("String " + urlName + " = "); ctxt.generateAttribute("url"); ctxt.generateJavaSource(";"); - //get the raw url according to "url" and "context" - ctxt.generateJavaSource("String " + baseUrlName + " = " + - "org.apache.jasper.tagplugins.jstl.Util.resolveUrl(" + urlName + ", " + contextName + ", pageContext);"); - ctxt.generateJavaSource("pageContext.setAttribute" + - "(\"url_without_param\", " + baseUrlName + ");"); + // get the raw url according to "url" and "context" + ctxt.generateJavaSource("String " + baseUrlName + " = " + "org.apache.jasper.tagplugins.jstl.Util.resolveUrl(" + + urlName + ", " + contextName + ", pageContext);"); + ctxt.generateJavaSource("pageContext.setAttribute" + "(\"url_without_param\", " + baseUrlName + ");"); - //add params + // add params ctxt.generateBody(); - ctxt.generateJavaSource("String " + resultName + " = " + - "(String)pageContext.getAttribute(\"url_without_param\");"); - ctxt.generateJavaSource("pageContext.removeAttribute" + - "(\"url_without_param\");"); - - //get the response object - ctxt.generateJavaSource("HttpServletResponse " + responseName + " = " + - "((HttpServletResponse) pageContext.getResponse());"); + ctxt.generateJavaSource( + "String " + resultName + " = " + "(String)pageContext.getAttribute(\"url_without_param\");"); + ctxt.generateJavaSource("pageContext.removeAttribute" + "(\"url_without_param\");"); + + // get the response object + ctxt.generateJavaSource( + "HttpServletResponse " + responseName + " = " + "((HttpServletResponse) pageContext.getResponse());"); - //if the url is relative, encode it + // if the url is relative, encode it ctxt.generateJavaSource("if(!org.apache.jasper.tagplugins.jstl.Util.isAbsoluteUrl(" + resultName + ")){"); - ctxt.generateJavaSource(" " + resultName + " = " - + responseName + ".encodeRedirectURL(" + resultName + ");"); + ctxt.generateJavaSource(" " + resultName + " = " + responseName + ".encodeRedirectURL(" + resultName + ");"); ctxt.generateJavaSource("}"); - //do redirect + // do redirect ctxt.generateJavaSource("try{"); ctxt.generateJavaSource(" " + responseName + ".sendRedirect(" + resultName + ");"); ctxt.generateJavaSource("}catch(java.io.IOException ex){"); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Remove.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Remove.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Remove.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Remove.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,18 +25,18 @@ @Override public void doTag(TagPluginContext ctxt) { - //scope flag + // scope flag boolean hasScope = ctxt.isAttributeSpecified("scope"); - //the value of the "var" + // the value of the "var" String strVar = ctxt.getConstantAttribute("var"); - //remove attribute from certain scope. - //default scope is "page". - if(hasScope){ + // remove attribute from certain scope. + // default scope is "page". + if (hasScope) { int iScope = Util.getScope(ctxt.getConstantAttribute("scope")); ctxt.generateJavaSource("pageContext.removeAttribute(\"" + strVar + "\"," + iScope + ");"); - }else{ + } else { ctxt.generateJavaSource("pageContext.removeAttribute(\"" + strVar + "\");"); } } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Set.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Set.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Set.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Set.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,76 +25,74 @@ @Override public void doTag(TagPluginContext ctxt) { - //the flags to indicate whether the attributes have been specified - boolean hasValue = false, hasVar = false, hasScope = false, - hasTarget = false; - - //the scope name + // the scope name String strScope; - //the id of the scope + // the id of the scope int iScope; - //initialize the flags - hasValue = ctxt.isAttributeSpecified("value"); - hasVar = ctxt.isAttributeSpecified("var"); - hasScope = ctxt.isAttributeSpecified("scope"); - hasTarget = ctxt.isAttributeSpecified("target"); + // the flags to indicate whether the attributes have been specified + // initialize the flags + boolean hasValue = ctxt.isAttributeSpecified("value"); + boolean hasVar = ctxt.isAttributeSpecified("var"); + boolean hasScope = ctxt.isAttributeSpecified("scope"); + boolean hasTarget = ctxt.isAttributeSpecified("target"); - //the temp variables name + // the temp variables name String resultName = ctxt.getTemporaryVariableName(); String targetName = ctxt.getTemporaryVariableName(); String propertyName = ctxt.getTemporaryVariableName(); - //initialize the "result" which will be assigned to the var or target.property + // initialize the "result" which will be assigned to the var or target.property ctxt.generateJavaSource("Object " + resultName + " = null;"); - if(hasValue){ + if (hasValue) { ctxt.generateJavaSource(resultName + " = "); ctxt.generateAttribute("value"); ctxt.generateJavaSource(";"); - }else{ + } else { ctxt.dontUseTagPlugin(); return; } - //initialize the strScope - if(hasScope){ + // initialize the strScope + if (hasScope) { strScope = ctxt.getConstantAttribute("scope"); - }else{ + } else { strScope = "page"; } - //get the iScope according to the strScope + // get the iScope according to the strScope iScope = Util.getScope(strScope); - String jspCtxt = null; + String jspCtxt; if (ctxt.isTagFile()) { jspCtxt = "this.getJspContext()"; } else { jspCtxt = "_jspx_page_context"; } - //if the attribute var has been specified then assign the result to the var; - if(hasVar){ + // if the attribute var has been specified then assign the result to the var; + if (hasVar) { String strVar = ctxt.getConstantAttribute("var"); ctxt.generateJavaSource("if(null != " + resultName + "){"); - ctxt.generateJavaSource(" " + jspCtxt + ".setAttribute(\"" + strVar + "\"," + resultName + "," + iScope + ");"); + ctxt.generateJavaSource( + " " + jspCtxt + ".setAttribute(\"" + strVar + "\"," + resultName + "," + iScope + ");"); ctxt.generateJavaSource("} else {"); - if(hasScope){ + if (hasScope) { ctxt.generateJavaSource(" " + jspCtxt + ".removeAttribute(\"" + strVar + "\"," + iScope + ");"); - }else{ + } else { ctxt.generateJavaSource(" " + jspCtxt + ".removeAttribute(\"" + strVar + "\");"); } ctxt.generateJavaSource("}"); - //else assign the result to the target.property - }else if(hasTarget){ + // else assign the result to the target.property + } else if (hasTarget) { - //generate the temp variable name + // generate the temp variable name String pdName = ctxt.getTemporaryVariableName(); String successFlagName = ctxt.getTemporaryVariableName(); String index = ctxt.getTemporaryVariableName(); String methodName = ctxt.getTemporaryVariableName(); - //initialize the property + // initialize the property ctxt.generateJavaSource("String " + propertyName + " = null;"); ctxt.generateJavaSource("if("); ctxt.generateAttribute("property"); @@ -104,57 +102,69 @@ ctxt.generateJavaSource(").toString();"); ctxt.generateJavaSource("}"); - //initialize the target + // initialize the target ctxt.generateJavaSource("Object " + targetName + " = "); ctxt.generateAttribute("target"); ctxt.generateJavaSource(";"); - //the target is ok + // the target is ok ctxt.generateJavaSource("if(" + targetName + " != null){"); - //if the target is a map, then put the result into the map with the key property + // if the target is a map, then put the result into the map with the key property ctxt.generateJavaSource(" if(" + targetName + " instanceof java.util.Map){"); ctxt.generateJavaSource(" if(null != " + resultName + "){"); - ctxt.generateJavaSource(" ((java.util.Map) " + targetName + ").put(" + propertyName + "," + resultName + ");"); + ctxt.generateJavaSource( + " ((java.util.Map) " + targetName + ").put(" + propertyName + "," + resultName + ");"); ctxt.generateJavaSource(" }else{"); ctxt.generateJavaSource(" ((java.util.Map) " + targetName + ").remove(" + propertyName + ");"); ctxt.generateJavaSource(" }"); - //else assign the result to the target.property + // else assign the result to the target.property ctxt.generateJavaSource(" }else{"); ctxt.generateJavaSource(" try{"); - //get all the property of the target - ctxt.generateJavaSource(" java.beans.PropertyDescriptor " + pdName + "[] = java.beans.Introspector.getBeanInfo(" + targetName + ".getClass()).getPropertyDescriptors();"); + // get all the property of the target + ctxt.generateJavaSource(" java.beans.PropertyDescriptor " + pdName + + "[] = java.beans.Introspector.getBeanInfo(" + targetName + + ".getClass()).getPropertyDescriptors();"); - //the success flag is to imply whether the assign is successful + // the success flag is to imply whether the assign is successful ctxt.generateJavaSource(" boolean " + successFlagName + " = false;"); - //find the right property - ctxt.generateJavaSource(" for(int " + index + "=0;" + index + "<" + pdName + ".length;" + index + "++){"); - ctxt.generateJavaSource(" if(" + pdName + "[" + index + "].getName().equals(" + propertyName + ")){"); - - //get the "set" method; - ctxt.generateJavaSource(" java.lang.reflect.Method " + methodName + " = " + pdName + "[" + index + "].getWriteMethod();"); + // find the right property + ctxt.generateJavaSource( + " for(int " + index + "=0;" + index + "<" + pdName + ".length;" + index + "++){"); + ctxt.generateJavaSource( + " if(" + pdName + "[" + index + "].getName().equals(" + propertyName + ")){"); + + // get the "set" method; + ctxt.generateJavaSource(" java.lang.reflect.Method " + methodName + " = " + pdName + + "[" + index + "].getWriteMethod();"); ctxt.generateJavaSource(" if(null == " + methodName + "){"); - ctxt.generateJavaSource(" throw new JspException(\"No setter method in <set> for property \"+" + propertyName + ");"); + ctxt.generateJavaSource( + " throw new JspException(\"No setter method in <set> for property \"+" + + propertyName + ");"); ctxt.generateJavaSource(" }"); - //invoke the method through the reflection + // invoke the method through the reflection ctxt.generateJavaSource(" if(" + resultName + " != null){"); - ctxt.generateJavaSource(" " + methodName + ".invoke(" + targetName + ", new Object[]{org.apache.el.lang.ELSupport.coerceToType(" + jspCtxt + ".getELContext(), " + resultName + ", " + methodName + ".getParameterTypes()[0])});"); + ctxt.generateJavaSource(" " + methodName + ".invoke(" + targetName + + ", new Object[]{org.apache.el.lang.ELSupport.coerceToType(" + jspCtxt + ".getELContext(), " + + resultName + ", " + methodName + ".getParameterTypes()[0])});"); ctxt.generateJavaSource(" }else{"); - ctxt.generateJavaSource(" " + methodName + ".invoke(" + targetName + ", new Object[]{null});"); + ctxt.generateJavaSource( + " " + methodName + ".invoke(" + targetName + ", new Object[]{null});"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" " + successFlagName + " = true;"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" if(!" + successFlagName + "){"); - ctxt.generateJavaSource(" throw new JspException(\"Invalid property in <set>:\"+" + propertyName + ");"); + ctxt.generateJavaSource(" throw new JspException(\"Invalid property in <set>:\"+" + + propertyName + ");"); ctxt.generateJavaSource(" }"); ctxt.generateJavaSource(" }"); - //catch the el exception and throw it as a JspException + // catch the el exception and throw it as a JspException ctxt.generateJavaSource(" catch (IllegalAccessException ex) {"); ctxt.generateJavaSource(" throw new JspException(ex);"); ctxt.generateJavaSource(" } catch (java.beans.IntrospectionException ex) {"); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Url.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Url.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/Url.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/Url.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,70 +25,68 @@ @Override public void doTag(TagPluginContext ctxt) { - //flags + // flags boolean hasVar, hasContext, hasScope; - //init flags + // init flags hasVar = ctxt.isAttributeSpecified("var"); hasContext = ctxt.isAttributeSpecified("context"); hasScope = ctxt.isAttributeSpecified("scope"); - //define name of the temp variables + // define name of the temp variables String valueName = ctxt.getTemporaryVariableName(); String contextName = ctxt.getTemporaryVariableName(); String baseUrlName = ctxt.getTemporaryVariableName(); String resultName = ctxt.getTemporaryVariableName(); String responseName = ctxt.getTemporaryVariableName(); - //get the scope + // get the scope String strScope = "page"; - if(hasScope){ + if (hasScope) { strScope = ctxt.getConstantAttribute("scope"); } int iScope = Util.getScope(strScope); - //get the value + // get the value ctxt.generateJavaSource("String " + valueName + " = "); ctxt.generateAttribute("value"); ctxt.generateJavaSource(";"); - //get the context + // get the context ctxt.generateJavaSource("String " + contextName + " = null;"); - if(hasContext){ + if (hasContext) { ctxt.generateJavaSource(contextName + " = "); ctxt.generateAttribute("context"); ctxt.generateJavaSource(";"); } - //get the raw url - ctxt.generateJavaSource("String " + baseUrlName + " = " + - "org.apache.jasper.tagplugins.jstl.Util.resolveUrl(" + valueName + ", " + contextName + ", pageContext);"); - ctxt.generateJavaSource("pageContext.setAttribute" + - "(\"url_without_param\", " + baseUrlName + ");"); + // get the raw url + ctxt.generateJavaSource("String " + baseUrlName + " = " + "org.apache.jasper.tagplugins.jstl.Util.resolveUrl(" + + valueName + ", " + contextName + ", pageContext);"); + ctxt.generateJavaSource("pageContext.setAttribute" + "(\"url_without_param\", " + baseUrlName + ");"); - //add params + // add params ctxt.generateBody(); - ctxt.generateJavaSource("String " + resultName + " = " + - "(String)pageContext.getAttribute(\"url_without_param\");"); + ctxt.generateJavaSource( + "String " + resultName + " = " + "(String)pageContext.getAttribute(\"url_without_param\");"); ctxt.generateJavaSource("pageContext.removeAttribute(\"url_without_param\");"); - //if the url is relative, encode it + // if the url is relative, encode it ctxt.generateJavaSource("if(!org.apache.jasper.tagplugins.jstl.Util.isAbsoluteUrl(" + resultName + ")){"); ctxt.generateJavaSource(" HttpServletResponse " + responseName + " = " + - "((HttpServletResponse) pageContext.getResponse());"); - ctxt.generateJavaSource(" " + resultName + " = " - + responseName + ".encodeURL(" + resultName + ");"); + "((HttpServletResponse) pageContext.getResponse());"); + ctxt.generateJavaSource(" " + resultName + " = " + responseName + ".encodeURL(" + resultName + ");"); ctxt.generateJavaSource("}"); - //if "var" is specified, the url string store in the attribute var defines - if(hasVar){ + // if "var" is specified, the url string store in the attribute var defines + if (hasVar) { String strVar = ctxt.getConstantAttribute("var"); - ctxt.generateJavaSource("pageContext.setAttribute" + - "(\"" + strVar + "\", " + resultName + ", " + iScope + ");"); + ctxt.generateJavaSource( + "pageContext.setAttribute" + "(\"" + strVar + "\", " + resultName + ", " + iScope + ");"); - //if var is not specified, just print out the url string - }else{ + // if var is not specified, just print out the url string + } else { ctxt.generateJavaSource("try{"); ctxt.generateJavaSource(" pageContext.getOut().print(" + resultName + ");"); ctxt.generateJavaSource("}catch(java.io.IOException ex){"); diff -Nru tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/When.java tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/When.java --- tomcat10-10.1.34/java/org/apache/jasper/tagplugins/jstl/core/When.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/tagplugins/jstl/core/When.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,7 +42,7 @@ ctxt.generateBody(); // We don't generate the closing "}" for the "if" here because there - // may be whitespaces in between 's. Instead we delay + // may be whitespaces in between 's. Instead, we delay // generating it until the next or or // } diff -Nru tomcat10-10.1.34/java/org/apache/jasper/util/FastRemovalDequeue.java tomcat10-10.1.52/java/org/apache/jasper/util/FastRemovalDequeue.java --- tomcat10-10.1.34/java/org/apache/jasper/util/FastRemovalDequeue.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/util/FastRemovalDequeue.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,31 +17,26 @@ package org.apache.jasper.util; /** - * The FastRemovalDequeue is a Dequeue that supports constant time removal of - * entries. This is achieved by using a doubly linked list and wrapping any object - * added to the collection with an Entry type, that is returned to the consumer. - * When removing an object from the list, the consumer provides this Entry object. - * - * The Entry type is nearly opaque to the consumer of the queue. The only public - * member is the getter for any object displaced when adding a new object to the - * queue. This can be used to destroy that object. - * - * The Entry object contains the links pointing to the neighbours in the doubly - * linked list, so that removal of an Entry does not need to search for it but - * instead can be done in constant time. - * + * The FastRemovalDequeue is a Dequeue that supports constant time removal of entries. This is achieved by using a + * doubly linked list and wrapping any object added to the collection with an Entry type, that is returned to the + * consumer. When removing an object from the list, the consumer provides this Entry object. + *

          + * The Entry type is nearly opaque to the consumer of the queue. The only public member is the getter for any object + * displaced when adding a new object to the queue. This can be used to destroy that object. + *

          + * The Entry object contains the links pointing to the neighbours in the doubly linked list, so that removal of an Entry + * does not need to search for it but instead can be done in constant time. + *

          * The implementation is fully thread-safe. - * - * Invalidation of Entry objects during removal from the list is done - * by setting their "valid" field to false. All public methods which take Entry - * objects as arguments are NOP if the entry is no longer valid. - * - * A typical use of the FastRemovalDequeue is a list of entries in sorted order, - * where the sort position of an object will only switch to first or last. - * - * Whenever the sort position needs to change, the consumer can remove the object - * and reinsert it in front or at the end in constant time. - * So keeping the list sorted is very cheap. + *

          + * Invalidation of Entry objects during removal from the list is done by setting their "valid" field to false. All + * public methods which take Entry objects as arguments are NOP if the entry is no longer valid. + *

          + * A typical use of the FastRemovalDequeue is a list of entries in sorted order, where the sort position of an object + * will only switch to first or last. + *

          + * Whenever the sort position needs to change, the consumer can remove the object and reinsert it in front or at the end + * in constant time. So keeping the list sorted is very cheap. * * @param The type of elements in the queue */ @@ -59,11 +54,10 @@ /** * Initialize empty queue. * - * @param maxSize The maximum size to which the queue will be allowed to - * grow + * @param maxSize The maximum size to which the queue will be allowed to grow */ public FastRemovalDequeue(int maxSize) { - if (maxSize <=1 ) { + if (maxSize <= 1) { maxSize = 2; } this.maxSize = maxSize; @@ -73,23 +67,23 @@ } /** - * Retrieve the size of the list. - * This method also needs to be externally synchronized to - * ensure correct publication of changes. + * Retrieve the size of the list. This method also needs to be externally synchronized to ensure correct publication + * of changes. * * @return the size of the list. - * */ + */ public synchronized int getSize() { return size; } /** - * Adds an object to the start of the list and returns the entry created for - * said object. The entry can later be reused for moving the entry. + * Adds an object to the start of the list and returns the entry created for said object. The entry can later be + * reused for moving the entry. * * @param object the object to prepend to the start of the list. + * * @return an entry for use when the object should be moved. - * */ + */ public synchronized Entry push(final T object) { Entry entry = new Entry(object); if (size >= maxSize) { @@ -108,12 +102,13 @@ } /** - * Adds an object to the end of the list and returns the entry created for - * said object. The entry can later be reused for moving the entry. + * Adds an object to the end of the list and returns the entry created for said object. The entry can later be + * reused for moving the entry. * * @param object the object to append to the end of the list. + * * @return an entry for use when the object should be moved. - * */ + */ public synchronized Entry unpop(final T object) { Entry entry = new Entry(object); if (size >= maxSize) { @@ -143,7 +138,7 @@ first = first.getNext(); content = element.getContent(); if (first == null) { - last =null; + last = null; } else { first.setPrevious(null); } @@ -201,16 +196,13 @@ } /** - * Moves the element in front. - * - * Could also be implemented as remove() and - * push(), but explicitly coding might be a bit faster. + * Moves the element in front. Could also be implemented as remove() and push(), but explicitly coding might be a + * bit faster. * * @param element the entry to move in front. - * */ + */ public synchronized void moveFirst(final Entry element) { - if (element.getValid() && - element.getPrevious() != null) { + if (element.getValid() && element.getPrevious() != null) { Entry prev = element.getPrevious(); Entry next = element.getNext(); prev.setNext(next); @@ -227,16 +219,13 @@ } /** - * Moves the element to the back. - * - * Could also be implemented as remove() and - * unpop(), but explicitly coding might be a bit faster. + * Moves the element to the back. Could also be implemented as remove() and unpop(), but explicitly coding might be + * a bit faster. * * @param element the entry to move to the back. - * */ + */ public synchronized void moveLast(final Entry element) { - if (element.getValid() && - element.getNext() != null) { + if (element.getValid() && element.getNext() != null) { Entry next = element.getNext(); Entry prev = element.getPrevious(); next.setPrevious(prev); @@ -253,10 +242,8 @@ } /** - * Implementation of a doubly linked list entry. - * All implementation details are private. - * For the consumer of the above collection, this - * is simply garbage in, garbage out. + * Implementation of a doubly linked list entry. All implementation details are private. For the consumer of the + * above collection, this is simply garbage in, garbage out. */ public class Entry { diff -Nru tomcat10-10.1.34/java/org/apache/jasper/util/UniqueAttributesImpl.java tomcat10-10.1.52/java/org/apache/jasper/util/UniqueAttributesImpl.java --- tomcat10-10.1.34/java/org/apache/jasper/util/UniqueAttributesImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/jasper/util/UniqueAttributesImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,8 @@ import org.xml.sax.helpers.AttributesImpl; /** - * Wraps the default attributes implementation and ensures that each attribute - * has a unique qname as required by the JSP specification. + * Wraps the default attributes implementation and ensures that each attribute has a unique qname as required by the JSP + * specification. */ public class UniqueAttributesImpl extends AttributesImpl { @@ -60,8 +60,7 @@ } @Override - public void addAttribute(String uri, String localName, String qName, - String type, String value) { + public void addAttribute(String uri, String localName, String qName, String type, String value) { if (qNames.add(qName)) { super.addAttribute(uri, localName, qName, type, value); } else { @@ -70,8 +69,7 @@ } @Override - public void setAttribute(int index, String uri, String localName, - String qName, String type, String value) { + public void setAttribute(int index, String uri, String localName, String qName, String type, String value) { qNames.remove(super.getQName(index)); if (qNames.add(qName)) { super.setAttribute(index, uri, localName, qName, type, value); @@ -114,7 +112,6 @@ } // Ordinary tag attributes can't be repeated, even with identical values - throw new IllegalArgumentException( - Localizer.getMessage("jsp.error.duplicateqname", qName)); + throw new IllegalArgumentException(Localizer.getMessage("jsp.error.duplicateqname", qName)); } } diff -Nru tomcat10-10.1.34/java/org/apache/juli/AsyncFileHandler.java tomcat10-10.1.52/java/org/apache/juli/AsyncFileHandler.java --- tomcat10-10.1.34/java/org/apache/juli/AsyncFileHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/AsyncFileHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -55,8 +55,8 @@ public static final int MAX_RECORDS = Integer .parseInt(System.getProperty("org.apache.juli.AsyncMaxRecordCount", Integer.toString(DEFAULT_MAX_RECORDS))); - private static final LoggerExecutorService LOGGER_SERVICE = new LoggerExecutorService(OVERFLOW_DROP_TYPE, - MAX_RECORDS); + private static final LoggerExecutorService LOGGER_SERVICE = + new LoggerExecutorService(OVERFLOW_DROP_TYPE, MAX_RECORDS); private final Object closeLock = new Object(); protected volatile boolean closed = false; @@ -119,17 +119,13 @@ // fill source entries, before we hand the record over to another // thread with another class loader record.getSourceMethodName(); - loggerService.execute(new Runnable() { - - @Override - public void run() { - /* - * During Tomcat shutdown, the Handlers are closed before the executor queue is flushed therefore the - * closed flag is ignored if the executor is shutting down. - */ - if (!closed || loggerService.isTerminating()) { - publishInternal(record); - } + loggerService.execute(() -> { + /* + * During Tomcat shutdown, the Handlers are closed before the executor queue is flushed therefore the closed + * flag is ignored if the executor is shutting down. + */ + if (!closed || loggerService.isTerminating()) { + publishInternal(record); } }); } @@ -186,7 +182,7 @@ Runtime.getRuntime().removeShutdownHook(dummyHook); } catch (IllegalStateException ise) { // JVM is shutting down. - // Allow up to 10s for for the queue to be emptied + // Allow up to 10s for the queue to be emptied shutdown(); try { awaitTermination(10, TimeUnit.SECONDS); diff -Nru tomcat10-10.1.34/java/org/apache/juli/ClassLoaderLogManager.java tomcat10-10.1.52/java/org/apache/juli/ClassLoaderLogManager.java --- tomcat10-10.1.34/java/org/apache/juli/ClassLoaderLogManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/ClassLoaderLogManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,7 +48,7 @@ */ public class ClassLoaderLogManager extends LogManager { - private static ThreadLocal addingLocalRootLogger = ThreadLocal.withInitial(() -> Boolean.FALSE); + private static final ThreadLocal addingLocalRootLogger = ThreadLocal.withInitial(() -> Boolean.FALSE); public static final String DEBUG_PROPERTY = ClassLoaderLogManager.class.getName() + ".debug"; @@ -83,7 +83,7 @@ * Map containing the classloader information, keyed per classloader. A weak hashmap is used to ensure no * classloader reference is leaked from application redeployment. */ - protected final Map classLoaderLoggers = new WeakHashMap<>(); // Guarded by this + protected final Map classLoaderLoggers = new WeakHashMap<>(); // Guarded by this /** @@ -352,7 +352,7 @@ for (Handler handler : clLogInfo.handlers.values()) { try { handler.close(); - } catch (Exception e) { + } catch (Exception ignore) { // Ignore } } @@ -381,7 +381,7 @@ AccessController.doPrivileged((PrivilegedAction) () -> { try { readConfiguration(classLoaderParam); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } return null; @@ -450,9 +450,9 @@ if (configFileStr != null) { try { is = new FileInputStream(replace(configFileStr)); - } catch (IOException e) { + } catch (IOException ioe) { System.err.println("Configuration error"); - e.printStackTrace(); + ioe.printStackTrace(); } } // Try the default JVM configuration @@ -460,9 +460,9 @@ File defaultFile = new File(new File(System.getProperty("java.home"), "conf"), "logging.properties"); try { is = new FileInputStream(defaultFile); - } catch (IOException e) { + } catch (IOException ioe) { System.err.println("Configuration error"); - e.printStackTrace(); + ioe.printStackTrace(); } } } @@ -513,19 +513,14 @@ ClassLoaderLogInfo info = classLoaderLoggers.get(classLoader); - try { + try (is) { info.props.load(is); - } catch (IOException e) { + } catch (IOException ioe) { // Report error System.err.println("Configuration error"); - e.printStackTrace(); - } finally { - try { - is.close(); - } catch (IOException ioe) { - // Ignore - } + ioe.printStackTrace(); } + // Ignore // Create handlers for the root logger of this classloader String rootHandlers = info.props.getProperty(".handlers"); @@ -537,7 +532,7 @@ String handlerName = (tok.nextToken().trim()); String handlerClassName = handlerName; String prefix = ""; - if (handlerClassName.length() <= 0) { + if (handlerClassName.isEmpty()) { continue; } // Parse and remove a prefix (prefix start with a digit, such as @@ -610,7 +605,7 @@ String replacement = replaceWebApplicationProperties(propName); if (replacement == null) { - replacement = propName.length() > 0 ? System.getProperty(propName) : null; + replacement = !propName.isEmpty() ? System.getProperty(propName) : null; } if (replacement != null) { builder.append(replacement); @@ -649,7 +644,7 @@ * Obtain the class loader to use to lookup loggers, obtain configuration etc. The search order is: *

            *
          1. Thread.currentThread().getContextClassLoader()
          2. - *
          3. The class laoder of this class
          4. + *
          5. The classloader of this class
          6. *
          * * @return The class loader to use to lookup loggers, obtain configuration etc. @@ -668,7 +663,7 @@ protected static final class LogNode { Logger logger; - final Map children = new HashMap<>(); + final Map children = new HashMap<>(); final LogNode parent; @@ -734,8 +729,8 @@ protected static final class ClassLoaderLogInfo { final LogNode rootNode; - final Map loggers = new ConcurrentHashMap<>(); - final Map handlers = new HashMap<>(); + final Map loggers = new ConcurrentHashMap<>(); + final Map handlers = new HashMap<>(); final Properties props = new Properties(); ClassLoaderLogInfo(final LogNode rootNode) { diff -Nru tomcat10-10.1.34/java/org/apache/juli/DateFormatCache.java tomcat10-10.1.52/java/org/apache/juli/DateFormatCache.java --- tomcat10-10.1.34/java/org/apache/juli/DateFormatCache.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/DateFormatCache.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,11 +35,11 @@ * implemented based on a cyclic buffer. New entries shift the range. *

          *

          - * The cache is not threadsafe. It can be used without synchronization via thread local instances, or with + * The cache is not thread safe. It can be used without synchronization via thread local instances, or with * synchronization as a global cache. *

          *

          - * The cache can be created with a parent cache to build a cache hierarchy. Access to the parent cache is threadsafe. + * The cache can be created with a parent cache to build a cache hierarchy. Access to the parent cache is thread safe. *

          */ public class DateFormatCache { @@ -114,10 +114,10 @@ /* Helper object to be able to call SimpleDateFormat.format(). */ private final Date currentDate = new Date(); - private String cache[]; - private SimpleDateFormat formatter; + private final String[] cache; + private final SimpleDateFormat formatter; - private Cache parent = null; + private final Cache parent; private Cache(Cache parent) { cache = new String[cacheSize]; @@ -166,7 +166,7 @@ first = seconds - (cacheSize - 1); last = seconds; offset = (index + 1) % cacheSize; - } else if (seconds < first) { + } else { for (int i = 1; i < first - seconds; i++) { cache[(index + i) % cacheSize] = null; } diff -Nru tomcat10-10.1.34/java/org/apache/juli/FileHandler.java tomcat10-10.1.52/java/org/apache/juli/FileHandler.java --- tomcat10-10.1.34/java/org/apache/juli/FileHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/FileHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,7 +33,6 @@ import java.time.DateTimeException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; -import java.time.temporal.ChronoUnit; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; @@ -87,8 +86,8 @@ public static final int DEFAULT_BUFFER_SIZE = -1; - private static final ExecutorService DELETE_FILES_SERVICE = Executors - .newSingleThreadExecutor(new ThreadFactory("FileHandlerLogFilesCleaner-")); + private static final ExecutorService DELETE_FILES_SERVICE = + Executors.newSingleThreadExecutor(new ThreadFactory("FileHandlerLogFilesCleaner-")); // ------------------------------------------------------------ Constructor @@ -231,7 +230,7 @@ } } - String result = null; + String result; try { result = getFormatter().format(record); } catch (Exception e) { @@ -364,10 +363,10 @@ // Get encoding for the logging file String encoding = getProperty(className + ".encoding", null); - if (encoding != null && encoding.length() > 0) { + if (encoding != null && !encoding.isEmpty()) { try { setEncoding(encoding); - } catch (UnsupportedEncodingException ex) { + } catch (UnsupportedEncodingException ignore) { // Ignore } } @@ -380,7 +379,7 @@ if (filterName != null) { try { setFilter((Filter) cl.loadClass(filterName).getConstructor().newInstance()); - } catch (Exception e) { + } catch (Exception ignore) { // Ignore } } @@ -390,7 +389,7 @@ if (formatterName != null) { try { setFormatter((Formatter) cl.loadClass(formatterName).getConstructor().newInstance()); - } catch (Exception e) { + } catch (Exception ignore) { // Ignore and fallback to defaults setFormatter(new OneLineFormatter()); } @@ -455,14 +454,14 @@ if (fos != null) { try { fos.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } if (os != null) { try { os.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -480,7 +479,7 @@ for (Path file : files) { Files.delete(file); } - } catch (IOException e) { + } catch (IOException ioe) { reportError("Unable to delete log files older than [" + maxDays + "] days", null, ErrorManager.GENERIC_FAILURE); } @@ -488,7 +487,7 @@ } private DirectoryStream streamFilesForDelete() throws IOException { - LocalDate maxDaysOffset = LocalDate.now().minus(maxDays.intValue(), ChronoUnit.DAYS); + LocalDate maxDaysOffset = LocalDate.now().minusDays(maxDays.intValue()); return Files.newDirectoryStream(getDirectoryAsPath(), path -> { boolean result = false; String date = obtainDateFromPath(path); @@ -496,8 +495,8 @@ try { LocalDate dateFromFile = LocalDate.from(DateTimeFormatter.ISO_LOCAL_DATE.parse(date)); result = dateFromFile.isBefore(maxDaysOffset); - } catch (DateTimeException e) { - // no-op + } catch (DateTimeException ignore) { + // Unable to determine date from path. File will not be included. } } return result; diff -Nru tomcat10-10.1.34/java/org/apache/juli/JdkLoggerFormatter.java tomcat10-10.1.52/java/org/apache/juli/JdkLoggerFormatter.java --- tomcat10-10.1.34/java/org/apache/juli/JdkLoggerFormatter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/JdkLoggerFormatter.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,6 @@ * * * Example: 1130122891846 Http11BaseProtocol I Initializing Coyote HTTP/1.1 on http-8800 - * - * @author Costin Manolache */ public class JdkLoggerFormatter extends Formatter { @@ -63,9 +61,7 @@ buf.append(time); // pad to 8 to make it more readable - for (int i = 0; i < 8 - buf.length(); i++) { - buf.append(' '); - } + buf.append(" ".repeat(Math.max(0, 8 - buf.length()))); // Append a readable representation of the log level. switch (level) { @@ -95,12 +91,10 @@ buf.append(' '); // pad to 20 chars - for (int i = 0; i < 8 - buf.length(); i++) { - buf.append(' '); - } + buf.append(" ".repeat(Math.max(0, 8 - buf.length()))); // Append the message - buf.append(message); + buf.append(LogUtil.escape(message)); // Append stack trace if not null if (t != null) { @@ -110,7 +104,7 @@ java.io.PrintWriter pw = new java.io.PrintWriter(sw); t.printStackTrace(pw); pw.close(); - buf.append(sw.toString()); + buf.append(LogUtil.escape(sw.toString())); } buf.append(System.lineSeparator()); diff -Nru tomcat10-10.1.34/java/org/apache/juli/JsonFormatter.java tomcat10-10.1.52/java/org/apache/juli/JsonFormatter.java --- tomcat10-10.1.34/java/org/apache/juli/JsonFormatter.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/JsonFormatter.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,207 @@ +/* + * 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.juli; + +import java.util.logging.LogManager; +import java.util.logging.LogRecord; + +/** + * Provides the same information as the one line format but using JSON formatting. All the information of the LogRecord + * is included as a one line JSON document, including the full stack trace of the associated exception if any. + *

          + * The LogRecord is mapped as attributes: + *

            + *
          • time: the log record timestamp, with the default format as {@code yyyy-MM-dd'T'HH:mm:ss.SSSX}
          • + *
          • level: the log level
          • + *
          • thread: the current on which the log occurred
          • + *
          • class: the class from which the log originated
          • + *
          • method: the method from which the log originated
          • + *
          • message: the log message
          • + *
          • throwable: the full stack trace from an exception, if present, represented as an array of string (the message + * first, then one string per stack trace element prefixed by a whitespace, then moving on to the cause exception if + * any)
          • + *
          + */ +public class JsonFormatter extends OneLineFormatter { + + private static final String DEFAULT_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSX"; + + public JsonFormatter() { + String timeFormat = LogManager.getLogManager().getProperty(JsonFormatter.class.getName() + ".timeFormat"); + if (timeFormat == null) { + timeFormat = DEFAULT_TIME_FORMAT; + } + setTimeFormat(timeFormat); + } + + @Override + public String format(LogRecord record) { + StringBuilder sb = new StringBuilder(); + sb.append('{'); + + // Timestamp + sb.append("\"time\": \""); + addTimestamp(sb, record.getMillis()); + sb.append("\", "); + + // Severity + sb.append("\"level\": \""); + sb.append(record.getLevel().getLocalizedName()); + sb.append("\", "); + + // Thread + sb.append("\"thread\": \""); + sb.append(resolveThreadName(record)); + sb.append("\", "); + + // Source + sb.append("\"class\": \""); + sb.append(record.getSourceClassName()); + sb.append("\", "); + sb.append("\"method\": \""); + sb.append(record.getSourceMethodName()); + sb.append("\", "); + + // Message + sb.append("\"message\": \""); + sb.append(JSONFilter.escape(formatMessage(record))); + + Throwable t = record.getThrown(); + if (t != null) { + sb.append("\", "); + + // Stack trace + sb.append("\"throwable\": ["); + boolean first = true; + do { + if (!first) { + sb.append(','); + } else { + first = false; + } + sb.append('\"').append(JSONFilter.escape(t.toString())).append('\"'); + for (StackTraceElement element : t.getStackTrace()) { + sb.append(',').append('\"').append(' ').append(JSONFilter.escape(element.toString())).append('\"'); + } + t = t.getCause(); + } while (t != null); + sb.append(']'); + } else { + sb.append('\"'); + } + + sb.append('}'); + // New line for next record + sb.append(System.lineSeparator()); + + return sb.toString(); + } + + + /** + * Provides escaping of values so they can be included in a JSON document. Escaping is based on the definition of + * JSON found in RFC 8259. + */ + public static class JSONFilter { + + /** + * Escape the given string. + * + * @param input the string + * + * @return the escaped string + */ + public static String escape(String input) { + return escape(input, 0, input.length()).toString(); + } + + /** + * Escape the given char sequence. + * + * @param input the char sequence + * @param off the offset on which escaping will start + * @param length the length which should be escaped + * + * @return the escaped char sequence corresponding to the specified range + */ + public static CharSequence escape(CharSequence input, int off, int length) { + /* + * While any character MAY be escaped, only U+0000 to U+001F (control characters), U+0022 (quotation mark) + * and U+005C (reverse solidus) MUST be escaped. + */ + StringBuilder escaped = null; + int lastUnescapedStart = off; + for (int i = off; i < length; i++) { + char c = input.charAt(i); + if (c < 0x20 || c == 0x22 || c == 0x5c || Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) { + if (escaped == null) { + escaped = new StringBuilder(length + 20); + } + if (lastUnescapedStart < i) { + escaped.append(input.subSequence(lastUnescapedStart, i)); + } + lastUnescapedStart = i + 1; + char popular = getPopularChar(c); + if (popular > 0) { + escaped.append('\\').append(popular); + } else { + escaped.append("\\u"); + escaped.append(String.format("%04X", Integer.valueOf(c))); + } + } + } + if (escaped == null) { + if (off == 0 && length == input.length()) { + return input; + } else { + return input.subSequence(off, length - off); + } + } else { + if (lastUnescapedStart < length) { + escaped.append(input.subSequence(lastUnescapedStart, length)); + } + return escaped.toString(); + } + } + + private JSONFilter() { + // Utility class. Hide the default constructor. + } + + private static char getPopularChar(char c) { + switch (c) { + case '"': + case '\\': + case '/': + return c; + case 0x8: + return 'b'; + case 0xc: + return 'f'; + case 0xa: + return 'n'; + case 0xd: + return 'r'; + case 0x9: + return 't'; + default: + return 0; + } + } + + } +} diff -Nru tomcat10-10.1.34/java/org/apache/juli/LogUtil.java tomcat10-10.1.52/java/org/apache/juli/LogUtil.java --- tomcat10-10.1.34/java/org/apache/juli/LogUtil.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/LogUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,64 @@ +/* + * 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.juli; + +public class LogUtil { + + private LogUtil() { + // Utility class. Hide default constructor + } + + + /** + * Escape a string so it can be displayed in a readable format. Characters that may not be printable in some/all of + * the contexts in which log messages will be viewed will be escaped using Java \\uNNNN escaping. + *

          + * All control characters are escaped apart from horizontal tab (\\u0009), new line (\\u000a) and carriage return + * (\\u000d). + * + * @param input The string to escape + * + * @return The escaped form of the input string + */ + @SuppressWarnings("null") // sb is not null when used + public static String escape(final String input) { + final int len = input.length(); + int i = 0; + int lastControl = -1; + StringBuilder sb = null; + while (i < len) { + char c = input.charAt(i); + if (Character.getType(c) == Character.CONTROL) { + if (!(c == '\t' || c == '\n' || c == '\r')) { + if (lastControl == -1) { + sb = new StringBuilder(len + 20); + } + sb.append(input.substring(lastControl + 1, i)); + sb.append(String.format("\\u%1$04x", Integer.valueOf(c))); + lastControl = i; + } + } + i++; + } + if (lastControl == -1) { + return input; + } else { + sb.append(input.substring(lastControl + 1, len)); + return sb.toString(); + } + } +} diff -Nru tomcat10-10.1.34/java/org/apache/juli/OneLineFormatter.java tomcat10-10.1.52/java/org/apache/juli/OneLineFormatter.java --- tomcat10-10.1.34/java/org/apache/juli/OneLineFormatter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/OneLineFormatter.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,8 +42,8 @@ private static final Object threadMxBeanLock = new Object(); private static volatile ThreadMXBean threadMxBean = null; private static final int THREAD_NAME_CACHE_SIZE = 10000; - private static final ThreadLocal threadNameCache = ThreadLocal - .withInitial(() -> new ThreadNameCache(THREAD_NAME_CACHE_SIZE)); + private static final ThreadLocal threadNameCache = + ThreadLocal.withInitial(() -> new ThreadNameCache(THREAD_NAME_CACHE_SIZE)); /* Timestamp format */ private static final String DEFAULT_TIME_FORMAT = "dd-MMM-yyyy HH:mm:ss.SSS"; @@ -51,12 +51,12 @@ /** * The size of our global date format cache */ - private static final int globalCacheSize = 30; + private static final int GLOBAL_CACHE_SIZE = 30; /** * The size of our thread local date format cache */ - private static final int localCacheSize = 5; + private static final int LOCAL_CACHE_SIZE = 5; /** * Thread local date format cache. @@ -100,9 +100,9 @@ cachedTimeFormat = timeFormat; } - final DateFormatCache globalDateCache = new DateFormatCache(globalCacheSize, cachedTimeFormat, null); - localDateCache = ThreadLocal - .withInitial(() -> new DateFormatCache(localCacheSize, cachedTimeFormat, globalDateCache)); + final DateFormatCache globalDateCache = new DateFormatCache(GLOBAL_CACHE_SIZE, cachedTimeFormat, null); + localDateCache = + ThreadLocal.withInitial(() -> new DateFormatCache(LOCAL_CACHE_SIZE, cachedTimeFormat, globalDateCache)); } @@ -130,14 +130,7 @@ // Thread sb.append(' '); sb.append('['); - final String threadName = Thread.currentThread().getName(); - if (threadName != null && threadName.startsWith(AsyncFileHandler.THREAD_PREFIX)) { - // If using the async handler can't get the thread name from the - // current thread. - sb.append(getThreadName(record.getThreadID())); - } else { - sb.append(threadName); - } + sb.append(resolveThreadName(record)); sb.append(']'); // Source @@ -148,7 +141,7 @@ // Message sb.append(' '); - sb.append(formatMessage(record)); + sb.append(LogUtil.escape(formatMessage(record))); // New line for next record sb.append(System.lineSeparator()); @@ -159,12 +152,23 @@ PrintWriter pw = new IndentingPrintWriter(sw); record.getThrown().printStackTrace(pw); pw.close(); - sb.append(sw.getBuffer()); + sb.append(LogUtil.escape(sw.toString())); } return sb.toString(); } + protected String resolveThreadName(LogRecord record) { + final String threadName = Thread.currentThread().getName(); + if (threadName != null && threadName.startsWith(AsyncFileHandler.THREAD_PREFIX)) { + // If using the async handler can't get the thread name from the + // current thread. + return getThreadName(record.getThreadID()); + } else { + return threadName; + } + } + protected void addTimestamp(StringBuilder buf, long timestamp) { String cachedTimeStamp = localDateCache.get().getFormat(timestamp); if (millisHandling == MillisHandling.NONE) { @@ -214,9 +218,13 @@ *

          * Words fail me to describe what I think of the design decision to use an int in LogRecord for a long value and the * resulting mess that follows. + * + * @param logRecordThreadId the thread id + * + * @return the thread name */ - private static String getThreadName(int logRecordThreadId) { - Map cache = threadNameCache.get(); + protected static String getThreadName(int logRecordThreadId) { + Map cache = threadNameCache.get(); String result = cache.get(Integer.valueOf(logRecordThreadId)); if (result != null) { @@ -250,7 +258,7 @@ /* * This is an LRU cache. */ - private static class ThreadNameCache extends LinkedHashMap { + private static class ThreadNameCache extends LinkedHashMap { private static final long serialVersionUID = 1L; @@ -262,7 +270,7 @@ } @Override - protected boolean removeEldestEntry(Entry eldest) { + protected boolean removeEldestEntry(Entry eldest) { return (size() > cacheSize); } } @@ -287,6 +295,10 @@ private enum MillisHandling { - NONE, APPEND, REPLACE_S, REPLACE_SS, REPLACE_SSS, + NONE, + APPEND, + REPLACE_S, + REPLACE_SS, + REPLACE_SSS, } } diff -Nru tomcat10-10.1.34/java/org/apache/juli/VerbatimFormatter.java tomcat10-10.1.52/java/org/apache/juli/VerbatimFormatter.java --- tomcat10-10.1.34/java/org/apache/juli/VerbatimFormatter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/VerbatimFormatter.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,9 +20,9 @@ import java.util.logging.LogRecord; /** - * Outputs the just the log message with no additional elements. Stack traces are not logged. Log messages are separated - * by System.lineSeparator(). This is intended for use by access logs and the like that need complete - * control over the output format. + * Outputs just the log message with no additional elements and no escaping. Stack traces are not logged. Log messages + * are separated by System.lineSeparator(). This is intended for use by access logs and the like that need + * complete control over the output format. */ public class VerbatimFormatter extends Formatter { @@ -31,5 +31,4 @@ // Timestamp + New line for next record return record.getMessage() + System.lineSeparator(); } - } diff -Nru tomcat10-10.1.34/java/org/apache/juli/logging/DirectJDKLog.java tomcat10-10.1.52/java/org/apache/juli/logging/DirectJDKLog.java --- tomcat10-10.1.34/java/org/apache/juli/logging/DirectJDKLog.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/logging/DirectJDKLog.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,23 +30,23 @@ public final Logger logger; // Alternate config reader and console format - private static final String SIMPLE_FMT="java.util.logging.SimpleFormatter"; - private static final String FORMATTER="org.apache.juli.formatter"; + private static final String SIMPLE_FMT = "java.util.logging.SimpleFormatter"; + private static final String FORMATTER = "org.apache.juli.formatter"; static { - if (System.getProperty("java.util.logging.config.class") == null && + if (System.getProperty("java.util.logging.config.class") == null && System.getProperty("java.util.logging.config.file") == null) { // default configuration - it sucks. Let's override at least the // formatter for the console try { - Formatter fmt= (Formatter) Class.forName(System.getProperty( - FORMATTER, SIMPLE_FMT)).getConstructor().newInstance(); + Formatter fmt = (Formatter) Class.forName(System.getProperty(FORMATTER, SIMPLE_FMT)).getConstructor() + .newInstance(); // it is also possible that the user modified jre/lib/logging.properties - // but that's really stupid in most cases - Logger root=Logger.getLogger(""); + Logger root = Logger.getLogger(""); for (Handler handler : root.getHandlers()) { // I only care about console - that's what's used in default config anyway - if (handler instanceof ConsoleHandler) { + if (handler instanceof ConsoleHandler) { handler.setFormatter(fmt); } } @@ -57,8 +57,8 @@ } } - DirectJDKLog(String name ) { - logger=Logger.getLogger(name); + DirectJDKLog(String name) { + logger = Logger.getLogger(name); } @Override @@ -151,25 +151,20 @@ log(Level.SEVERE, String.valueOf(message), t); } - // from commons logging. This would be my number one reason why java.util.logging - // is bad - design by committee can be really bad ! The impact on performance of - // using java.util.logging - and the ugliness if you need to wrap it - is far - // worse than the unfriendly and uncommon default format for logs. - private void log(Level level, String msg, Throwable ex) { if (logger.isLoggable(level)) { // Hack (?) to get the stack trace. - Throwable dummyException=new Throwable(); - StackTraceElement locations[]=dummyException.getStackTrace(); + Throwable dummyException = new Throwable(); + StackTraceElement[] locations = dummyException.getStackTrace(); // Caller will be the third element String cname = "unknown"; String method = "unknown"; - if (locations != null && locations.length >2) { + if (locations != null && locations.length > 2) { StackTraceElement caller = locations[2]; cname = caller.getClassName(); method = caller.getMethodName(); } - if (ex==null) { + if (ex == null) { logger.logp(level, cname, method, msg); } else { logger.logp(level, cname, method, msg, ex); @@ -178,8 +173,7 @@ } static Log getInstance(String name) { - return new DirectJDKLog( name ); + return new DirectJDKLog(name); } } - diff -Nru tomcat10-10.1.34/java/org/apache/juli/logging/Log.java tomcat10-10.1.52/java/org/apache/juli/logging/Log.java --- tomcat10-10.1.34/java/org/apache/juli/logging/Log.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/logging/Log.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,12 +17,11 @@ package org.apache.juli.logging; /** - *

          A simple logging interface abstracting logging APIs. In order to be - * instantiated successfully by {@link LogFactory}, classes that implement - * this interface must have a constructor that takes a single String - * parameter representing the "name" of this Log.

          - * - *

          The six logging levels used by Log are (in order):

          + * A simple logging interface abstracting logging APIs. In order to be instantiated successfully by {@link LogFactory}, + * classes that implement this interface must have a constructor that takes a single String parameter representing the + * "name" of this Log. + *

          + * The six logging levels used by Log are (in order): *

            *
          1. trace (the least serious)
          2. *
          3. debug
          4. @@ -31,112 +30,105 @@ *
          5. error
          6. *
          7. fatal (the most serious)
          8. *
          - *

          The mapping of these log levels to the concepts used by the underlying - * logging system is implementation dependent. - * The implementation should ensure, though, that this ordering behaves - * as expected.

          - * - *

          Performance is often a logging concern. - * By examining the appropriate property, - * a component can avoid expensive operations (producing information - * to be logged).

          - * - *

          For example, - * + *

          + * The mapping of these log levels to the concepts used by the underlying logging system is implementation dependent. + * The implementation should ensure, though, that this ordering behaves as expected. + *

          + * Performance is often a logging concern. By examining the appropriate property, a component can avoid expensive + * operations (producing information to be logged). + *

          + * For example, * if (log.isDebugEnabled()) { * ... do something expensive ... * log.debug(theResult); * } * - *

          - * - *

          Configuration of the underlying logging system will generally be done - * external to the Logging APIs, through whatever mechanism is supported by - * that system.

          - * - * @author Scott Sanders - * @author Rod Waldhoff + *

          + * Configuration of the underlying logging system will generally be done external to the Logging APIs, through whatever + * mechanism is supported by that system. */ public interface Log { - - // ----------------------------------------------------- Logging Properties - - /** - *

          Is debug logging currently enabled?

          + *

          + * Is debug logging currently enabled? + *

          + *

          + * Call this method to prevent having to perform expensive operations (for example, String + * concatenation) when the log level is more than debug. + *

          * - *

          Call this method to prevent having to perform expensive operations - * (for example, String concatenation) - * when the log level is more than debug.

          - * - * @return true if debug level logging is enabled, otherwise - * false + * @return true if debug level logging is enabled, otherwise false */ boolean isDebugEnabled(); /** - *

          Is error logging currently enabled?

          - * - *

          Call this method to prevent having to perform expensive operations - * (for example, String concatenation) - * when the log level is more than error.

          + *

          + * Is error logging currently enabled? + *

          + *

          + * Call this method to prevent having to perform expensive operations (for example, String + * concatenation) when the log level is more than error. + *

          * - * @return true if error level logging is enabled, otherwise - * false + * @return true if error level logging is enabled, otherwise false */ boolean isErrorEnabled(); /** - *

          Is fatal logging currently enabled?

          + *

          + * Is fatal logging currently enabled? + *

          + *

          + * Call this method to prevent having to perform expensive operations (for example, String + * concatenation) when the log level is more than fatal. + *

          * - *

          Call this method to prevent having to perform expensive operations - * (for example, String concatenation) - * when the log level is more than fatal.

          - * - * @return true if fatal level logging is enabled, otherwise - * false + * @return true if fatal level logging is enabled, otherwise false */ boolean isFatalEnabled(); /** - *

          Is info logging currently enabled?

          - * - *

          Call this method to prevent having to perform expensive operations - * (for example, String concatenation) - * when the log level is more than info.

          + *

          + * Is info logging currently enabled? + *

          + *

          + * Call this method to prevent having to perform expensive operations (for example, String + * concatenation) when the log level is more than info. + *

          * - * @return true if info level logging is enabled, otherwise - * false + * @return true if info level logging is enabled, otherwise false */ boolean isInfoEnabled(); /** - *

          Is trace logging currently enabled?

          + *

          + * Is trace logging currently enabled? + *

          + *

          + * Call this method to prevent having to perform expensive operations (for example, String + * concatenation) when the log level is more than trace. + *

          * - *

          Call this method to prevent having to perform expensive operations - * (for example, String concatenation) - * when the log level is more than trace.

          - * - * @return true if trace level logging is enabled, otherwise - * false + * @return true if trace level logging is enabled, otherwise false */ boolean isTraceEnabled(); /** - *

          Is warn logging currently enabled?

          - * - *

          Call this method to prevent having to perform expensive operations - * (for example, String concatenation) - * when the log level is more than warn.

          + *

          + * Is warn logging currently enabled? + *

          + *

          + * Call this method to prevent having to perform expensive operations (for example, String + * concatenation) when the log level is more than warn. + *

          * - * @return true if warn level logging is enabled, otherwise - * false + * @return true if warn level logging is enabled, otherwise false */ boolean isWarnEnabled(); @@ -145,7 +137,9 @@ /** - *

          Log a message with trace log level.

          + *

          + * Log a message with trace log level. + *

          * * @param message log this message */ @@ -153,16 +147,20 @@ /** - *

          Log an error with trace log level.

          + *

          + * Log an error with trace log level. + *

          * * @param message log this message - * @param t log this cause + * @param t log this cause */ void trace(Object message, Throwable t); /** - *

          Log a message with debug log level.

          + *

          + * Log a message with debug log level. + *

          * * @param message log this message */ @@ -170,16 +168,20 @@ /** - *

          Log an error with debug log level.

          + *

          + * Log an error with debug log level. + *

          * * @param message log this message - * @param t log this cause + * @param t log this cause */ void debug(Object message, Throwable t); /** - *

          Log a message with info log level.

          + *

          + * Log a message with info log level. + *

          * * @param message log this message */ @@ -187,16 +189,20 @@ /** - *

          Log an error with info log level.

          + *

          + * Log an error with info log level. + *

          * * @param message log this message - * @param t log this cause + * @param t log this cause */ void info(Object message, Throwable t); /** - *

          Log a message with warn log level.

          + *

          + * Log a message with warn log level. + *

          * * @param message log this message */ @@ -204,16 +210,20 @@ /** - *

          Log an error with warn log level.

          + *

          + * Log an error with warn log level. + *

          * * @param message log this message - * @param t log this cause + * @param t log this cause */ void warn(Object message, Throwable t); /** - *

          Log a message with error log level.

          + *

          + * Log a message with error log level. + *

          * * @param message log this message */ @@ -221,16 +231,20 @@ /** - *

          Log an error with error log level.

          + *

          + * Log an error with error log level. + *

          * * @param message log this message - * @param t log this cause + * @param t log this cause */ void error(Object message, Throwable t); /** - *

          Log a message with fatal log level.

          + *

          + * Log a message with fatal log level. + *

          * * @param message log this message */ @@ -238,10 +252,12 @@ /** - *

          Log an error with fatal log level.

          + *

          + * Log an error with fatal log level. + *

          * * @param message log this message - * @param t log this cause + * @param t log this cause */ void fatal(Object message, Throwable t); diff -Nru tomcat10-10.1.34/java/org/apache/juli/logging/LogConfigurationException.java tomcat10-10.1.52/java/org/apache/juli/logging/LogConfigurationException.java --- tomcat10-10.1.34/java/org/apache/juli/logging/LogConfigurationException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/logging/LogConfigurationException.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,11 +18,10 @@ /** - *

          An exception that is thrown only if a suitable LogFactory - * or Log instance cannot be created by the corresponding - * factory methods.

          - * - * @author Craig R. McClanahan + *

          + * An exception that is thrown only if a suitable LogFactory or Log instance cannot be created + * by the corresponding factory methods. + *

          */ public class LogConfigurationException extends RuntimeException { @@ -49,8 +48,7 @@ /** - * Construct a new exception with the specified cause and a derived - * detail message. + * Construct a new exception with the specified cause and a derived detail message. * * @param cause The underlying cause */ @@ -63,7 +61,7 @@ * Construct a new exception with the specified detail message and cause. * * @param message The detail message - * @param cause The underlying cause + * @param cause The underlying cause */ public LogConfigurationException(String message, Throwable cause) { super(message, cause); diff -Nru tomcat10-10.1.34/java/org/apache/juli/logging/LogFactory.java tomcat10-10.1.52/java/org/apache/juli/logging/LogFactory.java --- tomcat10-10.1.34/java/org/apache/juli/logging/LogFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/juli/logging/LogFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,46 +24,33 @@ import aQute.bnd.annotation.spi.ServiceConsumer; /** - * This is a modified LogFactory that uses a simple {@link ServiceLoader} based - * discovery mechanism with a default of using JDK based logging. An - * implementation that uses the full Commons Logging discovery mechanism is - * available as part of the Tomcat extras download. - * - * Why? It is an attempt to strike a balance between simpler code (no discovery) - * and providing flexibility - particularly for those projects that embed Tomcat - * or some of Tomcat's components - is an alternative logging + * This is a modified LogFactory that uses a simple {@link ServiceLoader} based discovery mechanism with a default of + * using JDK based logging. An implementation that uses the full Commons Logging discovery mechanism is available as + * part of the Tomcat extras download. + *

          + * Why? It is an attempt to strike a balance between simpler code (no discovery) and providing flexibility - + * particularly for those projects that embed Tomcat or some of Tomcat's components - is an alternative logging * implementation is desired. - * - * Note that this implementation is not just a wrapper around JDK logging (like - * the original commons-logging impl). It adds 2 features - a simpler - * configuration (which is in fact a subset of log4j.properties) and a - * formatter that is less ugly. - * - * The removal of 'abstract' preserves binary backward compatibility. It is - * possible to preserve the abstract - and introduce another (hardcoded) factory - * - but I see no benefit. - * - * Since this class is not intended to be extended - all protected methods are - * removed. This can be changed - but again, there is little value in keeping - * dead code. Just take a quick look at the removed code ( and it's complexity). - * - * -------------- - * + *

          + * Note that this implementation is not just a wrapper around JDK logging (like the original commons-logging impl). It + * adds 2 features - a simpler configuration (which is in fact a subset of log4j.properties) and a formatter that is + * less ugly. + *

          + * The removal of 'abstract' preserves binary backward compatibility. It is possible to preserve the abstract - and + * introduce another (hardcoded) factory - but I see no benefit. + *

          + * Since this class is not intended to be extended - all protected methods are removed. This can be changed - but again, + * there is little value in keeping dead code. Just take a quick look at the removed code ( and it's complexity). + *

          * Original comment: - *

          Factory for creating {@link Log} instances, with discovery and - * configuration features similar to that employed by standard Java APIs - * such as JAXP.

          - * - *

          IMPLEMENTATION NOTE - This implementation is heavily - * based on the SAXParserFactory and DocumentBuilderFactory implementations - * (corresponding to the JAXP pluggability APIs) found in Apache Xerces.

          - * - * - * @author Craig R. McClanahan - * @author Costin Manolache - * @author Richard A. Sitze + *

          + * Factory for creating {@link Log} instances, with discovery and configuration features similar to that employed by + * standard Java APIs such as JAXP. + *

          + * IMPLEMENTATION NOTE - This implementation is heavily based on the SAXParserFactory and + * DocumentBuilderFactory implementations (corresponding to the JAXP pluggability APIs) found in Apache Xerces. */ -@ServiceConsumer(value=Log.class) +@ServiceConsumer(value = Log.class) public class LogFactory { private static final LogFactory singleton = new LogFactory(); @@ -75,33 +62,29 @@ */ private LogFactory() { /* - * Work-around known a JRE bug. - * https://bugs.openjdk.java.net/browse/JDK-8194653 + * Work-around known a JRE bug. https://bugs.openjdk.java.net/browse/JDK-8194653 * - * Pre-load the default file system. No performance impact as we need to - * load the default file system anyway. Just do it earlier to avoid the - * potential deadlock. + * Preload the default file system. No performance impact as we need to load the default file system anyway. + * Just do it earlier to avoid the potential deadlock. * - * This can be removed once the oldest JRE supported by Tomcat includes - * a fix. + * This can be removed once the oldest JRE supported by Tomcat includes a fix. */ FileSystems.getDefault(); // Look via a ServiceLoader for a Log implementation that has a // constructor taking the String name. ServiceLoader logLoader = ServiceLoader.load(Log.class); - Constructor m=null; - for (Log log: logLoader) { - Class c=log.getClass(); + Constructor m = null; + for (Log log : logLoader) { + Class c = log.getClass(); try { - m=c.getConstructor(String.class); + m = c.getConstructor(String.class); break; - } - catch (NoSuchMethodException | SecurityException e) { + } catch (NoSuchMethodException | SecurityException e) { throw new Error(e); } } - discoveredLogConstructor=m; + discoveredLogConstructor = m; } @@ -110,23 +93,22 @@ // only those 2 methods need to change to use a different direct logger. /** - *

          Construct (if necessary) and return a Log instance, - * using the factory's current set of configuration attributes.

          + *

          + * Construct (if necessary) and return a Log instance, using the factory's current set of configuration + * attributes. + *

          + *

          + * NOTE - Depending upon the implementation of the LogFactory you are using, the + * Log instance you are returned may or may not be local to the current application, and may or may not + * be returned again on a subsequent call with the same name argument. + *

          * - *

          NOTE - Depending upon the implementation of - * the LogFactory you are using, the Log - * instance you are returned may or may not be local to the current - * application, and may or may not be returned again on a subsequent - * call with the same name argument.

          - * - * @param name Logical name of the Log instance to be - * returned (the meaning of this name is only known to the underlying - * logging implementation that is being wrapped) + * @param name Logical name of the Log instance to be returned (the meaning of this name is only known + * to the underlying logging implementation that is being wrapped) * * @return A log instance with the requested name * - * @exception LogConfigurationException if a suitable Log - * instance cannot be returned + * @exception LogConfigurationException if a suitable Log instance cannot be returned */ public Log getInstance(String name) throws LogConfigurationException { if (discoveredLogConstructor == null) { @@ -142,18 +124,16 @@ /** - * Convenience method to derive a name from the specified class and - * call getInstance(String) with it. + * Convenience method to derive a name from the specified class and call getInstance(String) with it. * * @param clazz Class for which a suitable Log name will be derived * * @return A log instance with a name of clazz.getName() * - * @exception LogConfigurationException if a suitable Log - * instance cannot be returned + * @exception LogConfigurationException if a suitable Log instance cannot be returned */ public Log getInstance(Class clazz) throws LogConfigurationException { - return getInstance( clazz.getName()); + return getInstance(clazz.getName()); } @@ -164,31 +144,28 @@ /** - *

          Construct (if necessary) and return a LogFactory - * instance, using the following ordered lookup procedure to determine - * the name of the implementation class to be loaded.

          + *

          + * Construct (if necessary) and return a LogFactory instance, using the following ordered lookup + * procedure to determine the name of the implementation class to be loaded. + *

          *
            - *
          • The org.apache.commons.logging.LogFactory system - * property.
          • + *
          • The org.apache.commons.logging.LogFactory system property.
          • *
          • The JDK 1.3 Service Discovery mechanism
          • - *
          • Use the properties file commons-logging.properties - * file, if found in the class path of this class. The configuration - * file is in standard java.util.Properties format and - * contains the fully qualified name of the implementation class - * with the key being the system property defined above.
          • + *
          • Use the properties file commons-logging.properties file, if found in the class path of this + * class. The configuration file is in standard java.util.Properties format and contains the fully + * qualified name of the implementation class with the key being the system property defined above.
          • *
          • Fall back to a default implementation class - * (org.apache.commons.logging.impl.LogFactoryImpl).
          • + * (org.apache.commons.logging.impl.LogFactoryImpl). *
          - * - *

          NOTE - If the properties file method of identifying the - * LogFactory implementation class is utilized, all of the - * properties defined in this file will be set as configuration attributes - * on the corresponding LogFactory instance.

          + *

          + * NOTE - If the properties file method of identifying the LogFactory implementation class is + * utilized, all of the properties defined in this file will be set as configuration attributes on the corresponding + * LogFactory instance. + *

          * * @return The singleton LogFactory instance * - * @exception LogConfigurationException if the implementation class is not - * available or cannot be instantiated. + * @exception LogConfigurationException if the implementation class is not available or cannot be instantiated. */ public static LogFactory getFactory() throws LogConfigurationException { return singleton; @@ -196,55 +173,45 @@ /** - * Convenience method to return a named logger, without the application - * having to care about factories. + * Convenience method to return a named logger, without the application having to care about factories. * * @param clazz Class from which a log name will be derived * * @return A log instance with a name of clazz.getName() * - * @exception LogConfigurationException if a suitable Log - * instance cannot be returned + * @exception LogConfigurationException if a suitable Log instance cannot be returned */ - public static Log getLog(Class clazz) - throws LogConfigurationException { + public static Log getLog(Class clazz) throws LogConfigurationException { return getFactory().getInstance(clazz); } /** - * Convenience method to return a named logger, without the application - * having to care about factories. + * Convenience method to return a named logger, without the application having to care about factories. * - * @param name Logical name of the Log instance to be - * returned (the meaning of this name is only known to the underlying - * logging implementation that is being wrapped) + * @param name Logical name of the Log instance to be returned (the meaning of this name is only known + * to the underlying logging implementation that is being wrapped) * * @return A log instance with the requested name * - * @exception LogConfigurationException if a suitable Log - * instance cannot be returned + * @exception LogConfigurationException if a suitable Log instance cannot be returned */ - public static Log getLog(String name) - throws LogConfigurationException { + public static Log getLog(String name) throws LogConfigurationException { return getFactory().getInstance(name); } /** - * Release any internal references to previously created {@link LogFactory} - * instances that have been associated with the specified class loader - * (if any), after calling the instance method release() on - * each of them. + * Release any internal references to previously created {@link LogFactory} instances that have been associated with + * the specified class loader (if any), after calling the instance method release() on each of them. * * @param classLoader ClassLoader for which to release the LogFactory */ public static void release(ClassLoader classLoader) { // JULI's log manager looks at the current classLoader so there is no // need to use the passed in classLoader, the default implementation - // does not so calling reset in that case will break things - if (!LogManager.getLogManager().getClass().getName().equals( - "java.util.logging.LogManager")) { + // does not so call reset in that case will break things + if (!LogManager.getLogManager().getClass().getName().equals("java.util.logging.LogManager")) { LogManager.getLogManager().reset(); } } diff -Nru tomcat10-10.1.34/java/org/apache/naming/ContextAccessController.java tomcat10-10.1.52/java/org/apache/naming/ContextAccessController.java --- tomcat10-10.1.34/java/org/apache/naming/ContextAccessController.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/ContextAccessController.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,6 @@ /** * Handles the access control on the JNDI contexts. - * - * @author Remy Maucherat */ public class ContextAccessController { @@ -45,15 +43,13 @@ /** * Set a security token for a Catalina context. Can be set only once. * - * @param name Name of the Catalina context + * @param name Name of the Catalina context * @param token Security token */ public static void setSecurityToken(Object name, Object token) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { - sm.checkPermission(new RuntimePermission( - ContextAccessController.class.getName() - + ".setSecurityToken")); + sm.checkPermission(new RuntimePermission(ContextAccessController.class.getName() + ".setSecurityToken")); } if ((!securityTokens.containsKey(name)) && (token != null)) { securityTokens.put(name, token); @@ -64,7 +60,7 @@ /** * Remove a security token for a context. * - * @param name Name of the Catalina context + * @param name Name of the Catalina context * @param token Security token */ public static void unsetSecurityToken(Object name, Object token) { @@ -77,15 +73,13 @@ /** * Check a submitted security token. * - * @param name Name of the Catalina context + * @param name Name of the Catalina context * @param token Submitted security token * - * @return true if the submitted token is equal to the token - * in the repository or if no token is present in the repository. - * Otherwise, false + * @return true if the submitted token is equal to the token in the repository or if no token is + * present in the repository. Otherwise, false */ - public static boolean checkSecurityToken - (Object name, Object token) { + public static boolean checkSecurityToken(Object name, Object token) { Object refToken = securityTokens.get(name); return (refToken == null || refToken.equals(token)); } @@ -94,7 +88,7 @@ /** * Allow writing to a context. * - * @param name Name of the Catalina context + * @param name Name of the Catalina context * @param token Security token */ public static void setWritable(Object name, Object token) { diff -Nru tomcat10-10.1.34/java/org/apache/naming/ContextBindings.java tomcat10-10.1.52/java/org/apache/naming/ContextBindings.java --- tomcat10-10.1.34/java/org/apache/naming/ContextBindings.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/ContextBindings.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,12 +29,9 @@ *
        • Calling thread with a NamingContext
        • *
        • Calling thread with object bound to the same naming context
        • *
        • Thread context class loader with a NamingContext
        • - *
        • Thread context class loader with object bound to the same - * NamingContext
        • + *
        • Thread context class loader with object bound to the same NamingContext
        • * * The objects are typically Catalina Server or Context objects. - * - * @author Remy Maucherat */ public class ContextBindings { @@ -81,8 +78,8 @@ /** * Binds an object and a naming context. * - * @param obj Object to bind with naming context - * @param context Associated naming context instance + * @param obj Object to bind with naming context + * @param context Associated naming context instance */ public static void bindContext(Object obj, Context context) { bindContext(obj, context, null); @@ -92,9 +89,9 @@ /** * Binds an object and a naming context. * - * @param obj Object to bind with naming context - * @param context Associated naming context instance - * @param token Security token + * @param obj Object to bind with naming context + * @param context Associated naming context instance + * @param token Security token */ public static void bindContext(Object obj, Context context, Object token) { if (ContextAccessController.checkSecurityToken(obj, token)) { @@ -119,7 +116,7 @@ /** * Retrieve a naming context. * - * @param obj Object bound to the required naming context + * @param obj Object bound to the required naming context */ static Context getContext(Object obj) { return objectBindings.get(obj); @@ -132,15 +129,13 @@ * @param obj Object bound to the required naming context * @param token Security token * - * @throws NamingException If no naming context is bound to the provided - * object + * @throws NamingException If no naming context is bound to the provided object */ public static void bindThread(Object obj, Object token) throws NamingException { if (ContextAccessController.checkSecurityToken(obj, token)) { Context context = objectBindings.get(obj); if (context == null) { - throw new NamingException( - sm.getString("contextBindings.unknownContext", obj)); + throw new NamingException(sm.getString("contextBindings.unknownContext", obj)); } Thread currentThread = Thread.currentThread(); threadBindings.put(currentThread, context); @@ -169,28 +164,24 @@ * * @return The naming context bound to the current thread. * - * @throws NamingException If no naming context is bound to the current - * thread + * @throws NamingException If no naming context is bound to the current thread */ public static Context getThread() throws NamingException { Context context = threadBindings.get(Thread.currentThread()); if (context == null) { - throw new NamingException - (sm.getString("contextBindings.noContextBoundToThread")); + throw new NamingException(sm.getString("contextBindings.noContextBoundToThread")); } return context; } /** - * Retrieves the name of the object bound to the naming context that is also - * bound to the current thread. + * Retrieves the name of the object bound to the naming context that is also bound to the current thread. */ static String getThreadName() throws NamingException { Object obj = threadObjectBindings.get(Thread.currentThread()); if (obj == null) { - throw new NamingException - (sm.getString("contextBindings.noContextBoundToThread")); + throw new NamingException(sm.getString("contextBindings.noContextBoundToThread")); } return obj.toString(); } @@ -199,8 +190,7 @@ /** * Tests if current thread is bound to a naming context. * - * @return true if the current thread is bound to a naming - * context, otherwise false + * @return true if the current thread is bound to a naming context, otherwise false */ public static boolean isThreadBound() { return threadBindings.containsKey(Thread.currentThread()); @@ -210,20 +200,17 @@ /** * Binds a naming context to a class loader. * - * @param obj Object bound to the required naming context - * @param token Security token - * @param classLoader The class loader to bind to the naming context + * @param obj Object bound to the required naming context + * @param token Security token + * @param classLoader The class loader to bind to the naming context * - * @throws NamingException If no naming context is bound to the provided - * object + * @throws NamingException If no naming context is bound to the provided object */ - public static void bindClassLoader(Object obj, Object token, - ClassLoader classLoader) throws NamingException { + public static void bindClassLoader(Object obj, Object token, ClassLoader classLoader) throws NamingException { if (ContextAccessController.checkSecurityToken(obj, token)) { Context context = objectBindings.get(obj); if (context == null) { - throw new NamingException - (sm.getString("contextBindings.unknownContext", obj)); + throw new NamingException(sm.getString("contextBindings.unknownContext", obj)); } clBindings.put(classLoader, context); clObjectBindings.put(classLoader, obj); @@ -234,12 +221,11 @@ /** * Unbinds a naming context and a class loader. * - * @param obj Object bound to the required naming context - * @param token Security token - * @param classLoader The class loader bound to the naming context + * @param obj Object bound to the required naming context + * @param token Security token + * @param classLoader The class loader bound to the naming context */ - public static void unbindClassLoader(Object obj, Object token, - ClassLoader classLoader) { + public static void unbindClassLoader(Object obj, Object token, ClassLoader classLoader) { if (ContextAccessController.checkSecurityToken(obj, token)) { Object o = clObjectBindings.get(classLoader); if (o == null || !o.equals(obj)) { @@ -254,14 +240,13 @@ /** * Retrieves the naming context bound to a class loader. * - * @return the naming context bound to current class loader or one of its - * parents + * @return the naming context bound to current class loader or one of its parents * * @throws NamingException If no naming context was bound */ public static Context getClassLoader() throws NamingException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); - Context context = null; + Context context; do { context = clBindings.get(cl); if (context != null) { @@ -273,28 +258,27 @@ /** - * Retrieves the name of the object bound to the naming context that is also - * bound to the thread context class loader. + * Retrieves the name of the object bound to the naming context that is also bound to the thread context class + * loader. */ static String getClassLoaderName() throws NamingException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); - Object obj = null; + Object obj; do { obj = clObjectBindings.get(cl); if (obj != null) { return obj.toString(); } } while ((cl = cl.getParent()) != null); - throw new NamingException (sm.getString("contextBindings.noContextBoundToCL")); + throw new NamingException(sm.getString("contextBindings.noContextBoundToCL")); } /** * Tests if the thread context class loader is bound to a context. * - * @return true if the thread context class loader or one of - * its parents is bound to a naming context, otherwise - * false + * @return true if the thread context class loader or one of its parents is bound to a naming context, + * otherwise false */ public static boolean isClassLoaderBound() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); diff -Nru tomcat10-10.1.34/java/org/apache/naming/EjbRef.java tomcat10-10.1.52/java/org/apache/naming/EjbRef.java --- tomcat10-10.1.34/java/org/apache/naming/EjbRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/EjbRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Represents a reference address to an EJB. - * - * @author Remy Maucherat */ public class EjbRef extends AbstractRef { @@ -31,8 +29,7 @@ /** * Default factory for this reference. */ - public static final String DEFAULT_FACTORY = - org.apache.naming.factory.Constants.DEFAULT_EJB_FACTORY; + public static final String DEFAULT_FACTORY = org.apache.naming.factory.Constants.DEFAULT_EJB_FACTORY; /** @@ -57,9 +54,9 @@ * EJB Reference. * * @param ejbType EJB type - * @param home Home interface classname - * @param remote Remote interface classname - * @param link EJB link + * @param home Home interface classname + * @param remote Remote interface classname + * @param link EJB link */ public EjbRef(String ejbType, String home, String remote, String link) { this(ejbType, home, remote, link, null, null); @@ -69,18 +66,16 @@ /** * EJB Reference. * - * @param ejbType EJB type - * @param home Home interface classname - * @param remote Remote interface classname - * @param link EJB link - * @param factory The possibly null class name of the object's factory. - * @param factoryLocation The possibly null location from which to load - * the factory (e.g. URL) + * @param ejbType EJB type + * @param home Home interface classname + * @param remote Remote interface classname + * @param link EJB link + * @param factory The possibly null class name of the object's factory. + * @param factoryLocation The possibly null location from which to load the factory (e.g. URL) */ - public EjbRef(String ejbType, String home, String remote, String link, - String factory, String factoryLocation) { + public EjbRef(String ejbType, String home, String remote, String link, String factory, String factoryLocation) { super(home, factory, factoryLocation); - StringRefAddr refAddr = null; + StringRefAddr refAddr; if (ejbType != null) { refAddr = new StringRefAddr(TYPE, ejbType); add(refAddr); diff -Nru tomcat10-10.1.34/java/org/apache/naming/HandlerRef.java tomcat10-10.1.52/java/org/apache/naming/HandlerRef.java --- tomcat10-10.1.34/java/org/apache/naming/HandlerRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/HandlerRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Represents a reference handler for a web service. - * - * @author Fabien Carrion */ public class HandlerRef extends AbstractRef { @@ -31,56 +29,55 @@ /** * Default factory for this reference. */ - public static final String DEFAULT_FACTORY = - org.apache.naming.factory.Constants.DEFAULT_HANDLER_FACTORY; + public static final String DEFAULT_FACTORY = org.apache.naming.factory.Constants.DEFAULT_HANDLER_FACTORY; /** * HandlerName address type. */ - public static final String HANDLER_NAME = "handlername"; + public static final String HANDLER_NAME = "handlername"; /** * Handler Classname address type. */ - public static final String HANDLER_CLASS = "handlerclass"; + public static final String HANDLER_CLASS = "handlerclass"; /** * Handler Classname address type. */ - public static final String HANDLER_LOCALPART = "handlerlocalpart"; + public static final String HANDLER_LOCALPART = "handlerlocalpart"; /** * Handler Classname address type. */ - public static final String HANDLER_NAMESPACE = "handlernamespace"; + public static final String HANDLER_NAMESPACE = "handlernamespace"; /** * Handler Classname address type. */ - public static final String HANDLER_PARAMNAME = "handlerparamname"; + public static final String HANDLER_PARAMNAME = "handlerparamname"; /** * Handler Classname address type. */ - public static final String HANDLER_PARAMVALUE = "handlerparamvalue"; + public static final String HANDLER_PARAMVALUE = "handlerparamvalue"; /** * Handler SoapRole address type. */ - public static final String HANDLER_SOAPROLE = "handlersoaprole"; + public static final String HANDLER_SOAPROLE = "handlersoaprole"; /** * Handler PortName address type. */ - public static final String HANDLER_PORTNAME = "handlerportname"; + public static final String HANDLER_PORTNAME = "handlerportname"; public HandlerRef(String refname, String handlerClass) { @@ -88,10 +85,9 @@ } - public HandlerRef(String refname, String handlerClass, - String factory, String factoryLocation) { + public HandlerRef(String refname, String handlerClass, String factory, String factoryLocation) { super(refname, factory, factoryLocation); - StringRefAddr refAddr = null; + StringRefAddr refAddr; if (refname != null) { refAddr = new StringRefAddr(HANDLER_NAME, refname); add(refAddr); diff -Nru tomcat10-10.1.34/java/org/apache/naming/LocalStrings.properties tomcat10-10.1.52/java/org/apache/naming/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/naming/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,8 @@ contextBindings.unknownContext=Unknown context name : [{0}] namingContext.alreadyBound=Name [{0}] is already bound in this Context -namingContext.contextExpected=Name is not bound to a Context -namingContext.failResolvingReference=Unexpected exception resolving reference +namingContext.contextExpected=Name [{0}] is not bound to a Context +namingContext.failResolvingReference=Unexpected exception resolving reference with name [{0}] namingContext.invalidName=Name is not valid namingContext.nameNotBound=Name [{0}] is not bound in this Context. Unable to find [{1}]. namingContext.noAbsoluteName=Cannot generate an absolute name for this namespace diff -Nru tomcat10-10.1.34/java/org/apache/naming/LocalStrings_cs.properties tomcat10-10.1.52/java/org/apache/naming/LocalStrings_cs.properties --- tomcat10-10.1.34/java/org/apache/naming/LocalStrings_cs.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LocalStrings_cs.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,4 +16,3 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations -namingContext.contextExpected=Jméno není svázáno s kontextem diff -Nru tomcat10-10.1.34/java/org/apache/naming/LocalStrings_de.properties tomcat10-10.1.52/java/org/apache/naming/LocalStrings_de.properties --- tomcat10-10.1.34/java/org/apache/naming/LocalStrings_de.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LocalStrings_de.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,7 +18,5 @@ contextBindings.unknownContext=Unbekannter Kontext-Name: [{0}] -namingContext.contextExpected=Ein Name ist nicht an den Context gebunden - selectorContext.methodUsingName=Aufruf der Methode [{0}] mit Namen [{1}] selectorContext.noJavaUrl=Auf diesen Kontext muss durch eine java:-URL zugegriffen werden diff -Nru tomcat10-10.1.34/java/org/apache/naming/LocalStrings_es.properties tomcat10-10.1.52/java/org/apache/naming/LocalStrings_es.properties --- tomcat10-10.1.34/java/org/apache/naming/LocalStrings_es.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LocalStrings_es.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,7 @@ contextBindings.unknownContext=Contexto [{0}] desconocido namingContext.alreadyBound=El nombre [{0}] este ya asociado en este Contexto -namingContext.contextExpected=El nombre no esta asociado a ningun Contexto -namingContext.failResolvingReference=Excepción inesperada resolviendo referencia +namingContext.contextExpected=El nombre [{0}] no está vinculado a un Contexto namingContext.invalidName=Nombre no valido namingContext.nameNotBound=El nombre [{0}] no este asociado a este contexto namingContext.noAbsoluteName=No se puede generar un nombre absoluto para este espacio de nombres diff -Nru tomcat10-10.1.34/java/org/apache/naming/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/naming/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/naming/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,8 @@ contextBindings.unknownContext=Nom de Contexte inconnu : [{0}] namingContext.alreadyBound=Le Nom [{0}] est déjà lié à ce Contexte -namingContext.contextExpected=Le Nom n'est pas lié à un Contexte -namingContext.failResolvingReference=Une erreur s est produite durant la résolution de la référence +namingContext.contextExpected=Le nom [{0}] n''est pas associé à un Context +namingContext.failResolvingReference=Erreur inattendue lors de la résolution de la référence avec le nom [{0}] namingContext.invalidName=Le Nom est invalide namingContext.nameNotBound=Le Nom [{0}] n''est pas lié à ce Contexte namingContext.noAbsoluteName=Impossible de générer un nom absolu pour cet espace de nommage (namespace) diff -Nru tomcat10-10.1.34/java/org/apache/naming/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/naming/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/naming/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,8 @@ contextBindings.unknownContext=未知のコンテキスト名です: [{0}] namingContext.alreadyBound=名前 [{0}] は既にこのコンテキストにバインドされています -namingContext.contextExpected=名前がコンテキストにバインドされていません -namingContext.failResolvingReference=参照の解決中に予測しない例外が発生しました +namingContext.contextExpected=名前 [{0}] はコンテキストにバインドされていません +namingContext.failResolvingReference=名前 [{0}] の参照を解決中に予期しない例外が発生しました namingContext.invalidName=名前は無効です namingContext.nameNotBound=名前 [{0}] はこのコンテキストにバインドされていません namingContext.noAbsoluteName=この名前空間に絶対名を生成できません diff -Nru tomcat10-10.1.34/java/org/apache/naming/LocalStrings_ko.properties tomcat10-10.1.52/java/org/apache/naming/LocalStrings_ko.properties --- tomcat10-10.1.34/java/org/apache/naming/LocalStrings_ko.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LocalStrings_ko.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,6 @@ contextBindings.unknownContext=알 수 없는 컨텍스트 이름: [{0}] namingContext.alreadyBound=Name [{0}]이(가) 이미 이 컨텍스트에 바인딩 되어 있습니다. -namingContext.contextExpected=Name이 컨텍스트에 바인딩 되지 않았습니다. -namingContext.failResolvingReference=참조를 결정하는 중 예기치 않은 예외 발생 namingContext.invalidName=Name이 유효하지 않습니다. namingContext.nameNotBound=Name [{0}]은(는) 이 컨텍스트에 바인딩되지 않았습니다. [{1}]을(를) 찾을 수 없습니다. namingContext.noAbsoluteName=이 네임스페이스를 위한 절대 이름을 생성할 수 없습니다. diff -Nru tomcat10-10.1.34/java/org/apache/naming/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/naming/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/naming/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,7 +16,5 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations -namingContext.contextExpected=Имя не привязано к контексту - selectorContext.methodUsingName=Вызов метода [{0}] с именем [{1}] selectorContext.methodUsingString=Вызов метода [{0}] для строки [{1}] diff -Nru tomcat10-10.1.34/java/org/apache/naming/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/naming/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/naming/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,7 @@ contextBindings.unknownContext=未知.上下文名:[{0}] namingContext.alreadyBound=名称[{0}]已在此上下文中绑定 -namingContext.contextExpected=上下文Context未绑定名称name -namingContext.failResolvingReference=解析引用时意外异常 +namingContext.contextExpected=名称 [{0}] 未被绑定到一个域 namingContext.invalidName=名称无效 namingContext.nameNotBound=名称[{0}]未在此上下文中绑定。找不到[{1}]。 namingContext.noAbsoluteName=无法为此命名空间生成绝对名称 diff -Nru tomcat10-10.1.34/java/org/apache/naming/LookupRef.java tomcat10-10.1.52/java/org/apache/naming/LookupRef.java --- tomcat10-10.1.34/java/org/apache/naming/LookupRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/LookupRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,7 +39,7 @@ public LookupRef(String resourceType, String factory, String factoryLocation, String lookupName) { super(resourceType, factory, factoryLocation); - if (lookupName != null && !lookupName.equals("")) { + if (lookupName != null && !lookupName.isEmpty()) { RefAddr ref = new StringRefAddr(LOOKUP_NAME, lookupName); add(ref); } diff -Nru tomcat10-10.1.34/java/org/apache/naming/NameParserImpl.java tomcat10-10.1.52/java/org/apache/naming/NameParserImpl.java --- tomcat10-10.1.34/java/org/apache/naming/NameParserImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/NameParserImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,19 +23,15 @@ /** * Parses names. - * - * @author Remy Maucherat */ -public class NameParserImpl - implements NameParser { +public class NameParserImpl implements NameParser { // ----------------------------------------------------- NameParser Methods @Override - public Name parse(String name) - throws NamingException { + public Name parse(String name) throws NamingException { return new CompositeName(name); } diff -Nru tomcat10-10.1.34/java/org/apache/naming/NamingContext.java tomcat10-10.1.52/java/org/apache/naming/NamingContext.java --- tomcat10-10.1.34/java/org/apache/naming/NamingContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/NamingContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,8 +44,6 @@ /** * Catalina JNDI Context implementation. - * - * @author Remy Maucherat */ public class NamingContext implements Context { @@ -68,7 +66,7 @@ /** * Builds a naming context. * - * @param env The environment to use to construct the naming context + * @param env The environment to use to construct the naming context * @param name The name of the associated Catalina Context */ public NamingContext(Hashtable env, String name) { @@ -79,17 +77,16 @@ /** * Builds a naming context. * - * @param env The environment to use to construct the naming context - * @param name The name of the associated Catalina Context + * @param env The environment to use to construct the naming context + * @param name The name of the associated Catalina Context * @param bindings The initial bindings for the naming context */ - public NamingContext(Hashtable env, String name, - HashMap bindings) { + public NamingContext(Hashtable env, String name, HashMap bindings) { this.env = new Hashtable<>(); this.name = name; // Populating the environment hashtable - if (env != null ) { + if (env != null) { Enumeration envEntries = env.keys(); while (envEntries.hasMoreElements()) { String entryName = envEntries.nextElement(); @@ -128,13 +125,14 @@ /** - * Determines if an attempt to write to a read-only context results in an - * exception or if the request is ignored. + * Determines if an attempt to write to a read-only context results in an exception or if the request is ignored. */ private boolean exceptionOnFailedWrite = true; + public boolean getExceptionOnFailedWrite() { return exceptionOnFailedWrite; } + public void setExceptionOnFailedWrite(boolean exceptionOnFailedWrite) { this.exceptionOnFailedWrite = exceptionOnFailedWrite; } @@ -143,43 +141,37 @@ // -------------------------------------------------------- Context Methods @Override - public Object lookup(Name name) - throws NamingException { + public Object lookup(Name name) throws NamingException { return lookup(name, true); } @Override - public Object lookup(String name) - throws NamingException { + public Object lookup(String name) throws NamingException { return lookup(new CompositeName(name), true); } @Override - public void bind(Name name, Object obj) - throws NamingException { + public void bind(Name name, Object obj) throws NamingException { bind(name, obj, false); } @Override - public void bind(String name, Object obj) - throws NamingException { + public void bind(String name, Object obj) throws NamingException { bind(new CompositeName(name), obj); } @Override - public void rebind(Name name, Object obj) - throws NamingException { + public void rebind(Name name, Object obj) throws NamingException { bind(name, obj, true); } @Override - public void rebind(String name, Object obj) - throws NamingException { + public void rebind(String name, Object obj) throws NamingException { rebind(new CompositeName(name), obj); } @@ -191,27 +183,24 @@ return; } - while ((!name.isEmpty()) && (name.get(0).length() == 0)) { + while ((!name.isEmpty()) && (name.get(0).isEmpty())) { name = name.getSuffix(1); } if (name.isEmpty()) { - throw new NamingException - (sm.getString("namingContext.invalidName")); + throw new NamingException(sm.getString("namingContext.invalidName")); } NamingEntry entry = bindings.get(name.get(0)); if (entry == null) { - throw new NameNotFoundException - (sm.getString("namingContext.nameNotBound", name, name.get(0))); + throw new NameNotFoundException(sm.getString("namingContext.nameNotBound", name, name.get(0))); } if (name.size() > 1) { if (entry.type == NamingEntry.CONTEXT) { ((Context) entry.value).unbind(name.getSuffix(1)); } else { - throw new NamingException - (sm.getString("namingContext.contextExpected")); + throw new NamingException(sm.getString("namingContext.contextExpected", name.get(0))); } } else { bindings.remove(name.get(0)); @@ -221,15 +210,13 @@ @Override - public void unbind(String name) - throws NamingException { + public void unbind(String name) throws NamingException { unbind(new CompositeName(name)); } @Override - public void rename(Name oldName, Name newName) - throws NamingException { + public void rename(Name oldName, Name newName) throws NamingException { Object value = lookup(oldName); bind(newName, value); unbind(oldName); @@ -237,17 +224,15 @@ @Override - public void rename(String oldName, String newName) - throws NamingException { + public void rename(String oldName, String newName) throws NamingException { rename(new CompositeName(oldName), new CompositeName(newName)); } @Override - public NamingEnumeration list(Name name) - throws NamingException { + public NamingEnumeration list(Name name) throws NamingException { // Removing empty parts - while ((!name.isEmpty()) && (name.get(0).length() == 0)) { + while ((!name.isEmpty()) && (name.get(0).isEmpty())) { name = name.getSuffix(1); } if (name.isEmpty()) { @@ -257,30 +242,26 @@ NamingEntry entry = bindings.get(name.get(0)); if (entry == null) { - throw new NameNotFoundException - (sm.getString("namingContext.nameNotBound", name, name.get(0))); + throw new NameNotFoundException(sm.getString("namingContext.nameNotBound", name, name.get(0))); } if (entry.type != NamingEntry.CONTEXT) { - throw new NamingException - (sm.getString("namingContext.contextExpected")); + throw new NamingException(sm.getString("namingContext.contextExpected", name.get(0))); } return ((Context) entry.value).list(name.getSuffix(1)); } @Override - public NamingEnumeration list(String name) - throws NamingException { + public NamingEnumeration list(String name) throws NamingException { return list(new CompositeName(name)); } @Override - public NamingEnumeration listBindings(Name name) - throws NamingException { + public NamingEnumeration listBindings(Name name) throws NamingException { // Removing empty parts - while ((!name.isEmpty()) && (name.get(0).length() == 0)) { + while ((!name.isEmpty()) && (name.get(0).isEmpty())) { name = name.getSuffix(1); } if (name.isEmpty()) { @@ -290,21 +271,18 @@ NamingEntry entry = bindings.get(name.get(0)); if (entry == null) { - throw new NameNotFoundException - (sm.getString("namingContext.nameNotBound", name, name.get(0))); + throw new NameNotFoundException(sm.getString("namingContext.nameNotBound", name, name.get(0))); } if (entry.type != NamingEntry.CONTEXT) { - throw new NamingException - (sm.getString("namingContext.contextExpected")); + throw new NamingException(sm.getString("namingContext.contextExpected", name.get(0))); } return ((Context) entry.value).listBindings(name.getSuffix(1)); } @Override - public NamingEnumeration listBindings(String name) - throws NamingException { + public NamingEnumeration listBindings(String name) throws NamingException { return listBindings(new CompositeName(name)); } @@ -316,35 +294,31 @@ return; } - while ((!name.isEmpty()) && (name.get(0).length() == 0)) { + while ((!name.isEmpty()) && (name.get(0).isEmpty())) { name = name.getSuffix(1); } if (name.isEmpty()) { - throw new NamingException - (sm.getString("namingContext.invalidName")); + throw new NamingException(sm.getString("namingContext.invalidName")); } NamingEntry entry = bindings.get(name.get(0)); if (entry == null) { - throw new NameNotFoundException - (sm.getString("namingContext.nameNotBound", name, name.get(0))); + throw new NameNotFoundException(sm.getString("namingContext.nameNotBound", name, name.get(0))); } if (name.size() > 1) { if (entry.type == NamingEntry.CONTEXT) { ((Context) entry.value).destroySubcontext(name.getSuffix(1)); } else { - throw new NamingException - (sm.getString("namingContext.contextExpected")); + throw new NamingException(sm.getString("namingContext.contextExpected", name.get(0))); } } else { if (entry.type == NamingEntry.CONTEXT) { ((Context) entry.value).close(); bindings.remove(name.get(0)); } else { - throw new NotContextException - (sm.getString("namingContext.contextExpected")); + throw new NotContextException(sm.getString("namingContext.contextExpected", name.get(0))); } } @@ -352,8 +326,7 @@ @Override - public void destroySubcontext(String name) - throws NamingException { + public void destroySubcontext(String name) throws NamingException { destroySubcontext(new CompositeName(name)); } @@ -374,31 +347,27 @@ @Override - public Context createSubcontext(String name) - throws NamingException { + public Context createSubcontext(String name) throws NamingException { return createSubcontext(new CompositeName(name)); } @Override - public Object lookupLink(Name name) - throws NamingException { + public Object lookupLink(Name name) throws NamingException { return lookup(name, false); } @Override - public Object lookupLink(String name) - throws NamingException { + public Object lookupLink(String name) throws NamingException { return lookup(new CompositeName(name), false); } @Override - public NameParser getNameParser(Name name) - throws NamingException { + public NameParser getNameParser(Name name) throws NamingException { - while ((!name.isEmpty()) && (name.get(0).length() == 0)) { + while ((!name.isEmpty()) && (name.get(0).isEmpty())) { name = name.getSuffix(1); } if (name.isEmpty()) { @@ -410,8 +379,7 @@ if (obj instanceof Context) { return ((Context) obj).getNameParser(name.getSuffix(1)); } else { - throw new NotContextException - (sm.getString("namingContext.contextExpected")); + throw new NotContextException(sm.getString("namingContext.contextExpected", name.get(0))); } } @@ -421,8 +389,7 @@ @Override - public NameParser getNameParser(String name) - throws NamingException { + public NameParser getNameParser(String name) throws NamingException { return getNameParser(new CompositeName(name)); } @@ -447,7 +414,7 @@ @Override - public Object removeFromEnvironment(String propName){ + public Object removeFromEnvironment(String propName) { return env.remove(propName); } @@ -468,10 +435,8 @@ @Override - public String getNameInNamespace() - throws NamingException { - throw new OperationNotSupportedException - (sm.getString("namingContext.noAbsoluteName")); + public String getNameInNamespace() throws NamingException { + throw new OperationNotSupportedException(sm.getString("namingContext.noAbsoluteName")); } @@ -496,16 +461,17 @@ /** * Retrieves the named object. * - * @param name the name of the object to look up + * @param name the name of the object to look up * @param resolveLinks If true, the links will be resolved + * * @return the object bound to name + * * @exception NamingException if a naming exception is encountered */ - protected Object lookup(Name name, boolean resolveLinks) - throws NamingException { + protected Object lookup(Name name, boolean resolveLinks) throws NamingException { // Removing empty parts - while ((!name.isEmpty()) && (name.get(0).length() == 0)) { + while ((!name.isEmpty()) && (name.get(0).isEmpty())) { name = name.getSuffix(1); } if (name.isEmpty()) { @@ -516,16 +482,14 @@ NamingEntry entry = bindings.get(name.get(0)); if (entry == null) { - throw new NameNotFoundException - (sm.getString("namingContext.nameNotBound", name, name.get(0))); + throw new NameNotFoundException(sm.getString("namingContext.nameNotBound", name, name.get(0))); } if (name.size() > 1) { - // If the size of the name is greater that 1, then we go through a - // number of subcontexts. + // If the size of the name is greater than 1, then we go through a + // number of sub contexts. if (entry.type != NamingEntry.CONTEXT) { - throw new NamingException - (sm.getString("namingContext.contextExpected")); + throw new NamingException(sm.getString("namingContext.contextExpected", name.get(0))); } return ((Context) entry.value).lookup(name.getSuffix(1)); } else { @@ -556,21 +520,20 @@ } if (entry.value instanceof ResourceRef) { boolean singleton = Boolean.parseBoolean( - (String) ((ResourceRef) entry.value).get( - ResourceRef.SINGLETON).getContent()); + (String) ((ResourceRef) entry.value).get(ResourceRef.SINGLETON).getContent()); if (singleton) { entry.type = NamingEntry.ENTRY; entry.value = obj; } } if (obj == null) { - throw new NamingException(sm.getString("namingContext.failResolvingReference")); + throw new NamingException(sm.getString("namingContext.failResolvingReference", name)); } return obj; } catch (NamingException e) { throw e; } catch (Exception e) { - String msg = sm.getString("namingContext.failResolvingReference"); + String msg = sm.getString("namingContext.failResolvingReference", name); log.warn(msg, e); NamingException ne = new NamingException(msg); ne.initCause(e); @@ -585,39 +548,35 @@ /** - * Binds a name to an object. All intermediate contexts and the target - * context (that named by all but terminal atomic component of the name) - * must already exist. + * Binds a name to an object. All intermediate contexts and the target context (that named by all but terminal + * atomic component of the name) must already exist. * - * @param name the name to bind; may not be empty - * @param obj the object to bind; possibly null + * @param name the name to bind; may not be empty + * @param obj the object to bind; possibly null * @param rebind if true, then perform a rebind (ie, overwrite) - * @exception NameAlreadyBoundException if name is already bound - * @exception javax.naming.directory.InvalidAttributesException if object - * did not supply all mandatory attributes - * @exception NamingException if a naming exception is encountered + * + * @exception NameAlreadyBoundException if name is already bound + * @exception javax.naming.directory.InvalidAttributesException if object did not supply all mandatory attributes + * @exception NamingException if a naming exception is encountered */ - protected void bind(Name name, Object obj, boolean rebind) - throws NamingException { + protected void bind(Name name, Object obj, boolean rebind) throws NamingException { if (!checkWritable()) { return; } - while ((!name.isEmpty()) && (name.get(0).length() == 0)) { + while ((!name.isEmpty()) && (name.get(0).isEmpty())) { name = name.getSuffix(1); } if (name.isEmpty()) { - throw new NamingException - (sm.getString("namingContext.invalidName")); + throw new NamingException(sm.getString("namingContext.invalidName")); } NamingEntry entry = bindings.get(name.get(0)); if (name.size() > 1) { if (entry == null) { - throw new NameNotFoundException(sm.getString( - "namingContext.nameNotBound", name, name.get(0))); + throw new NameNotFoundException(sm.getString("namingContext.nameNotBound", name, name.get(0))); } if (entry.type == NamingEntry.CONTEXT) { if (rebind) { @@ -626,34 +585,26 @@ ((Context) entry.value).bind(name.getSuffix(1), obj); } } else { - throw new NamingException - (sm.getString("namingContext.contextExpected")); + throw new NamingException(sm.getString("namingContext.contextExpected", name.get(0))); } } else { if ((!rebind) && (entry != null)) { - throw new NameAlreadyBoundException - (sm.getString("namingContext.alreadyBound", name.get(0))); + throw new NameAlreadyBoundException(sm.getString("namingContext.alreadyBound", name.get(0))); } else { // Getting the type of the object and wrapping it within a new // NamingEntry - Object toBind = - NamingManager.getStateToBind(obj, name, this, env); + Object toBind = NamingManager.getStateToBind(obj, name, this, env); if (toBind instanceof Context) { - entry = new NamingEntry(name.get(0), toBind, - NamingEntry.CONTEXT); + entry = new NamingEntry(name.get(0), toBind, NamingEntry.CONTEXT); } else if (toBind instanceof LinkRef) { - entry = new NamingEntry(name.get(0), toBind, - NamingEntry.LINK_REF); + entry = new NamingEntry(name.get(0), toBind, NamingEntry.LINK_REF); } else if (toBind instanceof Reference) { - entry = new NamingEntry(name.get(0), toBind, - NamingEntry.REFERENCE); + entry = new NamingEntry(name.get(0), toBind, NamingEntry.REFERENCE); } else if (toBind instanceof Referenceable) { toBind = ((Referenceable) toBind).getReference(); - entry = new NamingEntry(name.get(0), toBind, - NamingEntry.REFERENCE); + entry = new NamingEntry(name.get(0), toBind, NamingEntry.REFERENCE); } else { - entry = new NamingEntry(name.get(0), toBind, - NamingEntry.ENTRY); + entry = new NamingEntry(name.get(0), toBind, NamingEntry.ENTRY); } bindings.put(name.get(0), entry); } @@ -672,9 +623,11 @@ /** * Throws a naming exception is Context is not writable. + * * @return true if the Context is writable - * @throws NamingException if the Context is not writable and - * exceptionOnFailedWrite is true + * + * @throws NamingException if the Context is not writable and exceptionOnFailedWrite is + * true */ protected boolean checkWritable() throws NamingException { if (isWritable()) { diff -Nru tomcat10-10.1.34/java/org/apache/naming/NamingContextBindingsEnumeration.java tomcat10-10.1.52/java/org/apache/naming/NamingContextBindingsEnumeration.java --- tomcat10-10.1.34/java/org/apache/naming/NamingContextBindingsEnumeration.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/NamingContextBindingsEnumeration.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,18 +26,14 @@ /** * Naming enumeration implementation. - * - * @author Remy Maucherat */ -public class NamingContextBindingsEnumeration - implements NamingEnumeration { +public class NamingContextBindingsEnumeration implements NamingEnumeration { // ----------------------------------------------------------- Constructors - public NamingContextBindingsEnumeration(Iterator entries, - Context ctx) { + public NamingContextBindingsEnumeration(Iterator entries, Context ctx) { iterator = entries; this.ctx = ctx; } @@ -61,22 +57,19 @@ @Override - public Binding next() - throws NamingException { + public Binding next() throws NamingException { return nextElementInternal(); } @Override - public boolean hasMore() - throws NamingException { + public boolean hasMore() throws NamingException { return iterator.hasNext(); } @Override - public void close() - throws NamingException { + public void close() throws NamingException { } @@ -100,8 +93,7 @@ Object value; // If the entry is a reference, resolve it - if (entry.type == NamingEntry.REFERENCE - || entry.type == NamingEntry.LINK_REF) { + if (entry.type == NamingEntry.REFERENCE || entry.type == NamingEntry.LINK_REF) { try { value = ctx.lookup(new CompositeName(entry.name)); } catch (NamingException e) { diff -Nru tomcat10-10.1.34/java/org/apache/naming/NamingContextEnumeration.java tomcat10-10.1.52/java/org/apache/naming/NamingContextEnumeration.java --- tomcat10-10.1.34/java/org/apache/naming/NamingContextEnumeration.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/NamingContextEnumeration.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,11 +24,8 @@ /** * Naming enumeration implementation. - * - * @author Remy Maucherat */ -public class NamingContextEnumeration - implements NamingEnumeration { +public class NamingContextEnumeration implements NamingEnumeration { // ----------------------------------------------------------- Constructors @@ -52,22 +49,19 @@ @Override - public NameClassPair next() - throws NamingException { + public NameClassPair next() throws NamingException { return nextElement(); } @Override - public boolean hasMore() - throws NamingException { + public boolean hasMore() throws NamingException { return iterator.hasNext(); } @Override - public void close() - throws NamingException { + public void close() throws NamingException { } diff -Nru tomcat10-10.1.34/java/org/apache/naming/NamingEntry.java tomcat10-10.1.52/java/org/apache/naming/NamingEntry.java --- tomcat10-10.1.34/java/org/apache/naming/NamingEntry.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/NamingEntry.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,8 +19,6 @@ /** * Represents a binding in a NamingContext. - * - * @author Remy Maucherat */ public class NamingEntry { @@ -38,8 +36,7 @@ /** - * The type instance variable is used to avoid using RTTI when doing - * lookups. + * The type instance variable is used to avoid using RTTI when doing lookups. */ public int type; public final String name; diff -Nru tomcat10-10.1.34/java/org/apache/naming/ResourceEnvRef.java tomcat10-10.1.52/java/org/apache/naming/ResourceEnvRef.java --- tomcat10-10.1.34/java/org/apache/naming/ResourceEnvRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/ResourceEnvRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,6 @@ /** * Represents a reference address to a resource environment. - * - * @author Remy Maucherat */ public class ResourceEnvRef extends AbstractRef { @@ -29,8 +27,7 @@ /** * Default factory for this reference. */ - public static final String DEFAULT_FACTORY = - org.apache.naming.factory.Constants.DEFAULT_RESOURCE_ENV_FACTORY; + public static final String DEFAULT_FACTORY = org.apache.naming.factory.Constants.DEFAULT_RESOURCE_ENV_FACTORY; /** diff -Nru tomcat10-10.1.34/java/org/apache/naming/ResourceLinkRef.java tomcat10-10.1.52/java/org/apache/naming/ResourceLinkRef.java --- tomcat10-10.1.34/java/org/apache/naming/ResourceLinkRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/ResourceLinkRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Represents a reference address to a resource. - * - * @author Remy Maucherat */ public class ResourceLinkRef extends AbstractRef { @@ -31,8 +29,7 @@ /** * Default factory for this reference. */ - public static final String DEFAULT_FACTORY = - org.apache.naming.factory.Constants.DEFAULT_RESOURCE_LINK_FACTORY; + public static final String DEFAULT_FACTORY = org.apache.naming.factory.Constants.DEFAULT_RESOURCE_LINK_FACTORY; /** @@ -44,19 +41,15 @@ /** * ResourceLink Reference. * - * @param resourceClass Resource class - * @param globalName Global name - * @param factory The possibly null class name of the object's factory. - * @param factoryLocation The possibly null location from which to load the - * factory (e.g. URL) + * @param resourceClass Resource class + * @param globalName Global name + * @param factory The possibly null class name of the object's factory. + * @param factoryLocation The possibly null location from which to load the factory (e.g. URL) */ - public ResourceLinkRef(String resourceClass, String globalName, - String factory, String factoryLocation) { + public ResourceLinkRef(String resourceClass, String globalName, String factory, String factoryLocation) { super(resourceClass, factory, factoryLocation); - StringRefAddr refAddr = null; if (globalName != null) { - refAddr = new StringRefAddr(GLOBALNAME, globalName); - add(refAddr); + add(new StringRefAddr(GLOBALNAME, globalName)); } } diff -Nru tomcat10-10.1.34/java/org/apache/naming/ResourceRef.java tomcat10-10.1.52/java/org/apache/naming/ResourceRef.java --- tomcat10-10.1.34/java/org/apache/naming/ResourceRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/ResourceRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Represents a reference address to a resource. - * - * @author Remy Maucherat */ public class ResourceRef extends AbstractRef { @@ -31,8 +29,7 @@ /** * Default factory for this reference. */ - public static final String DEFAULT_FACTORY = - org.apache.naming.factory.Constants.DEFAULT_RESOURCE_FACTORY; + public static final String DEFAULT_FACTORY = org.apache.naming.factory.Constants.DEFAULT_RESOURCE_FACTORY; /** @@ -63,14 +60,13 @@ * Resource Reference. * * @param resourceClass Resource class - * @param description Description of the resource - * @param scope Resource scope - * @param auth Resource authentication - * @param singleton Is this resource a singleton (every lookup should return - * the same instance rather than a new instance)? + * @param description Description of the resource + * @param scope Resource scope + * @param auth Resource authentication + * @param singleton Is this resource a singleton (every lookup should return the same instance rather than a new + * instance)? */ - public ResourceRef(String resourceClass, String description, - String scope, String auth, boolean singleton) { + public ResourceRef(String resourceClass, String description, String scope, String auth, boolean singleton) { this(resourceClass, description, scope, auth, singleton, null, null); } @@ -78,21 +74,19 @@ /** * Resource Reference. * - * @param resourceClass Resource class - * @param description Description of the resource - * @param scope Resource scope - * @param auth Resource authentication - * @param singleton Is this resource a singleton (every lookup should return - * the same instance rather than a new instance)? - * @param factory The possibly null class name of the object's factory. - * @param factoryLocation The possibly null location from which to load the - * factory (e.g. URL) - */ - public ResourceRef(String resourceClass, String description, - String scope, String auth, boolean singleton, - String factory, String factoryLocation) { + * @param resourceClass Resource class + * @param description Description of the resource + * @param scope Resource scope + * @param auth Resource authentication + * @param singleton Is this resource a singleton (every lookup should return the same instance rather than a + * new instance)? + * @param factory The possibly null class name of the object's factory. + * @param factoryLocation The possibly null location from which to load the factory (e.g. URL) + */ + public ResourceRef(String resourceClass, String description, String scope, String auth, boolean singleton, + String factory, String factoryLocation) { super(resourceClass, factory, factoryLocation); - StringRefAddr refAddr = null; + StringRefAddr refAddr; if (description != null) { refAddr = new StringRefAddr(DESCRIPTION, description); add(refAddr); diff -Nru tomcat10-10.1.34/java/org/apache/naming/SelectorContext.java tomcat10-10.1.52/java/org/apache/naming/SelectorContext.java --- tomcat10-10.1.34/java/org/apache/naming/SelectorContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/SelectorContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,6 @@ /** * Catalina JNDI Context implementation. - * - * @author Remy Maucherat */ public class SelectorContext implements Context { @@ -65,6 +63,7 @@ /** * Builds a Catalina selector context using the given environment. + * * @param env The environment */ public SelectorContext(Hashtable env) { @@ -75,12 +74,11 @@ /** * Builds a Catalina selector context using the given environment. - * @param env The environment - * @param initialContext true if this is the main - * initial context + * + * @param env The environment + * @param initialContext true if this is the main initial context */ - public SelectorContext(Hashtable env, - boolean initialContext) { + public SelectorContext(Hashtable env, boolean initialContext) { this.env = env; this.initialContext = initialContext; } @@ -114,12 +112,10 @@ @Override - public Object lookup(Name name) - throws NamingException { + public Object lookup(Name name) throws NamingException { if (log.isTraceEnabled()) { - log.trace(sm.getString("selectorContext.methodUsingName", "lookup", - name)); + log.trace(sm.getString("selectorContext.methodUsingName", "lookup", name)); } // Strip the URL header @@ -130,12 +126,10 @@ @Override - public Object lookup(String name) - throws NamingException { + public Object lookup(String name) throws NamingException { if (log.isTraceEnabled()) { - log.trace(sm.getString("selectorContext.methodUsingString", "lookup", - name)); + log.trace(sm.getString("selectorContext.methodUsingString", "lookup", name)); } // Strip the URL header @@ -146,68 +140,58 @@ @Override - public void bind(Name name, Object obj) - throws NamingException { + public void bind(Name name, Object obj) throws NamingException { getBoundContext().bind(parseName(name), obj); } @Override - public void bind(String name, Object obj) - throws NamingException { + public void bind(String name, Object obj) throws NamingException { getBoundContext().bind(parseName(name), obj); } @Override - public void rebind(Name name, Object obj) - throws NamingException { + public void rebind(Name name, Object obj) throws NamingException { getBoundContext().rebind(parseName(name), obj); } @Override - public void rebind(String name, Object obj) - throws NamingException { + public void rebind(String name, Object obj) throws NamingException { getBoundContext().rebind(parseName(name), obj); } @Override - public void unbind(Name name) - throws NamingException { + public void unbind(Name name) throws NamingException { getBoundContext().unbind(parseName(name)); } @Override - public void unbind(String name) - throws NamingException { + public void unbind(String name) throws NamingException { getBoundContext().unbind(parseName(name)); } @Override - public void rename(Name oldName, Name newName) - throws NamingException { + public void rename(Name oldName, Name newName) throws NamingException { getBoundContext().rename(parseName(oldName), parseName(newName)); } @Override - public void rename(String oldName, String newName) - throws NamingException { + public void rename(String oldName, String newName) throws NamingException { getBoundContext().rename(parseName(oldName), parseName(newName)); } @Override - public NamingEnumeration list(Name name) - throws NamingException { + public NamingEnumeration list(Name name) throws NamingException { if (log.isTraceEnabled()) { - log.trace(sm.getString("selectorContext.methodUsingName", "list", - name)); + log.trace(sm.getString("selectorContext.methodUsingName", "list", name)); } return getBoundContext().list(parseName(name)); @@ -215,12 +199,10 @@ @Override - public NamingEnumeration list(String name) - throws NamingException { + public NamingEnumeration list(String name) throws NamingException { if (log.isTraceEnabled()) { - log.trace(sm.getString("selectorContext.methodUsingString", "list", - name)); + log.trace(sm.getString("selectorContext.methodUsingString", "list", name)); } return getBoundContext().list(parseName(name)); @@ -228,12 +210,10 @@ @Override - public NamingEnumeration listBindings(Name name) - throws NamingException { + public NamingEnumeration listBindings(Name name) throws NamingException { if (log.isTraceEnabled()) { - log.trace(sm.getString("selectorContext.methodUsingName", - "listBindings", name)); + log.trace(sm.getString("selectorContext.methodUsingName", "listBindings", name)); } return getBoundContext().listBindings(parseName(name)); @@ -241,12 +221,10 @@ @Override - public NamingEnumeration listBindings(String name) - throws NamingException { + public NamingEnumeration listBindings(String name) throws NamingException { if (log.isTraceEnabled()) { - log.trace(sm.getString("selectorContext.methodUsingString", - "listBindings", name)); + log.trace(sm.getString("selectorContext.methodUsingString", "listBindings", name)); } return getBoundContext().listBindings(parseName(name)); @@ -254,40 +232,34 @@ @Override - public void destroySubcontext(Name name) - throws NamingException { + public void destroySubcontext(Name name) throws NamingException { getBoundContext().destroySubcontext(parseName(name)); } @Override - public void destroySubcontext(String name) - throws NamingException { + public void destroySubcontext(String name) throws NamingException { getBoundContext().destroySubcontext(parseName(name)); } @Override - public Context createSubcontext(Name name) - throws NamingException { + public Context createSubcontext(Name name) throws NamingException { return getBoundContext().createSubcontext(parseName(name)); } @Override - public Context createSubcontext(String name) - throws NamingException { + public Context createSubcontext(String name) throws NamingException { return getBoundContext().createSubcontext(parseName(name)); } @Override - public Object lookupLink(Name name) - throws NamingException { + public Object lookupLink(Name name) throws NamingException { if (log.isTraceEnabled()) { - log.trace(sm.getString("selectorContext.methodUsingName", - "lookupLink", name)); + log.trace(sm.getString("selectorContext.methodUsingName", "lookupLink", name)); } return getBoundContext().lookupLink(parseName(name)); @@ -295,12 +267,10 @@ @Override - public Object lookupLink(String name) - throws NamingException { + public Object lookupLink(String name) throws NamingException { if (log.isTraceEnabled()) { - log.trace(sm.getString("selectorContext.methodUsingString", - "lookupLink", name)); + log.trace(sm.getString("selectorContext.methodUsingString", "lookupLink", name)); } return getBoundContext().lookupLink(parseName(name)); @@ -308,65 +278,56 @@ @Override - public NameParser getNameParser(Name name) - throws NamingException { + public NameParser getNameParser(Name name) throws NamingException { return getBoundContext().getNameParser(parseName(name)); } @Override - public NameParser getNameParser(String name) - throws NamingException { + public NameParser getNameParser(String name) throws NamingException { return getBoundContext().getNameParser(parseName(name)); } @Override - public Name composeName(Name name, Name prefix) - throws NamingException { + public Name composeName(Name name, Name prefix) throws NamingException { Name prefixClone = (Name) prefix.clone(); return prefixClone.addAll(name); } @Override - public String composeName(String name, String prefix) - throws NamingException { + public String composeName(String name, String prefix) throws NamingException { return prefix + "/" + name; } @Override - public Object addToEnvironment(String propName, Object propVal) - throws NamingException { + public Object addToEnvironment(String propName, Object propVal) throws NamingException { return getBoundContext().addToEnvironment(propName, propVal); } @Override - public Object removeFromEnvironment(String propName) - throws NamingException { + public Object removeFromEnvironment(String propName) throws NamingException { return getBoundContext().removeFromEnvironment(propName); } @Override - public Hashtable getEnvironment() - throws NamingException { + public Hashtable getEnvironment() throws NamingException { return getBoundContext().getEnvironment(); } @Override - public void close() - throws NamingException { + public void close() throws NamingException { getBoundContext().close(); } @Override - public String getNameInNamespace() - throws NamingException { + public String getNameInNamespace() throws NamingException { return prefix; } @@ -376,12 +337,12 @@ /** * Get the bound context. - * @return the Context bound with either the current thread or - * the current classloader + * + * @return the Context bound with either the current thread or the current classloader + * * @throws NamingException Bindings exception */ - protected Context getBoundContext() - throws NamingException { + protected Context getBoundContext() throws NamingException { if (initialContext) { String ICName = IC_PREFIX; @@ -411,13 +372,14 @@ /** * Strips the URL header. + * * @param name The name + * * @return the parsed name - * @throws NamingException if there is no "java:" header or if no - * naming context has been bound to this thread + * + * @throws NamingException if there is no "java:" header or if no naming context has been bound to this thread */ - protected String parseName(String name) - throws NamingException { + protected String parseName(String name) throws NamingException { if ((!initialContext) && (name.startsWith(prefix))) { return name.substring(prefixLength); @@ -425,8 +387,7 @@ if (initialContext) { return name; } else { - throw new NamingException - (sm.getString("selectorContext.noJavaUrl")); + throw new NamingException(sm.getString("selectorContext.noJavaUrl")); } } @@ -435,16 +396,16 @@ /** * Strips the URL header. + * * @param name The name + * * @return the parsed name - * @throws NamingException if there is no "java:" header or if no - * naming context has been bound to this thread + * + * @throws NamingException if there is no "java:" header or if no naming context has been bound to this thread */ - protected Name parseName(Name name) - throws NamingException { + protected Name parseName(Name name) throws NamingException { - if (!initialContext && !name.isEmpty() && - name.get(0).startsWith(prefix)) { + if (!initialContext && !name.isEmpty() && name.get(0).startsWith(prefix)) { if (name.get(0).equals(prefix)) { return name.getSuffix(1); } else { @@ -456,8 +417,7 @@ if (initialContext) { return name; } else { - throw new NamingException( - sm.getString("selectorContext.noJavaUrl")); + throw new NamingException(sm.getString("selectorContext.noJavaUrl")); } } diff -Nru tomcat10-10.1.34/java/org/apache/naming/ServiceRef.java tomcat10-10.1.52/java/org/apache/naming/ServiceRef.java --- tomcat10-10.1.34/java/org/apache/naming/ServiceRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/ServiceRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ /** * Represents a reference web service. - * - * @author Fabien Carrion */ public class ServiceRef extends AbstractRef { @@ -34,20 +32,19 @@ /** * Default factory for this reference. */ - public static final String DEFAULT_FACTORY = - org.apache.naming.factory.Constants.DEFAULT_SERVICE_FACTORY; + public static final String DEFAULT_FACTORY = org.apache.naming.factory.Constants.DEFAULT_SERVICE_FACTORY; /** * Service Classname address type. */ - public static final String SERVICE_INTERFACE = "serviceInterface"; + public static final String SERVICE_INTERFACE = "serviceInterface"; /** * ServiceQname address type. */ - public static final String SERVICE_NAMESPACE = "service namespace"; + public static final String SERVICE_NAMESPACE = "service namespace"; public static final String SERVICE_LOCAL_PART = "service local part"; @@ -76,25 +73,21 @@ /** - * The list to save the handler Reference objects, because they can't be - * saved in the addrs vector. + * The list to save the handler Reference objects, because they can't be saved in the addrs vector. */ private final List handlers = new CopyOnWriteArrayList<>(); - public ServiceRef(String refname, String serviceInterface, String[] serviceQname, - String wsdl, String jaxrpcmapping) { - this(refname, serviceInterface, serviceQname, wsdl, jaxrpcmapping, - null, null); + public ServiceRef(String refname, String serviceInterface, String[] serviceQname, String wsdl, + String jaxrpcmapping) { + this(refname, serviceInterface, serviceQname, wsdl, jaxrpcmapping, null, null); } - public ServiceRef(@SuppressWarnings("unused") String refname, - String serviceInterface, String[] serviceQname, - String wsdl, String jaxrpcmapping, - String factory, String factoryLocation) { + public ServiceRef(@SuppressWarnings("unused") String refname, String serviceInterface, String[] serviceQname, + String wsdl, String jaxrpcmapping, String factory, String factoryLocation) { super(serviceInterface, factory, factoryLocation); - StringRefAddr refAddr = null; + StringRefAddr refAddr; if (serviceInterface != null) { refAddr = new StringRefAddr(SERVICE_INTERFACE, serviceInterface); add(refAddr); @@ -120,6 +113,7 @@ /** * Add and Get Handlers classes. + * * @return the handler */ public HandlerRef getHandler() { diff -Nru tomcat10-10.1.34/java/org/apache/naming/StringManager.java tomcat10-10.1.52/java/org/apache/naming/StringManager.java --- tomcat10-10.1.34/java/org/apache/naming/StringManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/StringManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,27 +24,18 @@ import java.util.ResourceBundle; /** - * An internationalization / localization helper class which reduces - * the bother of handling ResourceBundles and takes care of the - * common cases of message formatting which otherwise require the - * creation of Object arrays and such. + * An internationalization / localization helper class which reduces the bother of handling ResourceBundles and takes + * care of the common cases of message formatting which otherwise require the creation of Object arrays and such. + *

          + * The StringManager operates on a package basis. One StringManager per package can be created and accessed via the + * getManager method call. + *

          + * The StringManager will look for a ResourceBundle named by the package name given plus the suffix of "LocalStrings". + * In practice, this means that the localized information will be contained in a LocalStrings.properties file located in + * the package directory of the classpath. + *

          + * Please see the documentation for java.util.ResourceBundle for more information. * - *

          The StringManager operates on a package basis. One StringManager - * per package can be created and accessed via the getManager method - * call. - * - *

          The StringManager will look for a ResourceBundle named by - * the package name given plus the suffix of "LocalStrings". In - * practice, this means that the localized information will be contained - * in a LocalStrings.properties file located in the package - * directory of the classpath. - * - *

          Please see the documentation for java.util.ResourceBundle for - * more information. - * - * @author James Duncan Davidson [duncan@eng.sun.com] - * @author James Todd [gonzo@eng.sun.com] - * @author Mel Martinez [mmartinez@g1440.com] * @see java.util.ResourceBundle */ public class StringManager { @@ -56,10 +47,8 @@ private final Locale locale; /** - * Creates a new StringManager for a given package. This is a - * private method and all access to it is arbitrated by the - * static getManager method call so that only one StringManager - * per package will be created. + * Creates a new StringManager for a given package. This is a private method and all access to it is arbitrated by + * the static getManager method call so that only one StringManager per package will be created. * * @param packageName Name of package to create StringManager for. */ @@ -68,16 +57,15 @@ ResourceBundle tempBundle = null; try { tempBundle = ResourceBundle.getBundle(bundleName, Locale.getDefault()); - } catch( MissingResourceException ex ) { + } catch (MissingResourceException ex) { // Try from the current loader (that's the case for trusted apps) // Should only be required if using a TC5 style classloader structure // where common != shared != server ClassLoader cl = Thread.currentThread().getContextClassLoader(); - if( cl != null ) { + if (cl != null) { try { - tempBundle = ResourceBundle.getBundle( - bundleName, Locale.getDefault(), cl); - } catch(MissingResourceException ex2) { + tempBundle = ResourceBundle.getBundle(bundleName, Locale.getDefault(), cl); + } catch (MissingResourceException ignore) { // Ignore } } @@ -92,19 +80,17 @@ } /** - * Get a string from the underlying resource bundle or return - * null if the String is not found. + * Get a string from the underlying resource bundle or return null if the String is not found. * - * @param key to desired resource String - * @return resource String matching key from underlying - * bundle or null if not found. - * @throws IllegalArgumentException if key is null. + * @param key to desired resource String + * + * @return resource String matching key from underlying bundle or null if not found. + * + * @throws IllegalArgumentException if key is null. */ public String getString(String key) { - if(key == null){ - String msg = "key may not have a null value"; - - throw new IllegalArgumentException(msg); + if (key == null) { + throw new IllegalArgumentException("key may not have a null value"); } String str = null; @@ -114,32 +100,30 @@ if (bundle != null) { str = bundle.getString(key); } - } catch(MissingResourceException mre) { - //bad: shouldn't mask an exception the following way: - // str = "[cannot find message associated with key '" + key + "' due to " + mre + "]"; - // because it hides the fact that the String was missing - // from the calling code. - //good: could just throw the exception (or wrap it in another) - // but that would probably cause much havoc on existing - // code. - //better: consistent with container pattern to - // simply return null. Calling code can then do - // a null check. - str = null; + } catch (MissingResourceException ignore) { + // bad: shouldn't mask an exception the following way: + // str = "[cannot find message associated with key '" + key + "' due to " + mre + "]"; + // because it hides the fact that the String was missing + // from the calling code. + // good: could just throw the exception (or wrap it in another) + // but that would probably cause much havoc on existing + // code. + // better: consistent with container pattern to + // simply return null. Calling code can then do + // a null check. + // str is already set to null } return str; } /** - * Get a string from the underlying resource bundle and format - * it with the given set of arguments. + * Get a string from the underlying resource bundle and format it with the given set of arguments. * * @param key The key for the required message * @param args The values to insert into the message * - * @return The request string formatted with the provided arguments or the - * key if the key was not found. + * @return The request string formatted with the provided arguments or the key if the key was not found. */ public String getString(final String key, final Object... args) { String value = getString(key); @@ -156,18 +140,17 @@ // STATIC SUPPORT METHODS // -------------------------------------------------------------- - private static final Map managers = new HashMap<>(); + private static final Map managers = new HashMap<>(); /** - * Get the StringManager for a particular package. If a manager for - * a package already exists, it will be reused, else a new - * StringManager will be created and returned. + * Get the StringManager for a particular package. If a manager for a package already exists, it will be reused, + * else a new StringManager will be created and returned. * * @param packageName The package name * * @return The instance associated with the given package */ - public static final synchronized StringManager getManager(String packageName) { + public static synchronized StringManager getManager(String packageName) { StringManager mgr = managers.get(packageName); if (mgr == null) { mgr = new StringManager(packageName); @@ -177,7 +160,7 @@ } - public static final StringManager getManager(Class clazz) { + public static StringManager getManager(Class clazz) { return getManager(clazz.getPackage().getName()); } } diff -Nru tomcat10-10.1.34/java/org/apache/naming/TransactionRef.java tomcat10-10.1.52/java/org/apache/naming/TransactionRef.java --- tomcat10-10.1.34/java/org/apache/naming/TransactionRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/TransactionRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,6 @@ /** * Represents a reference address to a transaction. - * - * @author Remy Maucherat */ public class TransactionRef extends AbstractRef { @@ -29,8 +27,7 @@ /** * Default factory for this reference. */ - public static final String DEFAULT_FACTORY = - org.apache.naming.factory.Constants.DEFAULT_TRANSACTION_FACTORY; + public static final String DEFAULT_FACTORY = org.apache.naming.factory.Constants.DEFAULT_TRANSACTION_FACTORY; /** @@ -44,7 +41,7 @@ /** * Resource Reference. * - * @param factory The factory class + * @param factory The factory class * @param factoryLocation The factory location */ public TransactionRef(String factory, String factoryLocation) { diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/BeanFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/BeanFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/BeanFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/BeanFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,10 +37,11 @@ /** * Object factory for any Resource conforming to the JavaBean spec. + *

          + * This factory can be configured in a <Context> element in your conf/server.xml + * configuration file. An example of factory configuration is: + *

          * - *

          This factory can be configured in a <Context> element - * in your conf/server.xml - * configuration file. An example of factory configuration is:

          *
            * <Resource name="jdbc/myDataSource"
            *           auth="SERVLET"
          @@ -56,8 +57,6 @@
            *           maxLimit="5"
            *           />
            * 
          - * - * @author Aner Perez [aner at ncstech.com] */ public class BeanFactory implements ObjectFactory { @@ -68,11 +67,13 @@ /** * Create a new Bean instance. * - * @param obj The reference object describing the Bean - * @param name the bound name - * @param nameCtx unused + * @param obj The reference object describing the Bean + * @param name the bound name + * @param nameCtx unused * @param environment unused + * * @return the object instance + * * @throws NamingException if an error occur creating the instance */ @Override @@ -84,7 +85,7 @@ try { Reference ref = (Reference) obj; String beanClassName = ref.getClassName(); - Class beanClass = null; + Class beanClass; ClassLoader tcl = Thread.currentThread().getContextClassLoader(); try { if (tcl != null) { @@ -92,7 +93,7 @@ } else { beanClass = Class.forName(beanClassName); } - } catch(ClassNotFoundException cnfe) { + } catch (ClassNotFoundException cnfe) { NamingException ne = new NamingException(sm.getString("beanFactory.classNotFound", beanClassName)); ne.initCause(cnfe); throw ne; @@ -117,18 +118,16 @@ ra = e.nextElement(); String propName = ra.getType(); - if (propName.equals(Constants.FACTORY) || - propName.equals("scope") || propName.equals("auth") || - propName.equals("forceString") || - propName.equals("singleton")) { + if (propName.equals(Constants.FACTORY) || propName.equals("scope") || propName.equals("auth") || + propName.equals("forceString") || propName.equals("singleton")) { continue; } - value = (String)ra.getContent(); + value = (String) ra.getContent(); Object[] valueArray = new Object[1]; - int i = 0; + int i; for (i = 0; i < pda.length; i++) { if (pda[i].getName().equals(propName)) { @@ -162,12 +161,12 @@ setProp = bean.getClass().getMethod(setterName, String.class); valueArray[0] = value; } catch (NoSuchMethodException nsme) { - throw new NamingException(sm.getString( - "beanFactory.noStringConversion", propName, propType.getName())); + throw new NamingException(sm.getString("beanFactory.noStringConversion", propName, + propType.getName())); } } else { - throw new NamingException(sm.getString( - "beanFactory.noStringConversion", propName, propType.getName())); + throw new NamingException( + sm.getString("beanFactory.noStringConversion", propName, propType.getName())); } if (setProp != null) { diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/Constants.java tomcat10-10.1.52/java/org/apache/naming/factory/Constants.java --- tomcat10-10.1.34/java/org/apache/naming/factory/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,8 +37,7 @@ public static final String DEFAULT_HANDLER_FACTORY = Package + ".HandlerFactory"; - public static final String DBCP_DATASOURCE_FACTORY = - "org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory"; + public static final String DBCP_DATASOURCE_FACTORY = "org.apache.tomcat.dbcp.dbcp2.BasicDataSourceFactory"; public static final String OPENEJB_EJB_FACTORY = Package + ".OpenEjbFactory"; diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/DataSourceLinkFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/DataSourceLinkFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/DataSourceLinkFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/DataSourceLinkFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,10 +31,10 @@ import javax.sql.DataSource; - /** - *

          Object factory for resource links for shared data sources.

          - * + *

          + * Object factory for resource links for shared data sources. + *

          */ public class DataSourceLinkFactory extends ResourceLinkFactory { @@ -46,14 +46,15 @@ @Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) - throws NamingException { + throws NamingException { Object result = super.getObjectInstance(obj, name, nameCtx, environment); // Can we process this request? - if (result!=null) { + if (result != null) { Reference ref = (Reference) obj; RefAddr userAttr = ref.get("username"); RefAddr passAttr = ref.get("password"); - if (userAttr != null && passAttr != null && userAttr.getContent() != null && passAttr.getContent() != null) { + if (userAttr != null && passAttr != null && userAttr.getContent() != null && + passAttr.getContent() != null) { result = wrapDataSource(result, userAttr.getContent().toString(), passAttr.getContent().toString()); } } @@ -62,13 +63,12 @@ protected Object wrapDataSource(Object datasource, String username, String password) throws NamingException { try { - DataSourceHandler handler = - new DataSourceHandler((DataSource)datasource, username, password); - return Proxy.newProxyInstance(datasource.getClass().getClassLoader(), - datasource.getClass().getInterfaces(), handler); - }catch (Exception x) { - if (x instanceof InvocationTargetException) { - Throwable cause = x.getCause(); + DataSourceHandler handler = new DataSourceHandler((DataSource) datasource, username, password); + return Proxy.newProxyInstance(datasource.getClass().getClassLoader(), datasource.getClass().getInterfaces(), + handler); + } catch (Exception e) { + if (e instanceof InvocationTargetException) { + Throwable cause = e.getCause(); if (cause instanceof ThreadDeath) { throw (ThreadDeath) cause; } @@ -76,22 +76,22 @@ throw (VirtualMachineError) cause; } if (cause instanceof Exception) { - x = (Exception) cause; + e = (Exception) cause; } } - if (x instanceof NamingException) { - throw (NamingException)x; + if (e instanceof NamingException) { + throw (NamingException) e; } else { - NamingException nx = new NamingException(x.getMessage()); - nx.initCause(x); + NamingException nx = new NamingException(e.getMessage()); + nx.initCause(e); throw nx; } } } /** - * Simple wrapper class that will allow a user to configure a ResourceLink for a data source - * so that when {@link javax.sql.DataSource#getConnection()} is called, it will invoke + * Simple wrapper class that will allow a user to configure a ResourceLink for a data source so that when + * {@link javax.sql.DataSource#getConnection()} is called, it will invoke * {@link javax.sql.DataSource#getConnection(String, String)} with the preconfigured username and password. */ public static class DataSourceHandler implements InvocationHandler { @@ -99,6 +99,7 @@ private final String username; private final String password; private final Method getConnection; + public DataSourceHandler(DataSource ds, String username, String password) throws Exception { this.ds = ds; this.username = username; @@ -109,17 +110,16 @@ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if ("getConnection".equals(method.getName()) && (args==null || args.length==0)) { - args = new String[] {username,password}; + if ("getConnection".equals(method.getName()) && (args == null || args.length == 0)) { + args = new String[] { username, password }; method = getConnection; } else if ("unwrap".equals(method.getName())) { - return unwrap((Class)args[0]); + return unwrap((Class) args[0]); } try { - return method.invoke(ds,args); - }catch (Throwable t) { - if (t instanceof InvocationTargetException - && t.getCause() != null) { + return method.invoke(ds, args); + } catch (Throwable t) { + if (t instanceof InvocationTargetException && t.getCause() != null) { throw t.getCause(); } else { throw t; @@ -138,7 +138,5 @@ } - - } diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/EjbFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/EjbFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/EjbFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/EjbFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,14 +23,15 @@ import javax.naming.spi.ObjectFactory; import org.apache.naming.EjbRef; +import org.apache.naming.StringManager; /** * Object factory for EJBs. - * - * @author Remy Maucherat */ public class EjbFactory extends FactoryBase { + private static final StringManager sm = StringManager.getManager(EjbFactory.class); + @Override protected boolean isReferenceTypeSupported(Object obj) { return obj instanceof EjbRef; @@ -40,12 +41,10 @@ protected ObjectFactory getDefaultFactory(Reference ref) throws NamingException { ObjectFactory factory; - String javaxEjbFactoryClassName = System.getProperty( - "jakarta.ejb.Factory", Constants.OPENEJB_EJB_FACTORY); + String javaxEjbFactoryClassName = System.getProperty("jakarta.ejb.Factory", Constants.OPENEJB_EJB_FACTORY); try { - factory = (ObjectFactory) - Class.forName(javaxEjbFactoryClassName).getConstructor().newInstance(); - } catch(Throwable t) { + factory = (ObjectFactory) Class.forName(javaxEjbFactoryClassName).getConstructor().newInstance(); + } catch (Throwable t) { if (t instanceof NamingException) { throw (NamingException) t; } @@ -55,8 +54,7 @@ if (t instanceof VirtualMachineError) { throw (VirtualMachineError) t; } - NamingException ex = new NamingException - ("Could not create resource factory instance"); + NamingException ex = new NamingException(sm.getString("factoryBase.factoryCreationError")); ex.initCause(t); throw ex; } @@ -70,8 +68,7 @@ if (linkRefAddr != null) { // Retrieving the EJB link String ejbLink = linkRefAddr.getContent().toString(); - Object beanObj = (new InitialContext()).lookup(ejbLink); - return beanObj; + return (new InitialContext()).lookup(ejbLink); } return null; } diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/FactoryBase.java tomcat10-10.1.52/java/org/apache/naming/factory/FactoryBase.java --- tomcat10-10.1.34/java/org/apache/naming/factory/FactoryBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/FactoryBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,16 +28,16 @@ import org.apache.naming.StringManager; /** - * Abstract base class that provides common functionality required by - * sub-classes. This class exists primarily to reduce code duplication. + * Abstract base class that provides common functionality required by subclasses. This class exists primarily to reduce + * code duplication. */ public abstract class FactoryBase implements ObjectFactory { private static final StringManager sm = StringManager.getManager(FactoryBase.class); @Override - public final Object getObjectInstance(Object obj, Name name, Context nameCtx, - Hashtable environment) throws Exception { + public final Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) + throws Exception { if (isReferenceTypeSupported(obj)) { Reference ref = (Reference) obj; @@ -47,28 +47,28 @@ return linked; } - ObjectFactory factory = null; + ObjectFactory factory; RefAddr factoryRefAddr = ref.get(Constants.FACTORY); if (factoryRefAddr != null) { // Using the specified factory String factoryClassName = factoryRefAddr.getContent().toString(); // Loading factory ClassLoader tcl = Thread.currentThread().getContextClassLoader(); - Class factoryClass = null; + Class factoryClass; try { if (tcl != null) { factoryClass = tcl.loadClass(factoryClassName); } else { factoryClass = Class.forName(factoryClassName); } - } catch(ClassNotFoundException e) { + } catch (ClassNotFoundException e) { NamingException ex = new NamingException(sm.getString("factoryBase.factoryClassError")); ex.initCause(e); throw ex; } try { factory = (ObjectFactory) factoryClass.getConstructor().newInstance(); - } catch(Throwable t) { + } catch (Throwable t) { if (t instanceof NamingException) { throw (NamingException) t; } @@ -99,37 +99,33 @@ /** - * Determines if this factory supports processing the provided reference - * object. + * Determines if this factory supports processing the provided reference object. * - * @param obj The object to be processed + * @param obj The object to be processed * - * @return true if this factory can process the object, - * otherwise false + * @return true if this factory can process the object, otherwise false */ protected abstract boolean isReferenceTypeSupported(Object obj); /** - * If a default factory is available for the given reference type, create - * the default factory. + * If a default factory is available for the given reference type, create the default factory. * - * @param ref The reference object to be processed + * @param ref The reference object to be processed * - * @return The default factory for the given reference object or - * null if no default factory exists. + * @return The default factory for the given reference object or null if no default factory exists. * - * @throws NamingException If the default factory cannot be created + * @throws NamingException If the default factory cannot be created */ - protected abstract ObjectFactory getDefaultFactory(Reference ref) - throws NamingException; + protected abstract ObjectFactory getDefaultFactory(Reference ref) throws NamingException; /** * If this reference is a link to another JNDI object, obtain that object. * - * @param ref The reference object to be processed + * @param ref The reference object to be processed + * + * @return The linked object or null if linked objects are not supported by or not configured for this + * reference object * - * @return The linked object or null if linked objects are - * not supported by or not configured for this reference object * @throws NamingException Error accessing linked object */ protected abstract Object getLinked(Reference ref) throws NamingException; diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/LookupFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/LookupFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/LookupFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/LookupFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,7 +25,6 @@ import javax.naming.Name; import javax.naming.NamingException; import javax.naming.RefAddr; -import javax.naming.Reference; import javax.naming.spi.ObjectFactory; import org.apache.juli.logging.Log; @@ -46,22 +45,24 @@ /** * Create a new Resource env instance. * - * @param obj The reference object describing the DataSource - * @param name the bound name - * @param nameCtx unused + * @param obj The reference object describing the DataSource + * @param name the bound name + * @param nameCtx unused * @param environment unused + * * @return the object instance + * * @throws Exception if an error occur creating the instance */ @Override - public Object getObjectInstance(Object obj, Name name, Context nameCtx, - Hashtable environment) throws Exception { + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) + throws Exception { String lookupName = null; Object result = null; if (obj instanceof LookupRef) { - Reference ref = (Reference) obj; + LookupRef ref = (LookupRef) obj; ObjectFactory factory = null; RefAddr lookupNameRefAddr = ref.get(LookupRef.LOOKUP_NAME); if (lookupNameRefAddr != null) { @@ -83,13 +84,12 @@ String factoryClassName = factoryRefAddr.getContent().toString(); // Loading factory ClassLoader tcl = Thread.currentThread().getContextClassLoader(); - Class factoryClass = null; + Class factoryClass; if (tcl != null) { try { factoryClass = tcl.loadClass(factoryClassName); } catch (ClassNotFoundException e) { - NamingException ex = new NamingException( - sm.getString("lookupFactory.loadFailed")); + NamingException ex = new NamingException(sm.getString("lookupFactory.loadFailed")); ex.initCause(e); throw ex; } @@ -97,8 +97,7 @@ try { factoryClass = Class.forName(factoryClassName); } catch (ClassNotFoundException e) { - NamingException ex = new NamingException( - sm.getString("lookupFactory.loadFailed")); + NamingException ex = new NamingException(sm.getString("lookupFactory.loadFailed")); ex.initCause(e); throw ex; } @@ -107,11 +106,7 @@ try { factory = (ObjectFactory) factoryClass.getConstructor().newInstance(); } catch (Throwable t) { - if (t instanceof NamingException) { - throw (NamingException) t; - } - NamingException ex = new NamingException( - sm.getString("lookupFactory.createFailed")); + NamingException ex = new NamingException(sm.getString("lookupFactory.createFailed")); ex.initCause(t); throw ex; } @@ -130,8 +125,8 @@ Class clazz = Class.forName(ref.getClassName()); if (result != null && !clazz.isAssignableFrom(result.getClass())) { - String msg = sm.getString("lookupFactory.typeMismatch", - name, ref.getClassName(), lookupName, result.getClass().getName()); + String msg = sm.getString("lookupFactory.typeMismatch", name, ref.getClassName(), lookupName, + result.getClass().getName()); NamingException ne = new NamingException(msg); log.warn(msg, ne); // Close the resource we no longer need if we know how to do so diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/MailSessionFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/MailSessionFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/MailSessionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/MailSessionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,19 +33,20 @@ import jakarta.mail.Session; /** - *

          Factory class that creates a JNDI named JavaMail Session factory, - * which can be used for managing inbound and outbound electronic mail - * messages via JavaMail APIs. All messaging environment properties - * described in the JavaMail Specification may be passed to the Session - * factory; however the following properties are the most commonly used:

          + *

          + * Factory class that creates a JNDI named JavaMail Session factory, which can be used for managing inbound and outbound + * electronic mail messages via JavaMail APIs. All messaging environment properties described in the JavaMail + * Specification may be passed to the Session factory; however the following properties are the most commonly used: + *

          *
            - *
          • mail.smtp.host - Hostname for outbound transport - * connections. Defaults to localhost if not specified.
          • + *
          • mail.smtp.host - Hostname for outbound transport connections. Defaults to localhost + * if not specified.
          • *
          + *

          + * This factory can be configured in a <Context> element in your conf/server.xml + * configuration file. An example of factory configuration is: + *

          * - *

          This factory can be configured in a - * <Context> element in your conf/server.xml - * configuration file. An example of factory configuration is:

          *
            * <Resource name="mail/smtp"
            *           auth="CONTAINER"
          @@ -53,8 +54,6 @@
            *           mail.smtp.host="mail.mycompany.com"
            *           />
            * 
          - * - * @author Craig R. McClanahan */ public class MailSessionFactory implements ObjectFactory { @@ -66,8 +65,7 @@ @Override - public Object getObjectInstance(Object refObj, Name name, Context context, - Hashtable env) throws Exception { + public Object getObjectInstance(Object refObj, Name name, Context context, Hashtable env) throws Exception { // Return null if we cannot create an object of the requested type final Reference ref = (Reference) refObj; @@ -107,25 +105,23 @@ Authenticator auth = null; if (password != null) { String user = props.getProperty("mail.smtp.user"); - if(user == null) { + if (user == null) { user = props.getProperty("mail.user"); } - if(user != null) { + if (user != null) { final PasswordAuthentication pa = new PasswordAuthentication(user, password); auth = new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return pa; - } - }; + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return pa; + } + }; } } // Create and return the new Session object - Session session = Session.getInstance(props, auth); - return session; - + return Session.getInstance(props, auth); }); } } diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/OpenEjbFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/OpenEjbFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/OpenEjbFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/OpenEjbFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,50 +23,37 @@ import javax.naming.InitialContext; import javax.naming.Name; import javax.naming.RefAddr; -import javax.naming.Reference; import javax.naming.spi.ObjectFactory; import org.apache.naming.EjbRef; /** * Object factory for EJBs. - * - * @author Jacek Laskowski - * @author Remy Maucherat */ public class OpenEjbFactory implements ObjectFactory { - - // -------------------------------------------------------------- Constants - - - protected static final String DEFAULT_OPENEJB_FACTORY = - "org.openejb.client.LocalInitialContextFactory"; - - - // -------------------------------------------------- ObjectFactory Methods - + protected static final String DEFAULT_OPENEJB_FACTORY = "org.openejb.client.LocalInitialContextFactory"; /** * Create a new EJB instance using OpenEJB. * - * @param obj The reference object describing the DataSource - * @param name the bound name - * @param nameCtx unused + * @param obj The reference object describing the DataSource + * @param name the bound name + * @param nameCtx unused * @param environment unused + * * @return the object instance + * * @throws Exception if an error occur creating the instance */ @Override - public Object getObjectInstance(Object obj, Name name, Context nameCtx, - Hashtable environment) - throws Exception { + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) + throws Exception { Object beanObj = null; if (obj instanceof EjbRef) { - - Reference ref = (Reference) obj; + EjbRef ref = (EjbRef) obj; String factory = DEFAULT_OPENEJB_FACTORY; RefAddr factoryRefAddr = ref.get("openejb.factory"); @@ -83,12 +70,8 @@ String ejbLink = linkRefAddr.getContent().toString(); beanObj = (new InitialContext(env)).lookup(ejbLink); } - } return beanObj; - } - - } diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/ResourceEnvFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/ResourceEnvFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/ResourceEnvFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/ResourceEnvFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ /** * Object factory for Resources env. - * - * @author Remy Maucherat */ public class ResourceEnvFactory extends FactoryBase { diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/ResourceFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/ResourceFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/ResourceFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/ResourceFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Object factory for Resources. - * - * @author Remy Maucherat */ public class ResourceFactory extends FactoryBase { @@ -44,11 +42,10 @@ if (ref.getClassName().equals("javax.sql.DataSource")) { String javaxSqlDataSourceFactoryClassName = - System.getProperty("javax.sql.DataSource.Factory", - Constants.DBCP_DATASOURCE_FACTORY); + System.getProperty("javax.sql.DataSource.Factory", Constants.DBCP_DATASOURCE_FACTORY); try { - factory = (ObjectFactory) Class.forName( - javaxSqlDataSourceFactoryClassName).getConstructor().newInstance(); + factory = (ObjectFactory) Class.forName(javaxSqlDataSourceFactoryClassName).getConstructor() + .newInstance(); } catch (Exception e) { NamingException ex = new NamingException(sm.getString("resourceFactory.factoryCreationError")); ex.initCause(e); @@ -56,12 +53,11 @@ } } else if (ref.getClassName().equals("jakarta.mail.Session")) { String javaxMailSessionFactoryClassName = - System.getProperty("jakarta.mail.Session.Factory", - "org.apache.naming.factory.MailSessionFactory"); + System.getProperty("jakarta.mail.Session.Factory", "org.apache.naming.factory.MailSessionFactory"); try { - factory = (ObjectFactory) Class.forName( - javaxMailSessionFactoryClassName).getConstructor().newInstance(); - } catch(Throwable t) { + factory = + (ObjectFactory) Class.forName(javaxMailSessionFactoryClassName).getConstructor().newInstance(); + } catch (Throwable t) { if (t instanceof NamingException) { throw (NamingException) t; } diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/ResourceLinkFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/ResourceLinkFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/ResourceLinkFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/ResourceLinkFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,16 +25,15 @@ import javax.naming.Name; import javax.naming.NamingException; import javax.naming.RefAddr; -import javax.naming.Reference; import javax.naming.spi.ObjectFactory; import org.apache.naming.ResourceLinkRef; import org.apache.naming.StringManager; /** - *

          Object factory for resource links.

          - * - * @author Remy Maucherat + *

          + * Object factory for resource links. + *

          */ public class ResourceLinkFactory implements ObjectFactory { @@ -47,8 +46,7 @@ */ private static Context globalContext = null; - private static Map> globalResourceRegistrations = - new ConcurrentHashMap<>(); + private static final Map> globalResourceRegistrations = new ConcurrentHashMap<>(); // --------------------------------------------------------- Public Methods @@ -60,15 +58,13 @@ public static void setGlobalContext(Context newGlobalContext) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { - sm.checkPermission(new RuntimePermission( - ResourceLinkFactory.class.getName() + ".setGlobalContext")); + sm.checkPermission(new RuntimePermission(ResourceLinkFactory.class.getName() + ".setGlobalContext")); } globalContext = newGlobalContext; } - public static void registerGlobalResourceAccess(Context globalContext, String localName, - String globalName) { + public static void registerGlobalResourceAccess(Context globalContext, String localName, String globalName) { validateGlobalContext(globalContext); ClassLoader cl = Thread.currentThread().getContextClassLoader(); // Web application initialization is single threaded so this is @@ -95,8 +91,7 @@ private static void validateGlobalContext(Context globalContext) { - if (ResourceLinkFactory.globalContext != null && - ResourceLinkFactory.globalContext != globalContext) { + if (ResourceLinkFactory.globalContext != null && ResourceLinkFactory.globalContext != globalContext) { throw new SecurityException(sm.getString("resourceLinkFactory.invalidGlobalContext")); } } @@ -120,25 +115,27 @@ /** * Create a new resource instance. * - * @param name the bound name - * @param nameCtx unused + * @param name the bound name + * @param nameCtx unused * @param environment unused + * * @return the object instance + * * @throws NamingException if an error occur creating the instance */ @Override - public Object getObjectInstance(Object obj, Name name, Context nameCtx, - Hashtable environment) throws NamingException { + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) + throws NamingException { if (!(obj instanceof ResourceLinkRef)) { return null; } // Can we process this request? - Reference ref = (Reference) obj; + ResourceLinkRef ref = (ResourceLinkRef) obj; // Read the global ref addr - String globalName = null; + String globalName; RefAddr refAddr = ref.get(ResourceLinkRef.GLOBALNAME); if (refAddr != null) { globalName = refAddr.getContent().toString(); @@ -147,24 +144,22 @@ if (!validateGlobalResourceAccess(globalName)) { return null; } - Object result = null; - result = globalContext.lookup(globalName); + Object result = globalContext.lookup(globalName); // Check the expected type String expectedClassName = ref.getClassName(); if (expectedClassName == null) { - throw new IllegalArgumentException( - sm.getString("resourceLinkFactory.nullType", name, globalName)); + throw new IllegalArgumentException(sm.getString("resourceLinkFactory.nullType", name, globalName)); } try { - Class expectedClazz = Class.forName( - expectedClassName, true, Thread.currentThread().getContextClassLoader()); + Class expectedClazz = + Class.forName(expectedClassName, true, Thread.currentThread().getContextClassLoader()); if (!expectedClazz.isAssignableFrom(result.getClass())) { - throw new IllegalArgumentException(sm.getString("resourceLinkFactory.wrongType", - name, globalName, expectedClassName, result.getClass().getName())); + throw new IllegalArgumentException(sm.getString("resourceLinkFactory.wrongType", name, globalName, + expectedClassName, result.getClass().getName())); } } catch (ClassNotFoundException e) { - throw new IllegalArgumentException(sm.getString("resourceLinkFactory.unknownType", - name, globalName, expectedClassName), e); + throw new IllegalArgumentException( + sm.getString("resourceLinkFactory.unknownType", name, globalName, expectedClassName), e); } return result; } diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/SendMailFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/SendMailFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/SendMailFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/SendMailFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,14 +33,16 @@ import jakarta.mail.internet.MimeMessage; import jakarta.mail.internet.MimePartDataSource; +import org.apache.tomcat.util.ExceptionUtils; + /** - * Factory class that creates a JNDI named javamail MimePartDataSource - * object which can be used for sending email using SMTP. + * Factory class that creates a JNDI named javamail MimePartDataSource object which can be used for sending email using + * SMTP. *

          - * Can be configured in the Context scope - * of your server.xml configuration file. + * Can be configured in the Context scope of your server.xml configuration file. *

          * Example: + * *

            * <Resource name="mail/send"
            *           auth="CONTAINER"
          @@ -54,58 +56,52 @@
            *           mail.smtp.dsn.ret="FULL"
            *           />
            * 
          - * - * @author Glenn Nielsen Rich Catlett */ - -public class SendMailFactory implements ObjectFactory -{ +public class SendMailFactory implements ObjectFactory { // The class name for the javamail MimeMessageDataSource - protected static final String DataSourceClassName = - "jakarta.mail.internet.MimePartDataSource"; + protected static final String DataSourceClassName = "jakarta.mail.internet.MimePartDataSource"; @Override - public Object getObjectInstance(Object refObj, Name name, Context ctx, - Hashtable env) throws Exception { - final Reference ref = (Reference)refObj; + public Object getObjectInstance(Object refObj, Name name, Context ctx, Hashtable env) throws Exception { + final Reference ref = (Reference) refObj; // Creation of the DataSource is wrapped inside a doPrivileged // so that javamail can read its default properties without // throwing Security Exceptions if (ref.getClassName().equals(DataSourceClassName)) { - return AccessController.doPrivileged( - (PrivilegedAction) () -> { - // set up the smtp session that will send the message - Properties props = new Properties(); - // enumeration of all refaddr - Enumeration list = ref.getAll(); - // current refaddr to be set - RefAddr refaddr; - // set transport to smtp - props.put("mail.transport.protocol", "smtp"); - - while (list.hasMoreElements()) { - refaddr = list.nextElement(); - - // set property - props.put(refaddr.getType(), refaddr.getContent()); - } - MimeMessage message = new MimeMessage( - Session.getInstance(props)); - try { - RefAddr fromAddr = ref.get("mail.from"); - String from = null; - if (fromAddr != null) { - from = (String) fromAddr.getContent(); - } - if (from != null) { - message.setFrom(new InternetAddress(from)); - } - message.setSubject(""); - } catch (Exception e) {/*Ignore*/} - MimePartDataSource mds = new MimePartDataSource(message); - return mds; - }); + return AccessController.doPrivileged((PrivilegedAction) () -> { + // set up the smtp session that will send the message + Properties props = new Properties(); + // enumeration of all refaddr + Enumeration list = ref.getAll(); + // current refaddr to be set + RefAddr refaddr; + // set transport to smtp + props.put("mail.transport.protocol", "smtp"); + + while (list.hasMoreElements()) { + refaddr = list.nextElement(); + + // set property + props.put(refaddr.getType(), refaddr.getContent()); + } + MimeMessage message = new MimeMessage(Session.getInstance(props)); + try { + RefAddr fromAddr = ref.get("mail.from"); + String from = null; + if (fromAddr != null) { + from = (String) fromAddr.getContent(); + } + if (from != null) { + message.setFrom(new InternetAddress(from)); + } + message.setSubject(""); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + // Otherwise ignore + } + return new MimePartDataSource(message); + }); } else { // We can't create an instance of the DataSource return null; } diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/TransactionFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/TransactionFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/TransactionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/TransactionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ /** * Object factory for User transactions. - * - * @author Remy Maucherat */ public class TransactionFactory extends FactoryBase { diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/webservices/ServiceProxy.java tomcat10-10.1.52/java/org/apache/naming/factory/webservices/ServiceProxy.java --- tomcat10-10.1.34/java/org/apache/naming/factory/webservices/ServiceProxy.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/webservices/ServiceProxy.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,16 +31,13 @@ /** * Object proxy for Web Services. - * - * @author Fabien Carrion */ public class ServiceProxy implements InvocationHandler { private static final StringManager sm = StringManager.getManager(ServiceProxy.class); /** - * Service object. - * used for delegation + * Service object. used for delegation */ private final Service service; @@ -61,14 +58,16 @@ /** * Constructs a new ServiceProxy wrapping given Service instance. + * * @param service the wrapped Service instance + * * @throws ServiceException should be never thrown */ public ServiceProxy(Service service) throws ServiceException { this.service = service; try { - portQNameClass = Service.class.getDeclaredMethod("getPort", new Class[]{QName.class, Class.class}); - portClass = Service.class.getDeclaredMethod("getPort", new Class[]{Class.class}); + portQNameClass = Service.class.getDeclaredMethod("getPort", new Class[] { QName.class, Class.class }); + portClass = Service.class.getDeclaredMethod("getPort", new Class[] { Class.class }); } catch (Exception e) { throw new ServiceException(e); } @@ -78,8 +77,7 @@ * @see InvocationHandler#invoke(Object, Method, Object[]) */ @Override - public Object invoke(Object proxy, Method method, Object[] args) - throws Throwable { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (portQNameClass.equals(method)) { return getProxyPortQNameClass(args); @@ -98,7 +96,9 @@ /** * @param args Method call arguments + * * @return Returns the correct Port + * * @throws ServiceException if port's QName is an unknown Port (not defined in WSDL). */ private Object getProxyPortQNameClass(Object[] args) throws ServiceException { @@ -106,7 +106,8 @@ String nameString = name.getLocalPart(); Class serviceendpointClass = (Class) args[1]; - for (@SuppressWarnings("unchecked") Iterator ports = service.getPorts(); ports.hasNext();) { + for (@SuppressWarnings("unchecked") + Iterator ports = service.getPorts(); ports.hasNext();) { QName portName = ports.next(); String portnameString = portName.getLocalPart(); if (portnameString.equals(nameString)) { @@ -127,7 +128,9 @@ /** * @param args Method call arguments + * * @return Returns the correct Port + * * @throws ServiceException if port's QName is an unknown Port */ private Remote getProxyPortClass(Object[] args) throws ServiceException { diff -Nru tomcat10-10.1.34/java/org/apache/naming/factory/webservices/ServiceRefFactory.java tomcat10-10.1.52/java/org/apache/naming/factory/webservices/ServiceRefFactory.java --- tomcat10-10.1.34/java/org/apache/naming/factory/webservices/ServiceRefFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/factory/webservices/ServiceRefFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,8 +53,6 @@ /** * Object factory for Web Services. - * - * @author Fabien Carrion */ public class ServiceRefFactory implements ObjectFactory { @@ -117,7 +115,7 @@ } try { if (wsdlRefAddr == null) { - service = factory.createService( serviceQname ); + service = factory.createService(serviceQname); } else { service = factory.createService(new URI(wsdlRefAddr).toURL(), serviceQname); } @@ -130,7 +128,7 @@ // Loading service Interface try { serviceInterfaceClass = tcl.loadClass(serviceInterface); - } catch(ClassNotFoundException e) { + } catch (ClassNotFoundException e) { NamingException ex = new NamingException("Could not load service Interface"); ex.initCause(e); throw ex; @@ -145,8 +143,8 @@ } service = factory.loadService(serviceInterfaceClass); } else { - service = factory.loadService( - new URI(wsdlRefAddr).toURL(), serviceInterfaceClass, new Properties()); + service = factory.loadService(new URI(wsdlRefAddr).toURL(), serviceInterfaceClass, + new Properties()); } } catch (Exception e) { NamingException ex = new NamingException("Could not create service"); @@ -174,7 +172,7 @@ for (String portName : ports.keySet()) { Port port = wsdlservice.getPort(portName); String endpoint = getSOAPLocation(port); - m.invoke(service, new Object[]{port.getName(), endpoint}); + m.invoke(service, new Object[] { port.getName(), endpoint }); portComponentRef.put(endpoint, new QName(port.getName())); } } catch (Exception e) { @@ -242,7 +240,7 @@ Class handlerClass = null; try { handlerClass = tcl.loadClass((String) tmp.getContent()); - } catch(ClassNotFoundException e) { + } catch (ClassNotFoundException e) { break; } @@ -289,12 +287,11 @@ if (!portNames.isEmpty()) { for (String portName : portNames) { - initHandlerChain(new QName(portName), handlerRegistry, - handlerInfo, soaproles); + initHandlerChain(new QName(portName), handlerRegistry, handlerInfo, soaproles); } } else { Enumeration e = portComponentRef.elements(); - while(e.hasMoreElements()) { + while (e.hasMoreElements()) { initHandlerChain(e.nextElement(), handlerRegistry, handlerInfo, soaproles); } } @@ -311,6 +308,7 @@ /** * @param port analyzed port + * * @return Returns the endpoint URL of the given Port */ private String getSOAPLocation(Port port) { @@ -327,8 +325,8 @@ } - private void initHandlerChain(QName portName, HandlerRegistry handlerRegistry, - HandlerInfo handlerInfo, List soaprolesToAdd) { + private void initHandlerChain(QName portName, HandlerRegistry handlerRegistry, HandlerInfo handlerInfo, + List soaprolesToAdd) { HandlerChain handlerChain = (HandlerChain) handlerRegistry.getHandlerChain(portName); @SuppressWarnings("unchecked") // Can't change the API Iterator iter = handlerChain.iterator(); @@ -337,13 +335,13 @@ handler.init(handlerInfo); } String[] soaprolesRegistered = handlerChain.getRoles(); - String [] soaproles = new String[soaprolesRegistered.length + soaprolesToAdd.size()]; + String[] soaproles = new String[soaprolesRegistered.length + soaprolesToAdd.size()]; int i; - for (i = 0;i < soaprolesRegistered.length; i++) { + for (i = 0; i < soaprolesRegistered.length; i++) { soaproles[i] = soaprolesRegistered[i]; } for (int j = 0; j < soaprolesToAdd.size(); j++) { - soaproles[i+j] = soaprolesToAdd.get(j); + soaproles[i + j] = soaprolesToAdd.get(j); } handlerChain.setRoles(soaproles); handlerRegistry.setHandlerChain(portName, handlerChain); diff -Nru tomcat10-10.1.34/java/org/apache/naming/java/javaURLContextFactory.java tomcat10-10.1.52/java/org/apache/naming/java/javaURLContextFactory.java --- tomcat10-10.1.34/java/org/apache/naming/java/javaURLContextFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/naming/java/javaURLContextFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,22 +31,15 @@ /** * Context factory for the "java:" namespace. *

          - * Important note : This factory MUST be associated with the "java" URL - * prefix, which can be done by either : + * Important note : This factory MUST be associated with the "java" URL prefix, which can be done by either : *

            - *
          • Adding a - * java.naming.factory.url.pkgs=org.apache.naming property - * to the JNDI properties file
          • - *
          • Setting an environment variable named Context.URL_PKG_PREFIXES with - * its value including the org.apache.naming package name. - * More detail about this can be found in the JNDI documentation : + *
          • Adding a java.naming.factory.url.pkgs=org.apache.naming property to the JNDI properties file
          • + *
          • Setting an environment variable named Context.URL_PKG_PREFIXES with its value including the org.apache.naming + * package name. More detail about this can be found in the JNDI documentation : * {@link javax.naming.spi.NamingManager#getURLContext(String, java.util.Hashtable)}.
          • *
          - * - * @author Remy Maucherat */ -public class javaURLContextFactory - implements ObjectFactory, InitialContextFactory { +public class javaURLContextFactory implements ObjectFactory, InitialContextFactory { // ----------------------------------------------------------- Constructors @@ -75,22 +68,22 @@ /** * Create a new Context's instance. - * @param obj unused - * @param name unused - * @param nameCtx unused + * + * @param obj unused + * @param name unused + * @param nameCtx unused * @param environment the environment used - * @return a selector context if the thread or classloader are bound, and - * null otherwise + * + * @return a selector context if the thread or classloader are bound, and null otherwise + * * @throws NamingException not thrown by this implementationm */ @SuppressWarnings("unchecked") @Override - public Object getObjectInstance(Object obj, Name name, Context nameCtx, - Hashtable environment) - throws NamingException { - if ((ContextBindings.isThreadBound()) || - (ContextBindings.isClassLoaderBound())) { - return new SelectorContext((Hashtable)environment); + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) + throws NamingException { + if ((ContextBindings.isThreadBound()) || (ContextBindings.isClassLoaderBound())) { + return new SelectorContext((Hashtable) environment); } return null; } @@ -98,28 +91,26 @@ /** * Get a new (writable) initial context. + * * @param environment the environment used - * @return a selector context if the thread or classloader are bound, and - * a shared writable context otherwise + * + * @return a selector context if the thread or classloader are bound, and a shared writable context otherwise + * * @throws NamingException not thrown by this implementationm */ @SuppressWarnings("unchecked") @Override - public Context getInitialContext(Hashtable environment) - throws NamingException { - if (ContextBindings.isThreadBound() || - (ContextBindings.isClassLoaderBound())) { + public Context getInitialContext(Hashtable environment) throws NamingException { + if (ContextBindings.isThreadBound() || (ContextBindings.isClassLoaderBound())) { // Redirect the request to the bound initial context - return new SelectorContext( - (Hashtable)environment, true); + return new SelectorContext((Hashtable) environment, true); } // If the thread is not bound, return a shared writable context if (initialContext == null) { - synchronized(javaURLContextFactory.class) { + synchronized (javaURLContextFactory.class) { if (initialContext == null) { - initialContext = new NamingContext( - (Hashtable)environment, MAIN); + initialContext = new NamingContext((Hashtable) environment, MAIN); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/ContextBind.java tomcat10-10.1.52/java/org/apache/tomcat/ContextBind.java --- tomcat10-10.1.34/java/org/apache/tomcat/ContextBind.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/ContextBind.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,42 +19,30 @@ public interface ContextBind { /** - * Change the current thread context class loader to the web application - * class loader. If no web application class loader is defined, or if the - * current thread is already using the web application class loader then no - * change will be made. If the class loader is changed and a - * {@link org.apache.catalina.ThreadBindingListener} is configured then - * {@link org.apache.catalina.ThreadBindingListener#bind()} will be called - * after the change has been made. + * Change the current thread context class loader to the web application class loader. If no web application class + * loader is defined, or if the current thread is already using the web application class loader then no change will + * be made. If the class loader is changed and a {@link org.apache.catalina.ThreadBindingListener} is configured + * then {@link org.apache.catalina.ThreadBindingListener#bind()} will be called after the change has been made. * - * @param usePrivilegedAction - * Should a {@link java.security.PrivilegedAction} be used when - * obtaining the current thread context class loader and setting - * the new one? - * @param originalClassLoader - * The current class loader if known to save this method having to - * look it up + * @param usePrivilegedAction Should a {@link java.security.PrivilegedAction} be used when obtaining the current + * thread context class loader and setting the new one? + * @param originalClassLoader The current class loader if known to save this method having to look it up * - * @return If the class loader has been changed by the method it will return - * the thread context class loader in use when the method was - * called. If no change was made then this method returns null. + * @return If the class loader has been changed by the method it will return the thread context class loader in use + * when the method was called. If no change was made then this method returns null. */ ClassLoader bind(boolean usePrivilegedAction, ClassLoader originalClassLoader); /** - * Restore the current thread context class loader to the original class - * loader in used before {@link #bind(boolean, ClassLoader)} was called. If - * no original class loader is passed to this method then no change will be - * made. If the class loader is changed and a - * {@link org.apache.catalina.ThreadBindingListener} is configured then - * {@link org.apache.catalina.ThreadBindingListener#unbind()} will be called - * before the change is made. + * Restore the current thread context class loader to the original class loader in used before + * {@link #bind(boolean, ClassLoader)} was called. If no original class loader is passed to this method then no + * change will be made. If the class loader is changed and a {@link org.apache.catalina.ThreadBindingListener} is + * configured then {@link org.apache.catalina.ThreadBindingListener#unbind()} will be called before the change is + * made. * - * @param usePrivilegedAction - * Should a {@link java.security.PrivilegedAction} be used when - * setting the current thread context class loader? - * @param originalClassLoader - * The class loader to restore as the thread context class loader + * @param usePrivilegedAction Should a {@link java.security.PrivilegedAction} be used when setting the current + * thread context class loader? + * @param originalClassLoader The class loader to restore as the thread context class loader */ void unbind(boolean usePrivilegedAction, ClassLoader originalClassLoader); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/InstanceManager.java tomcat10-10.1.52/java/org/apache/tomcat/InstanceManager.java --- tomcat10-10.1.34/java/org/apache/tomcat/InstanceManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/InstanceManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,28 +22,24 @@ public interface InstanceManager { - Object newInstance(Class clazz) throws IllegalAccessException, InvocationTargetException, - NamingException, InstantiationException, IllegalArgumentException, - NoSuchMethodException, SecurityException; - - Object newInstance(String className) throws IllegalAccessException, InvocationTargetException, - NamingException, InstantiationException, ClassNotFoundException, - IllegalArgumentException, NoSuchMethodException, SecurityException; - - Object newInstance(String fqcn, ClassLoader classLoader) throws IllegalAccessException, - InvocationTargetException, NamingException, InstantiationException, - ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, - SecurityException; + Object newInstance(Class clazz) throws IllegalAccessException, InvocationTargetException, NamingException, + InstantiationException, IllegalArgumentException, NoSuchMethodException, SecurityException; - void newInstance(Object o) - throws IllegalAccessException, InvocationTargetException, NamingException; + Object newInstance(String className) + throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, + ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, SecurityException; + + Object newInstance(String fqcn, ClassLoader classLoader) + throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, + ClassNotFoundException, IllegalArgumentException, NoSuchMethodException, SecurityException; + + void newInstance(Object o) throws IllegalAccessException, InvocationTargetException, NamingException; void destroyInstance(Object o) throws IllegalAccessException, InvocationTargetException; /** - * Called by the component using the InstanceManager periodically to perform - * any regular maintenance that might be required. By default, this method - * is a NO-OP. + * Called by the component using the InstanceManager periodically to perform any regular maintenance that might be + * required. By default, this method is a NO-OP. */ default void backgroundProcess() { // NO-OP by default diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/InstanceManagerBindings.java tomcat10-10.1.52/java/org/apache/tomcat/InstanceManagerBindings.java --- tomcat10-10.1.34/java/org/apache/tomcat/InstanceManagerBindings.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/InstanceManagerBindings.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,14 +21,16 @@ public final class InstanceManagerBindings { - private static final Map bindings = new ConcurrentHashMap<>(); + private static final Map bindings = new ConcurrentHashMap<>(); public static void bind(ClassLoader classLoader, InstanceManager instanceManager) { bindings.put(classLoader, instanceManager); } + public static void unbind(ClassLoader classLoader) { bindings.remove(classLoader); } + public static InstanceManager get(ClassLoader classLoader) { return bindings.get(classLoader); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/InstrumentableClassLoader.java tomcat10-10.1.52/java/org/apache/tomcat/InstrumentableClassLoader.java --- tomcat10-10.1.34/java/org/apache/tomcat/InstrumentableClassLoader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/InstrumentableClassLoader.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,58 +19,46 @@ import java.lang.instrument.ClassFileTransformer; /** - * Specifies a class loader capable of being decorated with - * {@link ClassFileTransformer}s. These transformers can instrument - * (or weave) the byte code of classes loaded through this class loader - * to alter their behavior. Currently only - * {@link org.apache.catalina.loader.WebappClassLoaderBase} implements this - * interface. This allows web application frameworks or JPA providers - * bundled with a web application to instrument web application classes - * as necessary. + * Specifies a class loader capable of being decorated with {@link ClassFileTransformer}s. These transformers can + * instrument (or weave) the byte code of classes loaded through this class loader to alter their behavior. Currently + * only {@link org.apache.catalina.loader.WebappClassLoaderBase} implements this interface. This allows web application + * frameworks or JPA providers bundled with a web application to instrument web application classes as necessary. *

          - * You should always program against the methods of this interface - * (whether using reflection or otherwise). The methods in - * {@code WebappClassLoaderBase} are protected by the default security - * manager if one is in use. + * You should always program against the methods of this interface (whether using reflection or otherwise). The methods + * in {@code WebappClassLoaderBase} are protected by the default security manager if one is in use. * * @since 8.0, 7.0.64 */ public interface InstrumentableClassLoader { /** - * Adds the specified class file transformer to this class loader. The - * transformer will then be able to instrument the bytecode of any - * classes loaded by this class loader after the invocation of this - * method. + * Adds the specified class file transformer to this class loader. The transformer will then be able to instrument + * the bytecode of any classes loaded by this class loader after the invocation of this method. * * @param transformer The transformer to add to the class loader + * * @throws IllegalArgumentException if the {@literal transformer} is null. */ void addTransformer(ClassFileTransformer transformer); /** - * Removes the specified class file transformer from this class loader. - * It will no longer be able to instrument the byte code of any classes - * loaded by the class loader after the invocation of this method. - * However, any classes already instrumented by this transformer before - * this method call will remain in their instrumented state. + * Removes the specified class file transformer from this class loader. It will no longer be able to instrument the + * byte code of any classes loaded by the class loader after the invocation of this method. However, any classes + * already instrumented by this transformer before this method call will remain in their instrumented state. * * @param transformer The transformer to remove */ void removeTransformer(ClassFileTransformer transformer); /** - * Returns a copy of this class loader without any class file - * transformers. This is a tool often used by Java Persistence API - * providers to inspect entity classes in the absence of any - * instrumentation, something that can't be guaranteed within the - * context of a {@link ClassFileTransformer}'s - * {@link ClassFileTransformer#transform(ClassLoader, String, Class, - * java.security.ProtectionDomain, byte[]) transform} method. + * Returns a copy of this class loader without any class file transformers. This is a tool often used by Java + * Persistence API providers to inspect entity classes in the absence of any instrumentation, something that can't + * be guaranteed within the context of a {@link ClassFileTransformer}'s + * {@link ClassFileTransformer#transform(ClassLoader, String, Class, java.security.ProtectionDomain, byte[]) + * transform} method. *

          - * The returned class loader's resource cache will have been cleared - * so that classes already instrumented will not be retained or - * returned. + * The returned class loader's resource cache will have been cleared so that classes already instrumented will not + * be retained or returned. * * @return the transformer-free copy of this class loader. */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/Jar.java tomcat10-10.1.52/java/org/apache/tomcat/Jar.java --- tomcat10-10.1.34/java/org/apache/tomcat/Jar.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/Jar.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,15 +22,12 @@ import java.util.jar.Manifest; /** - * Provides an abstraction for use by the various classes that need to scan - * JARs. The classes provided by the JRE for accessing JARs - * ({@link java.util.jar.JarFile} and {@link java.util.jar.JarInputStream}) have - * significantly different performance characteristics depending on the form of - * the URL used to access the JAR. For file based JAR {@link java.net.URL}s, - * {@link java.util.jar.JarFile} is faster but for non-file based - * {@link java.net.URL}s, {@link java.util.jar.JarFile} creates a copy of the - * JAR in the temporary directory so {@link java.util.jar.JarInputStream} is - * faster. + * Provides an abstraction for use by the various classes that need to scan JARs. The classes provided by the JRE for + * accessing JARs ({@link java.util.jar.JarFile} and {@link java.util.jar.JarInputStream}) have significantly different + * performance characteristics depending on the form of the URL used to access the JAR. For file based JAR + * {@link java.net.URL}s, {@link java.util.jar.JarFile} is faster but for non-file based {@link java.net.URL}s, + * {@link java.util.jar.JarFile} creates a copy of the JAR in the temporary directory so + * {@link java.util.jar.JarInputStream} is faster. */ public interface Jar extends AutoCloseable { @@ -40,12 +37,11 @@ URL getJarFileURL(); /** - * Obtain an {@link InputStream} for a given entry in a JAR. The caller is - * responsible for closing the stream. + * Obtain an {@link InputStream} for a given entry in a JAR. The caller is responsible for closing the stream. * - * @param name Entry to obtain an {@link InputStream} for - * @return An {@link InputStream} for the specified entry or null if - * the entry does not exist + * @param name Entry to obtain an {@link InputStream} for + * + * @return An {@link InputStream} for the specified entry or null if the entry does not exist * * @throws IOException if an I/O error occurs while processing the JAR file */ @@ -54,11 +50,10 @@ /** * Obtain the last modified time for the given resource in the JAR. * - * @param name Entry to obtain the modification time for + * @param name Entry to obtain the modification time for * - * @return The time (in the same format as - * {@link System#currentTimeMillis()} that the resource was last - * modified. Returns -1 if the entry does not exist + * @return The time (in the same format as {@link System#currentTimeMillis()}) that the resource was last modified. + * Returns -1 if the entry does not exist * * @throws IOException if an I/O error occurs while processing the JAR file */ @@ -67,10 +62,9 @@ /** * Determine if the given resource in present in the JAR. * - * @param name Entry to look for + * @param name Entry to look for * - * @return {@code true} if the entry is present in the JAR, otherwise - * {@code false} + * @return {@code true} if the entry is present in the JAR, otherwise {@code false} * * @throws IOException if an I/O error occurs while processing the JAR file */ @@ -90,26 +84,24 @@ /** * Obtains the name of the current entry. * - * @return The entry name + * @return The entry name */ String getEntryName(); /** * Obtains the input stream for the current entry. * - * @return The input stream - * @throws IOException If the stream cannot be obtained + * @return The input stream + * + * @throws IOException If the stream cannot be obtained */ InputStream getEntryInputStream() throws IOException; /** - * Obtain, in String form, the URL for an entry in this JAR. Note that for - * JARs nested in WAR files, the Tomcat specific war:file:... form will not - * be used, rather the jar:jar:file:... form (that the JRE does not - * understand will be used). Note that this means that any code using these - * URLs will need to understand the jar:jar:file:... form and use the - * {@link org.apache.tomcat.util.scan.JarFactory} to ensure resources are - * accessed correctly. + * Obtain, in String form, the URL for an entry in this JAR. Note that for JARs nested in WAR files, the Tomcat + * specific war:file:... form will not be used, rather the jar:jar:file:... form (that the JRE does not understand + * will be used). Note that this means that any code using these URLs will need to understand the jar:jar:file:... + * form and use the {@link org.apache.tomcat.util.scan.JarFactory} to ensure resources are accessed correctly. * * @param entry The entry to generate the URL for * @@ -127,10 +119,9 @@ Manifest getManifest() throws IOException; /** - * Resets the internal pointer used to track JAR entries to the beginning of - * the JAR. + * Resets the internal pointer used to track JAR entries to the beginning of the JAR. * - * @throws IOException If the pointer cannot be reset + * @throws IOException If the pointer cannot be reset */ void reset() throws IOException; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/JarScanFilter.java tomcat10-10.1.52/java/org/apache/tomcat/JarScanFilter.java --- tomcat10-10.1.34/java/org/apache/tomcat/JarScanFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/JarScanFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,18 +19,18 @@ public interface JarScanFilter { /** - * @param jarScanType The type of JAR scan currently being performed - * @param jarName The name of the JAR file (without any path - * information) to be checked to see if it should - * be included in the results or not - * @return true if the JAR should be returned in the results, - * false if it should be excluded + * @param jarScanType The type of JAR scan currently being performed + * @param jarName The name of the JAR file (without any path information) to be checked to see if it should be + * included in the results or not + * + * @return true if the JAR should be returned in the results, false if it should be + * excluded */ boolean check(JarScanType jarScanType, String jarName); /** - * @return true if all of the scans should be skipped which - * can improve startup performance. The default is false. + * @return true if all of the scans should be skipped which can improve startup performance. The + * default is false. */ default boolean isSkipAll() { return false; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/JarScanner.java tomcat10-10.1.52/java/org/apache/tomcat/JarScanner.java --- tomcat10-10.1.34/java/org/apache/tomcat/JarScanner.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/JarScanner.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,25 +19,21 @@ import jakarta.servlet.ServletContext; /** - * Scans a web application and classloader hierarchy for JAR files. Uses - * include TLD scanning and web-fragment.xml scanning. Uses a call-back - * mechanism so the caller can process each JAR found. + * Scans a web application and classloader hierarchy for JAR files. Uses include TLD scanning and web-fragment.xml + * scanning. Uses a call-back mechanism so the caller can process each JAR found. */ public interface JarScanner { /** - * Scan the provided ServletContext and classloader for JAR files. Each JAR - * file found will be passed to the callback handler to be processed. + * Scan the provided ServletContext and classloader for JAR files. Each JAR file found will be passed to the + * callback handler to be processed. * - * @param scanType The type of JAR scan to perform. This is passed to - * the filter which uses it to determine how to - * filter the results - * @param context The ServletContext - used to locate and access - * WEB-INF/lib - * @param callback The handler to process any JARs found + * @param scanType The type of JAR scan to perform. This is passed to the filter which uses it to determine how to + * filter the results + * @param context The ServletContext - used to locate and access WEB-INF/lib + * @param callback The handler to process any JARs found */ - void scan(JarScanType scanType, ServletContext context, - JarScannerCallback callback); + void scan(JarScanType scanType, ServletContext context, JarScannerCallback callback); JarScanFilter getJarScanFilter(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/JarScannerCallback.java tomcat10-10.1.52/java/org/apache/tomcat/JarScannerCallback.java --- tomcat10-10.1.34/java/org/apache/tomcat/JarScannerCallback.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/JarScannerCallback.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,35 +20,31 @@ import java.io.IOException; /** - * This interface is implemented by clients of the {@link JarScanner} to enable - * them to receive notification of a discovered JAR. + * This interface is implemented by clients of the {@link JarScanner} to enable them to receive notification of a + * discovered JAR. */ public interface JarScannerCallback { /** - * A JAR was found and may be accessed for further processing via the - * provided URL connection. The caller is responsible for closing the JAR. + * A JAR was found and may be accessed for further processing via the provided URL connection. The caller is + * responsible for closing the JAR. * * @param jar The JAR to process * @param webappPath The path, if any, to the JAR within the web application - * @param isWebapp Indicates if the JAR was found within a web - * application. If false the JAR should + * @param isWebapp Indicates if the JAR was found within a web application. If false the JAR should * be treated as being provided by the container * * @throws IOException if an I/O error occurs while scanning the JAR */ - void scan(Jar jar, String webappPath, boolean isWebapp) - throws IOException; + void scan(Jar jar, String webappPath, boolean isWebapp) throws IOException; /** - * A directory was found that is to be treated as an unpacked JAR. The - * directory may be accessed for further processing via the provided file. + * A directory was found that is to be treated as an unpacked JAR. The directory may be accessed for further + * processing via the provided file. * * @param file The directory containing the unpacked JAR. - * @param webappPath The path, if any, to the file within the web - * application - * @param isWebapp Indicates if the JAR was found within a web - * application. If false the JAR should + * @param webappPath The path, if any, to the file within the web application + * @param isWebapp Indicates if the JAR was found within a web application. If false the JAR should * be treated as being provided by the container * * @throws IOException if an I/O error occurs while scanning the JAR @@ -56,10 +52,9 @@ void scan(File file, String webappPath, boolean isWebapp) throws IOException; /** - * A directory structure was found within the web application at - * /WEB-INF/classes that should be handled as an unpacked JAR. Note that all - * resource access must be via the ServletContext to ensure that any - * additional resources are visible. + * A directory structure was found within the web application at /WEB-INF/classes that should be handled as an + * unpacked JAR. Note that all resource access must be via the ServletContext to ensure that any additional + * resources are visible. * * @throws IOException if an I/O error occurs while scanning WEB-INF/classes */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/SimpleInstanceManager.java tomcat10-10.1.52/java/org/apache/tomcat/SimpleInstanceManager.java --- tomcat10-10.1.34/java/org/apache/tomcat/SimpleInstanceManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/SimpleInstanceManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,7 @@ import javax.naming.NamingException; /** - * SimpleInstanceManager - * - * Implement the org.apache.tomcat.InstanceManager interface. + * SimpleInstanceManager implements the org.apache.tomcat.InstanceManager interface. */ public class SimpleInstanceManager implements InstanceManager { @@ -31,30 +29,28 @@ } @Override - public Object newInstance(Class clazz) throws IllegalAccessException, - InvocationTargetException, NamingException, InstantiationException, NoSuchMethodException { + public Object newInstance(Class clazz) throws IllegalAccessException, InvocationTargetException, NamingException, + InstantiationException, NoSuchMethodException { return prepareInstance(clazz.getConstructor().newInstance()); } @Override - public Object newInstance(String className) throws IllegalAccessException, - InvocationTargetException, NamingException, InstantiationException, - ClassNotFoundException, NoSuchMethodException { + public Object newInstance(String className) throws IllegalAccessException, InvocationTargetException, + NamingException, InstantiationException, ClassNotFoundException, NoSuchMethodException { Class clazz = Thread.currentThread().getContextClassLoader().loadClass(className); return prepareInstance(clazz.getConstructor().newInstance()); } @Override - public Object newInstance(String fqcn, ClassLoader classLoader) throws IllegalAccessException, - InvocationTargetException, NamingException, InstantiationException, - ClassNotFoundException, NoSuchMethodException { + public Object newInstance(String fqcn, ClassLoader classLoader) + throws IllegalAccessException, InvocationTargetException, NamingException, InstantiationException, + ClassNotFoundException, NoSuchMethodException { Class clazz = classLoader.loadClass(fqcn); return prepareInstance(clazz.getConstructor().newInstance()); } @Override - public void newInstance(Object o) throws IllegalAccessException, InvocationTargetException, - NamingException { + public void newInstance(Object o) throws IllegalAccessException, InvocationTargetException, NamingException { // NO-OP } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/buildutil/CheckEol.java tomcat10-10.1.52/java/org/apache/tomcat/buildutil/CheckEol.java --- tomcat10-10.1.34/java/org/apache/tomcat/buildutil/CheckEol.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/buildutil/CheckEol.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,13 +31,10 @@ import org.apache.tools.ant.types.FileSet; /** - * Ant task that checks that all the files in the given fileset have end-of-line - * delimiters that are appropriate. - * + * Ant task that checks that all the files in the given fileset have end-of-line delimiters that are appropriate. *

          - * The goal is to check whether we have problems with Subversion's svn:eol-style - * property or Git's autocrlf setting when files are committed on one OS and then - * checked on another one. + * The goal is to check whether we have problems with Subversion's svn:eol-style property or Git's autocrlf setting when + * files are committed on one OS and then checked on another one. */ public class CheckEol extends Task { @@ -52,8 +49,8 @@ * * @param fs The fileset to be checked. */ - public void addFileset( FileSet fs ) { - filesets.add( fs ); + public void addFileset(FileSet fs) { + filesets.add(fs); } /** @@ -61,12 +58,12 @@ * * @param mode The line ending mode (either LF or CRLF) */ - public void setMode( String mode ) { - this.mode = Mode.valueOf( mode.toUpperCase(Locale.ENGLISH) ); + public void setMode(String mode) { + this.mode = Mode.valueOf(mode.toUpperCase(Locale.ENGLISH)); } private Mode getMode() { - if ( mode != null ) { + if (mode != null) { return mode; } else { if ("\n".equals(System.lineSeparator())) { @@ -82,14 +79,13 @@ /** * Perform the check * - * @throws BuildException if an error occurs during execution of - * this task. + * @throws BuildException if an error occurs during execution of this task. */ @Override public void execute() throws BuildException { Mode mode = getMode(); - if ( mode == null ) { + if (mode == null) { log("Line ends check skipped, because OS line ends setting is neither LF nor CRLF.", Project.MSG_VERBOSE); return; } @@ -107,25 +103,21 @@ log("Checking line ends in " + files.length + " file(s)"); for (String filename : files) { File file = new File(basedir, filename); - log("Checking file '" + file + "' for correct line ends", - Project.MSG_DEBUG); + log("Checking file '" + file + "' for correct line ends", Project.MSG_DEBUG); try { check(file, errors, mode); - } catch (IOException e) { - throw new BuildException("Could not check file '" - + file.getAbsolutePath() + "'", e); + } catch (IOException ioe) { + throw new BuildException("Could not check file '" + file.getAbsolutePath() + "'", ioe); } count++; } } } if (count > 0) { - log("Done line ends check in " + count + " file(s), " - + errors.size() + " error(s) found."); + log("Done line ends check in " + count + " file(s), " + errors.size() + " error(s) found."); } - if (errors.size() > 0) { - String message = "The following files have wrong line ends: " - + errors; + if (!errors.isEmpty()) { + String message = "The following files have wrong line ends: " + errors; // We need to explicitly write the message to the log, because // long BuildException messages may be trimmed. E.g. I observed // this problem with Eclipse IDE 3.7. @@ -135,7 +127,8 @@ } private enum Mode { - LF, CRLF + LF, + CRLF } private static class CheckFailure { @@ -156,8 +149,7 @@ } private void check(File file, List errors, Mode mode) throws IOException { - try (FileInputStream fis = new FileInputStream(file); - BufferedInputStream is = new BufferedInputStream(fis)) { + try (FileInputStream fis = new FileInputStream(file); BufferedInputStream is = new BufferedInputStream(fis)) { int line = 1; int prev = -1; int ch; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/buildutil/MimeTypeMappings.java tomcat10-10.1.52/java/org/apache/tomcat/buildutil/MimeTypeMappings.java --- tomcat10-10.1.34/java/org/apache/tomcat/buildutil/MimeTypeMappings.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/buildutil/MimeTypeMappings.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,7 +49,7 @@ digester.parse(globalWebXml); Map webXmlMimeMappings = webXmlDefaultFragment.getMimeMappings(); - SortedMap sortedWebXmlMimeMappings = new TreeMap<>(webXmlMimeMappings); + SortedMap sortedWebXmlMimeMappings = new TreeMap<>(webXmlMimeMappings); File f = new File("java/org/apache/catalina/startup/MimeTypeMappings.properties"); try (FileOutputStream fos = new FileOutputStream(f); @@ -59,7 +59,7 @@ w.write(System.lineSeparator()); - for (Map.Entry mapping : sortedWebXmlMimeMappings.entrySet()) { + for (Map.Entry mapping : sortedWebXmlMimeMappings.entrySet()) { w.write(mapping.getKey()); w.write("="); w.write(mapping.getValue()); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/buildutil/RepeatableArchive.java tomcat10-10.1.52/java/org/apache/tomcat/buildutil/RepeatableArchive.java --- tomcat10-10.1.34/java/org/apache/tomcat/buildutil/RepeatableArchive.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/buildutil/RepeatableArchive.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,11 +41,9 @@ /** * Ant task to assist with repeatable builds. *

          - * While originally written to address an issue with Javadoc output, this task - * takes a generic approach that could be used with any archive. The task takes - * a set of zip (or jar, war etc) files as its input and sets the last modified - * time of every file in the archive to be the same as the last modified time - * of the archive. + * While originally written to address an issue with Javadoc output, this task takes a generic approach that could be + * used with any archive. The task takes a set of zip (or jar, war etc) files as its input and sets the last modified + * time of every file in the archive to be the same as the last modified time of the archive. */ public class RepeatableArchive extends Task { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/buildutil/Txt2Html.java tomcat10-10.1.52/java/org/apache/tomcat/buildutil/Txt2Html.java --- tomcat10-10.1.34/java/org/apache/tomcat/buildutil/Txt2Html.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/buildutil/Txt2Html.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,17 +34,12 @@ import org.apache.tools.ant.types.FileSet; /** - * Ant task to convert a given set of files from Text to HTML. - * Inserts an HTML header including pre tags and replaces special characters - * with their HTML escaped equivalents. - * - *

          This task is currently used by the ant script to build our examples

          - * - * @author Mark Roth + * Ant task to convert a given set of files from Text to HTML. Inserts an HTML header including pre tags and replaces + * special characters with their HTML escaped equivalents. + *

          + * This task is currently used by the ant script to build our examples */ -public class Txt2Html - extends Task -{ +public class Txt2Html extends Task { /** The directory to contain the resulting files */ private File todir; @@ -53,14 +48,13 @@ private final List filesets = new ArrayList<>(); /** - * The encoding of the source files (.java and .jsp). Once they use - * UTF-8, this will need to be updated. + * The encoding of the source files (.java and .jsp). Once they use UTF-8, this will need to be updated. */ private static final String SOURCE_ENCODING = "ISO-8859-1"; /** - * Line terminator to be used for separating lines of the generated - * HTML page, to be independent from "line.separator" system property. + * Line terminator to be used for separating lines of the generated HTML page, to be independent of the + * "line.separator" system property. */ private static final String LINE_SEPARATOR = "\r\n"; @@ -69,7 +63,7 @@ * * @param todir The directory */ - public void setTodir( File todir ) { + public void setTodir(File todir) { this.todir = todir; } @@ -78,20 +72,17 @@ * * @param fs The fileset to be converted. */ - public void addFileset( FileSet fs ) { - filesets.add( fs ); + public void addFileset(FileSet fs) { + filesets.add(fs); } /** * Perform the conversion * - * @throws BuildException if an error occurs during execution of - * this task. + * @throws BuildException if an error occurs during execution of this task. */ @Override - public void execute() - throws BuildException - { + public void execute() throws BuildException { int count = 0; // Step through each file and convert. @@ -102,23 +93,21 @@ for (String file : files) { File from = new File(basedir, file); File to = new File(todir, file + ".html"); - if (!to.exists() || - (from.lastModified() > to.lastModified())) { - log("Converting file '" + from.getAbsolutePath() + - "' to '" + to.getAbsolutePath(), Project.MSG_VERBOSE); + if (!to.exists() || (from.lastModified() > to.lastModified())) { + log("Converting file '" + from.getAbsolutePath() + "' to '" + to.getAbsolutePath(), + Project.MSG_VERBOSE); try { convert(from, to); - } catch (IOException e) { - throw new BuildException("Could not convert '" + - from.getAbsolutePath() + "' to '" + - to.getAbsolutePath() + "'", e); + } catch (IOException ioe) { + throw new BuildException( + "Could not convert '" + from.getAbsolutePath() + "' to '" + to.getAbsolutePath() + "'", + ioe); } count++; } } - if( count > 0 ) { - log( "Converted " + count + " file" + (count > 1 ? "s" : "") + - " to " + todir.getAbsolutePath() ); + if (count > 0) { + log("Converted " + count + " file" + (count > 1 ? "s" : "") + " to " + todir.getAbsolutePath()); } } } @@ -127,45 +116,43 @@ * Perform the actual copy and conversion * * @param from The input file - * @param to The output file + * @param to The output file + * * @throws IOException Thrown if an error occurs during the conversion */ - private void convert( File from, File to ) - throws IOException - { + private void convert(File from, File to) throws IOException { // Open files: - try (BufferedReader in = new BufferedReader(new InputStreamReader( - new FileInputStream(from), SOURCE_ENCODING))) { - try (PrintWriter out = new PrintWriter(new OutputStreamWriter( - new FileOutputStream(to), "UTF-8"))) { + try (BufferedReader in = + new BufferedReader(new InputStreamReader(new FileInputStream(from), SOURCE_ENCODING))) { + try (PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(to), "UTF-8"))) { // Output header: - out.print("" - + "Source Code

          " );
          +                out.print("" +
          +                        "Source Code
          ");
           
                           // Convert, line-by-line:
                           String line;
          -                while( (line = in.readLine()) != null ) {
          +                while ((line = in.readLine()) != null) {
                               StringBuilder result = new StringBuilder();
                               int len = line.length();
          -                    for( int i = 0; i < len; i++ ) {
          -                        char c = line.charAt( i );
          -                        switch( c ) {
          +                    for (int i = 0; i < len; i++) {
          +                        char c = line.charAt(i);
          +                        switch (c) {
                                       case '&':
          -                                result.append( "&" );
          +                                result.append("&");
                                           break;
                                       case '<':
          -                                result.append( "<" );
          +                                result.append("<");
                                           break;
                                       default:
          -                                result.append( c );
          +                                result.append(c);
                                   }
                               }
          -                    out.print( result.toString() + LINE_SEPARATOR );
          +                    out.print(result.toString() + LINE_SEPARATOR);
                           }
           
                           // Output footer:
          -                out.print( "
          " ); + out.print("
          "); } } @@ -173,4 +160,3 @@ } - diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/buildutil/translate/BackportEnglish.java tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/BackportEnglish.java --- tomcat10-10.1.34/java/org/apache/tomcat/buildutil/translate/BackportEnglish.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/BackportEnglish.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,15 +21,13 @@ import java.util.Set; /** - * Generates a set of English property files to back-port updates to a previous - * version. Where a key exists in the source and target versions the value is - * copied from the source to the target, overwriting the value in the target. - * The expectation is that the changes will be manually reviewed before - * committing them. + * Generates a set of English property files to back-port updates to a previous version. Where a key exists in the + * source and target versions the value is copied from the source to the target, overwriting the value in the target. + * The expectation is that the changes will be manually reviewed before committing them. */ public class BackportEnglish extends BackportBase { - private static Set keysToExclude = new HashSet<>(); + private static final Set keysToExclude = new HashSet<>(); public static void main(String... args) throws IOException { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/buildutil/translate/BackportTranslations.java tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/BackportTranslations.java --- tomcat10-10.1.34/java/org/apache/tomcat/buildutil/translate/BackportTranslations.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/BackportTranslations.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,10 +20,8 @@ import java.util.Properties; /** - * Generates a set of translated property files to back-port updates to a - * previous version. If the source and target use the same value for the English - * key then any translated value for that key is copied from the source to the - * target. + * Generates a set of translated property files to back-port updates to a previous version. If the source and target use + * the same value for the English key then any translated value for that key is copied from the source to the target. */ public class BackportTranslations extends BackportBase { @@ -41,7 +39,7 @@ protected void execute() throws IOException { for (String language : targetTranslations.keySet()) { // Skip source - if (language.length() == 0) { + if (language.isEmpty()) { continue; } @@ -53,8 +51,7 @@ } for (Object key : targetEnglish.keySet()) { - if (sourceTranslated.containsKey(key) && - targetEnglish.get(key).equals(sourceEnglish.get(key))) { + if (sourceTranslated.containsKey(key) && targetEnglish.get(key).equals(sourceEnglish.get(key))) { targetTranslated.put(key, sourceTranslated.get(key)); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/buildutil/translate/Import.java tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/Import.java --- tomcat10-10.1.34/java/org/apache/tomcat/buildutil/translate/Import.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/Import.java 2026-01-23 19:33:36.000000000 +0000 @@ -57,7 +57,7 @@ String key = (String) objKey; String value = props.getProperty(key); // Skip untranslated values - if (value.trim().length() == 0) { + if (value.trim().isEmpty()) { continue; } CompositeKey cKey = new CompositeKey(key); @@ -68,7 +68,8 @@ if (w != null) { w.close(); } - File outFile = new File(currentPkg.replace('.', File.separatorChar), Constants.L10N_PREFIX + language + Constants.L10N_SUFFIX); + File outFile = new File(currentPkg.replace('.', File.separatorChar), + Constants.L10N_PREFIX + language + Constants.L10N_SUFFIX); FileOutputStream fos = new FileOutputStream(outFile); w = new OutputStreamWriter(fos, StandardCharsets.UTF_8); org.apache.tomcat.buildutil.Utils.insertLicense(w); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/buildutil/translate/Utils.java tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/Utils.java --- tomcat10-10.1.34/java/org/apache/tomcat/buildutil/translate/Utils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/buildutil/translate/Utils.java 2026-01-23 19:33:36.000000000 +0000 @@ -64,13 +64,21 @@ try (FileInputStream fis = new FileInputStream(f); Reader r = new InputStreamReader(fis, StandardCharsets.UTF_8)) { props.load(r); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } return props; } + /* + * This is the formatting applied when exporting values from Tomcat to POE. + * + * The values have been read from Tomcat's property files (so the input is the actual value with none of the + * escaping required to represent that value in a property file) and are being written back out in a single file to + * be imported into POEditor. The padding for blank lines needs to be added followed by the common formatting + * required to convert a property value to the representation of that value in a file. + */ static String formatValueExport(String in) { String result; @@ -84,6 +92,14 @@ } + /* + * This is the formatting applied when importing values to Tomcat from POE. + * + * The values have been read from POE's property files (so the input is the actual value with none of the + * escaping required to represent that value in a property file) and are being written back out to individual files + * in Tomcat. The padding for blank lines needs to be removed followed by the common formatting required to convert + * a property value to the representation of that value in a file. + */ static String formatValueImport(String in) { String result; @@ -98,9 +114,8 @@ /* - * Values containing "[{n}]" and "'" need to have the "'" escaped as "''". - * POEditor attempts to do this automatically but does it for any value - * containing "{" or "}" leading to some unnecessary escaping. This method + * Values containing "[{n}]" and "'" need to have the "'" escaped as "''". POEditor attempts to do this + * automatically but does it for any value containing "{" or "}" leading to some unnecessary escaping. This method * undoes the unnecessary escaping. */ static String fixUnnecessaryEscaping(String key, String value) { @@ -112,8 +127,13 @@ /* - * Common formatting to convert a String for storage as a value in a - * property file. + * Common formatting to convert a String value for storage as a value in a property file. Values that contain + * line-breaks need the line-break in the value to be replaced with the correct representation of a line-break in a + * property file. Leading space needs to be escaped with a '\' and horizontal tabs need to be converted to "\t". + * + * Note that a single '\' needs to be escaped both in a Java string and in a property file so if a property value + * needs to contain a single `\` (e.g. to escape white space at the start of a line) that will appear as "\\\\" in + * the Java code. */ static String formatValueCommon(String in) { String result = in.replace("\n", "\\n\\\n"); @@ -194,8 +214,8 @@ Writer w = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) { String[] keys = translation.keySet().toArray(new String[0]); Arrays.sort(keys); - for (Object key : keys) { - w.write(key + "=" + formatValueExport(translation.getProperty((String) key)) + "\n"); + for (String key : keys) { + w.write(key + "=" + formatValueExport(translation.getProperty(key)) + "\n"); } } catch (IOException ioe) { ioe.printStackTrace(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/AbandonedTrace.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/AbandonedTrace.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/AbandonedTrace.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/AbandonedTrace.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -131,12 +131,12 @@ * @return List of objects. */ protected List getTrace() { - final int size = traceList.size(); - if (size == 0) { - return Collections.emptyList(); - } - final ArrayList result = new ArrayList<>(size); synchronized (this.traceList) { + final int size = traceList.size(); + if (size == 0) { + return Collections.emptyList(); + } + final ArrayList result = new ArrayList<>(size); final Iterator> iter = traceList.iterator(); while (iter.hasNext()) { final AbandonedTrace trace = iter.next().get(); @@ -147,8 +147,8 @@ result.add(trace); } } + return result; } - return result; } /** diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -72,8 +72,8 @@ private static final Log log = LogFactory.getLog(BasicDataSource.class); static { - // Attempt to prevent deadlocks - see DBCP - 272 - DriverManager.getDrivers(); + // Attempt to prevent deadlocks - see DBCP-272 + DriverManager.getDrivers(); // NOPMD try { // Load classes now to prevent AccessControlExceptions later // A number of classes are loaded when getConnection() is called @@ -153,7 +153,7 @@ * The property that controls if the pooled connections cache some state rather than query the database for current * state to improve performance. */ - private boolean cacheState = true; + private volatile boolean cacheState = true; /** * The instance of the JDBC Driver to use. @@ -219,7 +219,7 @@ */ private boolean poolPreparedStatements; - private boolean clearStatementPoolOnReturn; + private volatile boolean clearStatementPoolOnReturn; /** *

          @@ -330,19 +330,19 @@ /** * Controls access to the underlying connection. */ - private boolean accessToUnderlyingConnectionAllowed; + private volatile boolean accessToUnderlyingConnectionAllowed; private Duration maxConnDuration = Duration.ofMillis(-1); - private boolean logExpiredConnections = true; + private volatile boolean logExpiredConnections = true; private String jmxName; - private boolean registerConnectionMBean = true; + private volatile boolean registerConnectionMBean = true; - private boolean autoCommitOnReturn = true; + private volatile boolean autoCommitOnReturn = true; - private boolean rollbackOnReturn = true; + private volatile boolean rollbackOnReturn = true; private volatile Set disconnectionSqlCodes; @@ -353,7 +353,7 @@ */ private volatile Set disconnectionIgnoreSqlCodes; - private boolean fastFailValidation; + private volatile boolean fastFailValidation; /** * The object pool that internally manages our connections. @@ -389,6 +389,13 @@ private ObjectNameWrapper registeredJmxObjectName; /** + * Constructs a new instance. + */ + public BasicDataSource() { + // empty + } + + /** * Adds a custom connection property to the set that will be passed to our JDBC driver. This MUST * be called before the first connection is retrieved (along with all the other configuration property setters). * Calls to this method after the connection pool has been initialized have no effect. @@ -458,7 +465,7 @@ * with the specified {@link ClassLoader}. *

        • If {code driverClassName} is specified and the previous attempt fails, the class is loaded using the * context class loader of the current thread.
        • - *
        • If a driver still isn't loaded one is loaded via the {@link DriverManager} using the specified {code connectionString}. + *
        • If a driver still isn't loaded one is loaded via the {@link DriverManager} using the specified {code connectionString}.
        • * *

          * This method exists so subclasses can replace the implementation class. @@ -2021,8 +2028,8 @@ *

          * * @param disconnectionSqlCodes SQL State codes considered to signal fatal conditions - * @since 2.1 * @throws IllegalArgumentException if any SQL state codes overlap with those in {@link #disconnectionIgnoreSqlCodes}. + * @since 2.1 */ public void setDisconnectionSqlCodes(final Collection disconnectionSqlCodes) { Utils.checkSqlCodes(disconnectionSqlCodes, this.disconnectionIgnoreSqlCodes); @@ -2110,8 +2117,10 @@ } /** + * Sets whether connections created by this factory will fast fail validation. + * + * @param fastFailValidation true means connections created by this factory will fast fail validation. * @see #getFastFailValidation() - * @param fastFailValidation true means connections created by this factory will fast fail validation * @since 2.1 */ public void setFastFailValidation(final boolean fastFailValidation) { @@ -2155,6 +2164,8 @@ } /** + * Sets whether to log abandoned resources. + * * @param logAbandoned new logAbandoned property value */ public void setLogAbandoned(final boolean logAbandoned) { @@ -2392,8 +2403,9 @@ } /** - * @param removeAbandonedOnBorrow true means abandoned connections may be removed when connections are borrowed from - * the pool. + * Sets abandoned connections may be removed when connections are borrowed from the pool. + * + * @param removeAbandonedOnBorrow true means abandoned connections may be removed when connections are borrowed from the pool. * @see #getRemoveAbandonedOnBorrow() */ public void setRemoveAbandonedOnBorrow(final boolean removeAbandonedOnBorrow) { @@ -2401,6 +2413,8 @@ } /** + * Sets whether abandoned connections may be removed on pool maintenance. + * * @param removeAbandonedOnMaintenance true means abandoned connections may be removed on pool maintenance. * @see #getRemoveAbandonedOnMaintenance() */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -49,12 +49,12 @@ import org.apache.tomcat.dbcp.pool2.impl.GenericObjectPoolConfig; /** - * JNDI object factory that creates an instance of {@code BasicDataSource} that has been configured based on the - * {@code RefAddr} values of the specified {@code Reference}, which must match the names and data types of the - * {@code BasicDataSource} bean properties with the following exceptions: + * JNDI object factory that creates an instance of {@link BasicDataSource} that has been configured based on the + * {@link RefAddr} values of the specified {@code Reference}, which must match the names and data types of the + * {@link BasicDataSource} bean properties with the following exceptions: *
            *
          • {@code connectionInitSqls} must be passed to this factory as a single String using semicolon to delimit the - * statements whereas {@code BasicDataSource} requires a collection of Strings.
          • + * statements whereas {@link BasicDataSource} requires a collection of Strings. *
          * * @since 2.0 @@ -131,7 +131,6 @@ */ private static final String PROP_DISCONNECTION_IGNORE_SQL_CODES = "disconnectionIgnoreSqlCodes"; - /* * Block with obsolete properties from DBCP 1.x. Warn users that these are ignored and they should use the 2.x * properties. @@ -175,11 +174,11 @@ "Property " + NUPROP_MAX_ACTIVE + " is not used in DBCP2, use " + PROP_MAX_TOTAL + " instead. " + PROP_MAX_TOTAL + " default value is " + GenericObjectPoolConfig.DEFAULT_MAX_TOTAL + "."); NUPROP_WARNTEXT.put(NUPROP_REMOVE_ABANDONED, - "Property " + NUPROP_REMOVE_ABANDONED + " is not used in DBCP2," + " use one or both of " + "Property " + NUPROP_REMOVE_ABANDONED + " is not used in DBCP2, use one or both of " + PROP_REMOVE_ABANDONED_ON_BORROW + " or " + PROP_REMOVE_ABANDONED_ON_MAINTENANCE + " instead. " + "Both have default value set to false."); NUPROP_WARNTEXT.put(NUPROP_MAXWAIT, - "Property " + NUPROP_MAXWAIT + " is not used in DBCP2" + " , use " + PROP_MAX_WAIT_MILLIS + " instead. " + "Property " + NUPROP_MAXWAIT + " is not used in DBCP2 , use " + PROP_MAX_WAIT_MILLIS + " instead. " + PROP_MAX_WAIT_MILLIS + " default value is " + BaseObjectPoolConfig.DEFAULT_MAX_WAIT + "."); } @@ -240,31 +239,31 @@ value = value.toUpperCase(Locale.ROOT); int level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION; switch (value) { - case "NONE": - level = Connection.TRANSACTION_NONE; - break; - case "READ_COMMITTED": - level = Connection.TRANSACTION_READ_COMMITTED; - break; - case "READ_UNCOMMITTED": - level = Connection.TRANSACTION_READ_UNCOMMITTED; - break; - case "REPEATABLE_READ": - level = Connection.TRANSACTION_REPEATABLE_READ; - break; - case "SERIALIZABLE": - level = Connection.TRANSACTION_SERIALIZABLE; - break; - default: - try { - level = Integer.parseInt(value); - } catch (final NumberFormatException e) { - System.err.println("Could not parse defaultTransactionIsolation: " + value); - System.err.println("WARNING: defaultTransactionIsolation not set"); - System.err.println("using default value of database driver"); - level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION; - } - break; + case "NONE": + level = Connection.TRANSACTION_NONE; + break; + case "READ_COMMITTED": + level = Connection.TRANSACTION_READ_COMMITTED; + break; + case "READ_UNCOMMITTED": + level = Connection.TRANSACTION_READ_UNCOMMITTED; + break; + case "REPEATABLE_READ": + level = Connection.TRANSACTION_REPEATABLE_READ; + break; + case "SERIALIZABLE": + level = Connection.TRANSACTION_SERIALIZABLE; + break; + default: + try { + level = Integer.parseInt(value); + } catch (final NumberFormatException e) { + System.err.println("Could not parse defaultTransactionIsolation: " + value); + System.err.println("WARNING: defaultTransactionIsolation not set"); + System.err.println("using default value of database driver"); + level = PoolableConnectionFactory.UNKNOWN_TRANSACTION_ISOLATION; + } + break; } dataSource.setDefaultTransactionIsolation(level); }); @@ -306,9 +305,10 @@ final String value = properties.getProperty(PROP_CONNECTION_PROPERTIES); if (value != null) { - for (final Object key : getProperties(value).keySet()) { - final String propertyName = Objects.toString(key, null); - dataSource.addConnectionProperty(propertyName, getProperties(value).getProperty(propertyName)); + final Properties connectionProperties = getProperties(value); + for (final Object key : connectionProperties.keySet()) { + final String propertyName = Objects.toString(key); + dataSource.addConnectionProperty(propertyName, connectionProperties.getProperty(propertyName)); } } @@ -376,7 +376,14 @@ } /** - * Creates and return a new {@code BasicDataSource} instance. If no instance can be created, return + * Constructs a new instance. + */ + public BasicDataSourceFactory() { + // empty + } + + /** + * Creates and return a new {@link BasicDataSource} instance. If no instance can be created, return * {@code null} instead. * * @param obj diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceMXBean.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceMXBean.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceMXBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/BasicDataSourceMXBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -20,15 +20,15 @@ import java.sql.SQLException; /** - * Abstract factory interface for creating {@link java.sql.Connection}s. + * Abstract factory interface for creating {@link Connection}s. * * @since 2.0 */ public interface ConnectionFactory { /** - * Create a new {@link java.sql.Connection} in an implementation specific fashion. + * Create a new {@link Connection} in an implementation specific fashion. * - * @return a new {@link java.sql.Connection} + * @return a new {@link Connection} * @throws SQLException * if a database error occurs creating the connection */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactoryFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactoryFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactoryFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ConnectionFactoryFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/Constants.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/Constants.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -61,4 +61,14 @@ * @since 2.9.0 */ public static final String KEY_USER = "user"; + + /** + * Deprecated, only contains static methods. + * + * @deprecated Will be private in the next major version. + */ + @Deprecated + public Constants() { + // empty + } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DataSourceConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DataSourceConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DataSourceConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DataSourceConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -86,6 +86,8 @@ } /** + * Gets the data source.. + * * @return The data source. * @since 2.6.0 */ @@ -94,7 +96,9 @@ } /** - * @return The user name. + * Gets the user name, may be null. + * + * @return The user name, may be null. * @since 2.6.0 */ public String getUserName() { @@ -102,7 +106,9 @@ } /** - * @return The user password. + * Gets the user password, may be null. + * + * @return The user password, may be null. * @since 2.6.0 */ public char[] getUserPassword() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DataSourceMXBean.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DataSourceMXBean.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DataSourceMXBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DataSourceMXBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingCallableStatement.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingCallableStatement.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingCallableStatement.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingCallableStatement.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingConnection.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -69,8 +69,7 @@ private volatile C connection; private volatile boolean closed; - - private boolean cacheState = true; + private volatile boolean cacheState = true; private Boolean cachedAutoCommit; private Boolean cachedReadOnly; private String cachedCatalog; @@ -167,6 +166,11 @@ } } + /** + * Closes the underlying connection for {@link #close()}. + * + * @throws SQLException SQLException if a database access error occurs. + */ protected final void closeInternal() throws SQLException { try { passivate(); @@ -284,7 +288,7 @@ @Override public Statement createStatement(final int resultSetType, final int resultSetConcurrency, - final int resultSetHoldability) throws SQLException { + final int resultSetHoldability) throws SQLException { checkOpen(); try { return init(new DelegatingStatement(this, @@ -426,14 +430,14 @@ } /** - * If my underlying {@link Connection} is not a {@code DelegatingConnection}, returns it, otherwise recursively + * If my underlying {@link Connection} is not a {@link DelegatingConnection}, returns it, otherwise recursively * invokes this method on my delegate. *

          - * Hence this method will return the first delegate that is not a {@code DelegatingConnection}, or {@code null} when - * no non-{@code DelegatingConnection} delegate can be found by traversing this chain. + * Hence this method will return the first delegate that is not a {@link DelegatingConnection}, or {@code null} when + * no non-{@link DelegatingConnection} delegate can be found by traversing this chain. *

          *

          - * This method is useful when you may have nested {@code DelegatingConnection}s, and you want to make sure to obtain + * This method is useful when you may have nested {@link DelegatingConnection}s, and you want to make sure to obtain * a "genuine" {@link Connection}. *

          * @@ -541,11 +545,11 @@ } /** - * Handles the given {@code SQLException}. + * Handles the given {@link SQLException}. * * @param The throwable type. * @param e The SQLException - * @return the given {@code SQLException} + * @return the given {@link SQLException} * @since 2.7.0 */ protected T handleExceptionNoThrow(final T e) { @@ -643,10 +647,7 @@ @Override public boolean isWrapperFor(final Class iface) throws SQLException { - if (iface.isAssignableFrom(getClass())) { - return true; - } - if (iface.isAssignableFrom(connection.getClass())) { + if (iface.isAssignableFrom(getClass()) || iface.isAssignableFrom(connection.getClass())) { return true; } return connection.isWrapperFor(iface); @@ -710,7 +711,7 @@ @Override public CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency, - final int resultSetHoldability) throws SQLException { + final int resultSetHoldability) throws SQLException { checkOpen(); try { return init(new DelegatingCallableStatement(this, @@ -758,7 +759,7 @@ @Override public PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency, - final int resultSetHoldability) throws SQLException { + final int resultSetHoldability) throws SQLException { checkOpen(); try { return init(new DelegatingPreparedStatement(this, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingDatabaseMetaData.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -264,13 +264,13 @@ } /** - * If my underlying {@link ResultSet} is not a {@code DelegatingResultSet}, returns it, otherwise recursively invokes this method on my delegate. + * If my underlying {@link ResultSet} is not a {@link DelegatingResultSet}, returns it, otherwise recursively invokes this method on my delegate. *

          - * Hence this method will return the first delegate that is not a {@code DelegatingResultSet}, or {@code null} when no non-{@code DelegatingResultSet} + * Hence this method will return the first delegate that is not a {@link DelegatingResultSet}, or {@code null} when no non-{@link DelegatingResultSet} * delegate can be found by traversing this chain. *

          *

          - * This method is useful when you may have nested {@code DelegatingResultSet}s, and you want to make sure to obtain a "genuine" {@link ResultSet}. + * This method is useful when you may have nested {@link DelegatingResultSet}s, and you want to make sure to obtain a "genuine" {@link ResultSet}. *

          * * @return the innermost database meta data. @@ -580,10 +580,7 @@ @Override public boolean isWrapperFor(final Class iface) throws SQLException { - if (iface.isAssignableFrom(getClass())) { - return true; - } - if (iface.isAssignableFrom(databaseMetaData.getClass())) { + if (iface.isAssignableFrom(getClass()) || iface.isAssignableFrom(databaseMetaData.getClass())) { return true; } return databaseMetaData.isWrapperFor(iface); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingPreparedStatement.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -167,10 +167,14 @@ } } + /** + * Prepares internal states before calling {@link #passivate()}. + * + * @throws SQLException Thrown closing a traced resource or calling {@link #passivate()}. + */ protected void prepareToReturn() throws SQLException { setClosedInternal(true); removeThisTrace(getConnectionInternal()); - // The JDBC spec requires that a statement close any open // ResultSet's when it is closed. // FIXME The PreparedStatement we're wrapping should handle this for us. @@ -184,7 +188,6 @@ throw new SQLExceptionList(thrownList); } } - super.passivate(); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingResultSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -595,14 +595,14 @@ } /** - * If my underlying {@link ResultSet} is not a {@code DelegatingResultSet}, returns it, otherwise recursively + * If my underlying {@link ResultSet} is not a {@link DelegatingResultSet}, returns it, otherwise recursively * invokes this method on my delegate. *

          - * Hence this method will return the first delegate that is not a {@code DelegatingResultSet}, or {@code null} when - * no non-{@code DelegatingResultSet} delegate can be found by traversing this chain. + * Hence this method will return the first delegate that is not a {@link DelegatingResultSet}, or {@code null} when + * no non-{@link DelegatingResultSet} delegate can be found by traversing this chain. *

          *

          - * This method is useful when you may have nested {@code DelegatingResultSet}s, and you want to make sure to obtain + * This method is useful when you may have nested {@link DelegatingResultSet}s, and you want to make sure to obtain * a "genuine" {@link ResultSet}. *

          * @@ -1052,6 +1052,12 @@ } } + /** + * Handles a SQL exception by delegating to a DelegatingStatement or DelegatingConnection. + * + * @param e The exception to handle. + * @throws SQLException Throws the given exception if not handled. + */ protected void handleException(final SQLException e) throws SQLException { if (statement instanceof DelegatingStatement) { ((DelegatingStatement) statement).handleException(e); @@ -1123,10 +1129,7 @@ @Override public boolean isWrapperFor(final Class iface) throws SQLException { - if (iface.isAssignableFrom(getClass())) { - return true; - } - if (iface.isAssignableFrom(resultSet.getClass())) { + if (iface.isAssignableFrom(getClass()) || iface.isAssignableFrom(resultSet.getClass())) { return true; } return resultSet.isWrapperFor(iface); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DelegatingStatement.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -44,7 +44,7 @@ /** The connection that created me. **/ private DelegatingConnection connection; - private boolean closed; + private volatile boolean closed; /** * Create a wrapper for the Statement which traces this Statement to the Connection which created it and the code @@ -62,6 +62,8 @@ } /** + * Activates this instance by delegating to the underlying statement. + * * @throws SQLException * thrown by the delegating statement. * @since 2.4.0 made public, was protected in 2.3.0. @@ -92,6 +94,11 @@ } } + /** + * Checks whether this instance is closed and throws an exception if it is. + * + * @throws SQLException Thrown if this instance is closed. + */ protected void checkOpen() throws SQLException { if (isClosed()) { throw new SQLException(this.getClass().getName() + " with address: \"" + toString() + "\" is closed."); @@ -390,6 +397,11 @@ return getConnectionInternal(); // return the delegating connection that created this } + /** + * Gets the internal connection. + * + * @return the internal connection. + */ protected DelegatingConnection getConnectionInternal() { return connection; } @@ -438,14 +450,14 @@ } /** - * If my underlying {@link Statement} is not a {@code DelegatingStatement}, returns it, otherwise recursively + * If my underlying {@link Statement} is not a {@link DelegatingStatement}, returns it, otherwise recursively * invokes this method on my delegate. *

          - * Hence this method will return the first delegate that is not a {@code DelegatingStatement} or {@code null} when - * no non-{@code DelegatingStatement} delegate can be found by traversing this chain. + * Hence this method will return the first delegate that is not a {@link DelegatingStatement} or {@code null} when + * no non-{@link DelegatingStatement} delegate can be found by traversing this chain. *

          *

          - * This method is useful when you may have nested {@code DelegatingStatement}s, and you want to make sure to obtain + * This method is useful when you may have nested {@link DelegatingStatement}s, and you want to make sure to obtain * a "genuine" {@link Statement}. *

          * @@ -612,6 +624,12 @@ } } + /** + * Delegates the exception to the internal connection if set, otherwise rethrows it. + * + * @param e The exception to handle. + * @throws SQLException The given exception if not handled. + */ protected void handleException(final SQLException e) throws SQLException { if (connection == null) { throw e; @@ -627,6 +645,11 @@ return closed; } + /** + * Tests whether this instance is closed. + * + * @return whether this instance is closed. + */ protected boolean isClosedInternal() { return closed; } @@ -655,16 +678,15 @@ @Override public boolean isWrapperFor(final Class iface) throws SQLException { - if (iface.isAssignableFrom(getClass())) { - return true; - } - if (iface.isAssignableFrom(statement.getClass())) { + if (iface.isAssignableFrom(getClass()) || iface.isAssignableFrom(statement.getClass())) { return true; } return statement.isWrapperFor(iface); } /** + * Passivates this instance by delegating to the underlying statement. + * * @throws SQLException * thrown by the delegating statement. * @since 2.4.0 made public, was protected in 2.3.0. @@ -675,6 +697,11 @@ } } + /** + * Sets the closed internal state. + * + * @param closed whether the instance is now closed. + */ protected void setClosedInternal(final boolean closed) { this.closed = closed; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DriverConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -85,6 +85,6 @@ @Override public String toString() { return this.getClass().getName() + " [" + driver + ";" + connectionString + ";" - + Utils.cloneWithoutCredentials(properties) + "]"; + + Utils.cloneWithoutCredentials(properties) + "]"; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DriverFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DriverFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DriverFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DriverFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -60,7 +60,7 @@ } else { // Usage of DriverManager is not possible, as it does not // respect the ContextClassLoader - // N.B. This cast may cause ClassCastException which is + // This cast may cause ClassCastException which is // handled below driverToUse = (Driver) driverFromCCL.getConstructor().newInstance(); if (!driverToUse.acceptsURL(url)) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/DriverManagerConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -29,12 +29,12 @@ public class DriverManagerConnectionFactory implements ConnectionFactory { static { - // Related to DBCP-212 + // Related to DBCP-272 // Driver manager does not sync loading of drivers that use the service // provider interface. This will cause issues is multi-threaded // environments. This hack makes sure the drivers are loaded before // DBCP tries to use them. - DriverManager.getDrivers(); + DriverManager.getDrivers(); // NOPMD } private final String connectionUri; @@ -123,6 +123,8 @@ } /** + * Gets the connection URI. + * * @return The connection URI. * @since 2.6.0 */ @@ -131,7 +133,9 @@ } /** - * @return The Properties. + * Gets the Properties, may be null. + * + * @return The Properties, may be null. * @since 2.6.0 */ public Properties getProperties() { @@ -139,7 +143,9 @@ } /** - * @return The user name. + * Gets the user name, may be null. + * + * @return The user name, may be null. * @since 2.6.0 */ public String getUserName() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/Jdbc41Bridge.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/Jdbc41Bridge.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/Jdbc41Bridge.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/Jdbc41Bridge.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -485,4 +485,14 @@ } } + /** + * Deprecated, this class only contains static methods. + * + * @deprecated Constructor will be private in the next major release. + */ + @Deprecated + public Jdbc41Bridge() { + // empty + } + } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/LifetimeExceededException.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/LifetimeExceededException.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/LifetimeExceededException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/LifetimeExceededException.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/ListException.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ListException.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/ListException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ListException.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -27,6 +27,9 @@ private static final long serialVersionUID = 1L; + /** + * A list of causes. + */ private final List exceptionList; /** diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -5,7 +5,7 @@ # (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 +# https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/ObjectNameWrapper.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ObjectNameWrapper.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/ObjectNameWrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/ObjectNameWrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -42,7 +42,9 @@ return ManagementFactory.getPlatformMBeanServer(); } catch (final LinkageError | Exception e) { // ignore - JMX not available - log.debug("Failed to get platform MBeanServer", e); + if (log.isDebugEnabled()) { + log.debug("Failed to get platform MBeanServer", e); + } return null; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PStmtKey.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PStmtKey.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PStmtKey.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PStmtKey.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -17,6 +17,7 @@ package org.apache.tomcat.dbcp.dbcp2; import java.sql.Connection; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Arrays; @@ -48,17 +49,17 @@ private static final StatementBuilder StatementColumnNames = (c, k) -> c.prepareStatement(k.sql, k.columnNames); private static final StatementBuilder StatementConcurrency = (c, k) -> c.prepareStatement(k.sql, k.resultSetType.intValue(), k.resultSetConcurrency.intValue()); private static final StatementBuilder StatementHoldability = (c, k) -> c.prepareStatement(k.sql, k.resultSetType.intValue(), k.resultSetConcurrency.intValue(), - k.resultSetHoldability.intValue()); + k.resultSetHoldability.intValue()); private static final StatementBuilder StatementSQL = (c, k) -> c.prepareStatement(k.sql); private static StatementBuilder match(final StatementType statementType, final StatementBuilder prep, final StatementBuilder call) { switch (Objects.requireNonNull(statementType, "statementType")) { - case PREPARED_STATEMENT: - return prep; - case CALLABLE_STATEMENT: - return call; - default: - throw new IllegalArgumentException(statementType.toString()); + case PREPARED_STATEMENT: + return prep; + case CALLABLE_STATEMENT: + return call; + default: + throw new IllegalArgumentException(statementType.toString()); } } @@ -68,20 +69,20 @@ private final String sql; /** - * Result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or - * {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * Result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or + * {@link ResultSet#TYPE_SCROLL_SENSITIVE}. */ private final Integer resultSetType; /** - * Result set concurrency. A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * Result set concurrency. A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. */ private final Integer resultSetConcurrency; /** - * Result set holdability. One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} - * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. + * Result set holdability. One of the following {@link ResultSet} constants: {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} + * or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. */ private final Integer resultSetHoldability; @@ -96,8 +97,8 @@ private final String schema; /** - * A flag indicating whether auto-generated keys should be returned; one of {@code Statement.RETURN_GENERATED_KEYS} or - * {@code Statement.NO_GENERATED_KEYS}. + * A flag indicating whether auto-generated keys should be returned; one of {@link Statement#RETURN_GENERATED_KEYS} or + * {@link Statement#NO_GENERATED_KEYS}. */ private final Integer autoGeneratedKeys; @@ -136,10 +137,10 @@ * Constructs a key to uniquely identify a prepared statement. * * @param sql The SQL statement. - * @param resultSetType A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. - * @param resultSetConcurrency A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. + * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * @deprecated Use {@link #PStmtKey(String, String, String, int, int)}. */ @Deprecated @@ -165,7 +166,7 @@ * @param sql The SQL statement. * @param catalog The catalog. * @param autoGeneratedKeys A flag indicating whether auto-generated keys should be returned; one of - * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. + * {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}. * @deprecated Use {@link #PStmtKey(String, String, String, int)}. */ @Deprecated @@ -178,10 +179,10 @@ * * @param sql The SQL statement. * @param catalog The catalog. - * @param resultSetType A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. - * @param resultSetConcurrency A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. + * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * @deprecated Use {@link #PStmtKey(String, String, String, int, int)}. */ @Deprecated @@ -194,12 +195,12 @@ * * @param sql The SQL statement. * @param catalog The catalog. - * @param resultSetType a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. - * @param resultSetConcurrency A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE} - * @param resultSetHoldability One of the following {@code ResultSet} constants: - * {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. + * @param resultSetType a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. + * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE} + * @param resultSetHoldability One of the following {@link ResultSet} constants: + * {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. * @deprecated Use {@link #PStmtKey(String, String, String, int, int, int)}. */ @Deprecated @@ -212,12 +213,12 @@ * * @param sql The SQL statement. * @param catalog The catalog. - * @param resultSetType a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE} - * @param resultSetConcurrency A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. - * @param resultSetHoldability One of the following {@code ResultSet} constants: - * {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. + * @param resultSetType a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE} + * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. + * @param resultSetHoldability One of the following {@link ResultSet} constants: + * {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. * @param statementType The SQL statement type, prepared or callable. * @deprecated Use {@link #PStmtKey(String, String, String, int, int, int, PoolingConnection.StatementType)} */ @@ -225,7 +226,7 @@ public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability, final StatementType statementType) { this(sql, catalog, null, Integer.valueOf(resultSetType), Integer.valueOf(resultSetConcurrency), Integer.valueOf(resultSetHoldability), null, null, null, statementType, - k -> match(statementType, StatementHoldability, CallHoldability)); + k -> match(statementType, StatementHoldability, CallHoldability)); } /** @@ -233,17 +234,17 @@ * * @param sql The SQL statement. * @param catalog The catalog. - * @param resultSetType A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. - * @param resultSetConcurrency A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. + * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * @param statementType The SQL statement type, prepared or callable. * @deprecated Use {@link #PStmtKey(String, String, String, int, int, PoolingConnection.StatementType)}. */ @Deprecated public PStmtKey(final String sql, final String catalog, final int resultSetType, final int resultSetConcurrency, final StatementType statementType) { this(sql, catalog, null, Integer.valueOf(resultSetType), Integer.valueOf(resultSetConcurrency), null, null, null, null, statementType, - k -> match(statementType, StatementConcurrency, CallConcurrency)); + k -> match(statementType, StatementConcurrency, CallConcurrency)); } /** @@ -280,13 +281,13 @@ * @param catalog The catalog. * @param statementType The SQL statement type, prepared or callable. * @param autoGeneratedKeys A flag indicating whether auto-generated keys should be returned; one of - * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. + * {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}. * @deprecated Use {@link #PStmtKey(String, String, String, PoolingConnection.StatementType, Integer)} */ @Deprecated public PStmtKey(final String sql, final String catalog, final StatementType statementType, final Integer autoGeneratedKeys) { this(sql, catalog, null, null, null, null, autoGeneratedKeys, null, null, statementType, - k -> match(statementType, StatementAutoGeneratedKeys, CallSQL)); + k -> match(statementType, StatementAutoGeneratedKeys, CallSQL)); } /** @@ -308,7 +309,7 @@ * @param catalog The catalog. * @param schema The schema * @param autoGeneratedKeys A flag indicating whether auto-generated keys should be returned; one of - * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. + * {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}. * @since 2.5.0 */ public PStmtKey(final String sql, final String catalog, final String schema, final int autoGeneratedKeys) { @@ -321,10 +322,10 @@ * @param sql The SQL statement. * @param catalog The catalog. * @param schema The schema - * @param resultSetType A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. - * @param resultSetConcurrency A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. + * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. */ public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency) { this(sql, catalog, schema, resultSetType, resultSetConcurrency, StatementType.PREPARED_STATEMENT); @@ -336,12 +337,12 @@ * @param sql The SQL statement. * @param catalog The catalog. * @param schema The schema - * @param resultSetType a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. - * @param resultSetConcurrency A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE} - * @param resultSetHoldability One of the following {@code ResultSet} constants: - * {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. + * @param resultSetType a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. + * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE} + * @param resultSetHoldability One of the following {@link ResultSet} constants: + * {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. * @since 2.5.0 */ public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency, @@ -355,19 +356,19 @@ * @param sql The SQL statement. * @param catalog The catalog. * @param schema The schema. - * @param resultSetType a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE} - * @param resultSetConcurrency A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. - * @param resultSetHoldability One of the following {@code ResultSet} constants: - * {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. + * @param resultSetType a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE} + * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. + * @param resultSetHoldability One of the following {@link ResultSet} constants: + * {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. * @param statementType The SQL statement type, prepared or callable. * @since 2.5.0 */ public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability, final StatementType statementType) { this(sql, catalog, schema, Integer.valueOf(resultSetType), Integer.valueOf(resultSetConcurrency), Integer.valueOf(resultSetHoldability), null, null, null, statementType, - k -> match(statementType, StatementHoldability, CallHoldability)); + k -> match(statementType, StatementHoldability, CallHoldability)); } /** @@ -376,17 +377,17 @@ * @param sql The SQL statement. * @param catalog The catalog. * @param schema The schema. - * @param resultSetType A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. - * @param resultSetConcurrency A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * @param resultSetType A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. + * @param resultSetConcurrency A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * @param statementType The SQL statement type, prepared or callable. * @since 2.5.0 */ public PStmtKey(final String sql, final String catalog, final String schema, final int resultSetType, final int resultSetConcurrency, final StatementType statementType) { this(sql, catalog, schema, Integer.valueOf(resultSetType), Integer.valueOf(resultSetConcurrency), null, null, null, null, statementType, - k -> match(statementType, StatementConcurrency, CallConcurrency)); + k -> match(statementType, StatementConcurrency, CallConcurrency)); } /** @@ -403,8 +404,8 @@ } private PStmtKey(final String sql, final String catalog, final String schema, final Integer resultSetType, final Integer resultSetConcurrency, - final Integer resultSetHoldability, final Integer autoGeneratedKeys, final int[] columnIndexes, final String[] columnNames, - final StatementType statementType, final Function statementBuilder) { + final Integer resultSetHoldability, final Integer autoGeneratedKeys, final int[] columnIndexes, final String[] columnNames, + final StatementType statementType, final Function statementBuilder) { this.sql = Objects.requireNonNull(sql, "sql").trim(); this.catalog = catalog; this.schema = schema; @@ -420,8 +421,8 @@ // Root constructor. private PStmtKey(final String sql, final String catalog, final String schema, final Integer resultSetType, final Integer resultSetConcurrency, - final Integer resultSetHoldability, final Integer autoGeneratedKeys, final int[] columnIndexes, final String[] columnNames, - final StatementType statementType, final StatementBuilder statementBuilder) { + final Integer resultSetHoldability, final Integer autoGeneratedKeys, final int[] columnIndexes, final String[] columnNames, + final StatementType statementType, final StatementBuilder statementBuilder) { this.sql = sql; this.catalog = catalog; this.schema = schema; @@ -456,12 +457,12 @@ * @param schema The schema. * @param statementType The SQL statement type, prepared or callable. * @param autoGeneratedKeys A flag indicating whether auto-generated keys should be returned; one of - * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. + * {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}. * @since 2.5.0 */ public PStmtKey(final String sql, final String catalog, final String schema, final StatementType statementType, final Integer autoGeneratedKeys) { this(sql, catalog, schema, null, null, null, autoGeneratedKeys, null, null, statementType, - k -> match(statementType, StatementAutoGeneratedKeys, CallSQL)); + k -> match(statementType, StatementAutoGeneratedKeys, CallSQL)); } /** @@ -516,35 +517,16 @@ if (this == obj) { return true; } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { + if (obj == null || getClass() != obj.getClass()) { return false; } final PStmtKey other = (PStmtKey) obj; - if (!Objects.equals(autoGeneratedKeys, other.autoGeneratedKeys)) { - return false; - } - if (!Objects.equals(catalog, other.catalog)) { - return false; - } - if (!Arrays.equals(columnIndexes, other.columnIndexes)) { - return false; - } - if (!Arrays.equals(columnNames, other.columnNames)) { - return false; - } - if (!Objects.equals(resultSetConcurrency, other.resultSetConcurrency)) { - return false; - } - if (!Objects.equals(resultSetHoldability, other.resultSetHoldability)) { - return false; - } - if (!Objects.equals(resultSetType, other.resultSetType)) { + if (!Objects.equals(autoGeneratedKeys, other.autoGeneratedKeys) || !Objects.equals(catalog, other.catalog) + || !Arrays.equals(columnIndexes, other.columnIndexes) || !Arrays.equals(columnNames, other.columnNames)) { return false; } - if (!Objects.equals(schema, other.schema)) { + if (!Objects.equals(resultSetConcurrency, other.resultSetConcurrency) || !Objects.equals(resultSetHoldability, other.resultSetHoldability) + || !Objects.equals(resultSetType, other.resultSetType) || !Objects.equals(schema, other.schema)) { return false; } if (!Objects.equals(sql, other.sql)) { @@ -554,8 +536,8 @@ } /** - * Gets a flag indicating whether auto-generated keys should be returned; one of {@code Statement.RETURN_GENERATED_KEYS} - * or {@code Statement.NO_GENERATED_KEYS}. + * Gets a flag indicating whether auto-generated keys should be returned; one of {@link Statement#RETURN_GENERATED_KEYS} + * or {@link Statement#NO_GENERATED_KEYS}. * * @return a flag indicating whether auto-generated keys should be returned. */ @@ -591,8 +573,8 @@ } /** - * Gets the result set concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * Gets the result set concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * * @return The result set concurrency type. */ @@ -601,8 +583,8 @@ } /** - * Gets the result set holdability, one of the following {@code ResultSet} constants: - * {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. + * Gets the result set holdability, one of the following {@link ResultSet} constants: + * {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. * * @return The result set holdability. */ @@ -611,8 +593,8 @@ } /** - * Gets the result set type, one of {@code ResultSet.TYPE_FORWARD_ONLY}, {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or - * {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * Gets the result set type, one of {@link ResultSet#TYPE_FORWARD_ONLY}, {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or + * {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * * @return the result set type. */ @@ -650,7 +632,7 @@ @Override public int hashCode() { return Objects.hash(autoGeneratedKeys, catalog, Integer.valueOf(Arrays.hashCode(columnIndexes)), Integer.valueOf(Arrays.hashCode(columnNames)), - resultSetConcurrency, resultSetHoldability, resultSetType, schema, sql, statementType); + resultSetConcurrency, resultSetHoldability, resultSetType, schema, sql, statementType); } @Override diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolableCallableStatement.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableCallableStatement.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolableCallableStatement.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableCallableStatement.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnection.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -24,6 +24,7 @@ import java.time.Duration; import java.util.Collection; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -59,8 +60,9 @@ private final ObjectNameWrapper jmxObjectName; - // Use a prepared statement for validation, retaining the last used SQL to - // check if the validation query has changed. + /** + * Use a prepared statement for validation, retaining the last used SQL to check if the validation query has changed. + */ private PreparedStatement validationPreparedStatement; private String lastValidationSql; @@ -68,7 +70,7 @@ * Indicate that unrecoverable SQLException was thrown when using this connection. Such a connection should be * considered broken and not pass validation in the future. */ - private boolean fatalSqlExceptionThrown; + private final AtomicBoolean fatalSqlExceptionThrown = new AtomicBoolean(); /** * SQL State codes considered to signal fatal conditions. Overrides the defaults in @@ -83,13 +85,14 @@ */ private final Collection disconnectionIgnoreSqlCodes; - /** Whether or not to fast fail validation after fatal connection errors */ private final boolean fastFailValidation; private final Lock lock = new ReentrantLock(); /** + * Constructs a new instance. + * * @param conn * my underlying connection * @param pool @@ -103,6 +106,8 @@ } /** + * Constructs a new instance. + * * @param conn * my underlying connection * @param pool @@ -237,6 +242,8 @@ } /** + * Gets the disconnection SQL codes. + * * @return The disconnection SQL codes. * @since 2.6.0 */ @@ -254,7 +261,7 @@ @Override protected void handleException(final SQLException e) throws SQLException { - fatalSqlExceptionThrown |= isFatalException(e); + fatalSqlExceptionThrown.compareAndSet(false, isFatalException(e)); super.handleException(e); } @@ -264,6 +271,7 @@ * This method should not be used by a client to determine whether or not a connection should be return to the * connection pool (by calling {@link #close()}). Clients should always attempt to return a connection to the pool * once it is no longer required. + *

          */ @Override public boolean isClosed() throws SQLException { @@ -303,13 +311,15 @@ return false; } fatalException = disconnectionSqlCodes == null - ? sqlState.startsWith(Utils.DISCONNECTION_SQL_CODE_PREFIX) || Utils.isDisconnectionSqlCode(sqlState) - : disconnectionSqlCodes.contains(sqlState); + ? sqlState.startsWith(Utils.DISCONNECTION_SQL_CODE_PREFIX) || Utils.isDisconnectionSqlCode(sqlState) + : disconnectionSqlCodes.contains(sqlState); } return fatalException; } /** + * Tests whether to fail-fast. + * * @return Whether to fail-fast. * @since 2.6.0 */ @@ -384,11 +394,11 @@ * Validates the connection, using the following algorithm: *
            *
          1. If {@code fastFailValidation} (constructor argument) is {@code true} and this connection has previously - * thrown a fatal disconnection exception, a {@code SQLException} is thrown.
          2. + * thrown a fatal disconnection exception, a {@link SQLException} is thrown. *
          3. If {@code sql} is null, the driver's #{@link Connection#isValid(int) isValid(timeout)} is called. If it - * returns {@code false}, {@code SQLException} is thrown; otherwise, this method returns successfully.
          4. - *
          5. If {@code sql} is not null, it is executed as a query and if the resulting {@code ResultSet} contains at - * least one row, this method returns successfully. If not, {@code SQLException} is thrown.
          6. + * returns {@code false}, {@link SQLException} is thrown; otherwise, this method returns successfully. + *
          7. If {@code sql} is not null, it is executed as a query and if the resulting {@link ResultSet} contains at + * least one row, this method returns successfully. If not, {@link SQLException} is thrown.
          8. *
          * * @param sql @@ -400,7 +410,7 @@ * @since 2.10.0 */ public void validate(final String sql, Duration timeoutDuration) throws SQLException { - if (fastFailValidation && fatalSqlExceptionThrown) { + if (fastFailValidation && fatalSqlExceptionThrown.get()) { throw new SQLException(Utils.getMessage("poolableConnection.validate.fastFail")); } @@ -438,11 +448,11 @@ * Validates the connection, using the following algorithm: *
            *
          1. If {@code fastFailValidation} (constructor argument) is {@code true} and this connection has previously - * thrown a fatal disconnection exception, a {@code SQLException} is thrown.
          2. + * thrown a fatal disconnection exception, a {@link SQLException} is thrown. *
          3. If {@code sql} is null, the driver's #{@link Connection#isValid(int) isValid(timeout)} is called. If it - * returns {@code false}, {@code SQLException} is thrown; otherwise, this method returns successfully.
          4. - *
          5. If {@code sql} is not null, it is executed as a query and if the resulting {@code ResultSet} contains at - * least one row, this method returns successfully. If not, {@code SQLException} is thrown.
          6. + * returns {@code false}, {@link SQLException} is thrown; otherwise, this method returns successfully. + *
          7. If {@code sql} is not null, it is executed as a query and if the resulting {@link ResultSet} contains at + * least one row, this method returns successfully. If not, {@link SQLException} is thrown.
          8. *
          * * @param sql diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -66,7 +66,7 @@ private Collection disconnectionIgnoreSqlCodes; - private boolean fastFailValidation = true; + private volatile boolean fastFailValidation = true; private volatile ObjectPool pool; @@ -74,23 +74,23 @@ private Boolean defaultAutoCommit; - private boolean autoCommitOnReturn = true; + private volatile boolean autoCommitOnReturn = true; - private boolean rollbackOnReturn = true; + private volatile boolean rollbackOnReturn = true; - private int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION; + private volatile int defaultTransactionIsolation = UNKNOWN_TRANSACTION_ISOLATION; private String defaultCatalog; private String defaultSchema; - private boolean cacheState; + private volatile boolean cacheState; - private boolean poolStatements; + private volatile boolean poolStatements; - private boolean clearStatementPoolOnReturn; + private volatile boolean clearStatementPoolOnReturn; - private int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY; + private volatile int maxOpenPreparedStatements = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY; private Duration maxConnDuration = Duration.ofMillis(-1); @@ -99,7 +99,7 @@ private Duration defaultQueryTimeoutDuration; /** - * Creates a new {@code PoolableConnectionFactory}. + * Creates a new {@link PoolableConnectionFactory}. * * @param connFactory * the {@link ConnectionFactory} from which to obtain base {@link Connection}s @@ -113,29 +113,26 @@ @Override public void activateObject(final PooledObject p) throws SQLException { - validateLifetime(p); - - final PoolableConnection pConnection = p.getObject(); - pConnection.activate(); - - if (defaultAutoCommit != null && pConnection.getAutoCommit() != defaultAutoCommit.booleanValue()) { - pConnection.setAutoCommit(defaultAutoCommit.booleanValue()); + final PoolableConnection poolableConnection = p.getObject(); + poolableConnection.activate(); + if (defaultAutoCommit != null && poolableConnection.getAutoCommit() != defaultAutoCommit.booleanValue()) { + poolableConnection.setAutoCommit(defaultAutoCommit.booleanValue()); } if (defaultTransactionIsolation != UNKNOWN_TRANSACTION_ISOLATION - && pConnection.getTransactionIsolation() != defaultTransactionIsolation) { - pConnection.setTransactionIsolation(defaultTransactionIsolation); + && poolableConnection.getTransactionIsolation() != defaultTransactionIsolation) { + poolableConnection.setTransactionIsolation(defaultTransactionIsolation); } - if (defaultReadOnly != null && pConnection.isReadOnly() != defaultReadOnly.booleanValue()) { - pConnection.setReadOnly(defaultReadOnly.booleanValue()); + if (defaultReadOnly != null && poolableConnection.isReadOnly() != defaultReadOnly.booleanValue()) { + poolableConnection.setReadOnly(defaultReadOnly.booleanValue()); } - if (defaultCatalog != null && !defaultCatalog.equals(pConnection.getCatalog())) { - pConnection.setCatalog(defaultCatalog); + if (defaultCatalog != null && !defaultCatalog.equals(poolableConnection.getCatalog())) { + poolableConnection.setCatalog(defaultCatalog); } - if (defaultSchema != null && !defaultSchema.equals(Jdbc41Bridge.getSchema(pConnection))) { - Jdbc41Bridge.setSchema(pConnection, defaultSchema); + if (defaultSchema != null && !defaultSchema.equals(Jdbc41Bridge.getSchema(poolableConnection))) { + Jdbc41Bridge.setSchema(poolableConnection, defaultSchema); } - pConnection.setDefaultQueryTimeout(defaultQueryTimeoutDuration); + poolableConnection.setDefaultQueryTimeout(defaultQueryTimeoutDuration); } @Override @@ -699,8 +696,8 @@ * @param disconnectionSqlCodes * The disconnection SQL codes. * @see #getDisconnectionSqlCodes() - * @since 2.1 * @throws IllegalArgumentException if any SQL state codes overlap with those in {@link #disconnectionIgnoreSqlCodes}. + * @since 2.1 */ public void setDisconnectionSqlCodes(final Collection disconnectionSqlCodes) { Utils.checkSqlCodes(disconnectionSqlCodes, this.disconnectionIgnoreSqlCodes); @@ -718,9 +715,10 @@ } /** + * Sets whether connections created by this factory will fast fail validation. + * + * @param fastFailValidation true means connections created by this factory will fast fail validation * @see #isFastFailValidation() - * @param fastFailValidation - * true means connections created by this factory will fast fail validation * @since 2.1 */ public void setFastFailValidation(final boolean fastFailValidation) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionMXBean.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionMXBean.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionMXBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolableConnectionMXBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolablePreparedStatement.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolablePreparedStatement.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolablePreparedStatement.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolablePreparedStatement.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolingConnection.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -74,9 +74,9 @@ } /** Pool of {@link PreparedStatement}s. and {@link CallableStatement}s */ - private KeyedObjectPool pStmtPool; + private KeyedObjectPool stmtPool; - private boolean clearStatementPoolOnReturn; + private volatile boolean clearStatementPoolOnReturn; /** * Constructs a new instance. @@ -109,9 +109,9 @@ @Override public synchronized void close() throws SQLException { try { - if (null != pStmtPool) { - final KeyedObjectPool oldPool = pStmtPool; - pStmtPool = null; + if (null != stmtPool) { + final KeyedObjectPool oldPool = stmtPool; + stmtPool = null; try { oldPool.close(); } catch (final RuntimeException e) { @@ -140,9 +140,9 @@ * @since 2.8.0 */ public void connectionReturnedToPool() throws SQLException { - if (pStmtPool != null && clearStatementPoolOnReturn) { + if (stmtPool != null && clearStatementPoolOnReturn) { try { - pStmtPool.clear(); + stmtPool.clear(); } catch (final Exception e) { throw new SQLException("Error clearing statement pool", e); } @@ -168,7 +168,7 @@ * the SQL string used to define the statement * @param autoGeneratedKeys * A flag indicating whether auto-generated keys should be returned; one of - * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. + * {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}. * * @return the PStmtKey created for the given arguments. */ @@ -341,7 +341,7 @@ * @since 2.8.0 */ public KeyedObjectPool getStatementPool() { - return pStmtPool; + return stmtPool; } /** @@ -361,11 +361,11 @@ if (key.getStmtType() == StatementType.PREPARED_STATEMENT) { final PreparedStatement statement = (PreparedStatement) key.createStatement(getDelegate()); @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this - final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool, this); + final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, stmtPool, this); return new DefaultPooledObject<>(pps); } final CallableStatement statement = (CallableStatement) key.createStatement(getDelegate()); - final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool, this); + final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, stmtPool, this); return new DefaultPooledObject<>(pcs); } @@ -474,11 +474,11 @@ * Wraps an underlying exception. */ private PreparedStatement prepareStatement(final PStmtKey key) throws SQLException { - if (null == pStmtPool) { + if (null == stmtPool) { throw new SQLException("Statement pool is null - closed or invalid PoolingConnection."); } try { - return pStmtPool.borrowObject(key); + return stmtPool.borrowObject(key); } catch (final NoSuchElementException e) { throw new SQLException("MaxOpenPreparedStatements limit reached", e); } catch (final RuntimeException e) { @@ -509,7 +509,7 @@ * the SQL string used to define the PreparedStatement * @param autoGeneratedKeys * A flag indicating whether auto-generated keys should be returned; one of - * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. + * {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}. * @return a {@link PoolablePreparedStatement} * @throws SQLException * Wraps an underlying exception. @@ -610,19 +610,19 @@ * the prepared statement pool. */ public void setStatementPool(final KeyedObjectPool pool) { - pStmtPool = pool; + stmtPool = pool; } @Override public synchronized String toString() { - if (pStmtPool instanceof GenericKeyedObjectPool) { + if (stmtPool instanceof GenericKeyedObjectPool) { // DBCP-596 PoolingConnection.toString() causes StackOverflowError - final GenericKeyedObjectPool gkop = (GenericKeyedObjectPool) pStmtPool; + final GenericKeyedObjectPool gkop = (GenericKeyedObjectPool) stmtPool; if (gkop.getFactory() == this) { - return "PoolingConnection: " + pStmtPool.getClass() + "@" + System.identityHashCode(pStmtPool); + return "PoolingConnection: " + stmtPool.getClass() + "@" + System.identityHashCode(stmtPool); } } - return "PoolingConnection: " + Objects.toString(pStmtPool); + return "PoolingConnection: " + Objects.toString(stmtPool); } /** diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolingDataSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -129,7 +129,7 @@ } /** - * Returns a {@link java.sql.Connection} from my pool, according to the contract specified by + * Returns a {@link Connection} from my pool, according to the contract specified by * {@link ObjectPool#borrowObject}. */ @Override @@ -164,7 +164,6 @@ throw new UnsupportedOperationException(); } - /** * Throws {@link UnsupportedOperationException}. * diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolingDriver.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolingDriver.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/PoolingDriver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/PoolingDriver.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/SQLExceptionList.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/SQLExceptionList.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/SQLExceptionList.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/SQLExceptionList.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -31,6 +31,10 @@ public class SQLExceptionList extends SQLException { private static final long serialVersionUID = 1L; + + /** + * The list of causes. + */ private final List causeList; /** diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/SwallowedExceptionLogger.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/SwallowedExceptionLogger.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/SwallowedExceptionLogger.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/SwallowedExceptionLogger.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/Utils.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/Utils.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/Utils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/Utils.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -39,7 +39,7 @@ public final class Utils { private static final ResourceBundle messages = ResourceBundle - .getBundle(Utils.class.getPackage().getName() + ".LocalStrings"); + .getBundle(Utils.class.getPackage().getName() + ".LocalStrings"); /** * Whether the security manager is enabled. @@ -50,7 +50,7 @@ public static final boolean IS_SECURITY_ENABLED = isSecurityEnabled(); /** - * Any SQL_STATE starting with this value is considered a fatal disconnect. + * Any SQL State starting with this value is considered a fatal disconnect. */ public static final String DISCONNECTION_SQL_CODE_PREFIX = "08"; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/ConnectionImpl.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/ConnectionImpl.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/ConnectionImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/ConnectionImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -19,6 +19,7 @@ import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import org.apache.tomcat.dbcp.dbcp2.DelegatingCallableStatement; @@ -26,9 +27,9 @@ import org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement; /** - * This class is the {@code Connection} that will be returned from - * {@code PooledConnectionImpl.getConnection()}. Most methods are wrappers around the JDBC 1.x - * {@code Connection}. A few exceptions include preparedStatement and close. In accordance with the JDBC + * This class is the {@link Connection} that will be returned from + * {@link PooledConnectionImpl#getConnection()}. Most methods are wrappers around the JDBC 1.x + * {@link Connection}. A few exceptions include preparedStatement and close. In accordance with the JDBC * specification this Connection cannot be used after closed() is called. Any further usage will result in an * SQLException. *

          @@ -131,13 +132,13 @@ } /** - * If pooling of {@code CallableStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may - * be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}. + * If pooling of {@link CallableStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may + * be returned, otherwise delegate to the wrapped JDBC 1.x {@link Connection}. * * @param sql * an SQL statement that may contain one or more '?' parameter placeholders. Typically, this statement is * specified using JDBC call escape syntax. - * @return a default {@code CallableStatement} object containing the pre-compiled SQL statement. + * @return a default {@link CallableStatement} object containing the pre-compiled SQL statement. * @throws SQLException * Thrown if a database access error occurs or this method is called on a closed connection. * @since 2.4.0 @@ -154,23 +155,23 @@ } /** - * If pooling of {@code CallableStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may - * be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}. + * If pooling of {@link CallableStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may + * be returned, otherwise delegate to the wrapped JDBC 1.x {@link Connection}. * * @param sql - * a {@code String} object that is the SQL statement to be sent to the database; may contain on or + * a {@link String} object that is the SQL statement to be sent to the database; may contain on or * more '?' parameters. * @param resultSetType - * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. - * @return a {@code CallableStatement} object containing the pre-compiled SQL statement that will produce - * {@code ResultSet} objects with the given type and concurrency. + * a concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. + * @return a {@link CallableStatement} object containing the pre-compiled SQL statement that will produce + * {@link ResultSet} objects with the given type and concurrency. * @throws SQLException * Thrown if a database access error occurs, this method is called on a closed connection or the given - * parameters are not {@code ResultSet} constants indicating type and concurrency. + * parameters are not {@link ResultSet} constants indicating type and concurrency. * @since 2.4.0 */ @Override @@ -187,26 +188,26 @@ } /** - * If pooling of {@code CallableStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may - * be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}. + * If pooling of {@link CallableStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may + * be returned, otherwise delegate to the wrapped JDBC 1.x {@link Connection}. * * @param sql - * a {@code String} object that is the SQL statement to be sent to the database; may contain on or + * a {@link String} object that is the SQL statement to be sent to the database; may contain on or * more '?' parameters. * @param resultSetType - * one of the following {@code ResultSet} constants: {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * one of the following {@link ResultSet} constants: {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * one of the following {@code ResultSet} constants: {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * one of the following {@link ResultSet} constants: {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * @param resultSetHoldability - * one of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} - * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. - * @return a new {@code CallableStatement} object, containing the pre-compiled SQL statement, that will - * generate {@code ResultSet} objects with the given type, concurrency, and holdability. + * one of the following {@link ResultSet} constants: {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} + * or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. + * @return a new {@link CallableStatement} object, containing the pre-compiled SQL statement, that will + * generate {@link ResultSet} objects with the given type, concurrency, and holdability. * @throws SQLException * Thrown if a database access error occurs, this method is called on a closed connection or the given - * parameters are not {@code ResultSet} constants indicating type, concurrency, and holdability. + * parameters are not {@link ResultSet} constants indicating type, concurrency, and holdability. * @since 2.4.0 */ @Override @@ -223,8 +224,8 @@ } /** - * If pooling of {@code PreparedStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may - * be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}. + * If pooling of {@link PreparedStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may + * be returned, otherwise delegate to the wrapped JDBC 1.x {@link Connection}. * * @param sql * SQL statement to be prepared @@ -259,8 +260,8 @@ // /** - * If pooling of {@code PreparedStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may - * be returned, otherwise delegate to the wrapped JDBC 1.x {@link java.sql.Connection}. + * If pooling of {@link PreparedStatement}s is turned on in the {@link DriverAdapterCPDS}, a pooled object may + * be returned, otherwise delegate to the wrapped JDBC 1.x {@link Connection}. * * @throws SQLException * if this connection is closed or an error occurs in the wrapped connection. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/DriverAdapterCPDS.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -18,7 +18,9 @@ import java.io.PrintWriter; import java.io.Serializable; +import java.sql.Connection; import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.time.Duration; @@ -35,6 +37,7 @@ import javax.naming.StringRefAddr; import javax.naming.spi.ObjectFactory; import javax.sql.ConnectionPoolDataSource; +import javax.sql.DataSource; import javax.sql.PooledConnection; import org.apache.tomcat.dbcp.dbcp2.Constants; @@ -49,22 +52,22 @@ /** *

          * An adapter for JDBC drivers that do not include an implementation of {@link javax.sql.ConnectionPoolDataSource}, but - * still include a {@link java.sql.DriverManager} implementation. {@code ConnectionPoolDataSource}s are not used - * within general applications. They are used by {@code DataSource} implementations that pool - * {@code Connection}s, such as {@link org.apache.tomcat.dbcp.dbcp2.datasources.SharedPoolDataSource}. A J2EE container - * will normally provide some method of initializing the {@code ConnectionPoolDataSource} whose attributes are + * still include a {@link java.sql.DriverManager} implementation. {@link ConnectionPoolDataSource}s are not used + * within general applications. They are used by {@link DataSource} implementations that pool + * {@link Connection}s, such as {@link org.apache.tomcat.dbcp.dbcp2.datasources.SharedPoolDataSource}. A J2EE container + * will normally provide some method of initializing the {@link ConnectionPoolDataSource} whose attributes are * presented as bean getters/setters and then deploying it via JNDI. It is then available as a source of physical - * connections to the database, when the pooling {@code DataSource} needs to create a new physical connection. + * connections to the database, when the pooling {link DataSource} needs to create a new physical connection. *

          *

          * Although normally used within a JNDI environment, the DriverAdapterCPDS can be instantiated and initialized as any - * bean and then attached directly to a pooling {@code DataSource}. {@code Jdbc2PoolDataSource} can use the - * {@code ConnectionPoolDataSource} with or without the use of JNDI. + * bean and then attached directly to a pooling {link DataSource}. {@code Jdbc2PoolDataSource} can use the + * {link ConnectionPoolDataSource} with or without the use of JNDI. *

          *

          - * The DriverAdapterCPDS also provides {@code PreparedStatement} pooling which is not generally available in jdbc2 - * {@code ConnectionPoolDataSource} implementation, but is addressed within the JDBC 3 specification. The - * {@code PreparedStatement} pool in DriverAdapterCPDS has been in the DBCP package for some time, but it has not + * The DriverAdapterCPDS also provides {@link PreparedStatement} pooling which is not generally available in jdbc2 + * {@link ConnectionPoolDataSource} implementation, but is addressed within the JDBC 3 specification. The + * {@link PreparedStatement} pool in DriverAdapterCPDS has been in the DBCP package for some time, but it has not * undergone extensive testing in the configuration used here. It should be considered experimental and can be toggled * with the poolPreparedStatements attribute. *

          @@ -110,7 +113,7 @@ static { // Attempt to prevent deadlocks - see DBCP-272 - DriverManager.getDrivers(); + DriverManager.getDrivers(); // NOPMD } /** Description */ @@ -129,19 +132,28 @@ private String driver; /** Login TimeOut in seconds */ - private int loginTimeout; + private volatile int loginTimeout; /** Log stream. NOT USED */ private transient PrintWriter logWriter; - // PreparedStatement pool properties - private boolean poolPreparedStatements; - private int maxIdle = 10; + /** PreparedStatement pool property defaults to false. */ + private volatile boolean poolPreparedStatements; + + /** PreparedStatement pool property defaults to 10. */ + private volatile int maxIdle = 10; + + /** PreparedStatement pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_DURATION_BETWEEN_EVICTION_RUNS}. */ private Duration durationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS; - private int numTestsPerEvictionRun = -1; + + /** PreparedStatement pool property defaults to -1. */ + private volatile int numTestsPerEvictionRun = -1; + + /** PreparedStatement pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_MIN_EVICTABLE_IDLE_DURATION}. */ private Duration minEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION; - private int maxPreparedStatements = -1; + /** Maximum number of prepared statements, defaults to -1, meaning no limit. */ + private volatile int maxPreparedStatements = -1; /** Whether or not getConnection has been called */ private volatile boolean getConnectionCalled; @@ -248,7 +260,7 @@ /** * Gets the maximum number of prepared statements. * - * @return maxPrepartedStatements value + * @return maxPrepartedStatements, defaults to -1, meaning no limit. */ public int getMaxPreparedStatements() { return maxPreparedStatements; @@ -537,7 +549,7 @@ } /** - * Whether to toggle the pooling of {@code PreparedStatement}s + * Tests whether to toggle the pooling of {@link PreparedStatement}s * * @return value of poolPreparedStatements. */ @@ -656,7 +668,7 @@ /** * Sets the maximum number of prepared statements. * - * @param maxPreparedStatements the new maximum number of prepared statements + * @param maxPreparedStatements the new maximum number of prepared statements, <= 0 means no limit. */ public void setMaxPreparedStatements(final int maxPreparedStatements) { this.maxPreparedStatements = maxPreparedStatements; @@ -736,7 +748,7 @@ } /** - * Whether to toggle the pooling of {@code PreparedStatement}s + * Sets whether to toggle the pooling of {@link PreparedStatement}s * * @param poolPreparedStatements true to pool statements. * @throws IllegalStateException if {@link #getPooledConnection()} has been called diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PStmtKeyCPDS.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PStmtKeyCPDS.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PStmtKeyCPDS.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PStmtKeyCPDS.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -17,6 +17,8 @@ package org.apache.tomcat.dbcp.dbcp2.cpdsadapter; import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; import org.apache.tomcat.dbcp.dbcp2.PStmtKey; @@ -46,7 +48,7 @@ * The SQL statement. * @param autoGeneratedKeys * A flag indicating whether auto-generated keys should be returned; one of - * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. + * {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}. */ public PStmtKeyCPDS(final String sql, final int autoGeneratedKeys) { super(sql, null, autoGeneratedKeys); @@ -58,11 +60,11 @@ * @param sql * The SQL statement. * @param resultSetType - * A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. */ public PStmtKeyCPDS(final String sql, final int resultSetType, final int resultSetConcurrency) { super(sql, resultSetType, resultSetConcurrency); @@ -74,14 +76,14 @@ * @param sql * The SQL statement. * @param resultSetType - * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE} + * A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE} * @param resultSetHoldability - * One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} - * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. + * One of the following {@link ResultSet} constants: {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} + * or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. */ public PStmtKeyCPDS(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/PooledConnectionImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -19,6 +19,7 @@ import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; @@ -81,10 +82,10 @@ /** * Flag set to true, once {@link #close()} is called. */ - private boolean closed; + private volatile boolean closed; /** My pool of {@link PreparedStatement}s. */ - private KeyedObjectPool pStmtPool; + private KeyedObjectPool stmtPool; /** * Controls access to the underlying connection. @@ -146,8 +147,8 @@ } /** - * Closes the physical connection and marks this {@code PooledConnection} so that it may not be used to - * generate any more logical {@code Connection}s. + * Closes the physical connection and marks this {@link PooledConnection} so that it may not be used to + * generate any more logical {@link Connection}s. * * @throws SQLException * Thrown when an error occurs or the connection is already closed. @@ -157,11 +158,11 @@ assertOpen(); closed = true; try { - if (pStmtPool != null) { + if (stmtPool != null) { try { - pStmtPool.close(); + stmtPool.close(); } finally { - pStmtPool = null; + stmtPool = null; } } } catch (final RuntimeException e) { @@ -195,7 +196,7 @@ * The SQL statement. * @param autoGeneratedKeys * A flag indicating whether auto-generated keys should be returned; one of - * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. + * {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}. * @return a key to uniquely identify a prepared statement. */ protected PStmtKey createKey(final String sql, final int autoGeneratedKeys) { @@ -208,11 +209,11 @@ * @param sql * The SQL statement. * @param resultSetType - * A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * @return a key to uniquely identify a prepared statement. */ protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency) { @@ -225,14 +226,14 @@ * @param sql * The SQL statement. * @param resultSetType - * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE} + * A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE} * @param resultSetHoldability - * One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} - * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. + * One of the following {@link ResultSet} constants: {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} + * or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. * @return a key to uniquely identify a prepared statement. */ protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) { @@ -245,21 +246,21 @@ * @param sql * The SQL statement. * @param resultSetType - * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE} + * a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE} * @param resultSetConcurrency - * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * @param resultSetHoldability - * One of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} - * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. + * One of the following {@link ResultSet} constants: {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} + * or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. * @param statementType * The SQL statement type, prepared or callable. * @return a key to uniquely identify a prepared statement. * @since 2.4.0 */ protected PStmtKey createKey(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability, - final StatementType statementType) { + final StatementType statementType) { return new PStmtKey(sql, getCatalogOrNull(), getSchemaOrNull(), resultSetType, resultSetConcurrency, resultSetHoldability, statementType); } @@ -269,11 +270,11 @@ * @param sql * The SQL statement. * @param resultSetType - * A result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * A result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * A concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * A concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * @param statementType * The SQL statement type, prepared or callable. * @return a key to uniquely identify a prepared statement. @@ -424,13 +425,13 @@ if (key.getStmtType() == StatementType.PREPARED_STATEMENT) { final PreparedStatement statement = (PreparedStatement) key.createStatement(connection); @SuppressWarnings({"rawtypes", "unchecked" }) // Unable to find way to avoid this - final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, pStmtPool, + final PoolablePreparedStatement pps = new PoolablePreparedStatement(statement, key, stmtPool, delegatingConnection); return new DefaultPooledObject<>(pps); } final CallableStatement statement = (CallableStatement) key.createStatement(connection); @SuppressWarnings("unchecked") - final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, pStmtPool, + final PoolableCallableStatement pcs = new PoolableCallableStatement(statement, key, stmtPool, (DelegatingConnection) delegatingConnection); return new DefaultPooledObject<>(pcs); } @@ -466,17 +467,17 @@ * @param sql * an SQL statement that may contain one or more '?' parameter placeholders. Typically, this statement is * specified using JDBC call escape syntax. - * @return a default {@code CallableStatement} object containing the pre-compiled SQL statement. + * @return a default {@link CallableStatement} object containing the pre-compiled SQL statement. * @throws SQLException * Thrown if a database access error occurs or this method is called on a closed connection. * @since 2.4.0 */ CallableStatement prepareCall(final String sql) throws SQLException { - if (pStmtPool == null) { + if (stmtPool == null) { return getRawConnection().prepareCall(sql); } try { - return (CallableStatement) pStmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT)); + return (CallableStatement) stmtPool.borrowObject(createKey(sql, StatementType.CALLABLE_STATEMENT)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -488,28 +489,28 @@ * Creates or obtains a {@link CallableStatement} from my pool. * * @param sql - * a {@code String} object that is the SQL statement to be sent to the database; may contain on or + * a {@link String} object that is the SQL statement to be sent to the database; may contain on or * more '?' parameters. * @param resultSetType - * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. - * @return a {@code CallableStatement} object containing the pre-compiled SQL statement that will produce - * {@code ResultSet} objects with the given type and concurrency. + * a concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. + * @return a {@link CallableStatement} object containing the pre-compiled SQL statement that will produce + * {@link ResultSet} objects with the given type and concurrency. * @throws SQLException * Thrown if a database access error occurs, this method is called on a closed connection or the given - * parameters are not {@code ResultSet} constants indicating type and concurrency. + * parameters are not {@link ResultSet} constants indicating type and concurrency. * @since 2.4.0 */ CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException { - if (pStmtPool == null) { + if (stmtPool == null) { return getRawConnection().prepareCall(sql, resultSetType, resultSetConcurrency); } try { - return (CallableStatement) pStmtPool.borrowObject( + return (CallableStatement) stmtPool.borrowObject( createKey(sql, resultSetType, resultSetConcurrency, StatementType.CALLABLE_STATEMENT)); } catch (final RuntimeException e) { throw e; @@ -522,31 +523,31 @@ * Creates or obtains a {@link CallableStatement} from my pool. * * @param sql - * a {@code String} object that is the SQL statement to be sent to the database; may contain on or + * a {@link String} object that is the SQL statement to be sent to the database; may contain on or * more '?' parameters. * @param resultSetType - * one of the following {@code ResultSet} constants: {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * one of the following {@link ResultSet} constants: {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * one of the following {@code ResultSet} constants: {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * one of the following {@link ResultSet} constants: {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * @param resultSetHoldability - * one of the following {@code ResultSet} constants: {@code ResultSet.HOLD_CURSORS_OVER_COMMIT} - * or {@code ResultSet.CLOSE_CURSORS_AT_COMMIT}. - * @return a new {@code CallableStatement} object, containing the pre-compiled SQL statement, that will - * generate {@code ResultSet} objects with the given type, concurrency, and holdability. + * one of the following {@link ResultSet} constants: {@link ResultSet#HOLD_CURSORS_OVER_COMMIT} + * or {@link ResultSet#CLOSE_CURSORS_AT_COMMIT}. + * @return a new {@link CallableStatement} object, containing the pre-compiled SQL statement, that will + * generate {@link ResultSet} objects with the given type, concurrency, and holdability. * @throws SQLException * Thrown if a database access error occurs, this method is called on a closed connection or the given - * parameters are not {@code ResultSet} constants indicating type, concurrency, and holdability. + * parameters are not {@link ResultSet} constants indicating type, concurrency, and holdability. * @since 2.4.0 */ CallableStatement prepareCall(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) throws SQLException { - if (pStmtPool == null) { + if (stmtPool == null) { return getRawConnection().prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); } try { - return (CallableStatement) pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, + return (CallableStatement) stmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability, StatementType.CALLABLE_STATEMENT)); } catch (final RuntimeException e) { throw e; @@ -564,11 +565,11 @@ * the borrow failed. */ PreparedStatement prepareStatement(final String sql) throws SQLException { - if (pStmtPool == null) { + if (stmtPool == null) { return getRawConnection().prepareStatement(sql); } try { - return pStmtPool.borrowObject(createKey(sql)); + return stmtPool.borrowObject(createKey(sql)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -583,18 +584,18 @@ * an SQL statement that may contain one or more '?' IN parameter placeholders. * @param autoGeneratedKeys * a flag indicating whether auto-generated keys should be returned; one of - * {@code Statement.RETURN_GENERATED_KEYS} or {@code Statement.NO_GENERATED_KEYS}. + * {@link Statement#RETURN_GENERATED_KEYS} or {@link Statement#NO_GENERATED_KEYS}. * @return a {@link PoolablePreparedStatement} * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or * the borrow failed. * @see Connection#prepareStatement(String, int) */ PreparedStatement prepareStatement(final String sql, final int autoGeneratedKeys) throws SQLException { - if (pStmtPool == null) { + if (stmtPool == null) { return getRawConnection().prepareStatement(sql, autoGeneratedKeys); } try { - return pStmtPool.borrowObject(createKey(sql, autoGeneratedKeys)); + return stmtPool.borrowObject(createKey(sql, autoGeneratedKeys)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -606,14 +607,14 @@ * Creates or obtains a {@link PreparedStatement} from my pool. * * @param sql - * a {@code String} object that is the SQL statement to be sent to the database; may contain one or + * a {@link String} object that is the SQL statement to be sent to the database; may contain one or * more '?' IN parameters. * @param resultSetType - * a result set type; one of {@code ResultSet.TYPE_FORWARD_ONLY}, - * {@code ResultSet.TYPE_SCROLL_INSENSITIVE}, or {@code ResultSet.TYPE_SCROLL_SENSITIVE}. + * a result set type; one of {@link ResultSet#TYPE_FORWARD_ONLY}, + * {@link ResultSet#TYPE_SCROLL_INSENSITIVE}, or {@link ResultSet#TYPE_SCROLL_SENSITIVE}. * @param resultSetConcurrency - * a concurrency type; one of {@code ResultSet.CONCUR_READ_ONLY} or - * {@code ResultSet.CONCUR_UPDATABLE}. + * a concurrency type; one of {@link ResultSet#CONCUR_READ_ONLY} or + * {@link ResultSet#CONCUR_UPDATABLE}. * * @return a {@link PoolablePreparedStatement}. * @throws SQLException Thrown if a database access error occurs, this method is called on a closed connection, or @@ -622,11 +623,11 @@ */ PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency) throws SQLException { - if (pStmtPool == null) { + if (stmtPool == null) { return getRawConnection().prepareStatement(sql, resultSetType, resultSetConcurrency); } try { - return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency)); + return stmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -636,11 +637,11 @@ PreparedStatement prepareStatement(final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) throws SQLException { - if (pStmtPool == null) { + if (stmtPool == null) { return getRawConnection().prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); } try { - return pStmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); + return stmtPool.borrowObject(createKey(sql, resultSetType, resultSetConcurrency, resultSetHoldability)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -649,11 +650,11 @@ } PreparedStatement prepareStatement(final String sql, final int[] columnIndexes) throws SQLException { - if (pStmtPool == null) { + if (stmtPool == null) { return getRawConnection().prepareStatement(sql, columnIndexes); } try { - return pStmtPool.borrowObject(createKey(sql, columnIndexes)); + return stmtPool.borrowObject(createKey(sql, columnIndexes)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -662,11 +663,11 @@ } PreparedStatement prepareStatement(final String sql, final String[] columnNames) throws SQLException { - if (pStmtPool == null) { + if (stmtPool == null) { return getRawConnection().prepareStatement(sql, columnNames); } try { - return pStmtPool.borrowObject(createKey(sql, columnNames)); + return stmtPool.borrowObject(createKey(sql, columnNames)); } catch (final RuntimeException e) { throw e; } catch (final Exception e) { @@ -699,7 +700,7 @@ } public void setStatementPool(final KeyedObjectPool statementPool) { - pStmtPool = statementPool; + stmtPool = statementPool; } /** @@ -720,8 +721,8 @@ builder.append(statementEventListeners); builder.append(", closed="); builder.append(closed); - builder.append(", pStmtPool="); - builder.append(pStmtPool); + builder.append(", stmtPool="); + builder.append(stmtPool); builder.append(", accessToUnderlyingConnectionAllowed="); builder.append(accessToUnderlyingConnectionAllowed); builder.append("]"); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/package-info.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/package-info.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/package-info.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/cpdsadapter/package-info.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/AbstractConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/AbstractConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/AbstractConnectionFactory.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/AbstractConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,147 @@ +/* + * 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 + * + * https://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.dbcp.dbcp2.datasources; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.time.Duration; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import javax.sql.ConnectionPoolDataSource; +import javax.sql.PooledConnection; + +import org.apache.tomcat.dbcp.dbcp2.Utils; +import org.apache.tomcat.dbcp.pool2.PooledObject; + +/** + * Abstracts services for connection factories in this package. + */ +class AbstractConnectionFactory { + + protected final ConnectionPoolDataSource cpds; + protected Duration maxConnDuration = Duration.ofMillis(-1); + protected final boolean rollbackAfterValidation; + + /** + * Map of PooledConnectionAndInfo instances + */ + protected final Map pcMap = new ConcurrentHashMap<>(); + + /** + * Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated. + */ + protected final Set validatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); + protected final String validationQuery; + protected final Duration validationQueryTimeoutDuration; + + AbstractConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, + final Duration validationQueryTimeoutDuration, final boolean rollbackAfterValidation) { + this.cpds = cpds; + this.validationQuery = validationQuery; + this.validationQueryTimeoutDuration = validationQueryTimeoutDuration; + this.rollbackAfterValidation = rollbackAfterValidation; + } + + /** + * Sets the maximum lifetime of a connection after which the connection will always fail activation, + * passivation and validation. + * + * @param duration + * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds. + * @since 2.10.0 + */ + void setMaxConn(final Duration duration) { + this.maxConnDuration = duration; + } + + /** + * Converts a duration to seconds where a duration less than one second becomes 1 second. + * + * @param duration the duration to convert. + * @return a duration to seconds where a duration less than one second becomes 1 second. + * @throws ArithmeticException if the query validation timeout does not fit as seconds in an int. + */ + private int toSeconds(final Duration duration) { + if (duration.isNegative() || duration.isZero()) { + return 0; + } + final long seconds = validationQueryTimeoutDuration.getSeconds(); + return seconds != 0 ? Math.toIntExact(seconds) : 1; + } + + protected void validateLifetime(final PooledObject pooledObject) throws SQLException { + Utils.validateLifetime(pooledObject, maxConnDuration); + } + + public boolean validateObject(final PooledObject pooledObject) { + try { + validateLifetime(pooledObject); + } catch (final Exception e) { + return false; + } + boolean valid = false; + final PooledConnection pooledConn = pooledObject.getObject().getPooledConnection(); + Connection conn = null; + // logical Connection from the PooledConnection must be closed + // before another one can be requested and closing it will + // generate an event. Keep track so we know not to return + // the PooledConnection + validatingSet.add(pooledConn); + try { + final int timeoutSeconds = toSeconds(validationQueryTimeoutDuration); + if (validationQuery == null) { + try { + conn = pooledConn.getConnection(); + valid = conn.isValid(timeoutSeconds); + } catch (final SQLException e) { + valid = false; + } + } else { + Statement stmt = null; + ResultSet rset = null; + try { + conn = pooledConn.getConnection(); + stmt = conn.createStatement(); + if (timeoutSeconds > 0) { + stmt.setQueryTimeout(timeoutSeconds); + } + rset = stmt.executeQuery(validationQuery); + valid = rset.next(); + if (rollbackAfterValidation) { + conn.rollback(); + } + } catch (final Exception e) { + valid = false; + } finally { + Utils.closeQuietly((AutoCloseable) rset); + Utils.closeQuietly((AutoCloseable) stmt); + } + } + } finally { + Utils.closeQuietly((AutoCloseable) conn); + validatingSet.remove(pooledConn); + } + return valid; + } + +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/CPDSConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -17,21 +17,14 @@ package org.apache.tomcat.dbcp.dbcp2.datasources; import java.sql.Connection; -import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import java.time.Duration; -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import javax.sql.ConnectionEvent; import javax.sql.ConnectionEventListener; import javax.sql.ConnectionPoolDataSource; import javax.sql.PooledConnection; -import org.apache.tomcat.dbcp.dbcp2.Utils; import org.apache.tomcat.dbcp.pool2.ObjectPool; import org.apache.tomcat.dbcp.pool2.PooledObject; import org.apache.tomcat.dbcp.pool2.PooledObjectFactory; @@ -42,31 +35,16 @@ * * @since 2.0 */ -final class CPDSConnectionFactory +final class CPDSConnectionFactory extends AbstractConnectionFactory implements PooledObjectFactory, ConnectionEventListener, PooledConnectionManager { private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but I have no record of the underlying PooledConnection."; - private final ConnectionPoolDataSource cpds; - private final String validationQuery; - private final Duration validationQueryTimeoutDuration; - private final boolean rollbackAfterValidation; private ObjectPool pool; private UserPassKey userPassKey; - private Duration maxConnDuration = Duration.ofMillis(-1); /** - * Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated. - */ - private final Set validatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - /** - * Map of PooledConnectionAndInfo instances - */ - private final Map pcMap = new ConcurrentHashMap<>(); - - /** - * Creates a new {@code PoolableConnectionFactory}. + * Creates a new {@link PoolableConnectionFactory}. * * @param cpds * the ConnectionPoolDataSource from which to obtain PooledConnection's @@ -86,97 +64,14 @@ */ CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, final Duration validationQueryTimeoutDuration, final boolean rollbackAfterValidation, final String userName, - final char[] userPassword) { - this.cpds = cpds; - this.validationQuery = validationQuery; - this.validationQueryTimeoutDuration = validationQueryTimeoutDuration; + final char[] userPassword) { + super(cpds, validationQuery, validationQueryTimeoutDuration, rollbackAfterValidation); this.userPassKey = new UserPassKey(userName, userPassword); - this.rollbackAfterValidation = rollbackAfterValidation; - } - - /** - * Creates a new {@code PoolableConnectionFactory}. - * - * @param cpds - * the ConnectionPoolDataSource from which to obtain PooledConnection's - * @param validationQuery - * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one - * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate - * connections. - * @param validationQueryTimeoutDuration - * Timeout in seconds before validation fails - * @param rollbackAfterValidation - * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s. - * @param userName - * The user name to use to create connections - * @param userPassword - * The password to use to create connections - * @since 2.10.0 - */ - CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, final Duration validationQueryTimeoutDuration, - final boolean rollbackAfterValidation, final String userName, final String userPassword) { - this(cpds, validationQuery, validationQueryTimeoutDuration, rollbackAfterValidation, userName, Utils.toCharArray(userPassword)); - } - - /** - * Creates a new {@code PoolableConnectionFactory}. - * - * @param cpds - * the ConnectionPoolDataSource from which to obtain PooledConnection's - * @param validationQuery - * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one - * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate - * connections. - * @param validationQueryTimeoutSeconds - * Timeout in seconds before validation fails - * @param rollbackAfterValidation - * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s. - * @param userName - * The user name to use to create connections - * @param userPassword - * The password to use to create connections - * @since 2.4.0 - * @deprecated Use {@link #CPDSConnectionFactory(ConnectionPoolDataSource, String, Duration, boolean, String, char[])}. - */ - @Deprecated - CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, - final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation, final String userName, - final char[] userPassword) { - this.cpds = cpds; - this.validationQuery = validationQuery; - this.validationQueryTimeoutDuration = Duration.ofSeconds(validationQueryTimeoutSeconds); - this.userPassKey = new UserPassKey(userName, userPassword); - this.rollbackAfterValidation = rollbackAfterValidation; - } - - /** - * Creates a new {@code PoolableConnectionFactory}. - * - * @param cpds - * the ConnectionPoolDataSource from which to obtain PooledConnection's - * @param validationQuery - * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one - * row. May be {@code null} in which case {@link Connection#isValid(int)} will be used to validate - * connections. - * @param validationQueryTimeoutSeconds - * Timeout in seconds before validation fails - * @param rollbackAfterValidation - * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s. - * @param userName - * The user name to use to create connections - * @param userPassword - * The password to use to create connections - * @deprecated Use {@link #CPDSConnectionFactory(ConnectionPoolDataSource, String, Duration, boolean, String, String)}. - */ - @Deprecated - CPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, final int validationQueryTimeoutSeconds, - final boolean rollbackAfterValidation, final String userName, final String userPassword) { - this(cpds, validationQuery, validationQueryTimeoutSeconds, rollbackAfterValidation, userName, Utils.toCharArray(userPassword)); } @Override - public void activateObject(final PooledObject p) throws SQLException { - validateLifetime(p); + public void activateObject(final PooledObject pooledObject) throws SQLException { + validateLifetime(pooledObject); } /** @@ -216,7 +111,7 @@ try { pool.returnObject(pci); } catch (final Exception e) { - System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL"); + System.err.println("CLOSING DOWN CONNECTION AS IT COULD NOT BE RETURNED TO THE POOL"); pc.removeConnectionEventListener(this); try { doDestroyObject(pci); @@ -296,8 +191,9 @@ throw new IllegalStateException(NO_KEY_MESSAGE); } try { - pool.invalidateObject(pci); // Destroy instance and update pool counters pool.close(); // Clear any other instances in this pool and kill others as they come back + // Calling close before invalidate ensures that invalidate will not trigger a create attempt + pool.invalidateObject(pci); // Destroy instance and update pool counters } catch (final Exception ex) { throw new SQLException("Error invalidating connection", ex); } @@ -328,50 +224,12 @@ } /** - * Sets the maximum Duration of a connection after which the connection will always fail activation, - * passivation and validation. - * - * @param maxConnDuration - * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds. - * @since 2.10.0 - */ - public void setMaxConn(final Duration maxConnDuration) { - this.maxConnDuration = maxConnDuration; - } - - /** - * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation, - * passivation and validation. - * - * @param maxConnDuration - * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds. - * @since 2.9.0 - * @deprecated Use {@link #setMaxConn(Duration)}. - */ - @Deprecated - public void setMaxConnLifetime(final Duration maxConnDuration) { - this.maxConnDuration = maxConnDuration; - } - - /** - * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation, - * passivation and validation. - * - * @param maxConnLifetimeMillis - * A value of zero or less indicates an infinite lifetime. The default value is -1. - * @deprecated Use {@link #setMaxConn(Duration)}. - */ - @Deprecated - public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) { - setMaxConnLifetime(Duration.ofMillis(maxConnLifetimeMillis)); - } - - /** * Sets the database password used when creating new connections. * * @param userPassword * new password */ + @Override public synchronized void setPassword(final char[] userPassword) { this.userPassKey = new UserPassKey(userPassKey.getUserName(), userPassword); } @@ -420,61 +278,4 @@ builder.append("]"); return builder.toString(); } - - private void validateLifetime(final PooledObject p) throws SQLException { - Utils.validateLifetime(p, maxConnDuration); - } - - @Override - public boolean validateObject(final PooledObject p) { - try { - validateLifetime(p); - } catch (final Exception e) { - return false; - } - boolean valid = false; - final PooledConnection pconn = p.getObject().getPooledConnection(); - Connection conn = null; - validatingSet.add(pconn); - if (null == validationQuery) { - Duration timeoutDuration = validationQueryTimeoutDuration; - if (timeoutDuration.isNegative()) { - timeoutDuration = Duration.ZERO; - } - try { - conn = pconn.getConnection(); - valid = conn.isValid((int) timeoutDuration.getSeconds()); - } catch (final SQLException e) { - valid = false; - } finally { - Utils.closeQuietly((AutoCloseable) conn); - validatingSet.remove(pconn); - } - } else { - Statement stmt = null; - ResultSet rset = null; - // logical Connection from the PooledConnection must be closed - // before another one can be requested and closing it will - // generate an event. Keep track so we know not to return - // the PooledConnection - validatingSet.add(pconn); - try { - conn = pconn.getConnection(); - stmt = conn.createStatement(); - rset = stmt.executeQuery(validationQuery); - valid = rset.next(); - if (rollbackAfterValidation) { - conn.rollback(); - } - } catch (final Exception e) { - valid = false; - } finally { - Utils.closeQuietly((AutoCloseable) rset); - Utils.closeQuietly((AutoCloseable) stmt); - Utils.closeQuietly((AutoCloseable) conn); - validatingSet.remove(pconn); - } - } - return valid; - } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/CharArray.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/CharArray.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/CharArray.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/CharArray.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -23,19 +23,18 @@ import org.apache.tomcat.dbcp.dbcp2.Utils; /** - * A {@code char} array wrapper that does not reveal its contents inadvertently through toString(). In contrast to, for - * example, AtomicReference which toString()'s its contents. - * + * A {@code char} array wrapper that does not reveal its contents inadvertently through toString(). In contrast to, for example, AtomicReference which + * toString()'s its contents. + *

          * May contain null. + *

          * * @since 2.9.0 */ final class CharArray implements Serializable { - private static final long serialVersionUID = 1L; - static final CharArray NULL = new CharArray((char[]) null); - + private static final long serialVersionUID = 1L; private final char[] chars; CharArray(final char[] chars) { @@ -55,6 +54,18 @@ return Utils.toString(chars); } + /** + * Clears the content of the char array. + * + * @return {@code this} instance. + */ + CharArray clear() { + if (chars != null) { + Arrays.fill(chars, '\0'); + } + return this; + } + @Override public boolean equals(final Object obj) { if (this == obj) { @@ -80,5 +91,4 @@ public int hashCode() { return Arrays.hashCode(chars); } - } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -41,30 +41,30 @@ /** *

          - * The base class for {@code SharedPoolDataSource} and {@code PerUserPoolDataSource}. Many of the + * The base class for {@link SharedPoolDataSource} and {@link PerUserPoolDataSource}. Many of the * configuration properties are shared and defined here. This class is declared public in order to allow particular * usage with commons-beanutils; do not make direct use of it outside of commons-dbcp2. *

          * *

          - * A J2EE container will normally provide some method of initializing the {@code DataSource} whose attributes are + * A J2EE container will normally provide some method of initializing the {@link DataSource} whose attributes are * presented as bean getters/setters and then deploying it via JNDI. It is then available to an application as a source * of pooled logical connections to the database. The pool needs a source of physical connections. This source is in the - * form of a {@code ConnectionPoolDataSource} that can be specified via the {@link #setDataSourceName(String)} used + * form of a {@link ConnectionPoolDataSource} that can be specified via the {@link #setDataSourceName(String)} used * to lookup the source via JNDI. *

          * *

          * Although normally used within a JNDI environment, A DataSource can be instantiated and initialized as any bean. In - * this case the {@code ConnectionPoolDataSource} will likely be instantiated in a similar manner. This class + * this case the {@link ConnectionPoolDataSource} will likely be instantiated in a similar manner. This class * allows the physical source of connections to be attached directly to this pool using the * {@link #setConnectionPoolDataSource(ConnectionPoolDataSource)} method. *

          * *

          * The dbcp package contains an adapter, {@link org.apache.tomcat.dbcp.dbcp2.cpdsadapter.DriverAdapterCPDS}, that can be - * used to allow the use of {@code DataSource}'s based on this class with JDBC driver implementations that do not - * supply a {@code ConnectionPoolDataSource}, but still provide a {@link java.sql.Driver} implementation. + * used to allow the use of {@link DataSource}'s based on this class with JDBC driver implementations that do not + * supply a {@link ConnectionPoolDataSource}, but still provide a {@link java.sql.Driver} implementation. *

          * *

          @@ -111,32 +111,70 @@ /** Instance key */ private String instanceKey; - // Pool properties - private boolean defaultBlockWhenExhausted = BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED; + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_BLOCK_WHEN_EXHAUSTED}. */ + private volatile boolean defaultBlockWhenExhausted = BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_EVICTION_POLICY_CLASS_NAME}. */ private String defaultEvictionPolicyClassName = BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME; - private boolean defaultLifo = BaseObjectPoolConfig.DEFAULT_LIFO; - private int defaultMaxIdle = GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY; - private int defaultMaxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_LIFO}. */ + private volatile boolean defaultLifo = BaseObjectPoolConfig.DEFAULT_LIFO; + + /** Pool property defaults to {@link GenericKeyedObjectPoolConfig#DEFAULT_MAX_IDLE_PER_KEY}. */ + private volatile int defaultMaxIdle = GenericKeyedObjectPoolConfig.DEFAULT_MAX_IDLE_PER_KEY; + + /** Pool property defaults to {@link GenericKeyedObjectPoolConfig#DEFAULT_MAX_TOTAL}. */ + private volatile int defaultMaxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_MAX_WAIT}. */ private Duration defaultMaxWaitDuration = BaseObjectPoolConfig.DEFAULT_MAX_WAIT; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_MIN_EVICTABLE_IDLE_DURATION}. */ private Duration defaultMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_DURATION; - private int defaultMinIdle = GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY; - private int defaultNumTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; + + /** Pool property defaults to {@link GenericKeyedObjectPoolConfig#DEFAULT_MIN_IDLE_PER_KEY}. */ + private volatile int defaultMinIdle = GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_NUM_TESTS_PER_EVICTION_RUN}. */ + private volatile int defaultNumTestsPerEvictionRun = BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION}. */ private Duration defaultSoftMinEvictableIdleDuration = BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION; - private boolean defaultTestOnCreate = BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE; - private boolean defaultTestOnBorrow = BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW; - private boolean defaultTestOnReturn = BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN; - private boolean defaultTestWhileIdle = BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_TEST_ON_CREATE}. */ + private volatile boolean defaultTestOnCreate = BaseObjectPoolConfig.DEFAULT_TEST_ON_CREATE; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_TEST_ON_BORROW}. */ + private volatile boolean defaultTestOnBorrow = BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_TEST_ON_RETURN}. */ + private volatile boolean defaultTestOnReturn = BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_TEST_WHILE_IDLE}. */ + private volatile boolean defaultTestWhileIdle = BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE; + + /** Pool property defaults to {@link BaseObjectPoolConfig#DEFAULT_DURATION_BETWEEN_EVICTION_RUNS}. */ private Duration defaultDurationBetweenEvictionRuns = BaseObjectPoolConfig.DEFAULT_DURATION_BETWEEN_EVICTION_RUNS; - // Connection factory properties + /** Connection factory property defaults to null. */ private String validationQuery; + + /** Connection factory property defaults to -1 seconds. */ private Duration validationQueryTimeoutDuration = Duration.ofSeconds(-1); - private boolean rollbackAfterValidation; + + /** Connection factory property defaults to false. */ + private volatile boolean rollbackAfterValidation; + + /** Connection factory property defaults to -1 milliseconds. */ private Duration maxConnDuration = Duration.ofMillis(-1); - // Connection properties + /** Connection property defaults to false. */ private Boolean defaultAutoCommit; - private int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION; + + /** Connection property defaults to {@link #UNKNOWN_TRANSACTIONISOLATION}. */ + private volatile int defaultTransactionIsolation = UNKNOWN_TRANSACTIONISOLATION; + + /** Connection property defaults to false. */ private Boolean defaultReadOnly; /** @@ -198,7 +236,7 @@ public Connection getConnection(final String userName, final String userPassword) throws SQLException { if (instanceKey == null) { throw new SQLException("Must set the ConnectionPoolDataSource " - + "through setDataSourceName or setConnectionPoolDataSource" + " before calling getConnection."); + + "through setDataSourceName or setConnectionPoolDataSource before calling getConnection."); } getConnectionCalled = true; PooledConnectionAndInfo info = null; @@ -220,7 +258,7 @@ // Password has not changed, so refuse client, but return connection to the pool closeDueToException(info); throw new SQLException( - "Given password did not match password used" + " to create the PooledConnection.", ex); + "Given password did not match password used to create the PooledConnection.", ex); } catch (final javax.naming.NamingException ne) { throw new SQLException("NamingException encountered connecting to database", ne); } @@ -233,7 +271,7 @@ // Destroy and remove from pool manager.invalidate(info.getPooledConnection()); // Reset the password on the factory if using CPDSConnectionFactory - manager.setPassword(upkey.getPassword()); + manager.setPassword(upkey.getPasswordCharArray()); info = null; for (int i = 0; i < 10; i++) { // Bound the number of retries - only needed if bad instances return try { @@ -269,6 +307,12 @@ } } + /** + * Gets the pooled connection manager for the given key. + * + * @param upkey the key. + * @return the pooled connection manager for the given key. + */ protected abstract PooledConnectionManager getConnectionManager(UserPassKey upkey); /** @@ -416,11 +460,11 @@ } /** - * Gets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool - * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool. + * Gets the default value for {@link + * GenericObjectPool#getSoftMinEvictableIdleDuration()} for each per user pool. * - * @return The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool - * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool. + * @return The default value for {@link + * GenericObjectPool#getSoftMinEvictableIdleDuration()} for each per user pool. * @since 2.10.0 */ public Duration getDefaultSoftMinEvictableIdleDuration() { @@ -428,10 +472,10 @@ } /** - * Gets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Gets the default value for {@link * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool. * - * @return The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * @return The default value for {@link * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool. * @deprecated Use {@link #getDefaultSoftMinEvictableIdleDuration()}. */ @@ -441,10 +485,10 @@ } /** - * Gets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Gets the default value for {@link * GenericObjectPool#getTestOnBorrow()} for each per user pool. * - * @return The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * @return The default value for {@link * GenericObjectPool#getTestOnBorrow()} for each per user pool. */ public boolean getDefaultTestOnBorrow() { @@ -452,10 +496,10 @@ } /** - * Gets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Gets the default value for {@link * GenericObjectPool#getTestOnCreate()} for each per user pool. * - * @return The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * @return The default value for {@link * GenericObjectPool#getTestOnCreate()} for each per user pool. */ public boolean getDefaultTestOnCreate() { @@ -463,10 +507,10 @@ } /** - * Gets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Gets the default value for {@link * GenericObjectPool#getTestOnReturn()} for each per user pool. * - * @return The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * @return The default value for {@link * GenericObjectPool#getTestOnReturn()} for each per user pool. */ public boolean getDefaultTestOnReturn() { @@ -474,10 +518,10 @@ } /** - * Gets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Gets the default value for {@link * GenericObjectPool#getTestWhileIdle()} for each per user pool. * - * @return The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * @return The default value for {@link * GenericObjectPool#getTestWhileIdle()} for each per user pool. */ public boolean getDefaultTestWhileIdle() { @@ -666,7 +710,7 @@ /** * Gets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is {@code null} which - * will use the default value for the drive. + * will use the default value for the driver. * * @return value of defaultAutoCommit. */ @@ -677,7 +721,7 @@ /** * Gets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value * can be changed on the Connection using Connection.setReadOnly(boolean). The default is {@code null} which - * will use the default value for the drive. + * will use the default value for the driver. * * @return value of defaultReadOnly. */ @@ -686,7 +730,7 @@ } /** - * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from + * Tests whether a rollback will be issued after executing the SQL query that will be used to validate connections from * this pool before returning them to the caller. * * @return true if a rollback will be issued after executing the validation query @@ -733,7 +777,7 @@ + "set using setConnectionPoolDataSource."); } if (this.dataSourceName != null) { - throw new IllegalStateException("The DataSourceName has already been set. " + "It cannot be altered."); + throw new IllegalStateException("The DataSourceName has already been set. It cannot be altered."); } this.dataSourceName = dataSourceName; instanceKey = InstanceKeyDataSourceFactory.registerNewInstance(this); @@ -742,7 +786,7 @@ /** * Sets the value of defaultAutoCommit, which defines the state of connections handed out from this pool. The value * can be changed on the Connection using Connection.setAutoCommit(boolean). The default is {@code null} which - * will use the default value for the drive. + * will use the default value for the driver. * * @param defaultAutoCommit * Value to assign to defaultAutoCommit. @@ -902,7 +946,7 @@ /** * Sets the value of defaultReadOnly, which defines the state of connections handed out from this pool. The value * can be changed on the Connection using Connection.setReadOnly(boolean). The default is {@code null} which - * will use the default value for the drive. + * will use the default value for the driver. * * @param defaultReadOnly * Value to assign to defaultReadOnly. @@ -913,12 +957,12 @@ } /** - * Sets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool - * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool. + * Sets the default value for {@link + * GenericObjectPool#getSoftMinEvictableIdleDuration()} for each per user pool. * * @param defaultSoftMinEvictableIdleDuration - * The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool - * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool. + * The default value for {@link + * GenericObjectPool#getSoftMinEvictableIdleDuration()} for each per user pool. * @since 2.10.0 */ public void setDefaultSoftMinEvictableIdle(final Duration defaultSoftMinEvictableIdleDuration) { @@ -927,11 +971,11 @@ } /** - * Sets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Sets the default value for {@link * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool. * * @param softMinEvictableIdleTimeMillis - * The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * The default value for {@link * GenericObjectPool#getSoftMinEvictableIdleTimeMillis()} for each per user pool. * @deprecated Use {@link #setDefaultSoftMinEvictableIdle(Duration)}. */ @@ -942,11 +986,11 @@ } /** - * Sets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Sets the default value for {@link * GenericObjectPool#getTestOnBorrow()} for each per user pool. * * @param testOnBorrow - * The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * The default value for {@link * GenericObjectPool#getTestOnBorrow()} for each per user pool. */ public void setDefaultTestOnBorrow(final boolean testOnBorrow) { @@ -955,11 +999,11 @@ } /** - * Sets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Sets the default value for {@link * GenericObjectPool#getTestOnCreate()} for each per user pool. * * @param testOnCreate - * The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * The default value for {@link * GenericObjectPool#getTestOnCreate()} for each per user pool. */ public void setDefaultTestOnCreate(final boolean testOnCreate) { @@ -968,11 +1012,11 @@ } /** - * Sets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Sets the default value for {@link * GenericObjectPool#getTestOnReturn()} for each per user pool. * * @param testOnReturn - * The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * The default value for {@link * GenericObjectPool#getTestOnReturn()} for each per user pool. */ public void setDefaultTestOnReturn(final boolean testOnReturn) { @@ -981,11 +1025,11 @@ } /** - * Sets the default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * Sets the default value for {@link * GenericObjectPool#getTestWhileIdle()} for each per user pool. * * @param testWhileIdle - * The default value for {@link org.apache.tomcat.dbcp.pool2.impl.GenericObjectPool + * The default value for {@link * GenericObjectPool#getTestWhileIdle()} for each per user pool. */ public void setDefaultTestWhileIdle(final boolean testWhileIdle) { @@ -1017,14 +1061,14 @@ public void setDefaultTransactionIsolation(final int defaultTransactionIsolation) { assertInitializationAllowed(); switch (defaultTransactionIsolation) { - case Connection.TRANSACTION_NONE: - case Connection.TRANSACTION_READ_COMMITTED: - case Connection.TRANSACTION_READ_UNCOMMITTED: - case Connection.TRANSACTION_REPEATABLE_READ: - case Connection.TRANSACTION_SERIALIZABLE: - break; - default: - throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION); + case Connection.TRANSACTION_NONE: + case Connection.TRANSACTION_READ_COMMITTED: + case Connection.TRANSACTION_READ_UNCOMMITTED: + case Connection.TRANSACTION_REPEATABLE_READ: + case Connection.TRANSACTION_SERIALIZABLE: + break; + default: + throw new IllegalArgumentException(BAD_TRANSACTION_ISOLATION); } this.defaultTransactionIsolation = defaultTransactionIsolation; } @@ -1143,7 +1187,7 @@ } /** - * Whether a rollback will be issued after executing the SQL query that will be used to validate connections from + * Sets whether a rollback will be issued after executing the SQL query that will be used to validate connections from * this pool before returning them to the caller. Default behavior is NOT to issue a rollback. The setting will only * have an effect if a validation query is set * @@ -1155,6 +1199,13 @@ this.rollbackAfterValidation = rollbackAfterValidation; } + /** + * Sets up the defaults for a given connection. + * + * @param connection The target connection. + * @param userName The user name for the connection. + * @throws SQLException if a database access error occurs or this method is called on a closed connection + */ protected abstract void setupDefaults(Connection connection, String userName) throws SQLException; /** @@ -1193,9 +1244,18 @@ this.validationQueryTimeoutDuration = Duration.ofSeconds(validationQueryTimeoutSeconds); } + /** + * Tests and returns whether a JNDI context can be created to lookup a ConnectionPoolDataSource to then access a PooledConnection connection. + * + * @param userName An optional user name, may be null. + * @param userPassword An optional user user password, may be null. + * @return A ConnectionPoolDataSource from a JNDI context. + * @throws javax.naming.NamingException if a naming exception is encountered. + * @throws SQLException if a ConnectionPoolDataSource or PooledConnection is not available. + */ protected ConnectionPoolDataSource testCPDS(final String userName, final String userPassword) throws javax.naming.NamingException, SQLException { - // The source of physical db connections + // The source of physical database connections ConnectionPoolDataSource cpds = this.dataSource; if (cpds == null) { Context ctx = null; @@ -1206,12 +1266,11 @@ } final Object ds = ctx.lookup(dataSourceName); if (!(ds instanceof ConnectionPoolDataSource)) { - throw new SQLException("Illegal configuration: " + "DataSource " + dataSourceName + " (" - + ds.getClass().getName() + ")" + " doesn't implement javax.sql.ConnectionPoolDataSource"); + throw new SQLException("Illegal configuration: DataSource " + dataSourceName + " (" + + ds.getClass().getName() + ") doesn't implement javax.sql.ConnectionPoolDataSource"); } cpds = (ConnectionPoolDataSource) ds; } - // try to get a connection with the supplied userName/password PooledConnection conn = null; try { @@ -1247,6 +1306,11 @@ return builder.toString(); } + /** + * Appends this instance's fields to a string builder. + * + * @param builder the target string builder. + */ protected void toStringFields(final StringBuilder builder) { builder.append("getConnectionCalled="); builder.append(getConnectionCalled); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSourceFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSourceFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSourceFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/InstanceKeyDataSourceFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -37,7 +37,7 @@ import org.apache.tomcat.dbcp.dbcp2.Utils; /** - * A JNDI ObjectFactory which creates {@code SharedPoolDataSource}s or {@code PerUserPoolDataSource}s + * A JNDI ObjectFactory which creates {@link SharedPoolDataSource}s or {@link PerUserPoolDataSource}s * * @since 2.0 */ @@ -116,7 +116,7 @@ } } - private Boolean booleanValueOf(RefAddr refAddr) { + private Boolean booleanValueOf(final RefAddr refAddr) { return Boolean.valueOf(toString(refAddr)); } @@ -340,11 +340,11 @@ } } - private Duration toDurationFromMillis(RefAddr refAddr) { + private Duration toDurationFromMillis(final RefAddr refAddr) { return Duration.ofMillis(parseLong(refAddr)); } - private Duration toDurationFromSeconds(RefAddr refAddr) { + private Duration toDurationFromSeconds(final RefAddr refAddr) { return Duration.ofSeconds(parseInt(refAddr)); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/KeyedCPDSConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/KeyedCPDSConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/KeyedCPDSConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/KeyedCPDSConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -17,21 +17,14 @@ package org.apache.tomcat.dbcp.dbcp2.datasources; import java.sql.Connection; -import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import java.time.Duration; -import java.util.Collections; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import javax.sql.ConnectionEvent; import javax.sql.ConnectionEventListener; import javax.sql.ConnectionPoolDataSource; import javax.sql.PooledConnection; -import org.apache.tomcat.dbcp.dbcp2.Utils; import org.apache.tomcat.dbcp.pool2.KeyedObjectPool; import org.apache.tomcat.dbcp.pool2.KeyedPooledObjectFactory; import org.apache.tomcat.dbcp.pool2.PooledObject; @@ -42,31 +35,14 @@ * * @since 2.0 */ -final class KeyedCPDSConnectionFactory implements KeyedPooledObjectFactory, - ConnectionEventListener, PooledConnectionManager { +final class KeyedCPDSConnectionFactory extends AbstractConnectionFactory + implements KeyedPooledObjectFactory, ConnectionEventListener, PooledConnectionManager { - private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but " - + "I have no record of the underlying PooledConnection."; - - private final ConnectionPoolDataSource cpds; - private final String validationQuery; - private final Duration validationQueryTimeoutDuration; - private final boolean rollbackAfterValidation; + private static final String NO_KEY_MESSAGE = "close() was called on a Connection, but I have no record of the underlying PooledConnection."; private KeyedObjectPool pool; - private Duration maxConnLifetime = Duration.ofMillis(-1); - - /** - * Map of PooledConnections for which close events are ignored. Connections are muted when they are being validated. - */ - private final Set validatingSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - /** - * Map of PooledConnectionAndInfo instances - */ - private final Map pcMap = new ConcurrentHashMap<>(); /** - * Creates a new {@code KeyedPoolableConnectionFactory}. + * Creates a new {@code KeyedCPDSConnectionFactory}. * * @param cpds * the ConnectionPoolDataSource from which to obtain PooledConnections @@ -74,44 +50,20 @@ * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one * row. May be {@code null} in which case3 {@link Connection#isValid(int)} will be used to validate * connections. - * @param validationQueryTimeoutSeconds + * @param validationQueryTimeoutDuration * The Duration to allow for the validation query to complete * @param rollbackAfterValidation * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s. * @since 2.10.0 */ KeyedCPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, - final Duration validationQueryTimeoutSeconds, final boolean rollbackAfterValidation) { - this.cpds = cpds; - this.validationQuery = validationQuery; - this.validationQueryTimeoutDuration = validationQueryTimeoutSeconds; - this.rollbackAfterValidation = rollbackAfterValidation; - } - - /** - * Creates a new {@code KeyedPoolableConnectionFactory}. - * - * @param cpds - * the ConnectionPoolDataSource from which to obtain PooledConnections - * @param validationQuery - * a query to use to {@link #validateObject validate} {@link Connection}s. Should return at least one - * row. May be {@code null} in which case3 {@link Connection#isValid(int)} will be used to validate - * connections. - * @param validationQueryTimeoutSeconds - * The time, in seconds, to allow for the validation query to complete - * @param rollbackAfterValidation - * whether a rollback should be issued after {@link #validateObject validating} {@link Connection}s. - * @deprecated Use {@link #KeyedCPDSConnectionFactory(ConnectionPoolDataSource, String, Duration, boolean)}. - */ - @Deprecated - KeyedCPDSConnectionFactory(final ConnectionPoolDataSource cpds, final String validationQuery, - final int validationQueryTimeoutSeconds, final boolean rollbackAfterValidation) { - this(cpds, validationQuery, Duration.ofSeconds(validationQueryTimeoutSeconds), rollbackAfterValidation); + final Duration validationQueryTimeoutDuration, final boolean rollbackAfterValidation) { + super(cpds, validationQuery, validationQueryTimeoutDuration, rollbackAfterValidation); } @Override - public void activateObject(final UserPassKey key, final PooledObject p) throws SQLException { - validateLifetime(p); + public void activateObject(final UserPassKey ignored, final PooledObject pooledObject) throws SQLException { + validateLifetime(pooledObject); } /** @@ -146,7 +98,7 @@ try { pool.returnObject(pci.getUserPassKey(), pci); } catch (final Exception e) { - System.err.println("CLOSING DOWN CONNECTION AS IT COULD " + "NOT BE RETURNED TO THE POOL"); + System.err.println("CLOSING DOWN CONNECTION AS IT COULD NOT BE RETURNED TO THE POOL"); pc.removeConnectionEventListener(this); try { pool.invalidateObject(pci.getUserPassKey(), pci); @@ -185,8 +137,8 @@ * Closes the PooledConnection and stops listening for events from it. */ @Override - public void destroyObject(final UserPassKey key, final PooledObject p) throws SQLException { - final PooledConnection pooledConnection = p.getObject().getPooledConnection(); + public void destroyObject(final UserPassKey ignored, final PooledObject pooledObject) throws SQLException { + final PooledConnection pooledConnection = pooledObject.getObject().getPooledConnection(); pooledConnection.removeConnectionEventListener(this); pcMap.remove(pooledConnection); pooledConnection.close(); @@ -241,62 +193,28 @@ } else { pooledConnection = cpds.getPooledConnection(userName, password); } - if (pooledConnection == null) { throw new IllegalStateException("Connection pool data source returned null from getPooledConnection"); } - // should we add this object as a listener or the pool. // consider the validateObject method in decision pooledConnection.addConnectionEventListener(this); final PooledConnectionAndInfo pci = new PooledConnectionAndInfo(pooledConnection, userPassKey); pcMap.put(pooledConnection, pci); - return new DefaultPooledObject<>(pci); } @Override - public void passivateObject(final UserPassKey key, final PooledObject p) throws SQLException { - validateLifetime(p); - } - - /** - * Sets the maximum lifetime of a connection after which the connection will always fail activation, - * passivation and validation. - * - * @param maxConnLifetimeMillis - * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds. - * @since 2.10.0 - */ - public void setMaxConn(final Duration maxConnLifetimeMillis) { - this.maxConnLifetime = maxConnLifetimeMillis; + public void passivateObject(final UserPassKey ignored, final PooledObject pooledObject) throws SQLException { + validateLifetime(pooledObject); } /** - * Sets the maximum lifetime of a connection after which the connection will always fail activation, - * passivation and validation. - * - * @param maxConnLifetimeMillis - * A value of zero or less indicates an infinite lifetime. The default value is -1 milliseconds. - * @since 2.9.0 - * @deprecated Use {@link #setMaxConn(Duration)}. + * Does nothing. This factory does not cache user credentials. */ - @Deprecated - public void setMaxConnLifetime(final Duration maxConnLifetimeMillis) { - this.maxConnLifetime = maxConnLifetimeMillis; - } - - /** - * Sets the maximum lifetime in milliseconds of a connection after which the connection will always fail activation, - * passivation and validation. - * - * @param maxConnLifetimeMillis - * A value of zero or less indicates an infinite lifetime. The default value is -1. - * @deprecated Use {@link #setMaxConnLifetime(Duration)}. - */ - @Deprecated - public void setMaxConnLifetimeMillis(final long maxConnLifetimeMillis) { - setMaxConn(Duration.ofMillis(maxConnLifetimeMillis)); + @Override + public void setPassword(final char[] password) { + // Does nothing. This factory does not cache user credentials. } /** @@ -311,69 +229,21 @@ this.pool = pool; } - private void validateLifetime(final PooledObject pooledObject) throws SQLException { - Utils.validateLifetime(pooledObject, maxConnLifetime); - } - /** * Validates a pooled connection. + *

          + * A query validation timeout greater than 0 and less than 1 second is converted to 1 second. + *

          * - * @param key + * @param ignored * ignored * @param pooledObject * wrapped {@code PooledConnectionAndInfo} containing the connection to validate * @return true if validation succeeds + * @throws ArithmeticException if the query validation timeout does not fit as seconds in an int. */ @Override - public boolean validateObject(final UserPassKey key, final PooledObject pooledObject) { - try { - validateLifetime(pooledObject); - } catch (final Exception e) { - return false; - } - boolean valid = false; - final PooledConnection pooledConn = pooledObject.getObject().getPooledConnection(); - Connection conn = null; - validatingSet.add(pooledConn); - if (null == validationQuery) { - Duration timeoutDuration = validationQueryTimeoutDuration; - if (timeoutDuration.isNegative()) { - timeoutDuration = Duration.ZERO; - } - try { - conn = pooledConn.getConnection(); - valid = conn.isValid((int) timeoutDuration.getSeconds()); - } catch (final SQLException e) { - valid = false; - } finally { - Utils.closeQuietly((AutoCloseable) conn); - validatingSet.remove(pooledConn); - } - } else { - Statement stmt = null; - ResultSet rset = null; - // logical Connection from the PooledConnection must be closed - // before another one can be requested and closing it will - // generate an event. Keep track so we know not to return - // the PooledConnection - validatingSet.add(pooledConn); - try { - conn = pooledConn.getConnection(); - stmt = conn.createStatement(); - rset = stmt.executeQuery(validationQuery); - valid = rset.next(); - if (rollbackAfterValidation) { - conn.rollback(); - } - } catch (final Exception e) { - valid = false; - } finally { - Utils.closeQuietly((AutoCloseable) rset); - Utils.closeQuietly((AutoCloseable) stmt); - Utils.closeQuietly((AutoCloseable) conn); - validatingSet.remove(pooledConn); - } - } - return valid; + public boolean validateObject(final UserPassKey ignored, final PooledObject pooledObject) { + return super.validateObject(pooledObject); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSource.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -30,6 +30,7 @@ import javax.naming.Reference; import javax.naming.StringRefAddr; import javax.sql.ConnectionPoolDataSource; +import javax.sql.DataSource; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -41,7 +42,7 @@ /** *

          - * A pooling {@code DataSource} appropriate for deployment within J2EE environment. There are many configuration + * A pooling {@link DataSource} appropriate for deployment within J2EE environment. There are many configuration * options, most of which are defined in the parent class. This datasource uses individual pools per user, and some * properties can be set specifically for a given user, if the deployment environment can support initialization of * mapped properties. So for example, a pool of admin or write-access Connections can be guaranteed a certain number of @@ -641,7 +642,7 @@ } /** - * Returns a {@code PerUserPoolDataSource} {@link Reference}. + * Returns a {@link PerUserPoolDataSource} {@link Reference}. */ @Override public Reference getReference() throws NamingException { @@ -665,7 +666,6 @@ * @throws IOException Any of the usual Input/Output related exceptions. * @throws ClassNotFoundException A class of a serialized object cannot be found. */ - @SuppressWarnings("resource") private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); this.managers = readObjectImpl().managers; @@ -685,7 +685,7 @@ // the factory with the pool, so we do not have to do so // explicitly) final CPDSConnectionFactory factory = new CPDSConnectionFactory(cpds, getValidationQuery(), getValidationQueryTimeoutDuration(), - isRollbackAfterValidation(), userName, password); + isRollbackAfterValidation(), userName, Utils.toCharArray(password)); factory.setMaxConn(getMaxConnDuration()); // Create an object pool to contain our PooledConnections final GenericObjectPool pool = new GenericObjectPool<>(factory); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSourceFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSourceFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSourceFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PerUserPoolDataSourceFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -24,13 +24,20 @@ import javax.naming.Reference; /** - * A JNDI ObjectFactory which creates {@code SharedPoolDataSource}s + * A JNDI ObjectFactory which creates {@link SharedPoolDataSource}s * * @since 2.0 */ public class PerUserPoolDataSourceFactory extends InstanceKeyDataSourceFactory { private static final String PER_USER_POOL_CLASSNAME = PerUserPoolDataSource.class.getName(); + /** + * Constructs a new instance. + */ + public PerUserPoolDataSourceFactory() { + // empty + } + @SuppressWarnings("unchecked") // Avoid warnings on deserialization @Override protected InstanceKeyDataSource getNewInstance(final Reference ref) throws IOException, ClassNotFoundException { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PoolKey.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PoolKey.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PoolKey.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PoolKey.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -40,10 +40,7 @@ if (this == obj) { return true; } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { + if (obj == null || getClass() != obj.getClass()) { return false; } final PoolKey other = (PoolKey) obj; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionAndInfo.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionAndInfo.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionAndInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionAndInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionManager.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionManager.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/PooledConnectionManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.tomcat.dbcp.dbcp2.datasources; import java.sql.SQLException; @@ -31,9 +32,9 @@ * Closes the connection pool associated with the given user. * * @param userName - * user name + * user name. * @throws SQLException - * if an error occurs closing idle connections in the pool + * if an error occurs closing idle connections in the pool. */ void closePool(String userName) throws SQLException; @@ -41,27 +42,27 @@ * Closes the PooledConnection and remove it from the connection pool to which it belongs, adjusting pool counters. * * @param pc - * PooledConnection to be invalidated + * PooledConnection to be invalidated. * @throws SQLException - * if an SQL error occurs closing the connection + * if an SQL error occurs closing the connection. */ void invalidate(PooledConnection pc) throws SQLException; -// /** -// * Sets the database password used when creating connections. -// * -// * @param password password used when authenticating to the database -// * @since 2.10.0 -// */ -// default void setPassword(char[] password) { -// setPassword(String.copyValueOf(password)); -// } + /** + * Sets the database password used when creating connections. + * + * @param password password used when authenticating to the database. + * @since 2.14.0 + */ + default void setPassword(final char[] password) { + setPassword(String.copyValueOf(password)); + } /** * Sets the database password used when creating connections. * * @param password - * password used when authenticating to the database + * password used when authenticating to the database. */ void setPassword(String password); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -25,6 +25,7 @@ import javax.naming.Reference; import javax.naming.StringRefAddr; import javax.sql.ConnectionPoolDataSource; +import javax.sql.DataSource; import org.apache.tomcat.dbcp.pool2.KeyedObjectPool; import org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool; @@ -32,7 +33,7 @@ /** *

          - * A pooling {@code DataSource} appropriate for deployment within J2EE environment. There are many configuration + * A pooling {@link DataSource} appropriate for deployment within J2EE environment. There are many configuration * options, most of which are defined in the parent class. All users (based on user name) share a single maximum number * of Connections in this data source. *

          @@ -54,7 +55,7 @@ /** * Max total defaults to {@link GenericKeyedObjectPoolConfig#DEFAULT_MAX_TOTAL}. */ - private int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; + private volatile int maxTotal = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL; /** * Maps user credentials to pooled connection with credentials. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSourceFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSourceFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSourceFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/SharedPoolDataSourceFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -20,7 +20,7 @@ import javax.naming.Reference; /** - * A JNDI ObjectFactory which creates {@code SharedPoolDataSource}s + * A JNDI ObjectFactory which creates {@link SharedPoolDataSource}s * * @since 2.0 */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/UserPassKey.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -26,7 +26,7 @@ * Holds a user name and password pair. Serves as a poolable object key for the {@link KeyedObjectPool} backing a * {@link SharedPoolDataSource}. Two instances with the same user name are considered equal. This ensures that there * will be only one keyed pool for each user in the pool. The password is used (along with the user name) by the - * {@code KeyedCPDSConnectionFactory} when creating new connections. + * {@link KeyedCPDSConnectionFactory} when creating new connections. *

          * *

          @@ -60,6 +60,17 @@ } /** + * Clears the content of the name and password. + * + * @return {@code this} instance. + */ + UserPassKey clear() { + name.clear(); + password.clear(); + return this; + } + + /** * Only takes the user name into account. */ @Override @@ -67,10 +78,7 @@ if (this == obj) { return true; } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { + if (obj == null || getClass() != obj.getClass()) { return false; } final UserPassKey other = (UserPassKey) obj; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/package-info.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/package-info.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/datasources/package-info.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/datasources/package-info.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/BasicManagedDataSource.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/BasicManagedDataSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/BasicManagedDataSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/BasicManagedDataSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -67,8 +67,15 @@ /** Transaction Synchronization Registry */ private transient TransactionSynchronizationRegistry transactionSynchronizationRegistry; + /** + * Constructs a new instance. + */ + public BasicManagedDataSource() { + // empty + } + @Override - protected ConnectionFactory createConnectionFactory() throws SQLException { + protected synchronized ConnectionFactory createConnectionFactory() throws SQLException { if (transactionManager == null) { throw new SQLException("Transaction manager must be set before a connection can be created"); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/DataSourceXAConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/DataSourceXAConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/DataSourceXAConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/DataSourceXAConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -158,15 +158,35 @@ @Override public Connection createConnection() throws SQLException { // create a new XAConnection - final XAConnection xaConnection; - if (userName == null) { - xaConnection = xaDataSource.getXAConnection(); - } else { - xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword)); + XAConnection xaConnection = null; + Connection connection = null; + final XAResource xaResource; + try { + if (userName == null) { + xaConnection = xaDataSource.getXAConnection(); + } else { + xaConnection = xaDataSource.getXAConnection(userName, Utils.toString(userPassword)); + } + // get the real connection and XAResource from the connection + connection = xaConnection.getConnection(); + xaResource = xaConnection.getXAResource(); + } catch (final SQLException sqle) { + if (connection != null) { + try { + connection.close(); + } catch (final SQLException ignored) { + // Ignore + } + } + if (xaConnection != null) { + try { + xaConnection.close(); + } catch (final SQLException ignored) { + // Ignore + } + } + throw sqle; } - // get the real connection and XAResource from the connection - final Connection connection = xaConnection.getConnection(); - final XAResource xaResource = xaConnection.getXAResource(); // register the XA resource for the connection transactionRegistry.registerConnection(connection, xaResource); // The Connection we're returning is a handle on the XAConnection. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/LocalXAConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/LocalXAConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/LocalXAConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/LocalXAConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -103,7 +103,7 @@ connection.commit(); } } catch (final SQLException e) { - throw (XAException) new XAException().initCause(e); + throw newXAException("Commit failed.", e); } finally { try { connection.setAutoCommit(originalAutoCommit); @@ -180,6 +180,10 @@ return this == xaResource; } + private XAException newXAException(final String message, final SQLException cause) { + return (XAException) new XAException(message).initCause(cause); + } + /** * This method does nothing since the LocalXAConnection does not support two-phase-commit. This method will * return XAResource.XA_RDONLY if the connection isReadOnly(). This assumes that the physical connection is @@ -242,7 +246,7 @@ try { connection.rollback(); } catch (final SQLException e) { - throw (XAException) new XAException().initCause(e); + throw newXAException("Rollback failed.", e); } finally { try { connection.setAutoCommit(originalAutoCommit); @@ -282,12 +286,10 @@ public synchronized void start(final Xid xid, final int flag) throws XAException { if (flag == TMNOFLAGS) { // first time in this transaction - // make sure we aren't already in another tx if (this.currentXid != null) { throw new XAException("Already enlisted in another transaction with xid " + xid); } - // save off the current auto commit flag, so it can be restored after the transaction completes try { originalAutoCommit = connection.getAutoCommit(); @@ -295,20 +297,16 @@ // no big deal, just assume it was off originalAutoCommit = true; } - // update the auto commit flag try { connection.setAutoCommit(false); } catch (final SQLException e) { - throw (XAException) new XAException("Count not turn off auto commit for a XA transaction") - .initCause(e); + throw newXAException("Count not turn off auto commit for a XA transaction", e); } - this.currentXid = xid; } else if (flag == TMRESUME) { if (!xid.equals(this.currentXid)) { - throw new XAException("Attempting to resume in different transaction: expected " + this.currentXid - + ", but was " + xid); + throw new XAException("Attempting to resume in different transaction: expected " + this.currentXid + ", but was " + xid); } } else { throw new XAException("Unknown start flag " + flag); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedConnection.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedConnection.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedConnection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedConnection.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -51,6 +51,14 @@ * @since 2.0 */ protected class CompletionListener implements TransactionContextListener { + + /** + * Constructs a new instance. + */ + public CompletionListener() { + // empty + } + @Override public void afterCompletion(final TransactionContext completedContext, final boolean committed) { if (completedContext == transactionContext) { @@ -207,27 +215,23 @@ lock.lock(); try { transactionContext.completeTransaction(); + // If we were using a shared connection, clear the reference now that + // the transaction has completed + if (isSharedConnection) { + setDelegate(null); + isSharedConnection = false; + } } finally { lock.unlock(); } - - // If we were using a shared connection, clear the reference now that - // the transaction has completed - if (isSharedConnection) { - setDelegate(null); - isSharedConnection = false; - } - // autoCommit may have been changed directly on the underlying connection clearCachedState(); - // If this connection was closed during the transaction and there is // still a delegate present close it final Connection delegate = getDelegateInternal(); if (isClosedInternal() && delegate != null) { try { setDelegate(null); - if (!delegate.isClosed()) { delegate.close(); } @@ -253,15 +257,12 @@ // our listener is called. In that rare case, trigger the transaction complete call now transactionComplete(); } - // the existing transaction context ended (or we didn't have one), get the active transaction context transactionContext = transactionRegistry.getActiveTransactionContext(); - // if there is an active transaction context, and it already has a shared connection, use it if (transactionContext != null && transactionContext.getSharedConnection() != null) { // A connection for the connection factory has already been enrolled // in the transaction, replace our delegate with the enrolled connection - // return current connection to the pool final C connection = getDelegateInternal(); setDelegate(null); @@ -277,17 +278,14 @@ } } } - // add a listener to the transaction context transactionContext.addTransactionContextListener(new CompletionListener()); - // Set our delegate to the shared connection. Note that this will // always be of type C since it has been shared by another // connection from the same pool. @SuppressWarnings("unchecked") final C shared = (C) transactionContext.getSharedConnection(); setDelegate(shared); - // remember that we are using a shared connection, so it can be cleared after the // transaction completes isSharedConnection = true; @@ -303,12 +301,10 @@ throw new SQLException("Unable to acquire a new connection from the pool", e); } } - // if we have a transaction, out delegate becomes the shared delegate if (transactionContext != null) { // add a listener to the transaction context transactionContext.addTransactionContextListener(new CompletionListener()); - // register our connection as the shared connection try { transactionContext.setSharedConnection(connection); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedDataSource.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedDataSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedDataSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/ManagedDataSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnection.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnection.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnection.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/PoolableManagedConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/SynchronizationAdapter.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/SynchronizationAdapter.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/SynchronizationAdapter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/SynchronizationAdapter.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContext.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContext.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContextListener.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContextListener.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContextListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionContextListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionRegistry.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionRegistry.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionRegistry.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/TransactionRegistry.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/XAConnectionFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/XAConnectionFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/XAConnectionFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/XAConnectionFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/package-info.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/package-info.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/managed/package-info.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/managed/package-info.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -29,7 +29,6 @@ * transaction is committed or rolled back the enlisted connections are * committed or rolled back. *

          - * *

          * This package supports full XADataSources and non-XA data sources using * local transaction semantics. non-XA data sources commit and rollback as diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/package-info.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/package-info.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/dbcp2/package-info.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/dbcp2/package-info.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/BaseObject.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/BaseObject.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/BaseObject.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/BaseObject.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -23,6 +23,13 @@ */ public abstract class BaseObject { + /** + * Constructs a new instance. + */ + public BaseObject() { + // empty + } + @Override public String toString() { final StringBuilder builder = new StringBuilder(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/BaseObjectPool.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/BaseObjectPool.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/BaseObjectPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/BaseObjectPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -33,6 +33,13 @@ private volatile boolean closed; /** + * Constructs a new instance. + */ + public BaseObjectPool() { + // empty + } + + /** * Not supported in this base implementation. Subclasses should override * this behavior. * @@ -49,7 +56,6 @@ * closed. * * @throws IllegalStateException when this pool has been closed. - * * @see #isClosed() */ protected final void assertOpen() throws IllegalStateException { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/DestroyMode.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/DestroyMode.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/DestroyMode.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/DestroyMode.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/KeyedObjectPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -49,7 +49,7 @@ * } * } *

          - * {@link KeyedObjectPool} implementations may choose to store at most + * {@link KeyedObjectPool} implementations may choose to store at most * one instance per key value, or may choose to maintain a pool of instances * for each key (essentially creating a {@link java.util.Map Map} of * {@link ObjectPool pools}). @@ -66,7 +66,6 @@ * @see KeyedPooledObjectFactory * @see ObjectPool * @see org.apache.tomcat.dbcp.pool2.impl.GenericKeyedObjectPool GenericKeyedObjectPool - * * @since 2.0 */ public interface KeyedObjectPool extends Closeable { @@ -78,7 +77,6 @@ * "pre-loading" a pool with idle objects (Optional operation). * * @param key the key a new instance should be added to - * * @throws Exception * when {@link KeyedPooledObjectFactory#makeObject} fails. * @throws IllegalStateException @@ -161,9 +159,7 @@ *

          * * @param key the key used to obtain the object - * * @return an instance from this pool. - * * @throws IllegalStateException * after {@link #close close} has been called on this pool * @throws Exception @@ -190,7 +186,6 @@ * the given {@code key} (optional operation). * * @param key the key to clear - * * @throws UnsupportedOperationException when this implementation doesn't * support the operation * @@ -280,7 +275,6 @@ * * @param key the key used to obtain the object * @param obj a {@link #borrowObject borrowed} instance to be returned. - * * @throws Exception if the instance cannot be invalidated */ void invalidateObject(K key, V obj) throws Exception; @@ -303,7 +297,6 @@ * @param key the key used to obtain the object * @param obj a {@link #borrowObject borrowed} instance to be returned. * @param destroyMode destroy activation context provided to the factory - * * @throws Exception if the instance cannot be invalidated * @since 2.9.0 */ @@ -320,7 +313,6 @@ * * @param key the key used to obtain the object * @param obj a {@link #borrowObject borrowed} instance to be returned. - * * @throws IllegalStateException * if an attempt is made to return an object to the pool that * is in any state other than allocated (i.e. borrowed). diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/KeyedPooledObjectFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/KeyedPooledObjectFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/KeyedPooledObjectFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/KeyedPooledObjectFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -62,15 +62,13 @@ * While clients of a {@link KeyedObjectPool} borrow and return instances of * the underlying value type V, the factory methods act on instances of * {@link PooledObject PooledObject<V>}. These are the object wrappers that - * pools use to track and maintain state informations about the objects that + * pools use to track and maintain state information about the objects that * they manage. *

          * * @see KeyedObjectPool - * * @param The type of keys managed by this factory. * @param Type of element managed by this factory. - * * @since 2.0 */ public interface KeyedPooledObjectFactory { @@ -80,7 +78,6 @@ * * @param key the key used when selecting the object * @param p a {@code PooledObject} wrapping the instance to be activated - * * @throws Exception if there is a problem activating {@code obj}, * this exception may be swallowed by the pool. * @@ -102,7 +99,6 @@ * * @param key the key used when selecting the instance * @param p a {@code PooledObject} wrapping the instance to be destroyed - * * @throws Exception should be avoided as it may be swallowed by * the pool implementation. * @@ -117,7 +113,6 @@ * @param key the key used when selecting the instance * @param p a {@code PooledObject} wrapping the instance to be destroyed * @param destroyMode DestroyMode providing context to the factory - * * @throws Exception should be avoided as it may be swallowed by * the pool implementation. * @@ -136,7 +131,6 @@ * wrap it in a {@link PooledObject} to be managed by the pool. * * @param key the key used when constructing the object - * * @return a {@code PooledObject} wrapping an instance that can * be served by the pool. * @@ -150,7 +144,6 @@ * * @param key the key used when selecting the object * @param p a {@code PooledObject} wrapping the instance to be passivated - * * @throws Exception if there is a problem passivating {@code obj}, * this exception may be swallowed by the pool. * @@ -163,7 +156,6 @@ * * @param key the key used when selecting the object * @param p a {@code PooledObject} wrapping the instance to be validated - * * @return {@code false} if {@code obj} is not valid and should * be dropped from the pool, {@code true} otherwise. */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/ObjectPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -54,7 +54,6 @@ * @see PooledObjectFactory * @see KeyedObjectPool * @see BaseObjectPool - * * @since 2.0 */ public interface ObjectPool extends Closeable { @@ -110,7 +109,6 @@ *

          * * @return an instance from this pool. - * * @throws IllegalStateException * after {@link #close close} has been called on this pool. * @throws Exception @@ -176,7 +174,6 @@ *

          * * @param obj a {@link #borrowObject borrowed} instance to be disposed. - * * @throws Exception if the instance cannot be invalidated */ void invalidateObject(T obj) throws Exception; @@ -209,7 +206,6 @@ * a related method as defined in an implementation or sub-interface. * * @param obj a {@link #borrowObject borrowed} instance to be returned. - * * @throws IllegalStateException * if an attempt is made to return an object to the pool that * is in any state other than allocated (i.e. borrowed). diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PoolUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; @@ -46,6 +47,9 @@ * frequently. */ private static final class ErodingFactor { + + private static final float MAX_INTERVAL = 15f; + /** Determines frequency of "erosion" events */ private final float factor; @@ -53,7 +57,9 @@ private transient volatile long nextShrinkMillis; /** High water mark - largest numIdle encountered */ - private transient volatile int idleHighWaterMark; + private transient volatile int idleHighWaterMark = 1; + + private final ReentrantLock lock = new ReentrantLock(); /** * Creates a new ErodingFactor with the given erosion factor. @@ -61,10 +67,9 @@ * @param factor * erosion factor */ - ErodingFactor(final float factor) { + private ErodingFactor(final float factor) { this.factor = factor; - nextShrinkMillis = System.currentTimeMillis() + (long) (900000 * factor); // now + 15 min * factor - idleHighWaterMark = 1; + nextShrinkMillis = System.currentTimeMillis() + (long) (900_000 * factor); // now + 15 min * factor } /** @@ -72,7 +77,7 @@ * * @return next shrink time */ - public long getNextShrink() { + private long getNextShrink() { return nextShrinkMillis; } @@ -81,7 +86,7 @@ */ @Override public String toString() { - return "ErodingFactor{" + "factor=" + factor + + return "ErodingFactor{factor=" + factor + ", idleHighWaterMark=" + idleHighWaterMark + '}'; } @@ -95,11 +100,14 @@ */ public void update(final long nowMillis, final int numIdle) { final int idle = Math.max(0, numIdle); - idleHighWaterMark = Math.max(idle, idleHighWaterMark); - final float maxInterval = 15f; - final float minutes = maxInterval + - (1f - maxInterval) / idleHighWaterMark * idle; - nextShrinkMillis = nowMillis + (long) (minutes * 60000f * factor); + lock.lock(); + try { + idleHighWaterMark = Math.max(idle, idleHighWaterMark); + final float minutes = MAX_INTERVAL + (1f - MAX_INTERVAL) / idleHighWaterMark * idle; + nextShrinkMillis = nowMillis + (long) (minutes * 60000f * factor); + } finally { + lock.unlock(); + } } } /** @@ -129,7 +137,7 @@ * events * @see #erodingFactor */ - protected ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, + private ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final ErodingFactor erodingFactor) { if (keyedPool == null) { throw new IllegalArgumentException( @@ -150,7 +158,7 @@ * events * @see #erodingFactor */ - ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, + private ErodingKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) { this(keyedPool, new ErodingFactor(factor)); } @@ -315,7 +323,7 @@ */ @Override public String toString() { - return "ErodingKeyedObjectPool{" + "factor=" + + return "ErodingKeyedObjectPool{factor=" + erodingFactor + ", keyedPool=" + keyedPool + '}'; } } @@ -326,9 +334,8 @@ * may be invalidated instead of being added to idle capacity. * * @param type of objects in the pool - * */ - private static class ErodingObjectPool implements ObjectPool { + private static final class ErodingObjectPool implements ObjectPool { /** Underlying object pool */ private final ObjectPool pool; @@ -347,7 +354,7 @@ * events * @see #factor */ - ErodingObjectPool(final ObjectPool pool, final float factor) { + private ErodingObjectPool(final ObjectPool pool, final float factor) { this.pool = pool; this.factor = new ErodingFactor(factor); } @@ -356,7 +363,7 @@ * {@inheritDoc} */ @Override - public void addObject() throws Exception{ + public void addObject() throws Exception { pool.addObject(); } @@ -458,7 +465,7 @@ */ @Override public String toString() { - return "ErodingObjectPool{" + "factor=" + factor + ", pool=" + + return "ErodingObjectPool{factor=" + factor + ", pool=" + pool + '}'; } } @@ -487,7 +494,7 @@ * @param factor * erosion factor */ - ErodingPerKeyKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) { + private ErodingPerKeyKeyedObjectPool(final KeyedObjectPool keyedPool, final float factor) { super(keyedPool, null); this.factor = factor; } @@ -507,7 +514,7 @@ */ @Override public String toString() { - return "ErodingPerKeyKeyedObjectPool{" + "factor=" + factor + + return "ErodingPerKeyKeyedObjectPool{factor=" + factor + ", keyedPool=" + getKeyedPool() + '}'; } } @@ -596,7 +603,6 @@ * as the pool's minIdle setting. * * @param type of objects in the pool - * */ private static final class ObjectPoolMinIdleTimerTask extends TimerTask { @@ -666,7 +672,7 @@ * A synchronized (thread-safe) KeyedObjectPool backed by the specified * KeyedObjectPool. *

          - * Note: This should not be used on pool implementations that already + * Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. Wrapping a pool that {@link #wait() waits} for poolable * objects to be returned before allowing another one to be borrowed with @@ -897,7 +903,7 @@ * KeyedPooledObjectFactory and synchronizes access to the wrapped factory * methods. *

          - * Note: This should not be used on pool implementations that already + * Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. *

          @@ -1012,7 +1018,7 @@ * A synchronized (thread-safe) ObjectPool backed by the specified * ObjectPool. *

          - * Note: This should not be used on pool implementations that already + * Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. Wrapping a pool that {@link #wait() waits} for poolable * objects to be returned before allowing another one to be borrowed with @@ -1021,7 +1027,6 @@ *

          * * @param type of objects in the pool - * */ private static final class SynchronizedObjectPool implements ObjectPool { @@ -1031,7 +1036,7 @@ */ private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); - /** the underlying object pool */ + /** The underlying object pool */ private final ObjectPool pool; /** @@ -1187,7 +1192,7 @@ * PooledObjectFactory and synchronizes access to the wrapped factory * methods. *

          - * Note: This should not be used on pool implementations that already + * Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. *

          @@ -1335,7 +1340,6 @@ * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. * @param the type of the pool key * @param the type of pool entries - * * @return a {@link Map} of key and {@link TimerTask} pairs that will * periodically check the pools idle object count. * @throws IllegalArgumentException @@ -1353,10 +1357,7 @@ throw new IllegalArgumentException(MSG_NULL_KEYS); } final Map tasks = new HashMap<>(keys.size()); - for (K key : keys) { - final TimerTask task = checkMinIdle(keyedPool, key, minIdle, periodMillis); - tasks.put(key, task); - } + keys.forEach(key -> tasks.put(key, checkMinIdle(keyedPool, key, minIdle, periodMillis))); return tasks; } @@ -1378,7 +1379,6 @@ * keyedPool, see {@link Timer#schedule(TimerTask, long, long)}. * @param the type of the pool key * @param the type of pool entries - * * @return the {@link TimerTask} that will periodically check the pools idle * object count. * @throws IllegalArgumentException @@ -1419,7 +1419,6 @@ * the frequency in milliseconds to check the number of idle objects in a pool, * see {@link Timer#schedule(TimerTask, long, long)}. * @param the type of objects in the pool - * * @return the {@link TimerTask} that will periodically check the pools idle * object count. * @throws IllegalArgumentException @@ -1476,7 +1475,6 @@ * count when possible. * @param the type of the pool key * @param the type of pool entries - * * @throws IllegalArgumentException * when {@code keyedPool} is {@code null}. * @return a pool that adaptively decreases its size when idle objects are @@ -1511,7 +1509,6 @@ * shrinks less aggressively. * @param the type of the pool key * @param the type of pool entries - * * @throws IllegalArgumentException * when {@code keyedPool} is {@code null} or when {@code factor} * is not positive. @@ -1554,7 +1551,6 @@ * when true, each key is treated independently. * @param the type of the pool key * @param the type of pool entries - * * @throws IllegalArgumentException * when {@code keyedPool} is {@code null} or when {@code factor} * is not positive. @@ -1589,7 +1585,6 @@ * the ObjectPool to be decorated so it shrinks its idle count * when possible. * @param the type of objects in the pool - * * @throws IllegalArgumentException * when {@code pool} is {@code null}. * @return a pool that adaptively decreases its size when idle objects are @@ -1622,7 +1617,6 @@ * shrinks more aggressively. If 1 < factor then the pool * shrinks less aggressively. * @param the type of objects in the pool - * * @throws IllegalArgumentException * when {@code pool} is {@code null} or when {@code factor} is * not positive. @@ -1663,7 +1657,6 @@ * the number of idle objects to add for each {@code key}. * @param the type of the pool key * @param the type of pool entries - * * @throws Exception * when {@link KeyedObjectPool#addObject(Object)} fails. * @throws IllegalArgumentException @@ -1694,7 +1687,6 @@ * the number of idle objects to add for {@code key}. * @param the type of the pool key * @param the type of pool entries - * * @throws Exception * when {@link KeyedObjectPool#addObject(Object)} fails. * @throws IllegalArgumentException @@ -1720,7 +1712,6 @@ * @param count * the number of idle objects to add. * @param the type of objects in the pool - * * @throws Exception * when {@link ObjectPool#addObject()} fails. * @throws IllegalArgumentException @@ -1748,7 +1739,7 @@ * @return a synchronized view of the specified KeyedPooledObjectFactory. */ public static KeyedPooledObjectFactory synchronizedKeyedPooledFactory( - final KeyedPooledObjectFactory keyedFactory) { + final KeyedPooledObjectFactory keyedFactory) { return new SynchronizedKeyedPooledObjectFactory<>(keyedFactory); } @@ -1756,7 +1747,7 @@ * Returns a synchronized (thread-safe) KeyedObjectPool backed by the * specified KeyedObjectPool. *

          - * Note: This should not be used on pool implementations that already + * Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. Wrapping a pool that {@link #wait() waits} for poolable * objects to be returned before allowing another one to be borrowed with @@ -1769,7 +1760,6 @@ * KeyedObjectPool. * @param the type of the pool key * @param the type of pool entries - * * @return a synchronized view of the specified KeyedObjectPool. */ public static KeyedObjectPool synchronizedPool(final KeyedObjectPool keyedPool) { @@ -1789,7 +1779,7 @@ * Returns a synchronized (thread-safe) ObjectPool backed by the specified * ObjectPool. *

          - * Note: This should not be used on pool implementations that already + * Note: This should not be used on pool implementations that already * provide proper synchronization such as the pools provided in the Commons * Pool library. Wrapping a pool that {@link #wait() waits} for poolable * objects to be returned before allowing another one to be borrowed with diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/PooledObject.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PooledObject.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/PooledObject.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PooledObject.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -34,14 +34,37 @@ public interface PooledObject extends Comparable> { /** - * Tests whether the given PooledObject is null or contains a null. + * Gets the wrapped object or null. * - * @param pooledObject the PooledObject to test. - * @return whether the given PooledObject is null or contains a null. + * @param the type of object in the pool. + * @param pooledObject the PooledObject to unwrap, may be null. + * @return the wrapped object or null. + * @since 2.13.0 + */ + static T getObject(final PooledObject pooledObject) { + return pooledObject != null ? pooledObject.getObject() : null; + } + + /** + * Tests whether the given PooledObject is null or wraps a null. + * + * @param pooledObject the PooledObject to test, may be null. + * @return whether the given PooledObject is null or wraps a null. * @since 2.12.0 */ static boolean isNull(final PooledObject pooledObject) { - return pooledObject == null || pooledObject.getObject() == null; + return getObject(pooledObject) == null; + } + + /** + * Tests whether the given PooledObject isn't null and doesn't wraps a null. + * + * @param pooledObject the PooledObject to test, may be null. + * @return whether the given PooledObject isn't null and doesn't wraps a null. + * @since 2.13.0 + */ + static boolean nonNull(final PooledObject pooledObject) { + return getObject(pooledObject) != null; } /** diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/PooledObjectFactory.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PooledObjectFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/PooledObjectFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PooledObjectFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -65,98 +65,90 @@ *

          * * @param Type of element managed in this factory. - * * @see ObjectPool - * * @since 2.0 */ public interface PooledObjectFactory { - /** - * Reinitializes an instance to be returned by the pool. - * - * @param p a {@code PooledObject} wrapping the instance to be activated - * - * @throws Exception if there is a problem activating {@code obj}, - * this exception may be swallowed by the pool. - * - * @see #destroyObject - */ - void activateObject(PooledObject p) throws Exception; - - /** - * Destroys an instance no longer needed by the pool, using the default (NORMAL) - * DestroyMode. - *

          - * It is important for implementations of this method to be aware that there - * is no guarantee about what state {@code obj} will be in and the - * implementation should be prepared to handle unexpected errors. - *

          - *

          - * Also, an implementation must take in to consideration that instances lost - * to the garbage collector may never be destroyed. - *

          - * - * @param p a {@code PooledObject} wrapping the instance to be destroyed - * - * @throws Exception should be avoided as it may be swallowed by - * the pool implementation. - * - * @see #validateObject - * @see ObjectPool#invalidateObject - */ - void destroyObject(PooledObject p) throws Exception; - - /** - * Destroys an instance no longer needed by the pool, using the provided - * DestroyMode. - * - * @param p a {@code PooledObject} wrapping the instance to be destroyed - * @param destroyMode DestroyMode providing context to the factory - * - * @throws Exception should be avoided as it may be swallowed by - * the pool implementation. - * - * @see #validateObject - * @see ObjectPool#invalidateObject - * @see #destroyObject(PooledObject) - * @see DestroyMode - * @since 2.9.0 - */ - default void destroyObject(final PooledObject p, final DestroyMode destroyMode) throws Exception { - destroyObject(p); - } - - /** - * Creates an instance that can be served by the pool and wrap it in a - * {@link PooledObject} to be managed by the pool. - * - * @return a {@code PooledObject} wrapping an instance that can be served by the pool, not null. - * - * @throws Exception if there is a problem creating a new instance, - * this will be propagated to the code requesting an object. - */ - PooledObject makeObject() throws Exception; - - /** - * Uninitializes an instance to be returned to the idle object pool. - * - * @param p a {@code PooledObject} wrapping the instance to be passivated - * - * @throws Exception if there is a problem passivating {@code obj}, - * this exception may be swallowed by the pool. - * - * @see #destroyObject - */ - void passivateObject(PooledObject p) throws Exception; - - /** - * Ensures that the instance is safe to be returned by the pool. - * - * @param p a {@code PooledObject} wrapping the instance to be validated - * - * @return {@code false} if {@code obj} is not valid and should - * be dropped from the pool, {@code true} otherwise. - */ - boolean validateObject(PooledObject p); + /** + * Reinitializes an instance to be returned by the pool. + * + * @param p a {@code PooledObject} wrapping the instance to be activated + * @throws Exception if there is a problem activating {@code obj}, + * this exception may be swallowed by the pool. + * + * @see #destroyObject + */ + void activateObject(PooledObject p) throws Exception; + + /** + * Destroys an instance no longer needed by the pool, using the default (NORMAL) + * DestroyMode. + *

          + * It is important for implementations of this method to be aware that there + * is no guarantee about what state {@code obj} will be in and the + * implementation should be prepared to handle unexpected errors. + *

          + *

          + * Also, an implementation must take in to consideration that instances lost + * to the garbage collector may never be destroyed. + *

          + * + * @param p a {@code PooledObject} wrapping the instance to be destroyed + * @throws Exception should be avoided as it may be swallowed by + * the pool implementation. + * + * @see #validateObject + * @see ObjectPool#invalidateObject + */ + void destroyObject(PooledObject p) throws Exception; + + /** + * Destroys an instance no longer needed by the pool, using the provided + * DestroyMode. + * + * @param p a {@code PooledObject} wrapping the instance to be destroyed + * @param destroyMode DestroyMode providing context to the factory + * @throws Exception should be avoided as it may be swallowed by + * the pool implementation. + * + * @see #validateObject + * @see ObjectPool#invalidateObject + * @see #destroyObject(PooledObject) + * @see DestroyMode + * @since 2.9.0 + */ + default void destroyObject(final PooledObject p, final DestroyMode destroyMode) throws Exception { + destroyObject(p); + } + + /** + * Creates an instance that can be served by the pool and wrap it in a + * {@link PooledObject} to be managed by the pool. + * + * @return a {@code PooledObject} wrapping an instance that can be served by the pool, not null. + * @throws Exception if there is a problem creating a new instance, + * this will be propagated to the code requesting an object. + */ + PooledObject makeObject() throws Exception; + + /** + * Uninitializes an instance to be returned to the idle object pool. + * + * @param p a {@code PooledObject} wrapping the instance to be passivated + * @throws Exception if there is a problem passivating {@code obj}, + * this exception may be swallowed by the pool. + * + * @see #destroyObject + */ + void passivateObject(PooledObject p) throws Exception; + + /** + * Ensures that the instance is safe to be returned by the pool. + * + * @param p a {@code PooledObject} wrapping the instance to be validated + * @return {@code false} if {@code obj} is not valid and should + * be dropped from the pool, {@code true} otherwise. + */ + boolean validateObject(PooledObject p); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/PooledObjectState.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PooledObjectState.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/PooledObjectState.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/PooledObjectState.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/SwallowedExceptionListener.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/SwallowedExceptionListener.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/SwallowedExceptionListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/SwallowedExceptionListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/TrackedUse.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/TrackedUse.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/TrackedUse.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/TrackedUse.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/UsageTracking.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/UsageTracking.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/UsageTracking.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/UsageTracking.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -22,7 +22,6 @@ * more informed decisions and reporting to be made regarding abandoned objects. * * @param The type of object provided by the pool. - * * @since 2.0 */ public interface UsageTracking { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/AbandonedConfig.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/AbandonedConfig.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/AbandonedConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/AbandonedConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -123,7 +123,6 @@ * * @return boolean true if stack trace logging is turned on for abandoned * objects - * */ public boolean getLogAbandoned() { return this.logAbandoned; @@ -240,7 +239,6 @@ * * @param logAbandoned true turns on abandoned stack trace logging * @see #getLogAbandoned() - * */ public void setLogAbandoned(final boolean logAbandoned) { this.logAbandoned = logAbandoned; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/BaseGenericObjectPool.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/BaseGenericObjectPool.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/BaseGenericObjectPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/BaseGenericObjectPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -65,7 +65,7 @@ /** * The idle object eviction iterator. Holds a reference to the idle objects. */ - class EvictionIterator implements Iterator> { + final class EvictionIterator implements Iterator> { private final Deque> idleObjects; private final Iterator> idleObjectIterator; @@ -117,7 +117,7 @@ * * @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis */ - class Evictor implements Runnable { + final class Evictor implements Runnable { private ScheduledFuture scheduledFuture; @@ -205,41 +205,53 @@ * @param type of objects in the pool */ static class IdentityWrapper { + + /** + * Creates a new instance for the object inside a {@link PooledObject}. + * + * @param The type of the object in the {@link PooledObject}. + * @param p The {@link PooledObject}. + * @return a new instance. + */ + static IdentityWrapper unwrap(final PooledObject p) { + return new IdentityWrapper<>(p.getObject()); + } + /** Wrapped object */ - private final T instance; + private final T object; /** * Constructs a wrapper for an instance. * - * @param instance object to wrap + * @param object object to wrap */ - IdentityWrapper(final T instance) { - this.instance = instance; + IdentityWrapper(final T object) { + this.object = object; } @Override @SuppressWarnings("rawtypes") public boolean equals(final Object other) { - return other instanceof IdentityWrapper && ((IdentityWrapper) other).instance == instance; + return other instanceof IdentityWrapper && ((IdentityWrapper) other).object == object; } /** * @return the wrapped object */ - public T getObject() { - return instance; + T getObject() { + return object; } @Override public int hashCode() { - return System.identityHashCode(instance); + return System.identityHashCode(object); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append("IdentityWrapper [instance="); - builder.append(instance); + builder.append(object); builder.append("]"); return builder.toString(); } @@ -249,7 +261,7 @@ * Maintains a cache of values for a single metric and reports * statistics on the cached values. */ - private static class StatsStore { + private static final class StatsStore { private static final int NONE = -1; private final AtomicLong[] values; @@ -395,6 +407,7 @@ private volatile SwallowedExceptionListener swallowedExceptionListener; private volatile boolean messageStatistics; + private volatile boolean collectDetailedStatistics = BaseObjectPoolConfig.DEFAULT_COLLECT_DETAILED_STATISTICS; /** Additional configuration properties for abandoned object tracking. */ protected volatile AbandonedConfig abandonedConfig; @@ -483,6 +496,16 @@ } /** + * Returns the duration since the given start time. + * + * @param startInstant the start time + * @return the duration since the given start time + */ + final Duration durationSince(final Instant startInstant) { + return Duration.between(startInstant, Instant.now()); + } + + /** * Tries to ensure that the configured minimum number of idle instances are * available in the pool. * @throws Exception if an error occurs creating idle instances @@ -525,6 +548,20 @@ } /** + * Gets whether detailed timing statistics collection is enabled. + * When {@code false}, the pool will not collect detailed timing statistics for + * mean active time, mean idle time, and mean borrow wait time, + * improving performance under high load. + * + * @return {@code true} if detailed statistics collection is enabled, + * {@code false} if disabled for improved performance. + * @since 2.13.0 + */ + public boolean getCollectDetailedStatistics() { + return collectDetailedStatistics; + } + + /** * Gets the total number of objects created for this pool over the lifetime of * the pool. * @return the created object count @@ -579,7 +616,6 @@ * will be run. * * @return number of milliseconds to sleep between evictor runs - * * @see #setTimeBetweenEvictionRuns * @since 2.11.0 */ @@ -603,7 +639,6 @@ * used by this pool. * * @return The fully qualified class name of the {@link EvictionPolicy} - * * @see #setEvictionPolicyClassName(String) */ public final String getEvictionPolicyClassName() { @@ -935,7 +970,6 @@ * tested per run. * * @return max number of objects to examine during each evictor run - * * @see #setNumTestsPerEvictionRun * @see #setTimeBetweenEvictionRunsMillis */ @@ -980,7 +1014,6 @@ * removal is configured for this pool; Integer.MAX_VALUE otherwise. * * @see AbandonedConfig#getRemoveAbandonedTimeoutDuration() - * @see AbandonedConfig#getRemoveAbandonedTimeoutDuration() * @deprecated Use {@link #getRemoveAbandonedTimeoutDuration()}. * @since 2.11.0 */ @@ -1150,7 +1183,6 @@ * being returned from the {@code borrowObject()} method * * @see #setTestOnCreate - * * @since 2.2 */ public final boolean getTestOnCreate() { @@ -1182,7 +1214,6 @@ * the pool and destroyed. * * @return {@code true} if objects will be validated by the evictor - * * @see #setTestWhileIdle * @see #setTimeBetweenEvictionRunsMillis */ @@ -1196,7 +1227,6 @@ * will be run. * * @return number of milliseconds to sleep between evictor runs - * * @see #setTimeBetweenEvictionRuns * @since 2.10.0 * @deprecated {@link #getDurationBetweenEvictionRuns()}. @@ -1212,7 +1242,6 @@ * will be run. * * @return number of milliseconds to sleep between evictor runs - * * @see #setTimeBetweenEvictionRunsMillis * @deprecated Use {@link #getDurationBetweenEvictionRuns()}. */ @@ -1233,8 +1262,9 @@ } /** - * Tests whether this pool instance been closed. - * @return {@code true} when this pool has been closed. + * Tests whether this pool instance is closed. + * + * @return {@code true} when this pool is closed. */ public final boolean isClosed() { return closed; @@ -1265,7 +1295,7 @@ } while (!registered) { try { - ObjectName objName; + final ObjectName objName; // Skip the numeric suffix for the first pool in case there is // only one so the names are cleaner. if (i == 1) { @@ -1333,7 +1363,6 @@ * Sets the abandoned object removal configuration. * * @param abandonedConfig the new configuration to use. This is used by value. - * * @see AbandonedConfig * @since 2.11.0 */ @@ -1357,6 +1386,21 @@ } /** + * Sets whether detailed timing statistics collection is enabled. + * When {@code false}, the pool will not collect detailed timing statistics, + * improving performance under high load at the cost of reduced monitoring capabilities. + *

          + * This setting affects data collection for mean active time, mean idle time, and mean borrow wait time. + *

          + * + * @param collectDetailedStatistics whether to collect detailed statistics. + * @since 2.13.0 + */ + public void setCollectDetailedStatistics(final boolean collectDetailedStatistics) { + this.collectDetailedStatistics = collectDetailedStatistics; + } + + /** * Sets the receiver with the given configuration. * * @param config Initialization source. @@ -1382,6 +1426,7 @@ setEvictionPolicy(policy); } setEvictorShutdownTimeout(config.getEvictorShutdownTimeoutDuration()); + setCollectDetailedStatistics(config.getCollectDetailedStatistics()); } /** @@ -1833,7 +1878,6 @@ * {@code borrowObject()} method * * @see #getTestOnCreate - * * @since 2.2 */ public final void setTestOnCreate(final boolean testOnCreate) { @@ -1954,6 +1998,7 @@ startEvictor(Duration.ofMillis(-1L)); } + /** * Swallows an exception and notifies the configured listener for swallowed * exceptions queue. @@ -2054,17 +2099,19 @@ */ final void updateStatsBorrow(final PooledObject p, final Duration waitDuration) { borrowedCount.incrementAndGet(); - idleTimes.add(p.getIdleDuration()); - waitTimes.add(waitDuration); - - // lock-free optimistic-locking maximum - Duration currentMaxDuration; - do { - currentMaxDuration = maxBorrowWaitDuration.get(); - if (currentMaxDuration.compareTo(waitDuration) >= 0) { - break; - } - } while (!maxBorrowWaitDuration.compareAndSet(currentMaxDuration, waitDuration)); + // Only collect detailed statistics if enabled + if (collectDetailedStatistics) { + idleTimes.add(p.getIdleDuration()); + waitTimes.add(waitDuration); + // lock-free optimistic-locking maximum + Duration currentMaxDuration; + do { + currentMaxDuration = maxBorrowWaitDuration.get(); + if (currentMaxDuration.compareTo(waitDuration) >= 0) { + break; + } + } while (!maxBorrowWaitDuration.compareAndSet(currentMaxDuration, waitDuration)); + } } /** @@ -2075,8 +2122,25 @@ */ final void updateStatsReturn(final Duration activeTime) { returnedCount.incrementAndGet(); - activeTimes.add(activeTime); + // Only collect detailed statistics if enabled + if (collectDetailedStatistics) { + activeTimes.add(activeTime); + } } + /** + * Waits for notification on the given object for the specified duration. + * Duration.ZERO causes the thread to wait indefinitely. + * + * @param obj the object to wait on + * @param duration the duration to wait + * @throws InterruptedException if interrupted while waiting + * @throws IllegalArgumentException if the duration is negative + */ + final void wait(final Object obj, final Duration duration) throws InterruptedException { + if (!duration.isNegative()) { + obj.wait(duration.toMillis(), duration.getNano() % 1_000_000); + } + } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/BaseObjectPoolConfig.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/BaseObjectPoolConfig.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/BaseObjectPoolConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/BaseObjectPoolConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -33,7 +33,7 @@ public abstract class BaseObjectPoolConfig extends BaseObject implements Cloneable { /** - * The default value for the {@code lifo} configuration attribute. + * The default value for the {@code lifo} configuration attribute: {@value}. * * @see GenericObjectPool#getLifo() * @see GenericKeyedObjectPool#getLifo() @@ -41,7 +41,7 @@ public static final boolean DEFAULT_LIFO = true; /** - * The default value for the {@code fairness} configuration attribute. + * The default value for the {@code fairness} configuration attribute: {@value}. * * @see GenericObjectPool#getFairness() * @see GenericKeyedObjectPool#getFairness() @@ -49,7 +49,7 @@ public static final boolean DEFAULT_FAIRNESS = false; /** - * The default value for the {@code maxWait} configuration attribute. + * The default value for the {@code maxWait} configuration attribute: {@value}. * * @see GenericObjectPool#getMaxWaitDuration() * @see GenericKeyedObjectPool#getMaxWaitDuration() @@ -68,7 +68,7 @@ public static final Duration DEFAULT_MAX_WAIT = Duration.ofMillis(DEFAULT_MAX_WAIT_MILLIS); /** - * The default value for the {@code minEvictableIdleDuration} configuration attribute. + * The default value for the {@code minEvictableIdleDuration} configuration attribute: {@value}. * * @see GenericObjectPool#getMinEvictableIdleDuration() * @see GenericKeyedObjectPool#getMinEvictableIdleDuration() @@ -98,7 +98,7 @@ public static final Duration DEFAULT_MIN_EVICTABLE_IDLE_TIME = Duration.ofMillis(DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS); /** - * The default value for the {@code softMinEvictableIdleTime} configuration attribute. + * The default value for the {@code softMinEvictableIdleTime} configuration attribute: {@value}. * * @see GenericObjectPool#getSoftMinEvictableIdleDuration() * @see GenericKeyedObjectPool#getSoftMinEvictableIdleDuration() @@ -128,7 +128,7 @@ public static final Duration DEFAULT_SOFT_MIN_EVICTABLE_IDLE_DURATION = Duration.ofMillis(DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS); /** - * The default value for {@code evictorShutdownTimeout} configuration attribute. + * The default value for {@code evictorShutdownTimeout} configuration attribute: {@value}. * * @see GenericObjectPool#getEvictorShutdownTimeoutDuration() * @see GenericKeyedObjectPool#getEvictorShutdownTimeoutDuration() @@ -147,7 +147,7 @@ public static final Duration DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT = Duration.ofMillis(DEFAULT_EVICTOR_SHUTDOWN_TIMEOUT_MILLIS); /** - * The default value for the {@code numTestsPerEvictionRun} configuration attribute. + * The default value for the {@code numTestsPerEvictionRun} configuration attribute: {@value}. * * @see GenericObjectPool#getNumTestsPerEvictionRun() * @see GenericKeyedObjectPool#getNumTestsPerEvictionRun() @@ -155,17 +155,16 @@ public static final int DEFAULT_NUM_TESTS_PER_EVICTION_RUN = 3; /** - * The default value for the {@code testOnCreate} configuration attribute. + * The default value for the {@code testOnCreate} configuration attribute: {@value}. * * @see GenericObjectPool#getTestOnCreate() * @see GenericKeyedObjectPool#getTestOnCreate() - * * @since 2.2 */ public static final boolean DEFAULT_TEST_ON_CREATE = false; /** - * The default value for the {@code testOnBorrow} configuration attribute. + * The default value for the {@code testOnBorrow} configuration attribute: {@value}. * * @see GenericObjectPool#getTestOnBorrow() * @see GenericKeyedObjectPool#getTestOnBorrow() @@ -173,7 +172,7 @@ public static final boolean DEFAULT_TEST_ON_BORROW = false; /** - * The default value for the {@code testOnReturn} configuration attribute. + * The default value for the {@code testOnReturn} configuration attribute: {@value}. * * @see GenericObjectPool#getTestOnReturn() * @see GenericKeyedObjectPool#getTestOnReturn() @@ -181,7 +180,7 @@ public static final boolean DEFAULT_TEST_ON_RETURN = false; /** - * The default value for the {@code testWhileIdle} configuration attribute. + * The default value for the {@code testWhileIdle} configuration attribute: {@value}. * * @see GenericObjectPool#getTestWhileIdle() * @see GenericKeyedObjectPool#getTestWhileIdle() @@ -189,7 +188,7 @@ public static final boolean DEFAULT_TEST_WHILE_IDLE = false; /** - * The default value for the {@code timeBetweenEvictionRuns} configuration attribute. + * The default value for the {@code timeBetweenEvictionRuns} configuration attribute: {@value}. * * @see GenericObjectPool#getDurationBetweenEvictionRuns() * @see GenericKeyedObjectPool#getDurationBetweenEvictionRuns() @@ -218,7 +217,7 @@ public static final Duration DEFAULT_TIME_BETWEEN_EVICTION_RUNS = Duration.ofMillis(DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS); /** - * The default value for the {@code blockWhenExhausted} configuration attribute. + * The default value for the {@code blockWhenExhausted} configuration attribute: {@value}. * * @see GenericObjectPool#getBlockWhenExhausted() * @see GenericKeyedObjectPool#getBlockWhenExhausted() @@ -226,12 +225,12 @@ public static final boolean DEFAULT_BLOCK_WHEN_EXHAUSTED = true; /** - * The default value for enabling JMX for pools created with a configuration instance. + * The default value for enabling JMX for pools created with a configuration instance: {@value}. */ public static final boolean DEFAULT_JMX_ENABLE = true; /** - * The default value for the prefix used to name JMX enabled pools created with a configuration instance. + * The default value for the prefix used to name JMX enabled pools created with a configuration instance: {@value}. * * @see GenericObjectPool#getJmxName() * @see GenericKeyedObjectPool#getJmxName() @@ -255,6 +254,19 @@ */ public static final String DEFAULT_EVICTION_POLICY_CLASS_NAME = DefaultEvictionPolicy.class.getName(); + /** + * The default value for the {@code collectDetailedStatistics} configuration + * attribute. When {@code true}, the pool will collect detailed timing statistics + * for monitoring purposes. When {@code false}, detailed statistics collection + * is disabled, improving performance under high load. + *

          + * This setting affects data collection for mean active time, mean idle time, and mean borrow wait time. + *

          + * + * @since 2.13.0 + */ + public static final boolean DEFAULT_COLLECT_DETAILED_STATISTICS = true; + private boolean lifo = DEFAULT_LIFO; private boolean fairness = DEFAULT_FAIRNESS; @@ -292,6 +304,15 @@ private String jmxNameBase = DEFAULT_JMX_NAME_BASE; + private boolean collectDetailedStatistics = DEFAULT_COLLECT_DETAILED_STATISTICS; + + /** + * Constructs a new instance. + */ + public BaseObjectPoolConfig() { + // empty + } + /** * Gets the value for the {@code blockWhenExhausted} configuration attribute for pools created with this configuration instance. * @@ -304,6 +325,23 @@ } /** + * Gets the value for the {@code collectDetailedStatistics} configuration attribute + * for pools created with this configuration instance. + *

          + * This setting affects data collection for mean active time, mean idle time, and mean borrow wait time. + *

          + * + * @return {@code true} if detailed statistics collection is enabled, + * {@code false} if disabled for improved performance. + * @see GenericObjectPool#getCollectDetailedStatistics() + * @see GenericKeyedObjectPool#getCollectDetailedStatistics() + * @since 2.13.0 + */ + public boolean getCollectDetailedStatistics() { + return collectDetailedStatistics; + } + + /** * Gets the value for the {@code timeBetweenEvictionRuns} configuration attribute for pools created with this configuration instance. * * @return The current setting of {@code timeBetweenEvictionRuns} for this configuration instance @@ -420,7 +458,6 @@ * Gets the value for the {@code lifo} configuration attribute for pools created with this configuration instance. * * @return The current setting of {@code lifo} for this configuration instance - * * @see GenericObjectPool#getLifo() * @see GenericKeyedObjectPool#getLifo() */ @@ -626,6 +663,25 @@ } /** + * Sets the value for the {@code collectDetailedStatistics} configuration attribute + * for pools created with this configuration instance. When {@code false}, the pool + * will not collect detailed timing statistics, improving performance under high load + * at the cost of reduced monitoring capabilities. + *

          + * This setting affects data collection for mean active time, mean idle time, and mean borrow wait time. + *

          + * + * @param collectDetailedStatistics The new setting of {@code collectDetailedStatistics} + * for this configuration instance. + * @see GenericObjectPool#getCollectDetailedStatistics() + * @see GenericKeyedObjectPool#getCollectDetailedStatistics() + * @since 2.13.0 + */ + public void setCollectDetailedStatistics(final boolean collectDetailedStatistics) { + this.collectDetailedStatistics = collectDetailedStatistics; + } + + /** * Sets the value for the {@code evictionPolicyClass} configuration attribute for pools created with this configuration instance. * * @param evictionPolicy The new setting of {@code evictionPolicyClass} for this configuration instance @@ -955,5 +1011,7 @@ builder.append(jmxNamePrefix); builder.append(", jmxNameBase="); builder.append(jmxNameBase); + builder.append(", collectDetailedStatistics="); + builder.append(collectDetailedStatistics); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/CallStack.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/CallStack.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/CallStack.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/CallStack.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultEvictionPolicy.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -31,18 +31,24 @@ * {@link GenericKeyedObjectPoolConfig#getMinIdlePerKey()} idle objects in * the pool and the object has been idle for longer than * {@link GenericObjectPool#getSoftMinEvictableIdleDuration()} / - * {@link GenericKeyedObjectPool#getSoftMinEvictableIdleDuration()} + * {@link GenericKeyedObjectPool#getSoftMinEvictableIdleDuration()} * *

          * This class is immutable and thread-safe. *

          * * @param the type of objects in the pool. - * * @since 2.0 */ public class DefaultEvictionPolicy implements EvictionPolicy { + /** + * Constructs a new instance. + */ + public DefaultEvictionPolicy() { + // empty + } + @Override public boolean evict(final EvictionConfig config, final PooledObject underTest, final int idleCount) { // @formatter:off diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObject.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObject.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObject.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObject.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -34,7 +34,6 @@ *

          * * @param the type of object in the pool - * * @since 2.0 */ public class DefaultPooledObject implements PooledObject { @@ -339,5 +338,4 @@ usedBy.fillInStackTrace(); } - } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfo.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfo.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -24,7 +24,7 @@ import org.apache.tomcat.dbcp.pool2.PooledObject; /** - * Implementation of object that is used to provide information on pooled + * Implements providing information on pooled * objects via JMX. * * @since 2.0 @@ -65,7 +65,6 @@ return pooledObject.getLastBorrowInstant().toEpochMilli(); } - @Override public String getLastBorrowTimeFormatted() { return getTimeMillisFormatted(getLastBorrowTime()); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfoMBean.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfoMBean.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfoMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/DefaultPooledObjectInfoMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -90,7 +90,6 @@ * Gets a String form of the wrapper for debug purposes. The format is not fixed and may change at any time. * * @return A string representation of the pooled object. - * * @see Object#toString() */ String getPooledObjectToString(); @@ -99,7 +98,6 @@ * Gets the name of the class of the pooled object. * * @return The pooled object's class name. - * * @see Class#getName() */ String getPooledObjectType(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/EvictionConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/EvictionPolicy.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/EvictionPolicy.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/EvictionPolicy.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/EvictionPolicy.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -23,7 +23,7 @@ * DefaultEvictionPolicy} for a pool, users must provide an implementation of * this interface that provides the required eviction policy. * - * @param the type of objects in the pool + * @param the type of objects in the pool. * @since 2.0 */ public interface EvictionPolicy { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/EvictionTimer.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -21,19 +21,19 @@ import java.security.PrivilegedAction; import java.time.Duration; import java.util.HashMap; +import java.util.Iterator; import java.util.Map.Entry; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; - /** * Provides a shared idle object eviction timer for all pools. *

          * This class is currently implemented using {@link ScheduledThreadPoolExecutor}. This implementation may change in any * future release. This class keeps track of how many pools are using it. If no pools are using the timer, it is - * cancelled. This prevents a thread being left running which, in application server environments, can lead to memory + * canceled. This prevents a thread being left running which, in application server environments, can lead to memory * leads and/or prevent applications from shutting down or reloading cleanly. *

          *

          @@ -46,12 +46,12 @@ * * @since 2.0 */ -class EvictionTimer { +final class EvictionTimer { /** * Thread factory that creates a daemon thread, with the context class loader from this class. */ - private static class EvictorThreadFactory implements ThreadFactory { + private static final class EvictorThreadFactory implements ThreadFactory { @Override public Thread newThread(final Runnable runnable) { @@ -70,15 +70,21 @@ * Task that removes references to abandoned tasks and shuts * down the executor if there are no live tasks left. */ - private static class Reaper implements Runnable { + private static final class Reaper implements Runnable { @Override public void run() { synchronized (EvictionTimer.class) { - for (final Entry.Evictor>, WeakRunner.Evictor>> entry : TASK_MAP - .entrySet()) { + /* + * Need to use iterator over TASK_MAP so entries can be removed when iterating without triggering a + * ConcurrentModificationException. + */ + final Iterator.Evictor>, WeakRunner.Evictor>>> iterator = + TASK_MAP.entrySet().iterator(); + while (iterator.hasNext()) { + final Entry.Evictor>, WeakRunner.Evictor>> entry = iterator.next(); if (entry.getKey().get() == null) { executor.remove(entry.getValue()); - TASK_MAP.remove(entry.getKey()); + iterator.remove(); } } if (TASK_MAP.isEmpty() && executor != null) { @@ -95,7 +101,7 @@ * no longer reachable, run is no-op. * @param The kind of Runnable. */ - private static class WeakRunner implements Runnable { + private static final class WeakRunner implements Runnable { private final WeakReference ref; @@ -105,7 +111,7 @@ * @param ref the reference to track. */ private WeakRunner(final WeakReference ref) { - this.ref = ref; + this.ref = ref; } @Override @@ -114,25 +120,26 @@ if (task != null) { task.run(); } else { - executor.remove(this); - TASK_MAP.remove(ref); + synchronized (EvictionTimer.class) { + executor.remove(this); + TASK_MAP.remove(ref); + } } } } - /** Executor instance */ private static ScheduledThreadPoolExecutor executor; //@GuardedBy("EvictionTimer.class") /** Keys are weak references to tasks, values are runners managed by executor. */ private static final HashMap< - WeakReference.Evictor>, - WeakRunner.Evictor>> TASK_MAP = new HashMap<>(); // @GuardedBy("EvictionTimer.class") + WeakReference.Evictor>, + WeakRunner.Evictor>> TASK_MAP = new HashMap<>(); // @GuardedBy("EvictionTimer.class") /** * Removes the specified eviction task from the timer. * - * @param evictor Task to be cancelled. + * @param evictor Task to be canceled. * @param timeout If the associated executor is no longer required, how * long should this thread wait for the executor to * terminate? @@ -197,8 +204,8 @@ * server environments. * * @param task Task to be scheduled. - * @param delay Delay in milliseconds before task is executed. - * @param period Time in milliseconds between executions. + * @param delay Duration before task is executed. + * @param period Duration between executions. */ static synchronized void schedule( final BaseGenericObjectPool.Evictor task, final Duration delay, final Duration period) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -28,8 +28,8 @@ import java.util.NoSuchElementException; import java.util.Objects; import java.util.TreeMap; +import java.util.concurrent.BlockingDeque; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -83,10 +83,8 @@ *

          * * @see GenericObjectPool - * * @param The type of keys maintained by this pool. * @param Type of element pooled in this pool. - * * @since 2.0 */ public class GenericKeyedObjectPool extends BaseGenericObjectPool @@ -97,7 +95,7 @@ * * @param type of objects in the pool */ - private static class ObjectDeque { + private static final class ObjectDeque { private final LinkedBlockingDeque> idleObjects; @@ -105,7 +103,7 @@ * Number of instances created - number destroyed. * Invariant: createCount <= maxTotalPerKey */ - private final AtomicInteger createCount = new AtomicInteger(0); + private final AtomicInteger createCount = new AtomicInteger(); private long makeObjectCount; private final Object makeObjectCountLock = new Object(); @@ -130,16 +128,16 @@ * @param fairness true means client threads waiting to borrow / return instances * will be served as if waiting in a FIFO queue. */ - ObjectDeque(final boolean fairness) { + private ObjectDeque(final boolean fairness) { idleObjects = new LinkedBlockingDeque<>(fairness); } /** * Gets all the objects for the current key. * - * @return All the objects + * @return All the objects, */ - public Map, PooledObject> getAllObjects() { + Map, PooledObject> getAllObjects() { return allObjects; } @@ -147,27 +145,27 @@ * Gets the number of instances created - number destroyed. * Should always be less than or equal to maxTotalPerKey. * - * @return The net instance addition count for this deque + * @return The net instance addition count for this deque. */ - public AtomicInteger getCreateCount() { + AtomicInteger getCreateCount() { return createCount; } /** * Gets the idle objects for the current key. * - * @return The idle objects + * @return The idle objects. */ - public LinkedBlockingDeque> getIdleObjects() { + LinkedBlockingDeque> getIdleObjects() { return idleObjects; } /** * Gets the number of threads with an interest registered in this key. * - * @return The number of threads with a registered interest in this key + * @return The number of threads with a registered interest in this key. */ - public AtomicLong getNumInterested() { + AtomicLong getNumInterested() { return numInterested; } @@ -200,25 +198,35 @@ private volatile int minIdlePerKey = GenericKeyedObjectPoolConfig.DEFAULT_MIN_IDLE_PER_KEY; - private volatile int maxTotalPerKey = GenericKeyedObjectPoolConfig.DEFAULT_MAX_TOTAL_PER_KEY; + private volatile boolean reuseCapacityOnReturn = + GenericKeyedObjectPoolConfig.DEFAULT_REUSE_CAPACITY_ON_RETURN; + + private volatile boolean reuseCapacityOnMaintenance = + GenericKeyedObjectPoolConfig.DEFAULT_REUSE_CAPACITY_ON_MAINTENANCE; + private final KeyedPooledObjectFactory factory; private final boolean fairness; /* - * My hash of sub-pools (ObjectQueue). The list of keys must be kept + * My hash of sub-pools (ObjectQueue). The list of keys must be kept * in step with {@link #poolKeyList} using {@link #keyLock} to ensure any * changes to the list of current keys is made in a thread-safe manner. + * + * Correct operation of the pool requires that a Map implementation is used that + * supports concurrent read and write (e.g. ensureMinIdle() iterates over the key set + * while other threads may be adding or removing keys) therefore explicitly define + * this field as ConcurrentHashMap rather than Map. */ - private final Map> poolMap = + private final ConcurrentHashMap> poolMap = new ConcurrentHashMap<>(); // @GuardedBy("keyLock") for write access (and some read access) /* * List of pool keys - used to control eviction order. The list of keys - * must be kept in step with {@link #poolMap} using {@link #keyLock} + * must be kept in step with {@link #poolMap} using {@link #keyLock} * to ensure any changes to the list of current keys is made in a * thread-safe manner. */ @@ -232,11 +240,10 @@ * {@link #maxTotal} but there will never be more than {@link #maxTotal} * created at any one time. */ - private final AtomicInteger numTotal = new AtomicInteger(0); + private final AtomicInteger numTotal = new AtomicInteger(); private Iterator evictionKeyIterator; // @GuardedBy("evictionLock") - private K evictionKey; // @GuardedBy("evictionLock") /** @@ -299,13 +306,12 @@ * * @param key The key to associate with the idle object * @param p The wrapped object to add. - * * @throws Exception If the associated factory fails to passivate the object */ private void addIdleObject(final K key, final PooledObject p) throws Exception { - if (!PooledObject.isNull(p)) { + if (PooledObject.nonNull(p)) { factory.passivateObject(key, p); - final LinkedBlockingDeque> idleObjects = poolMap.get(key).getIdleObjects(); + final BlockingDeque> idleObjects = poolMap.get(key).getIdleObjects(); if (getLifo()) { idleObjects.addFirst(p); } else { @@ -329,16 +335,24 @@ *

          * * @param key the key a new instance should be added to - * * @throws Exception when {@link KeyedPooledObjectFactory#makeObject} * fails. */ @Override public void addObject(final K key) throws Exception { assertOpen(); - register(key); + final ObjectDeque objectDeque = register(key); try { - addIdleObject(key, create(key)); + // Attempt create and add only if there is capacity to add + // > to the overall instance count + // > to the pool under the key + final int maxtTotalPerKey = getMaxTotalPerKey(); + final int maxTotal = getMaxTotal(); + if ((maxTotal < 0 || getNumActive() + getNumIdle() < maxTotal) + && (maxtTotalPerKey < 0 || objectDeque.allObjects.size() < maxtTotalPerKey)) { + // Attempt to create and add a new instance under key + addIdleObject(key, create(key, getMaxWaitDuration())); + } } finally { deregister(key); } @@ -406,20 +420,21 @@ *

          * * @param key pool key - * @param borrowMaxWaitMillis The time to wait in milliseconds for an object - * to become available + * @param maxWaitDuration The time to wait for an object to become + * available * * @return object instance from the keyed pool - * * @throws NoSuchElementException if a keyed object instance cannot be * returned because the pool is exhausted. * * @throws Exception if a keyed object instance cannot be returned due to an * error + * @since 2.12.2 */ - public T borrowObject(final K key, final long borrowMaxWaitMillis) throws Exception { + public T borrowObject(final K key, final Duration maxWaitDuration) throws Exception { assertOpen(); - + final Instant startInstant = Instant.now(); + Duration remainingWaitDuration = maxWaitDuration; final AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnBorrow() && getNumIdle() < 2 && getNumActive() > getMaxTotal() - 3) { @@ -433,27 +448,28 @@ final boolean blockWhenExhausted = getBlockWhenExhausted(); boolean create; - final Instant waitTime = Instant.now(); final ObjectDeque objectDeque = register(key); try { while (p == null) { + remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant)); create = false; p = objectDeque.getIdleObjects().pollFirst(); if (p == null) { - p = create(key); - if (!PooledObject.isNull(p)) { + p = create(key, remainingWaitDuration); + if (PooledObject.nonNull(p)) { create = true; } + remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant)); } if (blockWhenExhausted) { if (PooledObject.isNull(p)) { - p = borrowMaxWaitMillis < 0 ? objectDeque.getIdleObjects().takeFirst(): - objectDeque.getIdleObjects().pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS); + p = maxWaitDuration.isNegative() ? objectDeque.getIdleObjects().takeFirst() + : objectDeque.getIdleObjects().pollFirst(remainingWaitDuration); } if (PooledObject.isNull(p)) { throw new NoSuchElementException(appendStats( - "Timeout waiting for idle object, borrowMaxWaitMillis=" + borrowMaxWaitMillis)); + "Timeout waiting for idle object, borrowMaxWaitMillis=" + maxWaitDuration.toMillis())); } } else if (PooledObject.isNull(p)) { throw new NoSuchElementException(appendStats("Pool exhausted")); @@ -462,7 +478,7 @@ p = null; } - if (!PooledObject.isNull(p)) { + if (PooledObject.nonNull(p)) { try { factory.activateObject(key, p); } catch (final Exception e) { @@ -473,12 +489,13 @@ } p = null; if (create) { - final NoSuchElementException nsee = new NoSuchElementException(appendStats("Unable to activate object")); + final NoSuchElementException nsee = new NoSuchElementException( + appendStats("Unable to activate object")); nsee.initCause(e); throw nsee; } } - if (!PooledObject.isNull(p) && getTestOnBorrow()) { + if (PooledObject.nonNull(p) && getTestOnBorrow()) { boolean validate = false; Throwable validationThrowable = null; try { @@ -509,18 +526,84 @@ deregister(key); } - updateStatsBorrow(p, Duration.between(waitTime, Instant.now())); + updateStatsBorrow(p, Duration.between(startInstant, Instant.now())); return p.getObject(); } + + /** + * Borrows an object from the sub-pool associated with the given key using + * the specified waiting time which only applies if + * {@link #getBlockWhenExhausted()} is true. + *

          + * If there is one or more idle instances available in the sub-pool + * associated with the given key, then an idle instance will be selected + * based on the value of {@link #getLifo()}, activated and returned. If + * activation fails, or {@link #getTestOnBorrow() testOnBorrow} is set to + * {@code true} and validation fails, the instance is destroyed and the + * next available instance is examined. This continues until either a valid + * instance is returned or there are no more idle instances available. + *

          + *

          + * If there are no idle instances available in the sub-pool associated with + * the given key, behavior depends on the {@link #getMaxTotalPerKey() + * maxTotalPerKey}, {@link #getMaxTotal() maxTotal}, and (if applicable) + * {@link #getBlockWhenExhausted()} and the value passed in to the + * {@code borrowMaxWaitMillis} parameter. If the number of instances checked + * out from the sub-pool under the given key is less than + * {@code maxTotalPerKey} and the total number of instances in + * circulation (under all keys) is less than {@code maxTotal}, a new + * instance is created, activated and (if applicable) validated and returned + * to the caller. If validation fails, a {@code NoSuchElementException} + * will be thrown. If the factory returns null when creating an instance, + * a {@code NullPointerException} is thrown. + *

          + *

          + * If the associated sub-pool is exhausted (no available idle instances and + * no capacity to create new ones), this method will either block + * ({@link #getBlockWhenExhausted()} is true) or throw a + * {@code NoSuchElementException} + * ({@link #getBlockWhenExhausted()} is false). + * The length of time that this method will block when + * {@link #getBlockWhenExhausted()} is true is determined by the value + * passed in to the {@code borrowMaxWait} parameter. + *

          + *

          + * When {@code maxTotal} is set to a positive value and this method is + * invoked when at the limit with no idle instances available under the requested + * key, an attempt is made to create room by clearing the oldest 15% of the + * elements from the keyed sub-pools. + *

          + *

          + * When the pool is exhausted, multiple calling threads may be + * simultaneously blocked waiting for instances to become available. A + * "fairness" algorithm has been implemented to ensure that threads receive + * available instances in request arrival order. + *

          + * + * @param key pool key + * @param maxWaitMillis The time to wait in milliseconds for an object to become + * available + * + * @return object instance from the keyed pool + * @throws NoSuchElementException if a keyed object instance cannot be + * returned because the pool is exhausted. + * + * @throws Exception if a keyed object instance cannot be returned due to an + * error + */ + + public T borrowObject(final K key, final long maxWaitMillis) throws Exception { + return borrowObject(key, Duration.ofMillis(maxWaitMillis)); + } + /** * Calculate the number of objects that need to be created to attempt to * maintain the minimum number of idle objects while not exceeded the limits * on the maximum number of objects either per key or totally. * * @param objectDeque The set of objects to check - * * @return The number of new objects to create */ private int calculateDeficit(final ObjectDeque objectDeque) { @@ -615,7 +698,7 @@ final ObjectDeque objectDeque = register(key); int freedCapacity = 0; try { - final LinkedBlockingDeque> idleObjects = objectDeque.getIdleObjects(); + final BlockingDeque> idleObjects = objectDeque.getIdleObjects(); PooledObject p = idleObjects.poll(); while (p != null) { try { @@ -644,11 +727,9 @@ // build sorted map of idle objects final TreeMap, K> map = new TreeMap<>(); - poolMap.forEach((key, value) -> { - // Each item into the map using the PooledObject object as the - // key. It then gets sorted based on the idle time - value.getIdleObjects().forEach(p -> map.put(p, key)); - }); + // Each item into the map using the PooledObject object as the + // key. It then gets sorted based on the idle time + poolMap.forEach((key, value) -> value.getIdleObjects().forEach(p -> map.put(p, key))); // Now iterate created map and kill the first 15% plus one to account // for zero @@ -657,9 +738,9 @@ while (iter.hasNext() && itemsToRemove > 0) { final Entry, K> entry = iter.next(); - // kind of backwards on naming. In the map, each key is the + // kind of backwards on naming. In the map, each key is the // PooledObject because it has the ordering with the timestamp - // value. Each value that the key references is the key of the + // value. Each value that the key references is the key of the // list it belongs to. final K key = entry.getValue(); final PooledObject p = entry.getKey(); @@ -676,7 +757,6 @@ } } - /** * Closes the keyed object pool. Once the pool is closed, * {@link #borrowObject(Object)} will fail with IllegalStateException, but @@ -709,24 +789,26 @@ jmxUnregister(); // Release any threads that were waiting for an object - poolMap.values().forEach(e -> e.getIdleObjects().interuptTakeWaiters()); + poolMap.values().forEach(e -> e.getIdleObjects().interruptTakeWaiters()); // This clear cleans up the keys now any waiting threads have been // interrupted clear(); } } - /** * Creates a new pooled object or null. * - * @param key Key associated with new pooled object. - * + * @param key Key associated with new pooled object. + * @param maxWaitDuration The time to wait in this method. If negative or ZERO, + * this method may wait indefinitely. * @return The new, wrapped pooled object. May return null. - * * @throws Exception If the objection creation fails. */ - private PooledObject create(final K key) throws Exception { + private PooledObject create(final K key, final Duration maxWaitDuration) throws Exception { + final Instant startInstant = Instant.now(); + Duration remainingWaitDuration = maxWaitDuration.isNegative() ? Duration.ZERO : maxWaitDuration; + int maxTotalPerKeySave = getMaxTotalPerKey(); // Per key if (maxTotalPerKeySave < 0) { maxTotalPerKeySave = Integer.MAX_VALUE; @@ -758,6 +840,7 @@ // call the factory Boolean create = null; while (create == null) { + remainingWaitDuration = maxWaitDuration.isNegative() ? Duration.ZERO : maxWaitDuration.minus(durationSince(startInstant)); synchronized (objectDeque.makeObjectCountLock) { final long newCreateCount = objectDeque.getCreateCount().incrementAndGet(); // Check against the per key limit @@ -771,12 +854,13 @@ // create a new object. Return and wait for an object to // be returned. create = Boolean.FALSE; - } else { + } else if (!remainingWaitDuration.isNegative()) { // There are makeObject() calls in progress that might // bring the pool to capacity. Those calls might also // fail so wait until they complete and then re-test if // the pool is at capacity or not. - objectDeque.makeObjectCountLock.wait(); + objectDeque.makeObjectCountLock.wait(remainingWaitDuration.toMillis(), + remainingWaitDuration.getNano() % 1_000_000); } } else { // The pool is not at capacity. Create a new object. @@ -822,7 +906,7 @@ } createdCount.incrementAndGet(); - objectDeque.getAllObjects().put(new IdentityWrapper<>(p.getObject()), p); + objectDeque.getAllObjects().put(IdentityWrapper.unwrap(p), p); return p; } @@ -871,14 +955,13 @@ /** * Destroy the wrapped, pooled object. * - * @param key The key associated with the object to destroy. + * @param key The key associated with the object to destroy * @param toDestroy The wrapped object to be destroyed * @param always Should the object be destroyed even if it is not currently * in the set of idle objects for the given key - * @param destroyMode DestroyMode context provided to the factory - * + * @param destroyMode {@link DestroyMode} context provided to the factory * @return {@code true} if the object was destroyed, otherwise {@code false} - * @throws Exception If the object destruction failed + * @throws Exception If the factory throws an exception during destruction */ private boolean destroy(final K key, final PooledObject toDestroy, final boolean always, final DestroyMode destroyMode) throws Exception { @@ -896,7 +979,7 @@ } } if (isIdle || always) { - objectDeque.getAllObjects().remove(new IdentityWrapper<>(toDestroy.getObject())); + objectDeque.getAllObjects().remove(IdentityWrapper.unwrap(toDestroy)); toDestroy.invalidate(); try { @@ -914,7 +997,6 @@ } } - @Override void ensureMinIdle() throws Exception { final int minIdlePerKeySave = getMinIdlePerKey(); @@ -940,7 +1022,6 @@ *

          * * @param key The key to check for idle objects - * * @throws Exception If a new object is required and cannot be created */ private void ensureMinIdle(final K key) throws Exception { @@ -1103,8 +1184,6 @@ } } underTest.endEvictionTest(idleObjects); - // TODO - May need to add code here once additional - // states are used } } } @@ -1113,9 +1192,11 @@ if (ac != null && ac.getRemoveAbandonedOnMaintenance()) { removeAbandoned(ac); } + if (reuseCapacityOnMaintenance) { + reuseCapacity(); + } } - /** * Gets a reference to the factory used to create, destroy and validate * the objects used by this pool. @@ -1164,7 +1245,6 @@ * is said to be exhausted. A negative value indicates no limit. * * @return the limit on the number of active instances per key - * * @see #setMaxTotalPerKey */ @Override @@ -1186,7 +1266,6 @@ *

          * * @return minimum size of the each keyed pool - * * @see #setTimeBetweenEvictionRunsMillis */ @Override @@ -1278,6 +1357,32 @@ return result; } + /** + * Gets whether to call {@link #reuseCapacity()} during pool maintenance (eviction). + * When true, the pool will attempt to reuse freed capacity at the end of each + * eviction run. + * + * @return {@code true} if capacity reuse is enabled during maintenance, {@code false} otherwise + * @see #setReuseCapacityOnMaintenance(boolean) + * @since 2.13.0 + */ + public boolean getReuseCapacityOnMaintenance() { + return reuseCapacityOnMaintenance; + } + + /** + * Gets whether to call {@link #reuseCapacity()} when returning objects to the pool. + * When true, the pool will check if there are threads waiting to borrow objects + * and attempt to reuse the capacity freed by the return operation. + * + * @return {@code true} if capacity reuse is enabled on return, {@code false} otherwise + * @see #setReuseCapacityOnReturn(boolean) + * @since 2.13.0 + */ + public boolean getReuseCapacityOnReturn() { + return reuseCapacityOnReturn; + } + @SuppressWarnings("boxing") // Commons Pool uses auto-boxing @Override String getStatsString() { @@ -1307,7 +1412,6 @@ * * @param key pool key * @param obj instance to invalidate - * * @throws Exception if an exception occurs destroying the * object * @throws IllegalStateException if obj does not belong to the pool @@ -1328,7 +1432,6 @@ * @param key pool key * @param obj instance to invalidate * @param destroyMode DestroyMode context provided to factory - * * @throws Exception if an exception occurs destroying the * object * @throws IllegalStateException if obj does not belong to the pool @@ -1355,7 +1458,7 @@ * to be borrowed) and active (currently borrowed). *

          * Note: This is named listAllObjects so it is presented as an operation via - * JMX. That means it won't be invoked unless the explicitly requested + * JMX. That means it won't be invoked unless explicitly requested * whereas all attributes will be automatically requested when viewing the * attributes for an object in a tool like JConsole. *

          @@ -1372,8 +1475,7 @@ * Registers a key for pool control and ensures that * {@link #getMinIdlePerKey()} idle instances are created. * - * @param key - The key to register for pool control. - * + * @param key The key to register for pool control. * @throws Exception If the associated factory throws an exception */ public void preparePool(final K key) throws Exception { @@ -1391,7 +1493,6 @@ *

          * * @param k The key to register - * * @return The objects currently associated with the given key. If this * method returns without throwing an exception then it will never * return null. @@ -1477,7 +1578,6 @@ * * @param key pool key * @param obj instance to return to the keyed pool - * * @throws IllegalStateException if an object is returned to the pool that * was not borrowed from it or if an object is * returned to the pool multiple times @@ -1530,7 +1630,7 @@ } final int maxIdle = getMaxIdlePerKey(); - final LinkedBlockingDeque> idleObjects = objectDeque.getIdleObjects(); + final BlockingDeque> idleObjects = objectDeque.getIdleObjects(); if (isClosed() || maxIdle > -1 && maxIdle <= idleObjects.size()) { try { @@ -1552,7 +1652,7 @@ } } } finally { - if (hasBorrowWaiters()) { + if (reuseCapacityOnReturn && hasBorrowWaiters()) { reuseCapacity(); } updateStatsReturn(activeTime); @@ -1575,7 +1675,7 @@ private void reuseCapacity() { final int maxTotalPerKeySave = getMaxTotalPerKey(); int maxQueueLength = 0; - LinkedBlockingDeque> mostLoadedPool = null; + BlockingDeque> mostLoadedPool = null; K mostLoadedKey = null; // Find the most loaded pool that could take a new instance @@ -1596,7 +1696,7 @@ try { // If there is no capacity to add, create will return null // and addIdleObject will no-op. - addIdleObject(mostLoadedKey, create(mostLoadedKey)); + addIdleObject(mostLoadedKey, create(mostLoadedKey, Duration.ZERO)); } catch (final Exception e) { swallowException(e); } finally { @@ -1610,7 +1710,7 @@ *

          * Always activates {@link #reuseCapacity()} at least once. * - * @param newCapacity number of new instances to attempt to create. + * @param newCapacity number of times to call {@link #reuseCapacity()} */ private void reuseCapacity(final int newCapacity) { final int bound = newCapacity < 1 ? 1 : newCapacity; @@ -1623,7 +1723,6 @@ * Sets the configuration. * * @param conf the new configuration to use. This is used by value. - * * @see GenericKeyedObjectPoolConfig */ public void setConfig(final GenericKeyedObjectPoolConfig conf) { @@ -1632,6 +1731,8 @@ setMaxTotalPerKey(conf.getMaxTotalPerKey()); setMaxTotal(conf.getMaxTotal()); setMinIdlePerKey(conf.getMinIdlePerKey()); + setReuseCapacityOnReturn(conf.getReuseCapacityOnReturn()); + setReuseCapacityOnMaintenance(conf.getReuseCapacityOnMaintenance()); } /** @@ -1660,7 +1761,6 @@ * is said to be exhausted. A negative value indicates no limit. * * @param maxTotalPerKey the limit on the number of active instances per key - * * @see #getMaxTotalPerKey */ public void setMaxTotalPerKey(final int maxTotalPerKey) { @@ -1681,7 +1781,6 @@ *

          * * @param minIdlePerKey The minimum size of the each keyed pool - * * @see #getMinIdlePerKey() * @see #getMaxIdlePerKey() * @see #setDurationBetweenEvictionRuns(Duration) @@ -1690,6 +1789,34 @@ this.minIdlePerKey = minIdlePerKey; } + /** + * Sets whether to call {@link #reuseCapacity()} during pool maintenance (eviction). + * When enabled, the pool will attempt to reuse capacity at the end of each + * eviction run. + * + * @param reuseCapacityOnMaintenance {@code true} to enable capacity reuse during + * maintenance, {@code false} to disable + * @see #getReuseCapacityOnMaintenance() + * @since 2.13.0 + */ + public void setReuseCapacityOnMaintenance(final boolean reuseCapacityOnMaintenance) { + this.reuseCapacityOnMaintenance = reuseCapacityOnMaintenance; + } + + /** + * Sets whether to call {@link #reuseCapacity()} when returning objects to the pool. + * When enabled, the pool will check if there are threads waiting to borrow objects + * and attempt to reuse capacity (across pools) on return. + * + * @param reuseCapacityOnReturn {@code true} to enable capacity reuse on return, + * {@code false} to disable + * @see #getReuseCapacityOnReturn() + * @since 2.13.0 + */ + public void setReuseCapacityOnReturn(final boolean reuseCapacityOnReturn) { + this.reuseCapacityOnReturn = reuseCapacityOnReturn; + } + @Override protected void toStringAppendFields(final StringBuilder builder) { super.toStringAppendFields(builder); @@ -1717,6 +1844,10 @@ builder.append(evictionKey); builder.append(", abandonedConfig="); builder.append(abandonedConfig); + builder.append(", reuseCapacityOnReturn="); + builder.append(reuseCapacityOnReturn); + builder.append(", reuseCapacityOnMaintenance="); + builder.append(reuseCapacityOnMaintenance); } /** diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolConfig.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolConfig.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -31,29 +31,48 @@ public class GenericKeyedObjectPoolConfig extends BaseObjectPoolConfig { /** - * The default value for the {@code maxTotalPerKey} configuration attribute. + * The default value for the {@code maxTotalPerKey} configuration attribute: {@value}. + * * @see GenericKeyedObjectPool#getMaxTotalPerKey() */ public static final int DEFAULT_MAX_TOTAL_PER_KEY = 8; /** - * The default value for the {@code maxTotal} configuration attribute. + * The default value for the {@code maxTotal} configuration attribute: {@value}. + * * @see GenericKeyedObjectPool#getMaxTotal() */ public static final int DEFAULT_MAX_TOTAL = -1; /** - * The default value for the {@code minIdlePerKey} configuration attribute. + * The default value for the {@code minIdlePerKey} configuration attribute: {@value}. + * * @see GenericKeyedObjectPool#getMinIdlePerKey() */ public static final int DEFAULT_MIN_IDLE_PER_KEY = 0; /** - * The default value for the {@code maxIdlePerKey} configuration attribute. + * The default value for the {@code maxIdlePerKey} configuration attribute: {@value}. + * * @see GenericKeyedObjectPool#getMaxIdlePerKey() */ public static final int DEFAULT_MAX_IDLE_PER_KEY = 8; + /** + * The default value for the {@code reuseCapacityOnReturn} configuration attribute: {@value}. + * + * @see GenericKeyedObjectPool#getReuseCapacityOnReturn() + * @since 2.13.0 + */ + public static final boolean DEFAULT_REUSE_CAPACITY_ON_RETURN = true; + + /** + * The default value for the {@code reuseCapacityOnMaintenance} configuration attribute: {@value}. + * + * @see GenericKeyedObjectPool#getReuseCapacityOnMaintenance() + * @since 2.13.0 + */ + public static final boolean DEFAULT_REUSE_CAPACITY_ON_MAINTENANCE = false; private int minIdlePerKey = DEFAULT_MIN_IDLE_PER_KEY; @@ -63,6 +82,10 @@ private int maxTotal = DEFAULT_MAX_TOTAL; + private boolean reuseCapacityOnReturn = DEFAULT_REUSE_CAPACITY_ON_RETURN; + + private boolean reuseCapacityOnMaintenance = DEFAULT_REUSE_CAPACITY_ON_MAINTENANCE; + /** * Constructs a new configuration with default settings. */ @@ -80,7 +103,7 @@ } /** - * Get the value for the {@code maxIdlePerKey} configuration attribute + * Gets the value for the {@code maxIdlePerKey} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxIdlePerKey} for this @@ -93,7 +116,7 @@ } /** - * Get the value for the {@code maxTotal} configuration attribute + * Gets the value for the {@code maxTotal} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxTotal} for this @@ -106,7 +129,7 @@ } /** - * Get the value for the {@code maxTotalPerKey} configuration attribute + * Gets the value for the {@code maxTotalPerKey} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxTotalPerKey} for this @@ -119,7 +142,7 @@ } /** - * Get the value for the {@code minIdlePerKey} configuration attribute + * Gets the value for the {@code minIdlePerKey} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code minIdlePerKey} for this @@ -132,7 +155,35 @@ } /** - * Set the value for the {@code maxIdlePerKey} configuration attribute for + * Gets the value for the {@code reuseCapacityOnMaintenance} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code reuseCapacityOnMaintenance} for this + * configuration instance + * + * @see GenericKeyedObjectPool#getReuseCapacityOnMaintenance() + * @since 2.13.0 + */ + public boolean getReuseCapacityOnMaintenance() { + return reuseCapacityOnMaintenance; + } + + /** + * Gets the value for the {@code reuseCapacityOnReturn} configuration attribute + * for pools created with this configuration instance. + * + * @return The current setting of {@code reuseCapacityOnReturn} for this + * configuration instance + * + * @see GenericKeyedObjectPool#getReuseCapacityOnReturn() + * @since 2.13.0 + */ + public boolean getReuseCapacityOnReturn() { + return reuseCapacityOnReturn; + } + + /** + * Sets the value for the {@code maxIdlePerKey} configuration attribute for * pools created with this configuration instance. * * @param maxIdlePerKey The new setting of {@code maxIdlePerKey} @@ -145,7 +196,7 @@ } /** - * Set the value for the {@code maxTotal} configuration attribute for + * Sets the value for the {@code maxTotal} configuration attribute for * pools created with this configuration instance. * * @param maxTotal The new setting of {@code maxTotal} @@ -158,7 +209,7 @@ } /** - * Set the value for the {@code maxTotalPerKey} configuration attribute for + * Sets the value for the {@code maxTotalPerKey} configuration attribute for * pools created with this configuration instance. * * @param maxTotalPerKey The new setting of {@code maxTotalPerKey} @@ -171,7 +222,7 @@ } /** - * Set the value for the {@code minIdlePerKey} configuration attribute for + * Sets the value for the {@code minIdlePerKey} configuration attribute for * pools created with this configuration instance. * * @param minIdlePerKey The new setting of {@code minIdlePerKey} @@ -183,6 +234,34 @@ this.minIdlePerKey = minIdlePerKey; } + /** + * Sets the value for the {@code reuseCapacityOnMaintenance} configuration attribute for + * pools created with this configuration instance. + * + * @param reuseCapacityOnMaintenance The new setting of {@code reuseCapacityOnMaintenance} + * for this configuration instance + * + * @see GenericKeyedObjectPool#setReuseCapacityOnMaintenance(boolean) + * @since 2.13.0 + */ + public void setReuseCapacityOnMaintenance(final boolean reuseCapacityOnMaintenance) { + this.reuseCapacityOnMaintenance = reuseCapacityOnMaintenance; + } + + /** + * Sets the value for the {@code reuseCapacityOnReturn} configuration attribute for + * pools created with this configuration instance. + * + * @param reuseCapacityOnReturn The new setting of {@code reuseCapacityOnReturn} + * for this configuration instance + * + * @see GenericKeyedObjectPool#setReuseCapacityOnReturn(boolean) + * @since 2.13.0 + */ + public void setReuseCapacityOnReturn(final boolean reuseCapacityOnReturn) { + this.reuseCapacityOnReturn = reuseCapacityOnReturn; + } + @Override protected void toStringAppendFields(final StringBuilder builder) { super.toStringAppendFields(builder); @@ -194,5 +273,9 @@ builder.append(maxTotalPerKey); builder.append(", maxTotal="); builder.append(maxTotal); + builder.append(", reuseCapacityOnReturn="); + builder.append(reuseCapacityOnReturn); + builder.append(", reuseCapacityOnMaintenance="); + builder.append(reuseCapacityOnMaintenance); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolMXBean.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolMXBean.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolMXBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericKeyedObjectPoolMXBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -74,9 +74,7 @@ *

          * * @see GenericKeyedObjectPool - * * @param Type of element pooled in this pool. - * * @since 2.0 */ public class GenericObjectPool extends BaseGenericObjectPool @@ -84,11 +82,7 @@ // JMX specific attributes private static final String ONAME_BASE = - "org.apache.commons.pool2:type=GenericObjectPool,name="; - - private static void wait(final Object obj, final Duration duration) throws InterruptedException { - obj.wait(duration.toMillis(), duration.getNano() % 1_000_000); - } + "org.apache.commons.pool2:type=GenericObjectPool,name="; private volatile String factoryType; @@ -102,17 +96,17 @@ * All of the objects currently associated with this pool in any state. It * excludes objects that have been destroyed. The size of * {@link #allObjects} will always be less than or equal to {@link - * #_maxActive}. Map keys are pooled objects, values are the PooledObject + * #getMaxTotal()}. Map keys are pooled objects, values are the PooledObject * wrappers used internally by the pool. */ private final ConcurrentHashMap, PooledObject> allObjects = new ConcurrentHashMap<>(); /* * The combined count of the currently created objects and those in the - * process of being created. Under load, it may exceed {@link #_maxActive} + * process of being created. Under load, it may exceed {@link #getMaxTotal()} * if multiple threads try and create a new object at the same time but * {@link #create()} will ensure that there are never more than - * {@link #_maxActive} objects created at any one time. + * {@link #getMaxTotal()} objects created at any one time. */ private final AtomicLong createCount = new AtomicLong(); @@ -185,11 +179,10 @@ * is null, this is a no-op (no exception, but no impact on the pool). * * @param p The object to make idle - * * @throws Exception If the factory fails to passivate the object */ private void addIdleObject(final PooledObject p) throws Exception { - if (!PooledObject.isNull(p)) { + if (PooledObject.nonNull(p)) { factory.passivateObject(p); if (getLifo()) { idleObjects.addFirst(p); @@ -200,10 +193,11 @@ } /** - * Creates an object, and place it into the pool. addObject() is useful for + * Creates an object, and places it into the pool. addObject() is useful for * "pre-loading" a pool with idle objects. *

          - * If there is no capacity available to add to the pool, this is a no-op + * If there is no capacity available to add to the pool, or there are already + * {@link #getMaxIdle()} idle instances in the pool, this is a no-op * (no exception, no impact to the pool). *

          *

          @@ -211,7 +205,6 @@ * is thrown. If there is no factory set (factory == null), an {@code IllegalStateException} * is thrown. *

          - * */ @Override public void addObject() throws Exception { @@ -219,11 +212,16 @@ if (factory == null) { throw new IllegalStateException("Cannot add objects without a factory."); } - addIdleObject(create()); + final int localMaxIdle = getMaxIdle(); + final int localMaxTotal = getMaxTotal(); + if ((localMaxIdle < 0 || getNumIdle() < localMaxIdle) && + (localMaxTotal < 0 || createCount.get() < localMaxTotal)) { + addIdleObject(create(getMaxWaitDuration())); + } } /** - * Equivalent to {@link #borrowObject(long) + * Equivalent to {@link #borrowObject(Duration) * borrowObject}({@link #getMaxWaitDuration()}). * * {@inheritDoc} @@ -248,8 +246,8 @@ *

          * If there are no idle instances available in the pool, behavior depends on * the {@link #getMaxTotal() maxTotal}, (if applicable) - * {@link #getBlockWhenExhausted()} and the value passed in to the - * {@code borrowMaxWaitMillis} parameter. If the number of instances + * {@link #getBlockWhenExhausted()} and the value passed in the + * {@code maxWaitDuration} parameter. If the number of instances * checked out from the pool is less than {@code maxTotal,} a new * instance is created, activated and (if applicable) validated and returned * to the caller. If validation fails, a {@code NoSuchElementException} @@ -263,8 +261,9 @@ * {@code NoSuchElementException} (if * {@link #getBlockWhenExhausted()} is false). The length of time that this * method will block when {@link #getBlockWhenExhausted()} is true is - * determined by the value passed in to the {@code borrowMaxWaitMillis} - * parameter. + * determined by the value passed in to the {@code maxWaitDuration} + * parameter. Passing a negative duration will cause this method to block + * indefinitely until an object becomes available. *

          *

          * When the pool is exhausted, multiple calling threads may be @@ -273,48 +272,43 @@ * available instances in request arrival order. *

          * - * @param borrowMaxWaitDuration The time to wait for an object - * to become available - * + * @param maxWaitDuration The time to wait for an object to become available, not null. * @return object instance from the pool * @throws NoSuchElementException if an instance cannot be returned * @throws Exception if an object instance cannot be returned due to an error * @since 2.10.0 */ - public T borrowObject(final Duration borrowMaxWaitDuration) throws Exception { + public T borrowObject(final Duration maxWaitDuration) throws Exception { assertOpen(); - + final Instant startInstant = Instant.now(); + final boolean negativeDuration = maxWaitDuration.isNegative(); + Duration remainingWaitDuration = maxWaitDuration; final AbandonedConfig ac = this.abandonedConfig; - if (ac != null && ac.getRemoveAbandonedOnBorrow() && getNumIdle() < 2 && - getNumActive() > getMaxTotal() - 3) { + if (ac != null && ac.getRemoveAbandonedOnBorrow() && getNumIdle() < 2 && getNumActive() > getMaxTotal() - 3) { removeAbandoned(ac); } - PooledObject p = null; - // Get local copy of current config so it is consistent for entire // method execution final boolean blockWhenExhausted = getBlockWhenExhausted(); - boolean create; - final Instant waitTime = Instant.now(); - while (p == null) { + remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant)); create = false; p = idleObjects.pollFirst(); if (p == null) { - p = create(); - if (!PooledObject.isNull(p)) { + p = create(remainingWaitDuration); + if (PooledObject.nonNull(p)) { create = true; } } if (blockWhenExhausted) { if (PooledObject.isNull(p)) { - p = borrowMaxWaitDuration.isNegative() ? idleObjects.takeFirst() : idleObjects.pollFirst(borrowMaxWaitDuration); + remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant)); + p = negativeDuration ? idleObjects.takeFirst() : idleObjects.pollFirst(remainingWaitDuration); } if (PooledObject.isNull(p)) { - throw new NoSuchElementException(appendStats( - "Timeout waiting for idle object, borrowMaxWaitDuration=" + borrowMaxWaitDuration)); + throw new NoSuchElementException(appendStats("Timeout waiting for idle object, maxWaitDuration=" + remainingWaitDuration)); } } else if (PooledObject.isNull(p)) { throw new NoSuchElementException(appendStats("Pool exhausted")); @@ -322,7 +316,6 @@ if (!p.allocate()) { p = null; } - if (!PooledObject.isNull(p)) { try { factory.activateObject(p); @@ -334,8 +327,7 @@ } p = null; if (create) { - final NoSuchElementException nsee = new NoSuchElementException( - appendStats("Unable to activate object")); + final NoSuchElementException nsee = new NoSuchElementException(appendStats("Unable to activate object")); nsee.initCause(e); throw nsee; } @@ -358,8 +350,7 @@ } p = null; if (create) { - final NoSuchElementException nsee = new NoSuchElementException( - appendStats("Unable to validate object")); + final NoSuchElementException nsee = new NoSuchElementException(appendStats("Unable to validate object")); nsee.initCause(validationThrowable); throw nsee; } @@ -367,9 +358,7 @@ } } } - - updateStatsBorrow(p, Duration.between(waitTime, Instant.now())); - + updateStatsBorrow(p, durationSince(startInstant)); return p.getObject(); } @@ -389,7 +378,7 @@ * If there are no idle instances available in the pool, behavior depends on * the {@link #getMaxTotal() maxTotal}, (if applicable) * {@link #getBlockWhenExhausted()} and the value passed in to the - * {@code borrowMaxWaitMillis} parameter. If the number of instances + * {@code maxWaitMillis} parameter. If the number of instances * checked out from the pool is less than {@code maxTotal,} a new * instance is created, activated and (if applicable) validated and returned * to the caller. If validation fails, a {@code NoSuchElementException} @@ -403,8 +392,9 @@ * {@code NoSuchElementException} (if * {@link #getBlockWhenExhausted()} is false). The length of time that this * method will block when {@link #getBlockWhenExhausted()} is true is - * determined by the value passed in to the {@code borrowMaxWaitMillis} - * parameter. + * determined by the value passed in to the {@code maxWaitMillis} + * parameter. Passing a negative duration will cause this method to block + * indefinitely until an object becomes available. *

          *

          * When the pool is exhausted, multiple calling threads may be @@ -413,18 +403,17 @@ * available instances in request arrival order. *

          * - * @param borrowMaxWaitMillis The time to wait in milliseconds for an object + * @param maxWaitMillis The time to wait in milliseconds for an object * to become available - * * @return object instance from the pool - * * @throws NoSuchElementException if an instance cannot be returned - * * @throws Exception if an object instance cannot be returned due to an * error + * @deprecated Use {@link #borrowObject(Duration)}. */ - public T borrowObject(final long borrowMaxWaitMillis) throws Exception { - return borrowObject(Duration.ofMillis(borrowMaxWaitMillis)); + @Deprecated + public T borrowObject(final long maxWaitMillis) throws Exception { + return borrowObject(Duration.ofMillis(maxWaitMillis)); } /** @@ -490,7 +479,7 @@ jmxUnregister(); // Release any threads that were waiting for an object - idleObjects.interuptTakeWaiters(); + idleObjects.interruptTakeWaiters(); } } @@ -504,20 +493,19 @@ * If the factory makeObject returns null, this method throws a NullPointerException. *

          * + * @param maxWaitDurationRequest The time to wait for capacity to create. * @return The new wrapped pooled object or null. - * @throws Exception if the object factory's {@code makeObject} fails + * @throws Exception if the object factory's {@code makeObject} fails. */ - private PooledObject create() throws Exception { + private PooledObject create(final Duration maxWaitDurationRequest) throws Exception { + final Instant startInstant = Instant.now(); + final Duration maxWaitDuration = maxWaitDurationRequest.isNegative() ? Duration.ZERO : maxWaitDurationRequest; int localMaxTotal = getMaxTotal(); // This simplifies the code later in this method if (localMaxTotal < 0) { localMaxTotal = Integer.MAX_VALUE; } - final Instant localStartInstant = Instant.now(); - final Duration maxWaitDurationRaw = getMaxWaitDuration(); - final Duration localMaxWaitDuration = maxWaitDurationRaw.isNegative() ? Duration.ZERO : maxWaitDurationRaw; - // Flag that indicates if create should: // - TRUE: call the factory to create an object // - FALSE: return null @@ -525,6 +513,8 @@ // call the factory Boolean create = null; while (create == null) { + // remainingWaitDuration handles spurious wakeup from wait(). + final Duration remainingWaitDuration = maxWaitDuration.minus(durationSince(startInstant)); synchronized (makeObjectCountLock) { final long newCreateCount = createCount.incrementAndGet(); if (newCreateCount > localMaxTotal) { @@ -541,7 +531,7 @@ // bring the pool to capacity. Those calls might also // fail so wait until they complete and then re-test if // the pool is at capacity or not. - wait(makeObjectCountLock, localMaxWaitDuration); + wait(makeObjectCountLock, remainingWaitDuration); } } else { // The pool is not at capacity. Create a new object. @@ -549,10 +539,9 @@ create = Boolean.TRUE; } } - - // Do not block more if maxWaitTimeMillis is set. - if (create == null && localMaxWaitDuration.compareTo(Duration.ZERO) > 0 && - Duration.between(localStartInstant, Instant.now()).compareTo(localMaxWaitDuration) >= 0) { + // Do not block more if remainingWaitDuration > 0. + if (create == null && remainingWaitDuration.compareTo(Duration.ZERO) > 0 && + durationSince(localStartInstant).compareTo(remainingWaitDuration) >= 0) { create = Boolean.FALSE; } } @@ -589,7 +578,7 @@ } createdCount.incrementAndGet(); - allObjects.put(new IdentityWrapper<>(p.getObject()), p); + allObjects.put(IdentityWrapper.unwrap(p), p); return p; } @@ -598,14 +587,13 @@ * * @param toDestroy The wrapped pooled object to destroy * @param destroyMode DestroyMode context provided to the factory - * * @throws Exception If the factory fails to destroy the pooled object * cleanly */ private void destroy(final PooledObject toDestroy, final DestroyMode destroyMode) throws Exception { toDestroy.invalidate(); idleObjects.remove(toDestroy); - allObjects.remove(new IdentityWrapper<>(toDestroy.getObject())); + allObjects.remove(IdentityWrapper.unwrap(toDestroy)); try { factory.destroyObject(toDestroy, destroyMode); } finally { @@ -614,6 +602,7 @@ } } + /** * Tries to ensure that {@code idleCount} idle instances exist in the pool. *

          @@ -637,7 +626,7 @@ } while (idleObjects.size() < idleCount) { - final PooledObject p = create(); + final PooledObject p = create(getMaxWaitDuration()); if (PooledObject.isNull(p)) { // Can't create objects, no reason to think another call to // create will work. Give up. @@ -770,8 +759,6 @@ } } underTest.endEvictionTest(idleObjects); - // TODO - May need to add code here once additional - // states are used } } } @@ -825,7 +812,6 @@ * * @return the maximum number of "idle" instances that can be held in the * pool or a negative value if there is no limit - * * @see #setMaxIdle */ @Override @@ -845,7 +831,6 @@ *

          * * @return The minimum number of objects. - * * @see #setMinIdle(int) * @see #setMaxIdle(int) * @see #setDurationBetweenEvictionRuns(Duration) @@ -929,7 +914,8 @@ * {@inheritDoc} *

          * Activation of this method decrements the active count and attempts to destroy the instance, using the provided - * {@link DestroyMode}. + * {@link DestroyMode}. To ensure liveness of the pool, {@link #addObject()} is called to replace the invalidated + * instance. *

          * * @throws Exception if an exception occurs destroying the object @@ -950,7 +936,9 @@ destroy(p, destroyMode); } } - ensureIdle(1, false); + if (!isClosed()) { + addObject(); + } } /** @@ -969,11 +957,12 @@ public Set listAllObjects() { return allObjects.values().stream().map(DefaultPooledObjectInfo::new).collect(Collectors.toSet()); } + /** * Tries to ensure that {@link #getMinIdle()} idle instances are available * in the pool. * - * @throws Exception If the associated factory throws an exception + * @throws Exception If the associated factory throws an exception. * @since 2.4 */ public void preparePool() throws Exception { @@ -985,7 +974,10 @@ /** * Recovers abandoned objects which have been checked out but - * not used since longer than the removeAbandonedTimeout. + * not used since longer than the removeAbandonedTimeout. For each object + * deemed abandoned, {@link #invalidateObject(Object)} is called. This + * results in the object being destroyed and then {@link #addObject()} is + * called to try to replace it. * * @param abandonedConfig The configuration to use to identify abandoned objects */ @@ -1033,82 +1025,82 @@ } return; // Object was abandoned and removed } + synchronized (p) { + markReturningState(p); - markReturningState(p); - - final Duration activeTime = p.getActiveDuration(); + final Duration activeTime = p.getActiveDuration(); - if (getTestOnReturn() && !factory.validateObject(p)) { - try { - destroy(p, DestroyMode.NORMAL); - } catch (final Exception e) { - swallowException(e); - } - try { - ensureIdle(1, false); - } catch (final Exception e) { - swallowException(e); + if (getTestOnReturn() && !factory.validateObject(p)) { + try { + destroy(p, DestroyMode.NORMAL); + } catch (final Exception e) { + swallowException(e); + } + try { + ensureIdle(1, false); + } catch (final Exception e) { + swallowException(e); + } + updateStatsReturn(activeTime); + return; } - updateStatsReturn(activeTime); - return; - } - try { - factory.passivateObject(p); - } catch (final Exception e1) { - swallowException(e1); - try { - destroy(p, DestroyMode.NORMAL); - } catch (final Exception e) { - swallowException(e); - } try { - ensureIdle(1, false); - } catch (final Exception e) { - swallowException(e); + factory.passivateObject(p); + } catch (final Exception e1) { + swallowException(e1); + try { + destroy(p, DestroyMode.NORMAL); + } catch (final Exception e) { + swallowException(e); + } + try { + ensureIdle(1, false); + } catch (final Exception e) { + swallowException(e); + } + updateStatsReturn(activeTime); + return; } - updateStatsReturn(activeTime); - return; - } - if (!p.deallocate()) { - throw new IllegalStateException( - "Object has already been returned to this pool or is invalid"); - } - - final int maxIdleSave = getMaxIdle(); - if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) { - try { - destroy(p, DestroyMode.NORMAL); - } catch (final Exception e) { - swallowException(e); - } - try { - ensureIdle(1, false); - } catch (final Exception e) { - swallowException(e); + if (!p.deallocate()) { + throw new IllegalStateException( + "Object has already been returned to this pool or is invalid"); } - } else { - if (getLifo()) { - idleObjects.addFirst(p); + + final int maxIdleSave = getMaxIdle(); + if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) { + try { + destroy(p, DestroyMode.NORMAL); + } catch (final Exception e) { + swallowException(e); + } + try { + ensureIdle(1, false); + } catch (final Exception e) { + swallowException(e); + } } else { - idleObjects.addLast(p); - } - if (isClosed()) { - // Pool closed while object was being added to idle objects. - // Make sure the returned object is destroyed rather than left - // in the idle object pool (which would effectively be a leak) - clear(); + if (getLifo()) { + idleObjects.addFirst(p); + } else { + idleObjects.addLast(p); + } + if (isClosed()) { + // Pool closed while object was being added to idle objects. + // Make sure the returned object is destroyed rather than left + // in the idle object pool (which would effectively be a leak) + clear(); + } } + updateStatsReturn(activeTime); } - updateStatsReturn(activeTime); } /** * Sets the base pool configuration. * * @param conf the new configuration to use. This is used by value. - * * @see GenericObjectPoolConfig */ public void setConfig(final GenericObjectPoolConfig conf) { @@ -1131,7 +1123,6 @@ * The cap on the number of "idle" instances in the pool. Use a * negative value to indicate an unlimited number of idle * instances - * * @see #getMaxIdle */ public void setMaxIdle(final int maxIdle) { @@ -1151,7 +1142,6 @@ * * @param minIdle * The minimum number of objects. - * * @see #getMinIdle() * @see #getMaxIdle() * @see #getDurationBetweenEvictionRuns() diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolConfig.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolConfig.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -31,30 +31,39 @@ public class GenericObjectPoolConfig extends BaseObjectPoolConfig { /** - * The default value for the {@code maxTotal} configuration attribute. + * The default value for the {@code maxTotal} configuration attribute: {@value}. + * * @see GenericObjectPool#getMaxTotal() */ public static final int DEFAULT_MAX_TOTAL = 8; /** - * The default value for the {@code maxIdle} configuration attribute. + * The default value for the {@code maxIdle} configuration attribute: {@value}. + * * @see GenericObjectPool#getMaxIdle() */ public static final int DEFAULT_MAX_IDLE = 8; /** - * The default value for the {@code minIdle} configuration attribute. + * The default value for the {@code minIdle} configuration attribute: {@value}. + * * @see GenericObjectPool#getMinIdle() */ public static final int DEFAULT_MIN_IDLE = 0; - private int maxTotal = DEFAULT_MAX_TOTAL; private int maxIdle = DEFAULT_MAX_IDLE; private int minIdle = DEFAULT_MIN_IDLE; + /** + * Constructs a new instance. + */ + public GenericObjectPoolConfig() { + // empty + } + @SuppressWarnings("unchecked") @Override public GenericObjectPoolConfig clone() { @@ -66,7 +75,7 @@ } /** - * Get the value for the {@code maxIdle} configuration attribute + * Gets the value for the {@code maxIdle} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxIdle} for this @@ -78,9 +87,8 @@ return maxIdle; } - /** - * Get the value for the {@code maxTotal} configuration attribute + * Gets the value for the {@code maxTotal} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code maxTotal} for this @@ -93,7 +101,7 @@ } /** - * Get the value for the {@code minIdle} configuration attribute + * Gets the value for the {@code minIdle} configuration attribute * for pools created with this configuration instance. * * @return The current setting of {@code minIdle} for this @@ -105,9 +113,8 @@ return minIdle; } - /** - * Set the value for the {@code maxIdle} configuration attribute for + * Sets the value for the {@code maxIdle} configuration attribute for * pools created with this configuration instance. * * @param maxIdle The new setting of {@code maxIdle} @@ -120,7 +127,7 @@ } /** - * Set the value for the {@code maxTotal} configuration attribute for + * Sets the value for the {@code maxTotal} configuration attribute for * pools created with this configuration instance. * * @param maxTotal The new setting of {@code maxTotal} @@ -133,7 +140,7 @@ } /** - * Set the value for the {@code minIdle} configuration attribute for + * Sets the value for the {@code minIdle} configuration attribute for * pools created with this configuration instance. * * @param minIdle The new setting of {@code minIdle} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolMXBean.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolMXBean.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolMXBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/GenericObjectPoolMXBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/InterruptibleReentrantLock.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -29,7 +29,7 @@ * * @since 2.0 */ -class InterruptibleReentrantLock extends ReentrantLock { +final class InterruptibleReentrantLock extends ReentrantLock { private static final long serialVersionUID = 1L; @@ -37,18 +37,18 @@ * Constructs a new InterruptibleReentrantLock with the given fairness policy. * * @param fairness true means threads should acquire contended locks as if - * waiting in a FIFO queue + * waiting in a FIFO queue. */ InterruptibleReentrantLock(final boolean fairness) { super(fairness); } /** - * Interrupts the threads that are waiting on a specific condition + * Interrupts the threads that are waiting on a specific condition. * * @param condition the condition on which the threads are waiting. */ - public void interruptWaiters(final Condition condition) { + void interruptWaiters(final Condition condition) { getWaitingThreads(condition).forEach(Thread::interrupt); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/LinkedBlockingDeque.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -22,10 +22,10 @@ import java.time.Duration; import java.util.AbstractQueue; import java.util.Collection; -import java.util.Deque; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.concurrent.BlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; @@ -65,8 +65,8 @@ * * @since 2.0 */ -class LinkedBlockingDeque extends AbstractQueue - implements Deque, Serializable { +final class LinkedBlockingDeque extends AbstractQueue + implements BlockingDeque, Serializable { /* * Implemented as a simple doubly-linked list protected by a @@ -101,7 +101,7 @@ /** * The next node to return in next() */ - Node next; + Node next; /** * nextItem holds on to item fields because once we claim that @@ -173,7 +173,6 @@ * iterator. * * @param n given node - * * @return next node */ abstract Node nextNode(Node n); @@ -222,20 +221,28 @@ } /** Descending iterator */ - private class DescendingItr extends AbstractItr { + private final class DescendingItr extends AbstractItr { @Override - Node firstNode() { return last; } + Node firstNode() { + return last; + } @Override - Node nextNode(final Node n) { return n.prev; } + Node nextNode(final Node n) { + return n.prev; + } } /** Forward iterator */ - private class Itr extends AbstractItr { + private final class Itr extends AbstractItr { @Override - Node firstNode() { return first; } + Node firstNode() { + return first; + } @Override - Node nextNode(final Node n) { return n.next; } + Node nextNode(final Node n) { + return n.next; } + } /** * Doubly-linked list node class. @@ -327,7 +334,6 @@ this(Integer.MAX_VALUE, fairness); } - // Basic linking and unlinking operations, called only while holding lock /** @@ -464,9 +470,7 @@ * Drains the queue to the specified collection. * * @param c The collection to add the elements to - * * @return number of elements added to the collection - * * @throws UnsupportedOperationException if the add operation is not * supported by the specified collection * @throws ClassCastException if the class of the elements held by this @@ -475,6 +479,7 @@ * @throws NullPointerException if c is null * @throws IllegalArgumentException if c is this instance */ + @Override public int drainTo(final Collection c) { return drainTo(c, Integer.MAX_VALUE); } @@ -483,9 +488,8 @@ * Drains no more than the specified number of elements from the queue to the * specified collection. * - * @param c collection to add the elements to + * @param collection collection to add the elements to * @param maxElements maximum number of elements to remove from the queue - * * @return number of elements added to the collection * @throws UnsupportedOperationException if the add operation is not * supported by the specified collection @@ -495,16 +499,17 @@ * @throws NullPointerException if c is null * @throws IllegalArgumentException if c is this instance */ - public int drainTo(final Collection c, final int maxElements) { - Objects.requireNonNull(c, "c"); - if (c == this) { + @Override + public int drainTo(final Collection collection, final int maxElements) { + Objects.requireNonNull(collection, "collection"); + if (collection == this) { throw new IllegalArgumentException(); } lock.lock(); try { final int n = Math.min(maxElements, count); for (int i = 0; i < n; i++) { - c.add(first.item); // In this order, in case add() throws. + collection.add(first.item); // In this order, in case add() throws. unlinkFirst(); } return n; @@ -552,10 +557,10 @@ * * @return number of threads waiting on this deque's notEmpty condition. */ - public int getTakeQueueLength() { + int getTakeQueueLength() { lock.lock(); try { - return lock.getWaitQueueLength(notEmpty); + return lock.getWaitQueueLength(notEmpty); } finally { lock.unlock(); } @@ -567,7 +572,7 @@ * * @return true if there is at least one thread waiting on this deque's notEmpty condition. */ - public boolean hasTakeWaiters() { + boolean hasTakeWaiters() { lock.lock(); try { return lock.hasWaiters(notEmpty); @@ -580,7 +585,7 @@ * Interrupts the threads currently waiting to take an object from the pool. See disclaimer on accuracy in * {@link java.util.concurrent.locks.ReentrantLock#getWaitingThreads(Condition)}. */ - public void interuptTakeWaiters() { + void interruptTakeWaiters() { lock.lock(); try { lock.interruptWaiters(notEmpty); @@ -610,7 +615,6 @@ * Links provided element as first element, or returns false if full. * * @param e The element to link as the first element. - * * @return {@code true} if successful, otherwise {@code false} */ private boolean linkFirst(final E e) { @@ -635,7 +639,6 @@ * Links provided element as last element, or returns false if full. * * @param e The element to link as the last element. - * * @return {@code true} if successful, otherwise {@code false} */ private boolean linkLast(final E e) { @@ -669,9 +672,7 @@ * * @param e element to link * @param timeout length of time to wait - * * @return {@code true} if successful, otherwise {@code false} - * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space @@ -689,13 +690,12 @@ * @param e element to link * @param timeout length of time to wait * @param unit units that timeout is expressed in - * * @return {@code true} if successful, otherwise {@code false} - * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ + @Override public boolean offer(final E e, final long timeout, final TimeUnit unit) throws InterruptedException { return offerLast(e, timeout, unit); } @@ -717,14 +717,12 @@ * * @param e element to link * @param timeout length of time to wait - * * @return {@code true} if successful, otherwise {@code false} - * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ - public boolean offerFirst(final E e, final Duration timeout) throws InterruptedException { + boolean offerFirst(final E e, final Duration timeout) throws InterruptedException { Objects.requireNonNull(e, "e"); long nanos = timeout.toNanos(); lock.lockInterruptibly(); @@ -748,13 +746,12 @@ * @param e element to link * @param timeout length of time to wait * @param unit units that timeout is expressed in - * * @return {@code true} if successful, otherwise {@code false} - * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ + @Override public boolean offerFirst(final E e, final long timeout, final TimeUnit unit) throws InterruptedException { return offerFirst(e, PoolImplUtils.toDuration(timeout, unit)); } @@ -776,9 +773,7 @@ * * @param e element to link * @param timeout length of time to wait - * * @return {@code true} if successful, otherwise {@code false} - * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whist waiting * for space @@ -807,13 +802,12 @@ * @param e element to link * @param timeout length of time to wait * @param unit units that timeout is expressed in - * * @return {@code true} if successful, otherwise {@code false} - * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whist waiting * for space */ + @Override public boolean offerLast(final E e, final long timeout, final TimeUnit unit) throws InterruptedException { return offerLast(e, PoolImplUtils.toDuration(timeout, unit)); } @@ -857,7 +851,6 @@ *

          This method is equivalent to {@link #pollFirst(long, TimeUnit)}. * * @param timeout length of time to wait - * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ @@ -873,10 +866,10 @@ * * @param timeout length of time to wait * @param unit units that timeout is expressed in - * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ + @Override public E poll(final long timeout, final TimeUnit unit) throws InterruptedException { return pollFirst(timeout, unit); } @@ -896,7 +889,6 @@ * to do so if the queue is empty. * * @param timeout length of time to wait - * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ @@ -923,10 +915,10 @@ * * @param timeout length of time to wait * @param unit units that timeout is expressed in - * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ + @Override public E pollFirst(final long timeout, final TimeUnit unit) throws InterruptedException { return pollFirst(PoolImplUtils.toDuration(timeout, unit)); } @@ -946,11 +938,10 @@ * to do so if the queue is empty. * * @param timeout length of time to wait - * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ - public E pollLast(final Duration timeout) + E pollLast(final Duration timeout) throws InterruptedException { long nanos = timeout.toNanos(); lock.lockInterruptibly(); @@ -974,10 +965,10 @@ * * @param timeout length of time to wait * @param unit units that timeout is expressed in - * * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ + @Override public E pollLast(final long timeout, final TimeUnit unit) throws InterruptedException { return pollLast(PoolImplUtils.toDuration(timeout, unit)); @@ -1002,11 +993,11 @@ *

          * * @param e element to link - * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ + @Override public void put(final E e) throws InterruptedException { putLast(e); } @@ -1016,11 +1007,11 @@ * is space to do so if the queue is full. * * @param e element to link - * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ + @Override public void putFirst(final E e) throws InterruptedException { Objects.requireNonNull(e, "e"); lock.lock(); @@ -1038,11 +1029,11 @@ * is space to do so if the queue is full. * * @param e element to link - * * @throws NullPointerException if e is null * @throws InterruptedException if the thread is interrupted whilst waiting * for space */ + @Override public void putLast(final E e) throws InterruptedException { Objects.requireNonNull(e, "e"); lock.lock(); @@ -1058,12 +1049,11 @@ // Stack methods /** - * Reconstitutes this deque from a stream (that is, - * deserialize it). + * Reconstitutes this deque from a stream (that is, deserialize it). + * * @param s the stream */ - private void readObject(final ObjectInputStream s) - throws IOException, ClassNotFoundException { + private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); count = 0; first = null; @@ -1071,7 +1061,7 @@ // Read in all elements and place in queue for (;;) { @SuppressWarnings("unchecked") - final E item = (E)s.readObject(); + final E item = (E) s.readObject(); if (item == null) { break; } @@ -1094,6 +1084,7 @@ * * @return The number of additional elements the queue is able to accept */ + @Override public int remainingCapacity() { lock.lock(); try { @@ -1266,6 +1257,7 @@ * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ + @Override public E take() throws InterruptedException { return takeFirst(); } @@ -1277,6 +1269,7 @@ * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ + @Override public E takeFirst() throws InterruptedException { lock.lock(); try { @@ -1297,6 +1290,7 @@ * @return the unlinked element * @throws InterruptedException if the current thread is interrupted */ + @Override public E takeLast() throws InterruptedException { lock.lock(); try { @@ -1347,12 +1341,11 @@ lock.lock(); try { if (a.length < count) { - a = (T[])java.lang.reflect.Array.newInstance - (a.getClass().getComponentType(), count); + a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), count); } int k = 0; for (Node p = first; p != null; p = p.next) { - a[k++] = (T)p.item; + a[k++] = (T) p.item; } if (a.length > k) { a[k] = null; @@ -1390,9 +1383,8 @@ p.next = n; n.prev = p; x.item = null; - // Don't mess with x's links. They may still be in use by - // an iterator. - --count; + // Don't mess with x's links. They may still be in use by an iterator. + --count; notFull.signal(); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/NoOpCallStack.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/NoOpCallStack.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/NoOpCallStack.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/NoOpCallStack.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -27,7 +27,7 @@ public class NoOpCallStack implements CallStack { /** - * Singleton instance. + * The singleton instance. */ public static final CallStack INSTANCE = new NoOpCallStack(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/PoolImplUtils.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/PoolImplUtils.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/PoolImplUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/PoolImplUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -32,13 +32,12 @@ * * @since 2.0 */ -class PoolImplUtils { +final class PoolImplUtils { /** * Identifies the concrete type of object that an object factory creates. * * @param factoryClass The factory to examine - * * @return the type of object the factory creates */ @SuppressWarnings("rawtypes") @@ -73,7 +72,6 @@ * @param type The interface that defines a generic type * @param clazz The class that implements the interface with a concrete type * @param The interface type - * * @return concrete type used by the implementation */ private static Object getGenericType(final Class type, final Class clazz) { @@ -135,7 +133,6 @@ * * @param clazz defining class * @param argType the type argument of interest - * * @return An instance of {@link Class} representing the type used by the type parameter or an instance of * {@link Integer} representing the index for the type in the definition of the defining class */ @@ -200,22 +197,22 @@ static ChronoUnit toChronoUnit(final TimeUnit timeUnit) { // TODO when using Java >= 9: Use TimeUnit.toChronoUnit(). switch (Objects.requireNonNull(timeUnit)) { - case NANOSECONDS: - return ChronoUnit.NANOS; - case MICROSECONDS: - return ChronoUnit.MICROS; - case MILLISECONDS: - return ChronoUnit.MILLIS; - case SECONDS: - return ChronoUnit.SECONDS; - case MINUTES: - return ChronoUnit.MINUTES; - case HOURS: - return ChronoUnit.HOURS; - case DAYS: - return ChronoUnit.DAYS; - default: - throw new IllegalArgumentException(timeUnit.toString()); + case NANOSECONDS: + return ChronoUnit.NANOS; + case MICROSECONDS: + return ChronoUnit.MICROS; + case MILLISECONDS: + return ChronoUnit.MILLIS; + case SECONDS: + return ChronoUnit.SECONDS; + case MINUTES: + return ChronoUnit.MINUTES; + case HOURS: + return ChronoUnit.HOURS; + case DAYS: + return ChronoUnit.DAYS; + default: + throw new IllegalArgumentException(timeUnit.toString()); } } @@ -227,7 +224,7 @@ * @return a Duration. */ static Duration toDuration(final long amount, final TimeUnit timeUnit) { - return Duration.of(amount, PoolImplUtils.toChronoUnit(timeUnit)); + return Duration.of(amount, toChronoUnit(timeUnit)); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/PooledSoftReference.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/PooledSoftReference.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/PooledSoftReference.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/PooledSoftReference.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/SoftReferenceObjectPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -23,6 +23,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.concurrent.BlockingDeque; import org.apache.tomcat.dbcp.pool2.BaseObjectPool; import org.apache.tomcat.dbcp.pool2.ObjectPool; @@ -58,17 +59,15 @@ /** Total number of instances that have been destroyed */ private long destroyCount; // @GuardedBy("this") - /** Total number of instances that have been created */ private long createCount; // @GuardedBy("this") /** Idle references - waiting to be borrowed */ - private final LinkedBlockingDeque> idleReferences = - new LinkedBlockingDeque<>(); + private final BlockingDeque> idleReferences = new LinkedBlockingDeque<>(); /** All references - checked out or waiting to be borrowed. */ private final ArrayList> allReferences = - new ArrayList<>(); + new ArrayList<>(); /** * Constructs a {@code SoftReferenceObjectPool} with the specified factory. @@ -263,11 +262,10 @@ } /** - * Destroys a {@code PooledSoftReference} and removes it from the idle and all + * Destroys a {@link PooledSoftReference} and removes it from the idle and all * references pools. * * @param toDestroy PooledSoftReference to destroy - * * @throws Exception If an error occurs while trying to destroy the object */ private void destroy(final PooledSoftReference toDestroy) throws Exception { @@ -331,7 +329,7 @@ final PooledSoftReference ref = findReference(obj); if (ref == null) { throw new IllegalStateException( - "Object to invalidate is not currently part of this pool"); + "Object to invalidate is not currently part of this pool"); } if (factory != null) { destroy(ref); @@ -348,7 +346,8 @@ // Remove wrappers for enqueued references from idle and allReferences lists removeClearedReferences(idleReferences.iterator()); removeClearedReferences(allReferences.iterator()); - while (refQueue.poll() != null) { // NOPMD + while (refQueue.poll() != null) { + // loop until null } } @@ -393,7 +392,7 @@ final PooledSoftReference ref = findReference(obj); if (ref == null) { throw new IllegalStateException( - "Returned object not currently part of this pool"); + "Returned object not currently part of this pool"); } if (factory != null) { if (!factory.validateObject(ref)) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/ThrowableCallStack.java tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/ThrowableCallStack.java --- tomcat10-10.1.34/java/org/apache/tomcat/dbcp/pool2/impl/ThrowableCallStack.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/dbcp/pool2/impl/ThrowableCallStack.java 2026-01-23 19:33:36.000000000 +0000 @@ -6,7 +6,7 @@ * (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 + * https://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, @@ -19,6 +19,7 @@ import java.io.PrintWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.time.Instant; /** * CallStack strategy that uses the stack trace from a {@link Throwable}. This strategy, while slower than the @@ -33,13 +34,32 @@ /** * A snapshot of a throwable. */ - private static class Snapshot extends Throwable { + private static final class Snapshot extends Throwable { + private static final long serialVersionUID = 1L; - private final long timestampMillis = System.currentTimeMillis(); + private final Instant timestamp; + + /** + * Constructs a new instance with its message set to the now instant. + */ + private Snapshot() { + this(Instant.now()); + } + + /** + * Constructs a new instance and use the timestamp as the message with using {@link DateTimeFormatter#ISO_INSTANT} for more precision. + * + * @param timestamp normally the now instant. + */ + private Snapshot(final Instant timestamp) { + super(timestamp.toString()); + this.timestamp = timestamp; + } } private final String messageFormat; + // We keep the SimpleDateFormat for backward compatibility instead of a DateTimeFormatter. //@GuardedBy("dateFormat") private final DateFormat dateFormat; @@ -77,7 +97,8 @@ message = messageFormat; } else { synchronized (dateFormat) { - message = dateFormat.format(Long.valueOf(snapshotRef.timestampMillis)); + // The throwable message is in {@link DateTimeFormatter#ISO_INSTANT} format for more precision. + message = dateFormat.format(Long.valueOf(snapshotRef.timestamp.toEpochMilli())); } } writer.println(message); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/AprStatus.java tomcat10-10.1.52/java/org/apache/tomcat/jni/AprStatus.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/AprStatus.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/AprStatus.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * 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.jni; + +import java.util.concurrent.locks.ReentrantReadWriteLock; + +/** + * Holds APR status without the need to load other classes. + */ +public class AprStatus { + private static volatile boolean aprInitialized = false; + private static volatile boolean aprAvailable = false; + private static volatile boolean useOpenSSL = true; + private static volatile boolean instanceCreated = false; + private static volatile int openSSLVersion = 0; + private static ReentrantReadWriteLock statusLock = new ReentrantReadWriteLock(); + + public static boolean isAprInitialized() { + return aprInitialized; + } + + public static boolean isAprAvailable() { + return aprAvailable; + } + + public static boolean getUseOpenSSL() { + return useOpenSSL; + } + + public static boolean isInstanceCreated() { + return instanceCreated; + } + + public static void setAprInitialized(boolean aprInitialized) { + AprStatus.aprInitialized = aprInitialized; + } + + public static void setAprAvailable(boolean aprAvailable) { + AprStatus.aprAvailable = aprAvailable; + } + + public static void setUseOpenSSL(boolean useOpenSSL) { + AprStatus.useOpenSSL = useOpenSSL; + } + + public static void setInstanceCreated(boolean instanceCreated) { + AprStatus.instanceCreated = instanceCreated; + } + + /** + * @return the openSSLVersion + */ + public static int getOpenSSLVersion() { + return openSSLVersion; + } + + /** + * @param openSSLVersion the openSSLVersion to set + */ + public static void setOpenSSLVersion(int openSSLVersion) { + AprStatus.openSSLVersion = openSSLVersion; + } + + /** + * Code that changes the status of the APR library MUST hold the write lock while making any changes. + *

          + * Code that needs the status to be consistent for an operation must hold the read lock for the duration of that + * operation. + * + * @return The read/write lock for APR library status + */ + public static ReentrantReadWriteLock getStatusLock() { + return statusLock; + } +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/Buffer.java tomcat10-10.1.52/java/org/apache/tomcat/jni/Buffer.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/Buffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/Buffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,8 +19,7 @@ import java.nio.ByteBuffer; /** - * Provides utilities related to the use of directly allocated - * {@link ByteBuffer} instances with native code. + * Provides utilities related to the use of directly allocated {@link ByteBuffer} instances with native code. */ public class Buffer { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/CertificateVerifier.java tomcat10-10.1.52/java/org/apache/tomcat/jni/CertificateVerifier.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/CertificateVerifier.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/CertificateVerifier.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,13 +22,14 @@ public interface CertificateVerifier { /** - * Returns {@code true} if the passed in certificate chain could be verified and so the handshake - * should be successful, {@code false} otherwise. + * Returns {@code true} if the passed in certificate chain could be verified and so the handshake should be + * successful, {@code false} otherwise. * - * @param ssl the SSL instance - * @param x509 the {@code X509} certificate chain - * @param authAlgorithm the auth algorithm - * @return verified {@code true} if verified successful, {@code false} otherwise + * @param ssl the SSL instance + * @param x509 the {@code X509} certificate chain + * @param authAlgorithm the auth algorithm + * + * @return verified {@code true} if verified successful, {@code false} otherwise */ boolean verify(long ssl, byte[][] x509, String authAlgorithm); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/FileInfo.java tomcat10-10.1.52/java/org/apache/tomcat/jni/FileInfo.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/FileInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/FileInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,10 +17,9 @@ package org.apache.tomcat.jni; /** - * Tomcat Native 1.2.33 and earlier won't initialise unless this class is - * present. This dummy class ensures initialisation gets as far as being able to - * check the version of the Tomcat Native library and reporting a version error - * if 1.2.33 or earlier is present. + * Tomcat Native 1.2.33 and earlier won't initialise unless this class is present. This dummy class ensures + * initialisation gets as far as being able to check the version of the Tomcat Native library and reporting a version + * error if 1.2.33 or earlier is present. */ public class FileInfo { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/Library.java tomcat10-10.1.52/java/org/apache/tomcat/jni/Library.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/Library.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/Library.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,7 @@ public final class Library { /* Default library names - use 2.x in preference to 1.x if both are available */ - private static final String [] NAMES = {"tcnative-2", "libtcnative-2", "tcnative-1", "libtcnative-1"}; + private static final String[] NAMES = { "tcnative-2", "libtcnative-2", "tcnative-1", "libtcnative-1" }; /* System property used to define CATALINA_HOME */ private static final String CATALINA_HOME_PROP = "catalina.home"; /* @@ -56,7 +56,7 @@ } if (!loaded) { String path = System.getProperty("java.library.path"); - String [] paths = path.split(File.pathSeparator); + String[] paths = path.split(File.pathSeparator); for (String value : NAMES) { try { System.loadLibrary(value); @@ -88,12 +88,11 @@ names.append(name); names.append(", "); } - throw new LibraryNotFoundError(names.substring(0, names.length() -2), err.toString()); + throw new LibraryNotFoundError(names.substring(0, names.length() - 2), err.toString()); } } - private Library(String libraryName) - { + private Library(String libraryName) { System.loadLibrary(libraryName); } @@ -101,43 +100,45 @@ * Create Tomcat Native's global APR pool. This has to be the first call to TCN library. */ private static native boolean initialize(); + /** * Destroys Tomcat Native's global APR pool. This has to be the last call to TCN library. This will destroy any APR * root pools that have not been explicitly destroyed. */ public static native void terminate(); + /* Internal function for loading APR Features */ private static native int version(int what); /* TCN_MAJOR_VERSION */ - public static int TCN_MAJOR_VERSION = 0; + public static int TCN_MAJOR_VERSION = 0; /* TCN_MINOR_VERSION */ - public static int TCN_MINOR_VERSION = 0; + public static int TCN_MINOR_VERSION = 0; /* TCN_PATCH_VERSION */ - public static int TCN_PATCH_VERSION = 0; + public static int TCN_PATCH_VERSION = 0; /* TCN_IS_DEV_VERSION */ public static int TCN_IS_DEV_VERSION = 0; /* APR_MAJOR_VERSION */ - public static int APR_MAJOR_VERSION = 0; + public static int APR_MAJOR_VERSION = 0; /* APR_MINOR_VERSION */ - public static int APR_MINOR_VERSION = 0; + public static int APR_MINOR_VERSION = 0; /* APR_PATCH_VERSION */ - public static int APR_PATCH_VERSION = 0; + public static int APR_PATCH_VERSION = 0; /* APR_IS_DEV_VERSION */ public static int APR_IS_DEV_VERSION = 0; /* TCN_VERSION_STRING */ public static native String versionString(); + /* APR_VERSION_STRING */ public static native String aprVersionString(); /** - * Setup any APR internal data structures. This MUST be the first function - * called for any APR library. + * Setup any APR internal data structures. This MUST be the first function called for any APR library. + * * @param libraryName the name of the library to load * - * @return {@code true} if the native code was initialized successfully - * otherwise {@code false} + * @return {@code true} if the native code was initialized successfully otherwise {@code false} * * @throws Exception if a problem occurred during initialization */ @@ -148,18 +149,17 @@ } else { _instance = new Library(libraryName); } - TCN_MAJOR_VERSION = version(0x01); - TCN_MINOR_VERSION = version(0x02); - TCN_PATCH_VERSION = version(0x03); + TCN_MAJOR_VERSION = version(0x01); + TCN_MINOR_VERSION = version(0x02); + TCN_PATCH_VERSION = version(0x03); TCN_IS_DEV_VERSION = version(0x04); - APR_MAJOR_VERSION = version(0x11); - APR_MINOR_VERSION = version(0x12); - APR_PATCH_VERSION = version(0x13); + APR_MAJOR_VERSION = version(0x11); + APR_MINOR_VERSION = version(0x12); + APR_PATCH_VERSION = version(0x13); APR_IS_DEV_VERSION = version(0x14); if (APR_MAJOR_VERSION < 1) { - throw new UnsatisfiedLinkError("Unsupported APR Version (" + - aprVersionString() + ")"); + throw new UnsatisfiedLinkError("Unsupported APR Version (" + aprVersionString() + ")"); } } return initialize(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/LibraryNotFoundError.java tomcat10-10.1.52/java/org/apache/tomcat/jni/LibraryNotFoundError.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/LibraryNotFoundError.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/LibraryNotFoundError.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,17 +23,15 @@ private final String libraryNames; /** - * @param libraryNames A list of the file names of the native libraries that - * failed to load - * @param errors A list of the error messages received when trying to load - * each of the libraries + * @param libraryNames A list of the file names of the native libraries that failed to load + * @param errors A list of the error messages received when trying to load each of the libraries */ - public LibraryNotFoundError(String libraryNames, String errors){ + public LibraryNotFoundError(String libraryNames, String errors) { super(errors); this.libraryNames = libraryNames; } - public String getLibraryNames(){ + public String getLibraryNames() { return libraryNames; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/Pool.java tomcat10-10.1.52/java/org/apache/tomcat/jni/Pool.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/Pool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/Pool.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,26 +17,23 @@ package org.apache.tomcat.jni; /** - * Provides access to APR memory pools which are used to manage memory - * allocations for natively created instances. + * Provides access to APR memory pools which are used to manage memory allocations for natively created instances. */ public class Pool { /** * Create a new pool. * - * @param parent The parent pool. If this is 0, the new pool is a root pool. - * If it is non-zero, the new pool will inherit all of its - * parent pool's attributes, except the apr_pool_t will be a - * sub-pool. + * @param parent The parent pool. If this is 0, the new pool is a root pool. If it is non-zero, the new pool will + * inherit all of its parent pool's attributes, except the apr_pool_t will be a sub-pool. * * @return The pool we have just created. - */ + */ public static native long create(long parent); /** - * Destroy the pool. This takes similar action as apr_pool_clear() and then - * frees all the memory. This will actually free the memory. + * Destroy the pool. This takes similar action as apr_pool_clear() and then frees all the memory. This will actually + * free the memory. * * @param pool The pool to destroy */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/SSL.java tomcat10-10.1.52/java/org/apache/tomcat/jni/SSL.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/SSL.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/SSL.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,61 +21,62 @@ /* * Type definitions mostly from mod_ssl */ - public static final int UNSET = -1; + public static final int UNSET = -1; /* * Define the certificate algorithm types */ public static final int SSL_ALGO_UNKNOWN = 0; - public static final int SSL_ALGO_RSA = (1<<0); - public static final int SSL_ALGO_DSA = (1<<1); - public static final int SSL_ALGO_ALL = (SSL_ALGO_RSA|SSL_ALGO_DSA); - - public static final int SSL_AIDX_RSA = 0; - public static final int SSL_AIDX_DSA = 1; - public static final int SSL_AIDX_ECC = 3; - public static final int SSL_AIDX_MAX = 4; + public static final int SSL_ALGO_RSA = (1 << 0); + public static final int SSL_ALGO_DSA = (1 << 1); + public static final int SSL_ALGO_ALL = (SSL_ALGO_RSA | SSL_ALGO_DSA); + + public static final int SSL_AIDX_RSA = 0; + public static final int SSL_AIDX_DSA = 1; + public static final int SSL_AIDX_ECC = 3; + public static final int SSL_AIDX_MAX = 4; /* * Define IDs for the temporary RSA keys and DH params */ - public static final int SSL_TMP_KEY_RSA_512 = 0; + public static final int SSL_TMP_KEY_RSA_512 = 0; public static final int SSL_TMP_KEY_RSA_1024 = 1; public static final int SSL_TMP_KEY_RSA_2048 = 2; public static final int SSL_TMP_KEY_RSA_4096 = 3; - public static final int SSL_TMP_KEY_DH_512 = 4; - public static final int SSL_TMP_KEY_DH_1024 = 5; - public static final int SSL_TMP_KEY_DH_2048 = 6; - public static final int SSL_TMP_KEY_DH_4096 = 7; - public static final int SSL_TMP_KEY_MAX = 8; + public static final int SSL_TMP_KEY_DH_512 = 4; + public static final int SSL_TMP_KEY_DH_1024 = 5; + public static final int SSL_TMP_KEY_DH_2048 = 6; + public static final int SSL_TMP_KEY_DH_4096 = 7; + public static final int SSL_TMP_KEY_MAX = 8; /* * Define the SSL options */ - public static final int SSL_OPT_NONE = 0; - public static final int SSL_OPT_RELSET = (1<<0); - public static final int SSL_OPT_STDENVVARS = (1<<1); - public static final int SSL_OPT_EXPORTCERTDATA = (1<<3); - public static final int SSL_OPT_FAKEBASICAUTH = (1<<4); - public static final int SSL_OPT_STRICTREQUIRE = (1<<5); - public static final int SSL_OPT_OPTRENEGOTIATE = (1<<6); - public static final int SSL_OPT_ALL = (SSL_OPT_STDENVVARS|SSL_OPT_EXPORTCERTDATA|SSL_OPT_FAKEBASICAUTH|SSL_OPT_STRICTREQUIRE|SSL_OPT_OPTRENEGOTIATE); + public static final int SSL_OPT_NONE = 0; + public static final int SSL_OPT_RELSET = (1 << 0); + public static final int SSL_OPT_STDENVVARS = (1 << 1); + public static final int SSL_OPT_EXPORTCERTDATA = (1 << 3); + public static final int SSL_OPT_FAKEBASICAUTH = (1 << 4); + public static final int SSL_OPT_STRICTREQUIRE = (1 << 5); + public static final int SSL_OPT_OPTRENEGOTIATE = (1 << 6); + public static final int SSL_OPT_ALL = (SSL_OPT_STDENVVARS | SSL_OPT_EXPORTCERTDATA | SSL_OPT_FAKEBASICAUTH | + SSL_OPT_STRICTREQUIRE | SSL_OPT_OPTRENEGOTIATE); /* * Define the SSL Protocol options */ - public static final int SSL_PROTOCOL_NONE = 0; - public static final int SSL_PROTOCOL_SSLV2 = (1<<0); - public static final int SSL_PROTOCOL_SSLV3 = (1<<1); - public static final int SSL_PROTOCOL_TLSV1 = (1<<2); - public static final int SSL_PROTOCOL_TLSV1_1 = (1<<3); - public static final int SSL_PROTOCOL_TLSV1_2 = (1<<4); - public static final int SSL_PROTOCOL_TLSV1_3 = (1<<5); + public static final int SSL_PROTOCOL_NONE = 0; + public static final int SSL_PROTOCOL_SSLV2 = (1 << 0); + public static final int SSL_PROTOCOL_SSLV3 = (1 << 1); + public static final int SSL_PROTOCOL_TLSV1 = (1 << 2); + public static final int SSL_PROTOCOL_TLSV1_1 = (1 << 3); + public static final int SSL_PROTOCOL_TLSV1_2 = (1 << 4); + public static final int SSL_PROTOCOL_TLSV1_3 = (1 << 5); public static final int SSL_PROTOCOL_ALL; static { if (version() >= 0x1010100f) { - SSL_PROTOCOL_ALL = (SSL_PROTOCOL_TLSV1 | SSL_PROTOCOL_TLSV1_1 | SSL_PROTOCOL_TLSV1_2 | - SSL_PROTOCOL_TLSV1_3); + SSL_PROTOCOL_ALL = + (SSL_PROTOCOL_TLSV1 | SSL_PROTOCOL_TLSV1_1 | SSL_PROTOCOL_TLSV1_2 | SSL_PROTOCOL_TLSV1_3); } else { SSL_PROTOCOL_ALL = (SSL_PROTOCOL_TLSV1 | SSL_PROTOCOL_TLSV1_1 | SSL_PROTOCOL_TLSV1_2); } @@ -85,154 +86,159 @@ /* * Define the SSL verify levels */ - public static final int SSL_CVERIFY_UNSET = UNSET; - public static final int SSL_CVERIFY_NONE = 0; - public static final int SSL_CVERIFY_OPTIONAL = 1; - public static final int SSL_CVERIFY_REQUIRE = 2; + public static final int SSL_CVERIFY_UNSET = UNSET; + public static final int SSL_CVERIFY_NONE = 0; + public static final int SSL_CVERIFY_OPTIONAL = 1; + public static final int SSL_CVERIFY_REQUIRE = 2; public static final int SSL_CVERIFY_OPTIONAL_NO_CA = 3; - /* Use either SSL_VERIFY_NONE or SSL_VERIFY_PEER, the last 2 options - * are 'ored' with SSL_VERIFY_PEER if they are desired + /* + * Use either SSL_VERIFY_NONE or SSL_VERIFY_PEER, the last 2 options are 'ored' with SSL_VERIFY_PEER if they are + * desired */ - public static final int SSL_VERIFY_NONE = 0; - public static final int SSL_VERIFY_PEER = 1; + public static final int SSL_VERIFY_NONE = 0; + public static final int SSL_VERIFY_PEER = 1; public static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = 2; - public static final int SSL_VERIFY_CLIENT_ONCE = 4; - public static final int SSL_VERIFY_PEER_STRICT = (SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT); + public static final int SSL_VERIFY_CLIENT_ONCE = 4; + public static final int SSL_VERIFY_PEER_STRICT = (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT); - public static final int SSL_OP_MICROSOFT_SESS_ID_BUG = 0x00000001; - public static final int SSL_OP_NETSCAPE_CHALLENGE_BUG = 0x00000002; + public static final int SSL_OP_MICROSOFT_SESS_ID_BUG = 0x00000001; + public static final int SSL_OP_NETSCAPE_CHALLENGE_BUG = 0x00000002; public static final int SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG = 0x00000008; - public static final int SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = 0x00000010; - public static final int SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = 0x00000020; - public static final int SSL_OP_MSIE_SSLV2_RSA_PADDING = 0x00000040; - public static final int SSL_OP_SSLEAY_080_CLIENT_DH_BUG = 0x00000080; - public static final int SSL_OP_TLS_D5_BUG = 0x00000100; - public static final int SSL_OP_TLS_BLOCK_PADDING_BUG = 0x00000200; - - /* Disable SSL 3.0/TLS 1.0 CBC vulnerability workaround that was added - * in OpenSSL 0.9.6d. Usually (depending on the application protocol) - * the workaround is not needed. Unfortunately some broken SSL/TLS - * implementations cannot handle it at all, which is why we include - * it in SSL_OP_ALL. */ - public static final int SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800; - - /* SSL_OP_ALL: various bug workarounds that should be rather harmless. - * This used to be 0x000FFFFFL before 0.9.7. */ - public static final int SSL_OP_ALL = 0x00000FFF; + public static final int SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG = 0x00000010; + public static final int SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER = 0x00000020; + public static final int SSL_OP_MSIE_SSLV2_RSA_PADDING = 0x00000040; + public static final int SSL_OP_SSLEAY_080_CLIENT_DH_BUG = 0x00000080; + public static final int SSL_OP_TLS_D5_BUG = 0x00000100; + public static final int SSL_OP_TLS_BLOCK_PADDING_BUG = 0x00000200; + + /* + * Disable SSL 3.0/TLS 1.0 CBC vulnerability workaround that was added in OpenSSL 0.9.6d. Usually (depending on the + * application protocol) the workaround is not needed. Unfortunately some broken SSL/TLS implementations cannot + * handle it at all, which is why we include it in SSL_OP_ALL. + */ + public static final int SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800; + + /* + * SSL_OP_ALL: various bug workarounds that should be rather harmless. This used to be 0x000FFFFFL before 0.9.7. + */ + public static final int SSL_OP_ALL = 0x00000FFF; /* As server, disallow session resumption on renegotiation */ public static final int SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 0x00010000; /* Don't use compression even if supported */ - public static final int SSL_OP_NO_COMPRESSION = 0x00020000; + public static final int SSL_OP_NO_COMPRESSION = 0x00020000; /* Permit unsafe legacy renegotiation */ - public static final int SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0x00040000; + public static final int SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 0x00040000; /* If set, always create a new key when using tmp_eddh parameters */ - public static final int SSL_OP_SINGLE_ECDH_USE = 0x00080000; + public static final int SSL_OP_SINGLE_ECDH_USE = 0x00080000; /* If set, always create a new key when using tmp_dh parameters */ - public static final int SSL_OP_SINGLE_DH_USE = 0x00100000; - /* Set to always use the tmp_rsa key when doing RSA operations, - * even when this violates protocol specs */ - public static final int SSL_OP_EPHEMERAL_RSA = 0x00200000; - /* Set on servers to choose the cipher according to the server's - * preferences */ - public static final int SSL_OP_CIPHER_SERVER_PREFERENCE = 0x00400000; - /* If set, a server will allow a client to issue an SSLv3.0 version number - * as latest version supported in the premaster secret, even when TLSv1.0 - * (version 3.1) was announced in the client hello. Normally this is - * forbidden to prevent version rollback attacks. */ - public static final int SSL_OP_TLS_ROLLBACK_BUG = 0x00800000; - - public static final int SSL_OP_NO_SSLv2 = 0x01000000; - public static final int SSL_OP_NO_SSLv3 = 0x02000000; - public static final int SSL_OP_NO_TLSv1 = 0x04000000; - public static final int SSL_OP_NO_TLSv1_2 = 0x08000000; - public static final int SSL_OP_NO_TLSv1_1 = 0x10000000; - - public static final int SSL_OP_NO_TICKET = 0x00004000; - - public static final int SSL_OP_NETSCAPE_CA_DN_BUG = 0x20000000; - public static final int SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = 0x40000000; - - public static final int SSL_CRT_FORMAT_UNDEF = 0; - public static final int SSL_CRT_FORMAT_ASN1 = 1; - public static final int SSL_CRT_FORMAT_TEXT = 2; - public static final int SSL_CRT_FORMAT_PEM = 3; + public static final int SSL_OP_SINGLE_DH_USE = 0x00100000; + /* + * Set to always use the tmp_rsa key when doing RSA operations, even when this violates protocol specs + */ + public static final int SSL_OP_EPHEMERAL_RSA = 0x00200000; + /* + * Set on servers to choose the cipher according to the server's preferences + */ + public static final int SSL_OP_CIPHER_SERVER_PREFERENCE = 0x00400000; + /* + * If set, a server will allow a client to issue an SSLv3.0 version number as latest version supported in the + * premaster secret, even when TLSv1.0 (version 3.1) was announced in the client hello. Normally this is forbidden + * to prevent version rollback attacks. + */ + public static final int SSL_OP_TLS_ROLLBACK_BUG = 0x00800000; + + public static final int SSL_OP_NO_SSLv2 = 0x01000000; + public static final int SSL_OP_NO_SSLv3 = 0x02000000; + public static final int SSL_OP_NO_TLSv1 = 0x04000000; + public static final int SSL_OP_NO_TLSv1_2 = 0x08000000; + public static final int SSL_OP_NO_TLSv1_1 = 0x10000000; + + public static final int SSL_OP_NO_TICKET = 0x00004000; + + public static final int SSL_OP_NETSCAPE_CA_DN_BUG = 0x20000000; + public static final int SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG = 0x40000000; + + public static final int SSL_CRT_FORMAT_UNDEF = 0; + public static final int SSL_CRT_FORMAT_ASN1 = 1; + public static final int SSL_CRT_FORMAT_TEXT = 2; + public static final int SSL_CRT_FORMAT_PEM = 3; public static final int SSL_CRT_FORMAT_NETSCAPE = 4; - public static final int SSL_CRT_FORMAT_PKCS12 = 5; - public static final int SSL_CRT_FORMAT_SMIME = 6; - public static final int SSL_CRT_FORMAT_ENGINE = 7; - - public static final int SSL_MODE_CLIENT = 0; - public static final int SSL_MODE_SERVER = 1; - public static final int SSL_MODE_COMBINED = 2; - - public static final int SSL_CONF_FLAG_CMDLINE = 0x0001; - public static final int SSL_CONF_FLAG_FILE = 0x0002; - public static final int SSL_CONF_FLAG_CLIENT = 0x0004; - public static final int SSL_CONF_FLAG_SERVER = 0x0008; - public static final int SSL_CONF_FLAG_SHOW_ERRORS = 0x0010; - public static final int SSL_CONF_FLAG_CERTIFICATE = 0x0020; - - public static final int SSL_CONF_TYPE_UNKNOWN = 0x0000; - public static final int SSL_CONF_TYPE_STRING = 0x0001; - public static final int SSL_CONF_TYPE_FILE = 0x0002; - public static final int SSL_CONF_TYPE_DIR = 0x0003; + public static final int SSL_CRT_FORMAT_PKCS12 = 5; + public static final int SSL_CRT_FORMAT_SMIME = 6; + public static final int SSL_CRT_FORMAT_ENGINE = 7; + + public static final int SSL_MODE_CLIENT = 0; + public static final int SSL_MODE_SERVER = 1; + public static final int SSL_MODE_COMBINED = 2; + + public static final int SSL_CONF_FLAG_CMDLINE = 0x0001; + public static final int SSL_CONF_FLAG_FILE = 0x0002; + public static final int SSL_CONF_FLAG_CLIENT = 0x0004; + public static final int SSL_CONF_FLAG_SERVER = 0x0008; + public static final int SSL_CONF_FLAG_SHOW_ERRORS = 0x0010; + public static final int SSL_CONF_FLAG_CERTIFICATE = 0x0020; + + public static final int SSL_CONF_TYPE_UNKNOWN = 0x0000; + public static final int SSL_CONF_TYPE_STRING = 0x0001; + public static final int SSL_CONF_TYPE_FILE = 0x0002; + public static final int SSL_CONF_TYPE_DIR = 0x0003; - public static final int SSL_SHUTDOWN_TYPE_UNSET = 0; + public static final int SSL_SHUTDOWN_TYPE_UNSET = 0; public static final int SSL_SHUTDOWN_TYPE_STANDARD = 1; - public static final int SSL_SHUTDOWN_TYPE_UNCLEAN = 2; + public static final int SSL_SHUTDOWN_TYPE_UNCLEAN = 2; public static final int SSL_SHUTDOWN_TYPE_ACCURATE = 3; - public static final int SSL_INFO_SESSION_ID = 0x0001; - public static final int SSL_INFO_CIPHER = 0x0002; - public static final int SSL_INFO_CIPHER_USEKEYSIZE = 0x0003; - public static final int SSL_INFO_CIPHER_ALGKEYSIZE = 0x0004; - public static final int SSL_INFO_CIPHER_VERSION = 0x0005; - public static final int SSL_INFO_CIPHER_DESCRIPTION = 0x0006; - public static final int SSL_INFO_PROTOCOL = 0x0007; - - /* To obtain the CountryName of the Client Certificate Issuer - * use the SSL_INFO_CLIENT_I_DN + SSL_INFO_DN_COUNTRYNAME - */ - public static final int SSL_INFO_CLIENT_S_DN = 0x0010; - public static final int SSL_INFO_CLIENT_I_DN = 0x0020; - public static final int SSL_INFO_SERVER_S_DN = 0x0040; - public static final int SSL_INFO_SERVER_I_DN = 0x0080; - - public static final int SSL_INFO_DN_COUNTRYNAME = 0x0001; - public static final int SSL_INFO_DN_STATEORPROVINCENAME = 0x0002; - public static final int SSL_INFO_DN_LOCALITYNAME = 0x0003; - public static final int SSL_INFO_DN_ORGANIZATIONNAME = 0x0004; + public static final int SSL_INFO_SESSION_ID = 0x0001; + public static final int SSL_INFO_CIPHER = 0x0002; + public static final int SSL_INFO_CIPHER_USEKEYSIZE = 0x0003; + public static final int SSL_INFO_CIPHER_ALGKEYSIZE = 0x0004; + public static final int SSL_INFO_CIPHER_VERSION = 0x0005; + public static final int SSL_INFO_CIPHER_DESCRIPTION = 0x0006; + public static final int SSL_INFO_PROTOCOL = 0x0007; + + /* + * To obtain the CountryName of the Client Certificate Issuer use the SSL_INFO_CLIENT_I_DN + SSL_INFO_DN_COUNTRYNAME + */ + public static final int SSL_INFO_CLIENT_S_DN = 0x0010; + public static final int SSL_INFO_CLIENT_I_DN = 0x0020; + public static final int SSL_INFO_SERVER_S_DN = 0x0040; + public static final int SSL_INFO_SERVER_I_DN = 0x0080; + + public static final int SSL_INFO_DN_COUNTRYNAME = 0x0001; + public static final int SSL_INFO_DN_STATEORPROVINCENAME = 0x0002; + public static final int SSL_INFO_DN_LOCALITYNAME = 0x0003; + public static final int SSL_INFO_DN_ORGANIZATIONNAME = 0x0004; public static final int SSL_INFO_DN_ORGANIZATIONALUNITNAME = 0x0005; - public static final int SSL_INFO_DN_COMMONNAME = 0x0006; - public static final int SSL_INFO_DN_TITLE = 0x0007; - public static final int SSL_INFO_DN_INITIALS = 0x0008; - public static final int SSL_INFO_DN_GIVENNAME = 0x0009; - public static final int SSL_INFO_DN_SURNAME = 0x000A; - public static final int SSL_INFO_DN_DESCRIPTION = 0x000B; - public static final int SSL_INFO_DN_UNIQUEIDENTIFIER = 0x000C; - public static final int SSL_INFO_DN_EMAILADDRESS = 0x000D; - - public static final int SSL_INFO_CLIENT_M_VERSION = 0x0101; - public static final int SSL_INFO_CLIENT_M_SERIAL = 0x0102; - public static final int SSL_INFO_CLIENT_V_START = 0x0103; - public static final int SSL_INFO_CLIENT_V_END = 0x0104; - public static final int SSL_INFO_CLIENT_A_SIG = 0x0105; - public static final int SSL_INFO_CLIENT_A_KEY = 0x0106; - public static final int SSL_INFO_CLIENT_CERT = 0x0107; - public static final int SSL_INFO_CLIENT_V_REMAIN = 0x0108; - - public static final int SSL_INFO_SERVER_M_VERSION = 0x0201; - public static final int SSL_INFO_SERVER_M_SERIAL = 0x0202; - public static final int SSL_INFO_SERVER_V_START = 0x0203; - public static final int SSL_INFO_SERVER_V_END = 0x0204; - public static final int SSL_INFO_SERVER_A_SIG = 0x0205; - public static final int SSL_INFO_SERVER_A_KEY = 0x0206; - public static final int SSL_INFO_SERVER_CERT = 0x0207; - /* Return client certificate chain. - * Add certificate chain number to that flag (0 ... verify depth) + public static final int SSL_INFO_DN_COMMONNAME = 0x0006; + public static final int SSL_INFO_DN_TITLE = 0x0007; + public static final int SSL_INFO_DN_INITIALS = 0x0008; + public static final int SSL_INFO_DN_GIVENNAME = 0x0009; + public static final int SSL_INFO_DN_SURNAME = 0x000A; + public static final int SSL_INFO_DN_DESCRIPTION = 0x000B; + public static final int SSL_INFO_DN_UNIQUEIDENTIFIER = 0x000C; + public static final int SSL_INFO_DN_EMAILADDRESS = 0x000D; + + public static final int SSL_INFO_CLIENT_M_VERSION = 0x0101; + public static final int SSL_INFO_CLIENT_M_SERIAL = 0x0102; + public static final int SSL_INFO_CLIENT_V_START = 0x0103; + public static final int SSL_INFO_CLIENT_V_END = 0x0104; + public static final int SSL_INFO_CLIENT_A_SIG = 0x0105; + public static final int SSL_INFO_CLIENT_A_KEY = 0x0106; + public static final int SSL_INFO_CLIENT_CERT = 0x0107; + public static final int SSL_INFO_CLIENT_V_REMAIN = 0x0108; + + public static final int SSL_INFO_SERVER_M_VERSION = 0x0201; + public static final int SSL_INFO_SERVER_M_SERIAL = 0x0202; + public static final int SSL_INFO_SERVER_V_START = 0x0203; + public static final int SSL_INFO_SERVER_V_END = 0x0204; + public static final int SSL_INFO_SERVER_A_SIG = 0x0205; + public static final int SSL_INFO_SERVER_A_KEY = 0x0206; + public static final int SSL_INFO_SERVER_CERT = 0x0207; + /* + * Return client certificate chain. Add certificate chain number to that flag (0 ... verify depth) */ - public static final int SSL_INFO_CLIENT_CERT_CHAIN = 0x0400; + public static final int SSL_INFO_CLIENT_CERT_CHAIN = 0x0400; /* Only support OFF and SERVER for now */ public static final long SSL_SESS_CACHE_OFF = 0x0000; @@ -248,12 +254,12 @@ public static native String versionString(); /** - * Initialize OpenSSL support. - * This function needs to be called once for the - * lifetime of JVM. Library.init() has to be called before. - * @param engine Support for external a Crypto Device ("engine"), - * usually - * a hardware accelerator card for crypto operations. + * Initialize OpenSSL support. This function needs to be called once for the lifetime of JVM. Library.init() has to + * be called before. + * + * @param engine Support for external a Crypto Device ("engine"), usually a hardware accelerator card for crypto + * operations. + * * @return APR status code */ public static native int initialize(String engine); @@ -261,9 +267,11 @@ /** * Get the status of FIPS Mode. * - * @return FIPS_mode return code. It is 0 if OpenSSL is not - * in FIPS mode, 1 if OpenSSL is in FIPS Mode. + * @return FIPS_mode return code. It is 0 if OpenSSL is not in FIPS mode, 1 if OpenSSL is + * in FIPS Mode. + * * @throws Exception If tcnative was not compiled with FIPS Mode available. + * * @see OpenSSL method FIPS_mode() */ public static native int fipsModeGet() throws Exception; @@ -274,8 +282,10 @@ * @param mode 1 - enable, 0 - disable * * @return FIPS_mode_set return code - * @throws Exception If tcnative was not compiled with FIPS Mode available, - * or if {@code FIPS_mode_set()} call returned an error value. + * + * @throws Exception If tcnative was not compiled with FIPS Mode available, or if {@code FIPS_mode_set()} call + * returned an error value. + * * @see OpenSSL method FIPS_mode_set() */ public static native int fipsModeSet(int mode) throws Exception; @@ -283,15 +293,16 @@ /** * Sets global random filename. * - * @param filename Filename to use. - * If set it will be used for SSL initialization - * and all contexts where explicitly not set. + * @param filename Filename to use. If set it will be used for SSL initialization and all contexts where explicitly + * not set. */ public static native void randSet(String filename); /** * Return the handshake completed count. + * * @param ssl SSL pointer + * * @return the count */ public static native int getHandshakeCount(long ssl); @@ -303,173 +314,210 @@ public static final int SSL_SENT_SHUTDOWN = 1; public static final int SSL_RECEIVED_SHUTDOWN = 2; - public static final int SSL_ERROR_NONE = 0; - public static final int SSL_ERROR_SSL = 1; - public static final int SSL_ERROR_WANT_READ = 2; - public static final int SSL_ERROR_WANT_WRITE = 3; + public static final int SSL_ERROR_NONE = 0; + public static final int SSL_ERROR_SSL = 1; + public static final int SSL_ERROR_WANT_READ = 2; + public static final int SSL_ERROR_WANT_WRITE = 3; public static final int SSL_ERROR_WANT_X509_LOOKUP = 4; - public static final int SSL_ERROR_SYSCALL = 5; /* look at error stack/return value/errno */ - public static final int SSL_ERROR_ZERO_RETURN = 6; - public static final int SSL_ERROR_WANT_CONNECT = 7; - public static final int SSL_ERROR_WANT_ACCEPT = 8; + public static final int SSL_ERROR_SYSCALL = 5; /* look at error stack/return value/errno */ + public static final int SSL_ERROR_ZERO_RETURN = 6; + public static final int SSL_ERROR_WANT_CONNECT = 7; + public static final int SSL_ERROR_WANT_ACCEPT = 8; /** * SSL_new - * @param ctx Server or Client context to use. - * @param server if true configure SSL instance to use accept handshake routines - * if false configure SSL instance to use connect handshake routines + * + * @param ctx Server or Client context to use. + * @param server if true configure SSL instance to use accept handshake routines if false configure SSL instance to + * use connect handshake routines + * * @return pointer to SSL instance (SSL *) */ public static native long newSSL(long ctx, boolean server); /** * BIO_ctrl_pending. + * * @param bio BIO pointer (BIO *) + * * @return the pending bytes count */ public static native int pendingWrittenBytesInBIO(long bio); /** * SSL_pending. + * * @param ssl SSL pointer (SSL *) + * * @return the pending bytes count */ public static native int pendingReadableBytesInSSL(long ssl); /** * BIO_write. - * @param bio BIO pointer + * + * @param bio BIO pointer * @param wbuf Buffer pointer * @param wlen Write length + * * @return the bytes count written */ public static native int writeToBIO(long bio, long wbuf, int wlen); /** * BIO_read. - * @param bio BIO pointer + * + * @param bio BIO pointer * @param rbuf Buffer pointer * @param rlen Read length + * * @return the bytes count read */ public static native int readFromBIO(long bio, long rbuf, int rlen); /** * SSL_write. - * @param ssl the SSL instance (SSL *) + * + * @param ssl the SSL instance (SSL *) * @param wbuf Buffer pointer * @param wlen Write length + * * @return the bytes count written */ public static native int writeToSSL(long ssl, long wbuf, int wlen); /** * SSL_read - * @param ssl the SSL instance (SSL *) + * + * @param ssl the SSL instance (SSL *) * @param rbuf Buffer pointer * @param rlen Read length + * * @return the bytes count read */ public static native int readFromSSL(long ssl, long rbuf, int rlen); /** * SSL_get_shutdown + * * @param ssl the SSL instance (SSL *) + * * @return the operation status */ public static native int getShutdown(long ssl); /** * SSL_free + * * @param ssl the SSL instance (SSL *) */ public static native void freeSSL(long ssl); /** * Wire up internal and network BIOs for the given SSL instance. - * + *

          * Warning: you must explicitly free this resource by calling freeBIO - * - * While the SSL's internal/application data BIO will be freed when freeSSL is called on - * the provided SSL instance, you must call freeBIO on the returned network BIO. + *

          + * While the SSL's internal/application data BIO will be freed when freeSSL is called on the provided SSL instance, + * you must call freeBIO on the returned network BIO. * * @param ssl the SSL instance (SSL *) + * * @return pointer to the Network BIO (BIO *) */ public static native long makeNetworkBIO(long ssl); /** * BIO_free + * * @param bio BIO pointer */ public static native void freeBIO(long bio); /** * SSL_shutdown + * * @param ssl the SSL instance (SSL *) + * * @return the operation status */ public static native int shutdownSSL(long ssl); /** - * Get the error number representing the last error OpenSSL encountered on - * this thread. + * Get the error number representing the last error OpenSSL encountered on this thread. + * * @return the last error number */ public static native int getLastErrorNumber(); /** * SSL_get_cipher. + * * @param ssl the SSL instance (SSL *) + * * @return the cipher name */ public static native String getCipherForSSL(long ssl); /** * SSL_get_version + * * @param ssl the SSL instance (SSL *) + * * @return the SSL version in use */ public static native String getVersion(long ssl); /** * SSL_do_handshake + * * @param ssl the SSL instance (SSL *) + * * @return the handshake status */ public static native int doHandshake(long ssl); /** * SSL_renegotiate + * * @param ssl the SSL instance (SSL *) + * * @return the operation status */ public static native int renegotiate(long ssl); /** * SSL_renegotiate_pending + * * @param ssl the SSL instance (SSL *) + * * @return the operation status */ public static native int renegotiatePending(long ssl); /** * SSL_verify_client_post_handshake + * * @param ssl the SSL instance (SSL *) + * * @return the operation status */ public static native int verifyClientPostHandshake(long ssl); /** * Is post handshake authentication in progress on this connection? + * * @param ssl the SSL instance (SSL *) + * * @return the operation status */ public static native int getPostHandshakeAuthInProgress(long ssl); /** * SSL_in_init. + * * @param ssl the SSL instance (SSL *) + * * @return the status */ public static native int isInInit(long ssl); @@ -480,52 +528,59 @@ /** * SSL_get0_alpn_selected + * * @param ssl the SSL instance (SSL *) + * * @return the ALPN protocol negotiated */ public static native String getAlpnSelected(long ssl); /** - * Get the peer certificate chain or {@code null} if non was send. + * Get the peer certificate chain or {@code null} if none was sent. + * * @param ssl the SSL instance (SSL *) + * * @return the certificate chain bytes */ public static native byte[][] getPeerCertChain(long ssl); /** - * Get the peer certificate or {@code null} if non was send. + * Get the peer certificate or {@code null} if none was sent. + * * @param ssl the SSL instance (SSL *) + * * @return the certificate bytes */ public static native byte[] getPeerCertificate(long ssl); /** * Get the error number representing for the given {@code errorNumber}. + * * @param errorNumber The error code + * * @return an error message */ public static native String getErrorString(long errorNumber); /** * SSL_get_time + * * @param ssl the SSL instance (SSL *) + * * @return returns the time at which the session ssl was established. The time is given in seconds since the Epoch */ public static native long getTime(long ssl); /** - * Set Type of Client Certificate verification and Maximum depth of CA Certificates - * in Client Certificate verification. - *
          - * This directive sets the Certificate verification level for the Client - * Authentication. Notice that this directive can be used both in per-server - * and per-directory context. In per-server context it applies to the client - * authentication process used in the standard SSL handshake when a connection - * is established. In per-directory context it forces an SSL renegotiation with - * the reconfigured client verification level after the HTTP request was read - * but before the HTTP response is sent. - *
          + * Set Type of Client Certificate verification and Maximum depth of CA Certificates in Client Certificate + * verification.
          + * This directive sets the Certificate verification level for the Client Authentication. Notice that this directive + * can be used both in per-server and per-directory context. In per-server context it applies to the client + * authentication process used in the standard SSL handshake when a connection is established. In per-directory + * context it forces an SSL renegotiation with the reconfigured client verification level after the HTTP request was + * read but before the HTTP response is sent.
          * The following levels are available for level: + * *

                * SSL_CVERIFY_NONE           - No client Certificate is required at all
                * SSL_CVERIFY_OPTIONAL       - The client may present a valid Certificate
          @@ -533,66 +588,80 @@
                * SSL_CVERIFY_OPTIONAL_NO_CA - The client may present a valid Certificate
                *                              but it need not to be (successfully) verifiable
                * 
          + * *
          - * The depth actually is the maximum number of intermediate certificate issuers, - * i.e. the number of CA certificates which are max allowed to be followed while - * verifying the client certificate. A depth of 0 means that self-signed client - * certificates are accepted only, the default depth of 1 means the client - * certificate can be self-signed or has to be signed by a CA which is directly - * known to the server (i.e. the CA's certificate is under - * {@code setCACertificatePath}, etc. + * The depth actually is the maximum number of intermediate certificate issuers, i.e. the number of CA certificates + * which are max allowed to be followed while verifying the client certificate. A depth of 0 means that self-signed + * client certificates are accepted only, the default depth of 1 means the client certificate can be self-signed or + * has to be signed by a CA which is directly known to the server (i.e. the CA's certificate is under + * {@code setCACertificatePath}, etc). * - * @param ssl the SSL instance (SSL *) + * @param ssl the SSL instance (SSL *) * @param level Type of Client Certificate verification. - * @param depth Maximum depth of CA Certificates in Client Certificate - * verification. + * @param depth Maximum depth of CA Certificates in Client Certificate verification. */ public static native void setVerify(long ssl, int level, int depth); /** * Set OpenSSL Option. - * @param ssl the SSL instance (SSL *) - * @param options See SSL.SSL_OP_* for option flags. + * + * @param ssl the SSL instance (SSL *) + * @param options See SSL.SSL_OP_* for option flags. */ public static native void setOptions(long ssl, int options); /** * Get OpenSSL Option. + * * @param ssl the SSL instance (SSL *) - * @return options See SSL.SSL_OP_* for option flags. + * + * @return options See SSL.SSL_OP_* for option flags. */ public static native int getOptions(long ssl); /** * Returns all cipher suites that are enabled for negotiation in an SSL handshake. + * * @param ssl the SSL instance (SSL *) + * * @return ciphers */ public static native String[] getCiphers(long ssl); /** - * Returns the cipher suites available for negotiation in SSL handshake. - *
          - * This complex directive uses a colon-separated cipher-spec string consisting - * of OpenSSL cipher specifications to configure the Cipher Suite the client - * is permitted to negotiate in the SSL handshake phase. Notice that this - * directive can be used both in per-server and per-directory context. - * In per-server context it applies to the standard SSL handshake when a - * connection is established. In per-directory context it forces an SSL - * renegotiation with the reconfigured Cipher Suite after the HTTP request - * was read but before the HTTP response is sent. - * @param ssl the SSL instance (SSL *) - * @param ciphers an SSL cipher specification + * Set the TLSv1.2 and below ciphers available for negotiation the in TLS handshake. + *

          + * This complex directive uses a colon-separated cipher-spec string consisting of OpenSSL cipher specifications to + * configure the ciphers the client is permitted to negotiate in the TLS handshake phase. + * + * @param ssl The SSL instance (SSL *) + * @param cipherList An OpenSSL cipher specification. + * + * @return true if the operation was successful + * + * @throws Exception An error occurred + */ + public static native boolean setCipherSuites(long ssl, String cipherList) throws Exception; + + /** + * Set the TLSv1.3 cipher suites available for negotiation the in TLS handshake. + *

          + * This uses a colon-separated list of TLSv1.3 cipher suite names in preference order. + * + * @param ssl The SSL instance (SSL *) + * @param cipherSuites An OpenSSL cipher suite list. + * * @return true if the operation was successful + * * @throws Exception An error occurred */ - public static native boolean setCipherSuites(long ssl, String ciphers) - throws Exception; + public static native boolean setCipherSuitesEx(long ssl, String cipherSuites) throws Exception; /** * Returns the ID of the session as byte array representation. * * @param ssl the SSL instance (SSL *) + * * @return the session as byte array representation obtained via SSL_SESSION_get_id. */ public static native byte[] getSessionId(long ssl); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/SSLConf.java tomcat10-10.1.52/java/org/apache/tomcat/jni/SSLConf.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/SSLConf.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/SSLConf.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,10 +21,10 @@ /** * Create a new SSL_CONF context. * - * @param pool The pool to use. - * @param flags The SSL_CONF flags to use. It can be any combination of - * the following: - *

          +     * @param pool  The pool to use.
          +     * @param flags The SSL_CONF flags to use. It can be any combination of the following:
          +     *
          +     *                  
                * {@link SSL#SSL_CONF_FLAG_CMDLINE}
                * {@link SSL#SSL_CONF_FLAG_FILE}
                * {@link SSL#SSL_CONF_FLAG_CLIENT}
          @@ -33,13 +33,13 @@
                * {@link SSL#SSL_CONF_FLAG_CERTIFICATE}
                * 
          * - * @return The Java representation of a pointer to the newly created - * SSL_CONF Context + * @return The Java representation of a pointer to the newly created SSL_CONF Context * * @throws Exception If the SSL_CONF context could not be created * * @see OpenSSL SSL_CONF_CTX_new - * @see OpenSSL SSL_CONF_CTX_set_flags + * @see OpenSSL + * SSL_CONF_CTX_set_flags */ public static native long make(long pool, int flags) throws Exception; @@ -55,13 +55,12 @@ /** * Check a command with an SSL_CONF context. * - * @param cctx SSL_CONF context to use. - * @param name command name. + * @param cctx SSL_CONF context to use. + * @param name command name. * @param value command value. * - * @return The result of the check based on the {@code SSL_CONF_cmd_value_type} - * call. Unknown types will result in an exception, as well as - * file and directory types with invalid file or directory names. + * @return The result of the check based on the {@code SSL_CONF_cmd_value_type} call. Unknown types will result in + * an exception, as well as file and directory types with invalid file or directory names. * * @throws Exception If the check fails. * @@ -70,22 +69,22 @@ public static native int check(long cctx, String name, String value) throws Exception; /** - * Assign an SSL context to an SSL_CONF context. - * All following calls to {@link #apply(long, String, String)} will be + * Assign an SSL context to an SSL_CONF context. All following calls to {@link #apply(long, String, String)} will be * applied to this SSL context. * * @param cctx SSL_CONF context to use. - * @param ctx SSL context to assign to the given SSL_CONF context. + * @param ctx SSL context to assign to the given SSL_CONF context. * - * @see OpenSSL SSL_CONF_CTX_set_ssl_ctx + * @see OpenSSL + * SSL_CONF_CTX_set_ssl_ctx */ public static native void assign(long cctx, long ctx); /** * Apply a command to an SSL_CONF context. * - * @param cctx SSL_CONF context to use. - * @param name command name. + * @param cctx SSL_CONF context to use. + * @param name command name. * @param value command value. * * @return The result of the native {@code SSL_CONF_cmd} call @@ -103,7 +102,8 @@ * * @return The result of the native {@code SSL_CONF_CTX_finish} call * - * @see OpenSSL SSL_CONF_CTX_finish + * @see OpenSSL + * SSL_CONF_CTX_finish */ public static native int finish(long cctx); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/SSLContext.java tomcat10-10.1.52/java/org/apache/tomcat/jni/SSLContext.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/SSLContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/SSLContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,16 +22,15 @@ public final class SSLContext { - public static final byte[] DEFAULT_SESSION_ID_CONTEXT = - new byte[] { 'd', 'e', 'f', 'a', 'u', 'l', 't' }; + public static final byte[] DEFAULT_SESSION_ID_CONTEXT = new byte[] { 'd', 'e', 'f', 'a', 'u', 'l', 't' }; /** * Create a new SSL context. * - * @param pool The pool to use. - * @param protocol The SSL protocol to use. It can be any combination of - * the following: - *
          +     * @param pool     The pool to use.
          +     * @param protocol The SSL protocol to use. It can be any combination of the following:
          +     *
          +     *                     
                * {@link SSL#SSL_PROTOCOL_SSLV2}
                * {@link SSL#SSL_PROTOCOL_SSLV3}
                * {@link SSL#SSL_PROTOCOL_TLSV1}
          @@ -40,15 +39,16 @@
                * {@link SSL#SSL_PROTOCOL_TLSV1_3}
                * {@link SSL#SSL_PROTOCOL_ALL} ( == all TLS versions, no SSL)
                * 
          - * @param mode SSL mode to use - *
          +     *
          +     * @param mode     SSL mode to use
          +     *
          +     *                     
                * SSL_MODE_CLIENT
                * SSL_MODE_SERVER
                * SSL_MODE_COMBINED
          -     * 
          + *
          * - * @return The Java representation of a pointer to the newly created SSL - * Context + * @return The Java representation of a pointer to the newly created SSL Context * * @throws Exception If the SSL Context could not be created */ @@ -56,149 +56,152 @@ /** * Free the resources used by the Context + * * @param ctx Server or Client context to free. + * * @return APR Status code. */ public static native int free(long ctx); /** * Set OpenSSL Option. - * @param ctx Server or Client context to use. - * @param options See SSL.SSL_OP_* for option flags. + * + * @param ctx Server or Client context to use. + * @param options See SSL.SSL_OP_* for option flags. */ public static native void setOptions(long ctx, int options); /** * Get OpenSSL Option. + * * @param ctx Server or Client context to use. - * @return options See SSL.SSL_OP_* for option flags. + * + * @return options See SSL.SSL_OP_* for option flags. */ public static native int getOptions(long ctx); /** * Clears OpenSSL Options. - * @param ctx Server or Client context to use. - * @param options See SSL.SSL_OP_* for option flags. + * + * @param ctx Server or Client context to use. + * @param options See SSL.SSL_OP_* for option flags. */ public static native void clearOptions(long ctx, int options); /** * Returns all cipher suites that are enabled for negotiation in an SSL handshake. + * * @param ctx Server or Client context to use. + * * @return ciphers */ public static native String[] getCiphers(long ctx); /** - * Cipher Suite available for negotiation in SSL handshake. - *
          - * This complex directive uses a colon-separated cipher-spec string consisting - * of OpenSSL cipher specifications to configure the Cipher Suite the client - * is permitted to negotiate in the SSL handshake phase. Notice that this - * directive can be used both in per-server and per-directory context. - * In per-server context it applies to the standard SSL handshake when a - * connection is established. In per-directory context it forces an SSL - * renegotiation with the reconfigured Cipher Suite after the HTTP request - * was read but before the HTTP response is sent. - * @param ctx Server or Client context to use. - * @param ciphers An OpenSSL cipher specification. + * Set the TLSv1.2 and below ciphers available for negotiation the in TLS handshake. + *

          + * This complex directive uses a colon-separated cipher-spec string consisting of OpenSSL cipher specifications to + * configure the ciphers the client is permitted to negotiate in the TLS handshake phase. + * + * @param ctx Server or Client context to use. + * @param cipherList An OpenSSL cipher specification. + * * @return true if the operation was successful + * * @throws Exception An error occurred */ - public static native boolean setCipherSuite(long ctx, String ciphers) - throws Exception; + public static native boolean setCipherSuite(long ctx, String cipherList) throws Exception; /** - * Set File of concatenated PEM-encoded CA CRLs or - * directory of PEM-encoded CA Certificates for Client Auth - *
          - * This directive sets the all-in-one file where you can assemble the - * Certificate Revocation Lists (CRL) of Certification Authorities (CA) - * whose clients you deal with. These are used for Client Authentication. - * Such a file is simply the concatenation of the various PEM-encoded CRL - * files, in order of preference. - *
          - * The files in this directory have to be PEM-encoded and are accessed through - * hash filenames. So usually you can't just place the Certificate files there: - * you also have to create symbolic links named hash-value.N. And you should - * always make sure this directory contains the appropriate symbolic links. - * Use the Makefile which comes with mod_ssl to accomplish this task. - * @param ctx Server or Client context to use. + * Set the TLSv1.3 cipher suites available for negotiation the in TLS handshake. + *

          + * This uses a colon-separated list of TLSv1.3 cipher suite names in preference order. + * + * @param ctx Server or Client context to use. + * @param cipherSuites An OpenSSL cipher suite list. + * + * @return true if the operation was successful + * + * @throws Exception An error occurred + */ + public static native boolean setCipherSuitesEx(long ctx, String cipherSuites) throws Exception; + + /** + * Set File of concatenated PEM-encoded CA CRLs or directory of PEM-encoded CA Certificates for Client Auth
          + * This directive sets the all-in-one file where you can assemble the Certificate Revocation Lists (CRL) of + * Certification Authorities (CA) whose clients you deal with. These are used for Client Authentication. Such a file + * is simply the concatenation of the various PEM-encoded CRL files, in order of preference.
          + * The files in this directory have to be PEM-encoded and are accessed through hash filenames. So usually you can't + * just place the Certificate files there: you also have to create symbolic links named hash-value.N. And you should + * always make sure this directory contains the appropriate symbolic links. Use the Makefile which comes with + * mod_ssl to accomplish this task. + * + * @param ctx Server or Client context to use. * @param file File of concatenated PEM-encoded CA CRLs for Client Auth. * @param path Directory of PEM-encoded CA Certificates for Client Auth. + * * @return true if the operation was successful + * * @throws Exception An error occurred */ - public static native boolean setCARevocation(long ctx, String file, - String path) - throws Exception; + public static native boolean setCARevocation(long ctx, String file, String path) throws Exception; /** - * Set File of PEM-encoded Server CA Certificates - *
          - * This directive sets the optional all-in-one file where you can assemble the - * certificates of Certification Authorities (CA) which form the certificate - * chain of the server certificate. This starts with the issuing CA certificate - * of of the server certificate and can range up to the root CA certificate. - * Such a file is simply the concatenation of the various PEM-encoded CA - * Certificate files, usually in certificate chain order. - *
          - * But be careful: Providing the certificate chain works only if you are using - * a single (either RSA or DSA) based server certificate. If you are using a - * coupled RSA+DSA certificate pair, this will work only if actually both - * certificates use the same certificate chain. Else the browsers will be - * confused in this situation. - * @param ctx Server or Client context to use. - * @param file File of PEM-encoded Server CA Certificates. - * @param skipfirst Skip first certificate if chain file is inside - * certificate file. + * Set File of PEM-encoded Server CA Certificates
          + * This directive sets the optional all-in-one file where you can assemble the certificates of Certification + * Authorities (CA) which form the certificate chain of the server certificate. This starts with the issuing CA + * certificate of the server certificate and can range up to the root CA certificate. Such a file is simply the + * concatenation of the various PEM-encoded CA Certificate files, usually in certificate chain order.
          + * But be careful: Providing the certificate chain works only if you are using a single (either RSA or DSA) based + * server certificate. If you are using a coupled RSA+DSA certificate pair, this will work only if actually both + * certificates use the same certificate chain. Else the browsers will be confused in this situation. + * + * @param ctx Server or Client context to use. + * @param file File of PEM-encoded Server CA Certificates. + * @param skipfirst Skip first certificate if chain file is inside certificate file. + * * @return true if the operation was successful */ - public static native boolean setCertificateChainFile(long ctx, String file, - boolean skipfirst); + public static native boolean setCertificateChainFile(long ctx, String file, boolean skipfirst); /** - * Set Certificate - *
          - * Point setCertificateFile at a PEM encoded certificate. If - * the certificate is encrypted, then you will be prompted for a - * pass phrase. Note that a kill -HUP will prompt again. A test - * certificate can be generated with 'make certificate' under - * built time. Keep in mind that if you've both a RSA and a DSA - * certificate you can configure both in parallel (to also allow - * the use of DSA ciphers, etc.) - *
          - * If the key is not combined with the certificate, use key param - * to point at the key file. Keep in mind that if - * you've both a RSA and a DSA private key you can configure - * both in parallel (to also allow the use of DSA ciphers, etc.) - * @param ctx Server or Client context to use. - * @param cert Certificate file. - * @param key Private Key file to use if not in cert. - * @param password Certificate password. If null and certificate - * is encrypted, password prompt will be displayed. - * @param idx Certificate index SSL_AIDX_RSA or SSL_AIDX_DSA. + * Set Certificate
          + * Point setCertificateFile at a PEM encoded certificate. If the certificate is encrypted, then you will be prompted + * for a pass phrase. Note that a kill -HUP will prompt again. A test certificate can be generated with 'make + * certificate' under built time. Keep in mind that if you've both a RSA and a DSA certificate you can configure + * both in parallel (to also allow the use of DSA ciphers, etc.)
          + * If the key is not combined with the certificate, use key param to point at the key file. Keep in mind that if + * you've both a RSA and a DSA private key you can configure both in parallel (to also allow the use of DSA ciphers, + * etc.) + * + * @param ctx Server or Client context to use. + * @param cert Certificate file. + * @param key Private Key file to use if not in cert. + * @param password Certificate password. If null and certificate is encrypted, password prompt will be displayed. + * @param idx Certificate index SSL_AIDX_RSA or SSL_AIDX_DSA. + * * @return true if the operation was successful + * * @throws Exception An error occurred */ - public static native boolean setCertificate(long ctx, String cert, - String key, String password, - int idx) - throws Exception; + public static native boolean setCertificate(long ctx, String cert, String key, String password, int idx) + throws Exception; /** - * Set the size of the internal session cache. - * http://www.openssl.org/docs/ssl/SSL_CTX_sess_set_cache_size.html - * @param ctx Server or Client context to use. + * Set the size of the internal session cache. http://www.openssl.org/docs/ssl/SSL_CTX_sess_set_cache_size.html + * + * @param ctx Server or Client context to use. * @param size The cache size + * * @return the value set */ public static native long setSessionCacheSize(long ctx, long size); /** - * Get the size of the internal session cache. - * http://www.openssl.org/docs/ssl/SSL_CTX_sess_get_cache_size.html + * Get the size of the internal session cache. http://www.openssl.org/docs/ssl/SSL_CTX_sess_get_cache_size.html + * * @param ctx Server or Client context to use. + * * @return the size */ public static native long getSessionCacheSize(long ctx); @@ -206,8 +209,10 @@ /** * Set the timeout for the internal session cache in seconds. * http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html - * @param ctx Server or Client context to use. + * + * @param ctx Server or Client context to use. * @param timeoutSeconds Timeout value + * * @return the value set */ public static native long setSessionCacheTimeout(long ctx, long timeoutSeconds); @@ -215,90 +220,98 @@ /** * Get the timeout for the internal session cache in seconds. * http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html + * * @param ctx Server or Client context to use. + * * @return the timeout */ public static native long getSessionCacheTimeout(long ctx); /** * Set the mode of the internal session cache and return the previous used mode. - * @param ctx Server or Client context to use. + * + * @param ctx Server or Client context to use. * @param mode The mode to set + * * @return the value set */ public static native long setSessionCacheMode(long ctx, long mode); /** * Get the mode of the current used internal session cache. + * * @param ctx Server or Client context to use. + * * @return the value set */ public static native long getSessionCacheMode(long ctx); /* - * Session resumption statistics methods. - * http://www.openssl.org/docs/ssl/SSL_CTX_sess_number.html + * Session resumption statistics methods. http://www.openssl.org/docs/ssl/SSL_CTX_sess_number.html */ public static native long sessionAccept(long ctx); + public static native long sessionAcceptGood(long ctx); + public static native long sessionAcceptRenegotiate(long ctx); + public static native long sessionCacheFull(long ctx); + public static native long sessionCbHits(long ctx); + public static native long sessionConnect(long ctx); + public static native long sessionConnectGood(long ctx); + public static native long sessionConnectRenegotiate(long ctx); + public static native long sessionHits(long ctx); + public static native long sessionMisses(long ctx); + public static native long sessionNumber(long ctx); + public static native long sessionTimeouts(long ctx); /** * Set TLS session keys. This allows us to share keys across TFEs. - * @param ctx Server or Client context to use. + * + * @param ctx Server or Client context to use. * @param keys Some session keys */ public static native void setSessionTicketKeys(long ctx, byte[] keys); /** - * Set File and Directory of concatenated PEM-encoded CA Certificates - * for Client Auth - *
          - * This directive sets the all-in-one file where you can assemble the - * Certificates of Certification Authorities (CA) whose clients you deal with. - * These are used for Client Authentication. Such a file is simply the - * concatenation of the various PEM-encoded Certificate files, in order of - * preference. This can be used alternatively and/or additionally to - * path. - *
          - * The files in this directory have to be PEM-encoded and are accessed through - * hash filenames. So usually you can't just place the Certificate files there: - * you also have to create symbolic links named hash-value.N. And you should - * always make sure this directory contains the appropriate symbolic links. - * Use the Makefile which comes with mod_ssl to accomplish this task. - * @param ctx Server or Client context to use. - * @param file File of concatenated PEM-encoded CA Certificates for - * Client Auth. + * Set File and Directory of concatenated PEM-encoded CA Certificates for Client Auth
          + * This directive sets the all-in-one file where you can assemble the Certificates of Certification Authorities (CA) + * whose clients you deal with. These are used for Client Authentication. Such a file is simply the concatenation of + * the various PEM-encoded Certificate files, in order of preference. This can be used alternatively and/or + * additionally to path.
          + * The files in this directory have to be PEM-encoded and are accessed through hash filenames. So usually you can't + * just place the Certificate files there: you also have to create symbolic links named hash-value.N. And you should + * always make sure this directory contains the appropriate symbolic links. Use the Makefile which comes with + * mod_ssl to accomplish this task. + * + * @param ctx Server or Client context to use. + * @param file File of concatenated PEM-encoded CA Certificates for Client Auth. * @param path Directory of PEM-encoded CA Certificates for Client Auth. + * * @return true if the operation was successful + * * @throws Exception An error occurred */ - public static native boolean setCACertificate(long ctx, String file, - String path) - throws Exception; + public static native boolean setCACertificate(long ctx, String file, String path) throws Exception; /** - * Set Type of Client Certificate verification and Maximum depth of CA Certificates - * in Client Certificate verification. - *
          - * This directive sets the Certificate verification level for the Client - * Authentication. Notice that this directive can be used both in per-server - * and per-directory context. In per-server context it applies to the client - * authentication process used in the standard SSL handshake when a connection - * is established. In per-directory context it forces an SSL renegotiation with - * the reconfigured client verification level after the HTTP request was read - * but before the HTTP response is sent. - *
          + * Set Type of Client Certificate verification and Maximum depth of CA Certificates in Client Certificate + * verification.
          + * This directive sets the Certificate verification level for the Client Authentication. Notice that this directive + * can be used both in per-server and per-directory context. In per-server context it applies to the client + * authentication process used in the standard SSL handshake when a connection is established. In per-directory + * context it forces an SSL renegotiation with the reconfigured client verification level after the HTTP request was + * read but before the HTTP response is sent.
          * The following levels are available for level: + * *

                * SSL_CVERIFY_NONE           - No client Certificate is required at all
                * SSL_CVERIFY_OPTIONAL       - The client may present a valid Certificate
          @@ -306,34 +319,30 @@
                * SSL_CVERIFY_OPTIONAL_NO_CA - The client may present a valid Certificate
                *                              but it need not to be (successfully) verifiable
                * 
          + * *
          - * The depth actually is the maximum number of intermediate certificate issuers, - * i.e. the number of CA certificates which are max allowed to be followed while - * verifying the client certificate. A depth of 0 means that self-signed client - * certificates are accepted only, the default depth of 1 means the client - * certificate can be self-signed or has to be signed by a CA which is directly - * known to the server (i.e. the CA's certificate is under + * The depth actually is the maximum number of intermediate certificate issuers, i.e. the number of CA certificates + * which are max allowed to be followed while verifying the client certificate. A depth of 0 means that self-signed + * client certificates are accepted only, the default depth of 1 means the client certificate can be self-signed or + * has to be signed by a CA which is directly known to the server (i.e. the CA's certificate is under * setCACertificatePath), etc. - * @param ctx Server or Client context to use. + * + * @param ctx Server or Client context to use. * @param level Type of Client Certificate verification. - * @param depth Maximum depth of CA Certificates in Client Certificate - * verification. + * @param depth Maximum depth of CA Certificates in Client Certificate verification. */ public static native void setVerify(long ctx, int level, int depth); /** - * When tc-native encounters a SNI extension in the TLS handshake it will - * call this method to determine which OpenSSL SSLContext to use for the - * connection. - * - * @param currentCtx The OpenSSL SSLContext that the handshake started to - * use. This will be the default OpenSSL SSLContext for - * the endpoint associated with the socket. - * @param sniHostName The host name requested by the client - * - * @return The Java representation of the pointer to the OpenSSL SSLContext - * to use for the given host or zero if no SSLContext could be - * identified + * When tc-native encounters a SNI extension in the TLS handshake it will call this method to determine which + * OpenSSL SSLContext to use for the connection. + * + * @param currentCtx The OpenSSL SSLContext that the handshake started to use. This will be the default OpenSSL + * SSLContext for the endpoint associated with the socket. + * @param sniHostName The host name requested by the client + * + * @return The Java representation of the pointer to the OpenSSL SSLContext to use for the given host or zero if no + * SSLContext could be identified */ public static long sniCallBack(long currentCtx, String sniHostName) { SNICallBack sniCallBack = sniCallBacks.get(Long.valueOf(currentCtx)); @@ -347,50 +356,47 @@ } /** - * A map of default SSL Contexts to SNICallBack instances (in Tomcat these - * are instances of AprEndpoint) that will be used to determine the SSL - * Context to use bases on the SNI host name. It is structured this way - * since a Tomcat instance may have several TLS enabled endpoints that each - * have different SSL Context mappings for the same host name. + * A map of default SSL Contexts to SNICallBack instances (in Tomcat these are instances of AprEndpoint) that will + * be used to determine the SSL Context to use bases on the SNI host name. It is structured this way since a Tomcat + * instance may have several TLS enabled endpoints that each have different SSL Context mappings for the same host + * name. */ private static final Map sniCallBacks = new ConcurrentHashMap<>(); /** - * Interface implemented by components that will receive the call back to - * select an OpenSSL SSLContext based on the host name requested by the - * client. + * Interface implemented by components that will receive the call back to select an OpenSSL SSLContext based on the + * host name requested by the client. */ public interface SNICallBack { /** - * This callback is made during the TLS handshake when the client uses - * the SNI extension to request a specific TLS host. + * This callback is made during the TLS handshake when the client uses the SNI extension to request a specific + * TLS host. * - * @param sniHostName The host name requested by the client - must be in - * lower case + * @param sniHostName The host name requested by the client - must be in lower case * - * @return The Java representation of the pointer to the OpenSSL - * SSLContext to use for the given host or zero if no SSLContext - * could be identified + * @return The Java representation of the pointer to the OpenSSL SSLContext to use for the given host or zero if + * no SSLContext could be identified */ long getSslContext(String sniHostName); } /** - * Allow to hook {@link CertificateVerifier} into the handshake processing. - * This will call {@code SSL_CTX_set_cert_verify_callback} and so replace the default verification - * callback used by openssl - * @param ctx Server or Client context to use. + * Allow to hook {@link CertificateVerifier} into the handshake processing. This will call + * {@code SSL_CTX_set_cert_verify_callback} and so replace the default verification callback used by openssl + * + * @param ctx Server or Client context to use. * @param verifier the verifier to call during handshake. */ public static native void setCertVerifyCallback(long ctx, CertificateVerifier verifier); /** * Set application layer protocol for application layer protocol negotiation extension - * @param ctx Server context to use. - * @param alpnProtos protocols in priority order - * @param selectorFailureBehavior see {@link SSL#SSL_SELECTOR_FAILURE_NO_ADVERTISE} - * and {@link SSL#SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL} + * + * @param ctx Server context to use. + * @param alpnProtos protocols in priority order + * @param selectorFailureBehavior see {@link SSL#SSL_SELECTOR_FAILURE_NO_ADVERTISE} and + * {@link SSL#SSL_SELECTOR_FAILURE_CHOOSE_MY_LAST_PROTOCOL} */ public static native void setAlpnProtos(long ctx, String[] alpnProtos, int selectorFailureBehavior); @@ -398,42 +404,45 @@ * Set the context within which session be reused (server side only) * http://www.openssl.org/docs/ssl/SSL_CTX_set_session_id_context.html * - * @param ctx Server context to use. - * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name - * of the application and/or the hostname and/or service name + * @param ctx Server context to use. + * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name of the application + * and/or the hostname and/or service name + * * @return {@code true} if success, {@code false} otherwise. */ public static native boolean setSessionIdContext(long ctx, byte[] sidCtx); /** - * Set CertificateRaw - *
          + * Set CertificateRaw
          * Use keystore a certificate and key to fill the BIOP - * @param ctx Server or Client context to use. - * @param cert Byte array with the certificate in DER encoding. - * @param key Byte array with the Private Key file in PEM format. + * + * @param ctx Server or Client context to use. + * @param cert Byte array with the certificate in DER encoding. + * @param key Byte array with the Private Key file in PEM format. * @param sslAidxRsa Certificate index SSL_AIDX_RSA or SSL_AIDX_DSA. + * * @return {@code true} if success, {@code false} otherwise. */ public static native boolean setCertificateRaw(long ctx, byte[] cert, byte[] key, int sslAidxRsa); /** - * Add a certificate to the certificate chain. Certs should be added in - * order starting with the issuer of the host certs and working up the - * certificate chain to the CA. - * - *
          + * Add a certificate to the certificate chain. Certs should be added in order starting with the issuer of the host + * certs and working up the certificate chain to the CA.
          * Use keystore a certificate chain to fill the BIOP - * @param ctx Server or Client context to use. + * + * @param ctx Server or Client context to use. * @param cert Byte array with the certificate in DER encoding. + * * @return {@code true} if success, {@code false} otherwise. */ public static native boolean addChainCertificateRaw(long ctx, byte[] cert); /** * Add a CA certificate we accept as issuer for peer certs - * @param ctx Server or Client context to use. + * + * @param ctx Server or Client context to use. * @param cert Byte array with the certificate in DER encoding. + * * @return {@code true} if success, {@code false} otherwise. */ public static native boolean addClientCACertificateRaw(long ctx, byte[] cert); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/jni/Sockaddr.java tomcat10-10.1.52/java/org/apache/tomcat/jni/Sockaddr.java --- tomcat10-10.1.34/java/org/apache/tomcat/jni/Sockaddr.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/jni/Sockaddr.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,10 +17,9 @@ package org.apache.tomcat.jni; /** - * Tomcat Native 1.2.33 and earlier won't initialise unless this class is - * present. This dummy class ensures initialisation gets as far as being able to - * check the version of the Tomcat Native library and reporting a version error - * if 1.2.33 or earlier is present. + * Tomcat Native 1.2.33 and earlier won't initialise unless this class is present. This dummy class ensures + * initialisation gets as far as being able to check the version of the Tomcat Native library and reporting a version + * error if 1.2.33 or earlier is present. */ public class Sockaddr { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/Diagnostics.java tomcat10-10.1.52/java/org/apache/tomcat/util/Diagnostics.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/Diagnostics.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/Diagnostics.java 2026-01-23 19:33:36.000000000 +0000 @@ -76,34 +76,25 @@ private static final Log log = LogFactory.getLog(Diagnostics.class); - private static final SimpleDateFormat timeformat = - new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + private static final SimpleDateFormat timeformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); /* Some platform MBeans */ - private static final ClassLoadingMXBean classLoadingMXBean = - ManagementFactory.getClassLoadingMXBean(); - private static final CompilationMXBean compilationMXBean = - ManagementFactory.getCompilationMXBean(); - private static final OperatingSystemMXBean operatingSystemMXBean = - ManagementFactory.getOperatingSystemMXBean(); - private static final RuntimeMXBean runtimeMXBean = - ManagementFactory.getRuntimeMXBean(); - private static final ThreadMXBean threadMXBean = - ManagementFactory.getThreadMXBean(); + private static final ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean(); + private static final CompilationMXBean compilationMXBean = ManagementFactory.getCompilationMXBean(); + private static final OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); + private static final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + private static final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); // XXX Not sure whether the following MBeans should better // be retrieved on demand, i.e. whether they can change // dynamically in the MBeanServer. private static final PlatformLoggingMXBean loggingMXBean = - ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class); - private static final MemoryMXBean memoryMXBean = - ManagementFactory.getMemoryMXBean(); + ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class); + private static final MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); private static final List garbageCollectorMXBeans = - ManagementFactory.getGarbageCollectorMXBeans(); - private static final List memoryManagerMXBeans = - ManagementFactory.getMemoryManagerMXBeans(); - private static final List memoryPoolMXBeans = - ManagementFactory.getMemoryPoolMXBeans(); + ManagementFactory.getGarbageCollectorMXBeans(); + private static final List memoryManagerMXBeans = ManagementFactory.getMemoryManagerMXBeans(); + private static final List memoryPoolMXBeans = ManagementFactory.getMemoryPoolMXBeans(); /** * Check whether thread contention monitoring is enabled. @@ -146,8 +137,8 @@ threadMXBean.setThreadCpuTimeEnabled(enable); boolean checkValue = threadMXBean.isThreadCpuTimeEnabled(); if (enable != checkValue) { - log.error(sm.getString("diagnostics.setPropertyFail", "threadCpuTimeEnabled", - Boolean.valueOf(enable), Boolean.valueOf(checkValue))); + log.error(sm.getString("diagnostics.setPropertyFail", "threadCpuTimeEnabled", Boolean.valueOf(enable), + Boolean.valueOf(checkValue))); } } @@ -167,8 +158,8 @@ classLoadingMXBean.setVerbose(verbose); boolean checkValue = classLoadingMXBean.isVerbose(); if (verbose != checkValue) { - log.error(sm.getString("diagnostics.setPropertyFail", "verboseClassLoading", - Boolean.valueOf(verbose), Boolean.valueOf(checkValue))); + log.error(sm.getString("diagnostics.setPropertyFail", "verboseClassLoading", Boolean.valueOf(verbose), + Boolean.valueOf(checkValue))); } } @@ -176,15 +167,14 @@ * Set logger level * * @param loggerName the name of the logger - * @param levelName the level to set + * @param levelName the level to set */ public static void setLoggerLevel(String loggerName, String levelName) { loggingMXBean.setLoggerLevel(loggerName, levelName); String checkValue = loggingMXBean.getLoggerLevel(loggerName); if (!checkValue.equals(levelName)) { String propertyName = "loggerLevel[" + loggerName + "]"; - log.error(sm.getString("diagnostics.setPropertyFail", propertyName, - levelName, checkValue)); + log.error(sm.getString("diagnostics.setPropertyFail", propertyName, levelName, checkValue)); } } @@ -197,8 +187,8 @@ memoryMXBean.setVerbose(verbose); boolean checkValue = memoryMXBean.isVerbose(); if (verbose != checkValue) { - log.error(sm.getString("diagnostics.setPropertyFail", "verboseGarbageCollection", - Boolean.valueOf(verbose), Boolean.valueOf(checkValue))); + log.error(sm.getString("diagnostics.setPropertyFail", "verboseGarbageCollection", Boolean.valueOf(verbose), + Boolean.valueOf(checkValue))); } } @@ -215,7 +205,7 @@ * @param name name of the MemoryPoolMXBean or "all" */ public static void resetPeakUsage(String name) { - for (MemoryPoolMXBean mbean: memoryPoolMXBeans) { + for (MemoryPoolMXBean mbean : memoryPoolMXBeans) { if (name.equals("all") || name.equals(mbean.getName())) { mbean.resetPeakUsage(); } @@ -225,12 +215,13 @@ /** * Set usage threshold in MemoryPoolMXBean * - * @param name name of the MemoryPoolMXBean + * @param name name of the MemoryPoolMXBean * @param threshold the threshold to set + * * @return true if setting the threshold succeeded */ public static boolean setUsageThreshold(String name, long threshold) { - for (MemoryPoolMXBean mbean: memoryPoolMXBeans) { + for (MemoryPoolMXBean mbean : memoryPoolMXBeans) { if (name.equals(mbean.getName())) { try { mbean.setUsageThreshold(threshold); @@ -247,12 +238,13 @@ /** * Set collection usage threshold in MemoryPoolMXBean * - * @param name name of the MemoryPoolMXBean + * @param name name of the MemoryPoolMXBean * @param threshold the collection threshold to set + * * @return true if setting the threshold succeeded */ public static boolean setCollectionUsageThreshold(String name, long threshold) { - for (MemoryPoolMXBean mbean: memoryPoolMXBeans) { + for (MemoryPoolMXBean mbean : memoryPoolMXBeans) { if (name.equals(mbean.getName())) { try { mbean.setCollectionUsageThreshold(threshold); @@ -270,19 +262,17 @@ * Formats the thread dump header for one thread. * * @param ti the ThreadInfo describing the thread + * * @return the formatted thread dump header */ private static String getThreadDumpHeader(ThreadInfo ti) { - StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\""); - sb.append(" Id=" + ti.getThreadId()); - sb.append(" cpu=" + threadMXBean.getThreadCpuTime(ti.getThreadId()) + - " ns"); - sb.append(" usr=" + threadMXBean.getThreadUserTime(ti.getThreadId()) + - " ns"); - sb.append(" blocked " + ti.getBlockedCount() + " for " + - ti.getBlockedTime() + " ms"); - sb.append(" waited " + ti.getWaitedCount() + " for " + - ti.getWaitedTime() + " ms"); + StringBuilder sb = new StringBuilder(); + sb.append("\"").append(ti.getThreadName()).append("\""); + sb.append(" Id=").append(ti.getThreadId()); + sb.append(" cpu=").append(threadMXBean.getThreadCpuTime(ti.getThreadId())).append(" ns"); + sb.append(" usr=").append(threadMXBean.getThreadUserTime(ti.getThreadId())).append(" ns"); + sb.append(" blocked ").append(ti.getBlockedCount()).append(" for ").append(ti.getBlockedTime()).append(" ms"); + sb.append(" waited ").append(ti.getWaitedCount()).append(" for ").append(ti.getWaitedTime()).append(" ms"); if (ti.isSuspended()) { sb.append(" (suspended)"); @@ -291,7 +281,7 @@ sb.append(" (running in native)"); } sb.append(CRLF); - sb.append(INDENT3 + "java.lang.Thread.State: " + ti.getThreadState()); + sb.append(INDENT3 + "java.lang.Thread.State: ").append(ti.getThreadState()); sb.append(CRLF); return sb.toString(); } @@ -300,13 +290,13 @@ * Formats the thread dump for one thread. * * @param ti the ThreadInfo describing the thread + * * @return the formatted thread dump */ private static String getThreadDump(ThreadInfo ti) { StringBuilder sb = new StringBuilder(getThreadDumpHeader(ti)); for (LockInfo li : ti.getLockedSynchronizers()) { - sb.append(INDENT2 + "locks " + - li.toString() + CRLF); + sb.append(INDENT2 + "locks ").append(li.toString()).append(CRLF); } boolean start = true; StackTraceElement[] stes = ti.getStackTrace(); @@ -317,26 +307,22 @@ } for (int i = 0; i < stes.length; i++) { StackTraceElement ste = stes[i]; - sb.append(INDENT2 + - "at " + ste.toString() + CRLF); + sb.append(INDENT2 + "at ").append(ste.toString()).append(CRLF); if (start) { if (ti.getLockName() != null) { - sb.append(INDENT2 + "- waiting on (a " + - ti.getLockName() + ")"); + sb.append(INDENT2 + "- waiting on (a ").append(ti.getLockName()).append(")"); if (ti.getLockOwnerName() != null) { - sb.append(" owned by " + ti.getLockOwnerName() + - " Id=" + ti.getLockOwnerId()); + sb.append(" owned by ").append(ti.getLockOwnerName()).append(" Id=") + .append(ti.getLockOwnerId()); } sb.append(CRLF); } start = false; } if (monitorDepths[i] != null) { - MonitorInfo mi = (MonitorInfo)monitorDepths[i]; - sb.append(INDENT2 + - "- locked (a " + mi.toString() + ")"+ - " index " + mi.getLockedStackDepth() + - " frame " + mi.getLockedStackFrame().toString()); + MonitorInfo mi = (MonitorInfo) monitorDepths[i]; + sb.append(INDENT2 + "- locked (a ").append(mi.toString()).append(")").append(" index "); + sb.append(mi.getLockedStackDepth()).append(" frame ").append(mi.getLockedStackFrame().toString()); sb.append(CRLF); } @@ -348,6 +334,7 @@ * Formats the thread dump for a list of threads. * * @param tinfos the ThreadInfo array describing the thread list + * * @return the formatted thread dump */ private static String getThreadDump(ThreadInfo[] tinfos) { @@ -360,32 +347,23 @@ } /** - * Check if any threads are deadlocked. If any, print - * the thread dump for those threads. + * Check if any threads are deadlocked. If any, print the thread dump for those threads. * - * @return a deadlock message and the formatted thread dump - * of the deadlocked threads + * @return a deadlock message and the formatted thread dump of the deadlocked threads */ public static String findDeadlock() { - ThreadInfo[] tinfos = null; long[] ids = threadMXBean.findDeadlockedThreads(); if (ids != null) { - tinfos = threadMXBean.getThreadInfo(threadMXBean.findDeadlockedThreads(), - true, true); + ThreadInfo[] tinfos = threadMXBean.getThreadInfo(threadMXBean.findDeadlockedThreads(), true, true); if (tinfos != null) { - StringBuilder sb = - new StringBuilder(sm.getString("diagnostics.deadlockFound")); - sb.append(CRLF); - sb.append(getThreadDump(tinfos)); - return sb.toString(); + return sm.getString("diagnostics.deadlockFound") + CRLF + getThreadDump(tinfos); } } return ""; } /** - * Retrieves a formatted JVM thread dump. - * The default StringManager will be used. + * Retrieves a formatted JVM thread dump. The default StringManager will be used. * * @return the formatted JVM thread dump */ @@ -394,29 +372,27 @@ } /** - * Retrieves a formatted JVM thread dump. - * The given list of locales will be used - * to retrieve a StringManager. + * Retrieves a formatted JVM thread dump. The given list of locales will be used to retrieve a StringManager. * * @param requestedLocales list of locales to use + * * @return the formatted JVM thread dump */ public static String getThreadDump(Enumeration requestedLocales) { - return getThreadDump( - StringManager.getManager(PACKAGE, requestedLocales)); + return getThreadDump(StringManager.getManager(PACKAGE, requestedLocales)); } /** - * Retrieve a JVM thread dump formatted - * using the given StringManager. + * Retrieve a JVM thread dump formatted using the given StringManager. * * @param requestedSm the StringManager to use + * * @return the formatted JVM thread dump */ public static String getThreadDump(StringManager requestedSm) { StringBuilder sb = new StringBuilder(); - synchronized(timeformat) { + synchronized (timeformat) { sb.append(timeformat.format(new Date())); } sb.append(CRLF); @@ -428,7 +404,7 @@ sb.append(runtimeMXBean.getVmVersion()); String vminfo = System.getProperty(vminfoSystemProperty); if (vminfo != null) { - sb.append(" " + vminfo); + sb.append(" ").append(vminfo); } sb.append("):" + CRLF); sb.append(CRLF); @@ -442,25 +418,26 @@ /** * Format contents of a MemoryUsage object. - * @param name a text prefix used in formatting + * + * @param name a text prefix used in formatting * @param usage the MemoryUsage object to format + * * @return the formatted contents */ private static String formatMemoryUsage(String name, MemoryUsage usage) { if (usage != null) { StringBuilder sb = new StringBuilder(); - sb.append(INDENT1 + name + " init: " + usage.getInit() + CRLF); - sb.append(INDENT1 + name + " used: " + usage.getUsed() + CRLF); - sb.append(INDENT1 + name + " committed: " + usage.getCommitted() + CRLF); - sb.append(INDENT1 + name + " max: " + usage.getMax() + CRLF); + sb.append(INDENT1).append(name).append(" init: ").append(usage.getInit()).append(CRLF); + sb.append(INDENT1).append(name).append(" used: ").append(usage.getUsed()).append(CRLF); + sb.append(INDENT1).append(name).append(" committed: ").append(usage.getCommitted()).append(CRLF); + sb.append(INDENT1).append(name).append(" max: ").append(usage.getMax()).append(CRLF); return sb.toString(); } return ""; } /** - * Retrieves a formatted JVM information text. - * The default StringManager will be used. + * Retrieves a formatted JVM information text. The default StringManager will be used. * * @return the formatted JVM information text */ @@ -469,11 +446,10 @@ } /** - * Retrieves a formatted JVM information text. - * The given list of locales will be used - * to retrieve a StringManager. + * Retrieves a formatted JVM information text. The given list of locales will be used to retrieve a StringManager. * * @param requestedLocales list of locales to use + * * @return the formatted JVM information text */ public static String getVMInfo(Enumeration requestedLocales) { @@ -481,188 +457,182 @@ } /** - * Retrieve a JVM information text formatted - * using the given StringManager. + * Retrieve a JVM information text formatted using the given StringManager. * * @param requestedSm the StringManager to use + * * @return the formatted JVM information text */ public static String getVMInfo(StringManager requestedSm) { StringBuilder sb = new StringBuilder(); - synchronized(timeformat) { + synchronized (timeformat) { sb.append(timeformat.format(new Date())); } sb.append(CRLF); sb.append(requestedSm.getString("diagnostics.vmInfoRuntime")); sb.append(":" + CRLF); - sb.append(INDENT1 + "vmName: " + runtimeMXBean.getVmName() + CRLF); - sb.append(INDENT1 + "vmVersion: " + runtimeMXBean.getVmVersion() + CRLF); - sb.append(INDENT1 + "vmVendor: " + runtimeMXBean.getVmVendor() + CRLF); - sb.append(INDENT1 + "specName: " + runtimeMXBean.getSpecName() + CRLF); - sb.append(INDENT1 + "specVersion: " + runtimeMXBean.getSpecVersion() + CRLF); - sb.append(INDENT1 + "specVendor: " + runtimeMXBean.getSpecVendor() + CRLF); - sb.append(INDENT1 + "managementSpecVersion: " + - runtimeMXBean.getManagementSpecVersion() + CRLF); - sb.append(INDENT1 + "name: " + runtimeMXBean.getName() + CRLF); - sb.append(INDENT1 + "startTime: " + runtimeMXBean.getStartTime() + CRLF); - sb.append(INDENT1 + "uptime: " + runtimeMXBean.getUptime() + CRLF); - sb.append(INDENT1 + "isBootClassPathSupported: " + - runtimeMXBean.isBootClassPathSupported() + CRLF); + sb.append(INDENT1 + "vmName: ").append(runtimeMXBean.getVmName()).append(CRLF); + sb.append(INDENT1 + "vmVersion: ").append(runtimeMXBean.getVmVersion()).append(CRLF); + sb.append(INDENT1 + "vmVendor: ").append(runtimeMXBean.getVmVendor()).append(CRLF); + sb.append(INDENT1 + "specName: ").append(runtimeMXBean.getSpecName()).append(CRLF); + sb.append(INDENT1 + "specVersion: ").append(runtimeMXBean.getSpecVersion()).append(CRLF); + sb.append(INDENT1 + "specVendor: ").append(runtimeMXBean.getSpecVendor()).append(CRLF); + sb.append(INDENT1 + "managementSpecVersion: ").append(runtimeMXBean.getManagementSpecVersion()).append(CRLF); + sb.append(INDENT1 + "name: ").append(runtimeMXBean.getName()).append(CRLF); + sb.append(INDENT1 + "startTime: ").append(runtimeMXBean.getStartTime()).append(CRLF); + sb.append(INDENT1 + "uptime: ").append(runtimeMXBean.getUptime()).append(CRLF); + sb.append(INDENT1 + "isBootClassPathSupported: ").append(runtimeMXBean.isBootClassPathSupported()).append(CRLF); sb.append(CRLF); sb.append(requestedSm.getString("diagnostics.vmInfoOs")); sb.append(":" + CRLF); - sb.append(INDENT1 + "name: " + operatingSystemMXBean.getName() + CRLF); - sb.append(INDENT1 + "version: " + operatingSystemMXBean.getVersion() + CRLF); - sb.append(INDENT1 + "architecture: " + operatingSystemMXBean.getArch() + CRLF); - sb.append(INDENT1 + "availableProcessors: " + - operatingSystemMXBean.getAvailableProcessors() + CRLF); - sb.append(INDENT1 + "systemLoadAverage: " + - operatingSystemMXBean.getSystemLoadAverage() + CRLF); + sb.append(INDENT1 + "name: ").append(operatingSystemMXBean.getName()).append(CRLF); + sb.append(INDENT1 + "version: ").append(operatingSystemMXBean.getVersion()).append(CRLF); + sb.append(INDENT1 + "architecture: ").append(operatingSystemMXBean.getArch()).append(CRLF); + sb.append(INDENT1 + "availableProcessors: ").append(operatingSystemMXBean.getAvailableProcessors()) + .append(CRLF); + sb.append(INDENT1 + "systemLoadAverage: ").append(operatingSystemMXBean.getSystemLoadAverage()).append(CRLF); sb.append(CRLF); sb.append(requestedSm.getString("diagnostics.vmInfoThreadMxBean")); sb.append(":" + CRLF); - sb.append(INDENT1 + "isCurrentThreadCpuTimeSupported: " + - threadMXBean.isCurrentThreadCpuTimeSupported() + CRLF); - sb.append(INDENT1 + "isThreadCpuTimeSupported: " + - threadMXBean.isThreadCpuTimeSupported() + CRLF); - sb.append(INDENT1 + "isThreadCpuTimeEnabled: " + - threadMXBean.isThreadCpuTimeEnabled() + CRLF); - sb.append(INDENT1 + "isObjectMonitorUsageSupported: " + - threadMXBean.isObjectMonitorUsageSupported() + CRLF); - sb.append(INDENT1 + "isSynchronizerUsageSupported: " + - threadMXBean.isSynchronizerUsageSupported() + CRLF); - sb.append(INDENT1 + "isThreadContentionMonitoringSupported: " + - threadMXBean.isThreadContentionMonitoringSupported() + CRLF); - sb.append(INDENT1 + "isThreadContentionMonitoringEnabled: " + - threadMXBean.isThreadContentionMonitoringEnabled() + CRLF); + sb.append(INDENT1 + "isCurrentThreadCpuTimeSupported: ").append(threadMXBean.isCurrentThreadCpuTimeSupported()) + .append(CRLF); + sb.append(INDENT1 + "isThreadCpuTimeSupported: ").append(threadMXBean.isThreadCpuTimeSupported()).append(CRLF); + sb.append(INDENT1 + "isThreadCpuTimeEnabled: ").append(threadMXBean.isThreadCpuTimeEnabled()).append(CRLF); + sb.append(INDENT1 + "isObjectMonitorUsageSupported: ").append(threadMXBean.isObjectMonitorUsageSupported()) + .append(CRLF); + sb.append(INDENT1 + "isSynchronizerUsageSupported: ").append(threadMXBean.isSynchronizerUsageSupported()) + .append(CRLF); + sb.append(INDENT1 + "isThreadContentionMonitoringSupported: ") + .append(threadMXBean.isThreadContentionMonitoringSupported()).append(CRLF); + sb.append(INDENT1 + "isThreadContentionMonitoringEnabled: ") + .append(threadMXBean.isThreadContentionMonitoringEnabled()).append(CRLF); sb.append(CRLF); sb.append(requestedSm.getString("diagnostics.vmInfoThreadCounts")); sb.append(":" + CRLF); - sb.append(INDENT1 + "daemon: " + threadMXBean.getDaemonThreadCount() + CRLF); - sb.append(INDENT1 + "total: " + threadMXBean.getThreadCount() + CRLF); - sb.append(INDENT1 + "peak: " + threadMXBean.getPeakThreadCount() + CRLF); - sb.append(INDENT1 + "totalStarted: " + - threadMXBean.getTotalStartedThreadCount() + CRLF); + sb.append(INDENT1 + "daemon: ").append(threadMXBean.getDaemonThreadCount()).append(CRLF); + sb.append(INDENT1 + "total: ").append(threadMXBean.getThreadCount()).append(CRLF); + sb.append(INDENT1 + "peak: ").append(threadMXBean.getPeakThreadCount()).append(CRLF); + sb.append(INDENT1 + "totalStarted: ").append(threadMXBean.getTotalStartedThreadCount()).append(CRLF); sb.append(CRLF); sb.append(requestedSm.getString("diagnostics.vmInfoStartup")); sb.append(":" + CRLF); - for (String arg: runtimeMXBean.getInputArguments()) { - sb.append(INDENT1 + arg + CRLF); + for (String arg : runtimeMXBean.getInputArguments()) { + sb.append(INDENT1).append(arg).append(CRLF); } sb.append(CRLF); sb.append(requestedSm.getString("diagnostics.vmInfoPath")); sb.append(":" + CRLF); if (runtimeMXBean.isBootClassPathSupported()) { - sb.append(INDENT1 + "bootClassPath: " + runtimeMXBean.getBootClassPath() + CRLF); + sb.append(INDENT1 + "bootClassPath: ").append(runtimeMXBean.getBootClassPath()).append(CRLF); } - sb.append(INDENT1 + "classPath: " + runtimeMXBean.getClassPath() + CRLF); - sb.append(INDENT1 + "libraryPath: " + runtimeMXBean.getLibraryPath() + CRLF); + sb.append(INDENT1 + "classPath: ").append(runtimeMXBean.getClassPath()).append(CRLF); + sb.append(INDENT1 + "libraryPath: ").append(runtimeMXBean.getLibraryPath()).append(CRLF); sb.append(CRLF); sb.append(requestedSm.getString("diagnostics.vmInfoClassLoading")); sb.append(":" + CRLF); - sb.append(INDENT1 + "loaded: " + - classLoadingMXBean.getLoadedClassCount() + CRLF); - sb.append(INDENT1 + "unloaded: " + - classLoadingMXBean.getUnloadedClassCount() + CRLF); - sb.append(INDENT1 + "totalLoaded: " + - classLoadingMXBean.getTotalLoadedClassCount() + CRLF); - sb.append(INDENT1 + "isVerbose: " + - classLoadingMXBean.isVerbose() + CRLF); + sb.append(INDENT1 + "loaded: ").append(classLoadingMXBean.getLoadedClassCount()).append(CRLF); + sb.append(INDENT1 + "unloaded: ").append(classLoadingMXBean.getUnloadedClassCount()).append(CRLF); + sb.append(INDENT1 + "totalLoaded: ").append(classLoadingMXBean.getTotalLoadedClassCount()).append(CRLF); + sb.append(INDENT1 + "isVerbose: ").append(classLoadingMXBean.isVerbose()).append(CRLF); sb.append(CRLF); sb.append(requestedSm.getString("diagnostics.vmInfoClassCompilation")); sb.append(":" + CRLF); - sb.append(INDENT1 + "name: " + compilationMXBean.getName() + CRLF); - sb.append(INDENT1 + "totalCompilationTime: " + - compilationMXBean.getTotalCompilationTime() + CRLF); - sb.append(INDENT1 + "isCompilationTimeMonitoringSupported: " + - compilationMXBean.isCompilationTimeMonitoringSupported() + CRLF); + sb.append(INDENT1 + "name: ").append(compilationMXBean.getName()).append(CRLF); + sb.append(INDENT1 + "totalCompilationTime: ").append(compilationMXBean.getTotalCompilationTime()).append(CRLF); + sb.append(INDENT1 + "isCompilationTimeMonitoringSupported: ") + .append(compilationMXBean.isCompilationTimeMonitoringSupported()).append(CRLF); sb.append(CRLF); - for (MemoryManagerMXBean mbean: memoryManagerMXBeans) { + for (MemoryManagerMXBean mbean : memoryManagerMXBeans) { sb.append(requestedSm.getString("diagnostics.vmInfoMemoryManagers", mbean.getName())); sb.append(":" + CRLF); - sb.append(INDENT1 + "isValid: " + mbean.isValid() + CRLF); + sb.append(INDENT1 + "isValid: ").append(mbean.isValid()).append(CRLF); sb.append(INDENT1 + "mbean.getMemoryPoolNames: " + CRLF); String[] names = mbean.getMemoryPoolNames(); Arrays.sort(names); - for (String name: names) { - sb.append(INDENT2 + name + CRLF); + for (String name : names) { + sb.append(INDENT2).append(name).append(CRLF); } sb.append(CRLF); } - for (GarbageCollectorMXBean mbean: garbageCollectorMXBeans) { + for (GarbageCollectorMXBean mbean : garbageCollectorMXBeans) { sb.append(requestedSm.getString("diagnostics.vmInfoGarbageCollectors", mbean.getName())); sb.append(":" + CRLF); - sb.append(INDENT1 + "isValid: " + mbean.isValid() + CRLF); + sb.append(INDENT1 + "isValid: ").append(mbean.isValid()).append(CRLF); sb.append(INDENT1 + "mbean.getMemoryPoolNames: " + CRLF); String[] names = mbean.getMemoryPoolNames(); Arrays.sort(names); - for (String name: names) { - sb.append(INDENT2 + name + CRLF); + for (String name : names) { + sb.append(INDENT2).append(name).append(CRLF); } - sb.append(INDENT1 + "getCollectionCount: " + mbean.getCollectionCount() + CRLF); - sb.append(INDENT1 + "getCollectionTime: " + mbean.getCollectionTime() + CRLF); + sb.append(INDENT1 + "getCollectionCount: ").append(mbean.getCollectionCount()).append(CRLF); + sb.append(INDENT1 + "getCollectionTime: ").append(mbean.getCollectionTime()).append(CRLF); sb.append(CRLF); } sb.append(requestedSm.getString("diagnostics.vmInfoMemory")); sb.append(":" + CRLF); - sb.append(INDENT1 + "isVerbose: " + memoryMXBean.isVerbose() + CRLF); - sb.append(INDENT1 + "getObjectPendingFinalizationCount: " + memoryMXBean.getObjectPendingFinalizationCount() + CRLF); + sb.append(INDENT1 + "isVerbose: ").append(memoryMXBean.isVerbose()).append(CRLF); + sb.append(INDENT1 + "getObjectPendingFinalizationCount: ") + .append(memoryMXBean.getObjectPendingFinalizationCount()).append(CRLF); sb.append(formatMemoryUsage("heap", memoryMXBean.getHeapMemoryUsage())); sb.append(formatMemoryUsage("non-heap", memoryMXBean.getNonHeapMemoryUsage())); sb.append(CRLF); - for (MemoryPoolMXBean mbean: memoryPoolMXBeans) { + for (MemoryPoolMXBean mbean : memoryPoolMXBeans) { sb.append(requestedSm.getString("diagnostics.vmInfoMemoryPools", mbean.getName())); sb.append(":" + CRLF); - sb.append(INDENT1 + "isValid: " + mbean.isValid() + CRLF); - sb.append(INDENT1 + "getType: " + mbean.getType() + CRLF); + sb.append(INDENT1 + "isValid: ").append(mbean.isValid()).append(CRLF); + sb.append(INDENT1 + "getType: ").append(mbean.getType()).append(CRLF); sb.append(INDENT1 + "mbean.getMemoryManagerNames: " + CRLF); String[] names = mbean.getMemoryManagerNames(); Arrays.sort(names); - for (String name: names) { - sb.append(INDENT2 + name + CRLF); + for (String name : names) { + sb.append(INDENT2).append(name).append(CRLF); } - sb.append(INDENT1 + "isUsageThresholdSupported: " + mbean.isUsageThresholdSupported() + CRLF); + sb.append(INDENT1 + "isUsageThresholdSupported: ").append(mbean.isUsageThresholdSupported()).append(CRLF); try { - sb.append(INDENT1 + "isUsageThresholdExceeded: " + mbean.isUsageThresholdExceeded() + CRLF); + sb.append(INDENT1 + "isUsageThresholdExceeded: ").append(mbean.isUsageThresholdExceeded()).append(CRLF); } catch (UnsupportedOperationException ex) { // IGNORE } - sb.append(INDENT1 + "isCollectionUsageThresholdSupported: " + mbean.isCollectionUsageThresholdSupported() + CRLF); + sb.append(INDENT1 + "isCollectionUsageThresholdSupported: ") + .append(mbean.isCollectionUsageThresholdSupported()).append(CRLF); try { - sb.append(INDENT1 + "isCollectionUsageThresholdExceeded: " + mbean.isCollectionUsageThresholdExceeded() + CRLF); + sb.append(INDENT1 + "isCollectionUsageThresholdExceeded: ") + .append(mbean.isCollectionUsageThresholdExceeded()).append(CRLF); } catch (UnsupportedOperationException ex) { // IGNORE } try { - sb.append(INDENT1 + "getUsageThreshold: " + mbean.getUsageThreshold() + CRLF); + sb.append(INDENT1 + "getUsageThreshold: ").append(mbean.getUsageThreshold()).append(CRLF); } catch (UnsupportedOperationException ex) { // IGNORE } try { - sb.append(INDENT1 + "getUsageThresholdCount: " + mbean.getUsageThresholdCount() + CRLF); + sb.append(INDENT1 + "getUsageThresholdCount: ").append(mbean.getUsageThresholdCount()).append(CRLF); } catch (UnsupportedOperationException ex) { // IGNORE } try { - sb.append(INDENT1 + "getCollectionUsageThreshold: " + mbean.getCollectionUsageThreshold() + CRLF); + sb.append(INDENT1 + "getCollectionUsageThreshold: ").append(mbean.getCollectionUsageThreshold()) + .append(CRLF); } catch (UnsupportedOperationException ex) { // IGNORE } try { - sb.append(INDENT1 + "getCollectionUsageThresholdCount: " + mbean.getCollectionUsageThresholdCount() + CRLF); + sb.append(INDENT1 + "getCollectionUsageThresholdCount: ") + .append(mbean.getCollectionUsageThresholdCount()).append(CRLF); } catch (UnsupportedOperationException ex) { // IGNORE } @@ -678,8 +648,8 @@ Map props = runtimeMXBean.getSystemProperties(); ArrayList keys = new ArrayList<>(props.keySet()); Collections.sort(keys); - for (String prop: keys) { - sb.append(INDENT1 + prop + ": " + props.get(prop) + CRLF); + for (String prop : keys) { + sb.append(INDENT1).append(prop).append(": ").append(props.get(prop)).append(CRLF); } sb.append(CRLF); @@ -687,10 +657,9 @@ sb.append(":" + CRLF); List loggers = loggingMXBean.getLoggerNames(); Collections.sort(loggers); - for (String logger: loggers) { - sb.append(INDENT1 + logger + - ": level=" + loggingMXBean.getLoggerLevel(logger) + - ", parent=" + loggingMXBean.getParentLoggerName(logger) + CRLF); + for (String logger : loggers) { + sb.append(INDENT1).append(logger).append(": level=").append(loggingMXBean.getLoggerLevel(logger)); + sb.append(", parent=").append(loggingMXBean.getParentLoggerName(logger)).append(CRLF); } sb.append(CRLF); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/ExceptionUtils.java tomcat10-10.1.52/java/org/apache/tomcat/util/ExceptionUtils.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/ExceptionUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/ExceptionUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,8 @@ public class ExceptionUtils { /** - * Checks whether the supplied Throwable is one that needs to be - * rethrown and swallows all others. + * Checks whether the supplied Throwable is one that needs to be rethrown and swallows all others. + * * @param t the Throwable to check */ public static void handleThrowable(Throwable t) { @@ -44,11 +44,11 @@ } /** - * Checks whether the supplied Throwable is an instance of - * InvocationTargetException and returns the throwable that is - * wrapped by it, if there is any. + * Checks whether the supplied Throwable is an instance of InvocationTargetException and returns the + * throwable that is wrapped by it, if there is any. * * @param t the Throwable to check + * * @return t or t.getCause() */ public static Throwable unwrapInvocationTargetException(Throwable t) { @@ -60,10 +60,9 @@ /** - * NO-OP method provided to enable simple pre-loading of this class. Since - * the class is used extensively in error handling, it is prudent to - * pre-load it to avoid any failure to load this class masking the true - * problem during error handling. + * NO-OP method provided to enable simple preloading of this class. Since the class is used extensively in error + * handling, it is prudent to preload it to avoid any failure to load this class masking the true problem during + * error handling. */ public static void preload() { // NO-OP diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/IntrospectionUtils.java tomcat10-10.1.52/java/org/apache/tomcat/util/IntrospectionUtils.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/IntrospectionUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/IntrospectionUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,29 +38,28 @@ private static final StringManager sm = StringManager.getManager(IntrospectionUtils.class); /** - * Find a method with the right name If found, call the method ( if param is - * int or boolean we'll convert value to the right type before) - that means - * you can have setDebug(1). - * @param o The object to set a property on - * @param name The property name + * Find a method with the right name If found, call the method ( if param is int or boolean we'll convert value to + * the right type before) - that means you can have setDebug(1). + * + * @param o The object to set a property on + * @param name The property name * @param value The property value + * * @return true if operation was successful */ public static boolean setProperty(Object o, String name, String value) { return setProperty(o, name, value, true, null); } - public static boolean setProperty(Object o, String name, String value, - boolean invokeSetProperty) { + public static boolean setProperty(Object o, String name, String value, boolean invokeSetProperty) { return setProperty(o, name, value, invokeSetProperty, null); } @SuppressWarnings("null") // setPropertyMethodVoid is not null when used - public static boolean setProperty(Object o, String name, String value, - boolean invokeSetProperty, StringBuilder actualMethod) { + public static boolean setProperty(Object o, String name, String value, boolean invokeSetProperty, + StringBuilder actualMethod) { if (log.isTraceEnabled()) { - log.trace("IntrospectionUtils: setProperty(" + - o.getClass() + " " + name + "=" + value + ")"); + log.trace("IntrospectionUtils: setProperty(" + o.getClass() + " " + name + "=" + value + ")"); } if (actualMethod == null && XReflectionIntrospectionUtils.isEnabled()) { @@ -70,16 +69,16 @@ String setter = "set" + capitalize(name); try { - Method methods[] = findMethods(o.getClass()); + Method[] methods = findMethods(o.getClass()); Method setPropertyMethodVoid = null; Method setPropertyMethodBool = null; // First, the ideal case - a setFoo( String ) method for (Method item : methods) { - Class paramT[] = item.getParameterTypes(); - if (setter.equals(item.getName()) && paramT.length == 1 - && "java.lang.String".equals(paramT[0].getName())) { - item.invoke(o, new Object[]{value}); + Class[] paramT = item.getParameterTypes(); + if (setter.equals(item.getName()) && paramT.length == 1 && + "java.lang.String".equals(paramT[0].getName())) { + item.invoke(o, value); if (actualMethod != null) { actualMethod.append(item.getName()).append("(\"").append(escape(value)).append("\")"); } @@ -90,16 +89,14 @@ // Try a setFoo ( int ) or ( boolean ) for (Method method : methods) { boolean ok = true; - if (setter.equals(method.getName()) - && method.getParameterTypes().length == 1) { + if (setter.equals(method.getName()) && method.getParameterTypes().length == 1) { // match - find the type and invoke it Class paramType = method.getParameterTypes()[0]; - Object params[] = new Object[1]; + Object[] params = new Object[1]; // Try a setFoo ( int ) - if ("java.lang.Integer".equals(paramType.getName()) - || "int".equals(paramType.getName())) { + if ("java.lang.Integer".equals(paramType.getName()) || "int".equals(paramType.getName())) { try { params[0] = Integer.valueOf(value); } catch (NumberFormatException ex) { @@ -107,14 +104,15 @@ } if (actualMethod != null) { if ("java.lang.Integer".equals(paramType.getName())) { - actualMethod.append(method.getName()).append("(Integer.valueOf(\"").append(value).append("\"))"); + actualMethod.append(method.getName()).append("(Integer.valueOf(\"").append(value) + .append("\"))"); } else { - actualMethod.append(method.getName()).append("(Integer.parseInt(\"").append(value).append("\"))"); + actualMethod.append(method.getName()).append("(Integer.parseInt(\"").append(value) + .append("\"))"); } } // Try a setFoo ( long ) - } else if ("java.lang.Long".equals(paramType.getName()) - || "long".equals(paramType.getName())) { + } else if ("java.lang.Long".equals(paramType.getName()) || "long".equals(paramType.getName())) { try { params[0] = Long.valueOf(value); } catch (NumberFormatException ex) { @@ -122,25 +120,28 @@ } if (actualMethod != null) { if ("java.lang.Long".equals(paramType.getName())) { - actualMethod.append(method.getName()).append("(Long.valueOf(\"").append(value).append("\"))"); + actualMethod.append(method.getName()).append("(Long.valueOf(\"").append(value) + .append("\"))"); } else { - actualMethod.append(method.getName()).append("(Long.parseLong(\"").append(value).append("\"))"); + actualMethod.append(method.getName()).append("(Long.parseLong(\"").append(value) + .append("\"))"); } } // Try a setFoo ( boolean ) - } else if ("java.lang.Boolean".equals(paramType.getName()) - || "boolean".equals(paramType.getName())) { + } else if ("java.lang.Boolean".equals(paramType.getName()) || + "boolean".equals(paramType.getName())) { params[0] = Boolean.valueOf(value); if (actualMethod != null) { if ("java.lang.Boolean".equals(paramType.getName())) { - actualMethod.append(method.getName()).append("(Boolean.valueOf(\"").append(value).append("\"))"); + actualMethod.append(method.getName()).append("(Boolean.valueOf(\"").append(value) + .append("\"))"); } else { - actualMethod.append(method.getName()).append("(Boolean.parseBoolean(\"").append(value).append("\"))"); + actualMethod.append(method.getName()).append("(Boolean.parseBoolean(\"").append(value) + .append("\"))"); } } // Try a setFoo ( InetAddress ) - } else if ("java.net.InetAddress".equals(paramType - .getName())) { + } else if ("java.net.InetAddress".equals(paramType.getName())) { try { params[0] = InetAddress.getByName(value); } catch (UnknownHostException exc) { @@ -150,13 +151,13 @@ ok = false; } if (actualMethod != null) { - actualMethod.append(method.getName()).append("(InetAddress.getByName(\"").append(value).append("\"))"); + actualMethod.append(method.getName()).append("(InetAddress.getByName(\"").append(value) + .append("\"))"); } // Unknown type } else { if (log.isTraceEnabled()) { - log.trace("IntrospectionUtils: Unknown type " + - paramType.getName()); + log.trace("IntrospectionUtils: Unknown type " + paramType.getName()); } } @@ -178,25 +179,24 @@ } // Ok, no setXXX found, try a setProperty("name", "value") - if (invokeSetProperty && (setPropertyMethodBool != null || - setPropertyMethodVoid != null)) { + if (invokeSetProperty && (setPropertyMethodBool != null || setPropertyMethodVoid != null)) { if (actualMethod != null) { - actualMethod.append("setProperty(\"").append(name).append("\", \"").append(escape(value)).append("\")"); + actualMethod.append("setProperty(\"").append(name).append("\", \"").append(escape(value)) + .append("\")"); } - Object params[] = new Object[2]; + Object[] params = new Object[2]; params[0] = name; params[1] = value; if (setPropertyMethodBool != null) { try { - return ((Boolean) setPropertyMethodBool.invoke(o, - params)).booleanValue(); - }catch (IllegalArgumentException biae) { - //the boolean method had the wrong - //parameter types. lets try the other - if (setPropertyMethodVoid!=null) { + return ((Boolean) setPropertyMethodBool.invoke(o, params)).booleanValue(); + } catch (IllegalArgumentException biae) { + // the boolean method had the wrong + // parameter types. let's try the other + if (setPropertyMethodVoid != null) { setPropertyMethodVoid.invoke(o, params); return true; - }else { + } else { throw biae; } } @@ -216,8 +216,8 @@ } /** - * @param s - * the input string + * @param s the input string + * * @return escaped string, per Java rule */ public static String escape(String s) { @@ -252,12 +252,12 @@ String isGetter = "is" + capitalize(name); try { - Method methods[] = findMethods(o.getClass()); + Method[] methods = findMethods(o.getClass()); Method getPropertyMethod = null; // First, the ideal case - a getFoo() method for (Method method : methods) { - Class paramT[] = method.getParameterTypes(); + Class[] paramT = method.getParameterTypes(); if (getter.equals(method.getName()) && paramT.length == 0) { return method.invoke(o, (Object[]) null); } @@ -272,7 +272,7 @@ // Ok, no setXXX found, try a getProperty("name") if (getPropertyMethod != null) { - Object params[] = new Object[1]; + Object[] params = new Object[1]; params[0] = name; return getPropertyMethod.invoke(o, params); } @@ -291,36 +291,29 @@ } /** - * Replaces ${NAME} in the value with the value of the property 'NAME'. - * Replaces ${NAME:DEFAULT} with the value of the property 'NAME:DEFAULT', - * if the property 'NAME:DEFAULT' is not set, - * the expression is replaced with the value of the property 'NAME', - * if the property 'NAME' is not set, - * the expression is replaced with 'DEFAULT'. - * If the property is not set and there is no default the value will be - * returned unmodified. + * Replaces ${NAME} in the value with the value of the property 'NAME'. Replaces ${NAME:DEFAULT} with the value of + * the property 'NAME:DEFAULT', if the property 'NAME:DEFAULT' is not set, the expression is replaced with the value + * of the property 'NAME', if the property 'NAME' is not set, the expression is replaced with 'DEFAULT'. If the + * property is not set and there is no default the value will be returned unmodified. * - * @param value The value - * @param staticProp Replacement properties + * @param value The value + * @param staticProp Replacement properties * @param dynamicProp Replacement properties - * @param classLoader Class loader associated with the code requesting the - * property + * @param classLoader Class loader associated with the code requesting the property * * @return the replacement value */ - public static String replaceProperties(String value, - Hashtable staticProp, PropertySource dynamicProp[], - ClassLoader classLoader) { - return replaceProperties(value, staticProp, dynamicProp, classLoader, 0); + public static String replaceProperties(String value, Hashtable staticProp, + PropertySource[] dynamicProp, ClassLoader classLoader) { + return replaceProperties(value, staticProp, dynamicProp, classLoader, 0); } - private static String replaceProperties(String value, - Hashtable staticProp, PropertySource dynamicProp[], - ClassLoader classLoader, int iterationCount) { - if (value == null || value.indexOf("${") < 0) { + private static String replaceProperties(String value, Hashtable staticProp, + PropertySource[] dynamicProp, ClassLoader classLoader, int iterationCount) { + if (value == null || !value.contains("${")) { return value; } - if (iterationCount >=20) { + if (iterationCount >= 20) { log.warn(sm.getString("introspectionUtils.tooManyIterations", value)); return value; } @@ -330,7 +323,7 @@ int pos; while ((pos = value.indexOf('$', prev)) >= 0) { if (pos > 0) { - sb.append(value.substring(prev, pos)); + sb.append(value, prev, pos); } if (pos == (value.length() - 1)) { sb.append('$'); @@ -369,7 +362,7 @@ sb.append(value.substring(prev)); } String newval = sb.toString(); - if (newval.indexOf("${") < 0) { + if (!newval.contains("${")) { return newval; } if (newval.equals(value)) { @@ -378,11 +371,11 @@ if (log.isTraceEnabled()) { log.trace("IntrospectionUtils.replaceProperties iter on: " + newval); } - return replaceProperties(newval, staticProp, dynamicProp, classLoader, iterationCount+1); + return replaceProperties(newval, staticProp, dynamicProp, classLoader, iterationCount + 1); } - private static String getProperty(String name, Hashtable staticProp, - PropertySource[] dynamicProp, ClassLoader classLoader) { + private static String getProperty(String name, Hashtable staticProp, PropertySource[] dynamicProp, + ClassLoader classLoader) { String v = null; if (staticProp != null) { v = (String) staticProp.get(name); @@ -404,14 +397,16 @@ /** * Reverse of Introspector.decapitalize. + * * @param name The name + * * @return the capitalized string */ public static String capitalize(String name) { - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { return name; } - char chars[] = name.toCharArray(); + char[] chars = name.toCharArray(); chars[0] = Character.toUpperCase(chars[0]); return new String(chars); } @@ -424,7 +419,7 @@ private static final Map,Method[]> objectMethods = new ConcurrentHashMap<>(); public static Method[] findMethods(Class c) { - Method methods[] = objectMethods.get(c); + Method[] methods = objectMethods.get(c); if (methods != null) { return methods; } @@ -434,12 +429,11 @@ return methods; } - public static Method findMethod(Class c, String name, - Class params[]) { - Method methods[] = findMethods(c); + public static Method findMethod(Class c, String name, Class[] params) { + Method[] methods = findMethods(c); for (Method method : methods) { if (method.getName().equals(name)) { - Class methodParams[] = method.getParameterTypes(); + Class[] methodParams = method.getParameterTypes(); if (params == null) { if (methodParams.length == 0) { return method; @@ -465,18 +459,17 @@ return null; } - public static Object callMethod1(Object target, String methodN, - Object param1, String typeParam1, ClassLoader cl) throws Exception { + public static Object callMethod1(Object target, String methodN, Object param1, String typeParam1, ClassLoader cl) + throws Exception { if (target == null || methodN == null || param1 == null) { throw new IllegalArgumentException(sm.getString("introspectionUtils.nullParameter")); } if (log.isTraceEnabled()) { - log.trace("IntrospectionUtils: callMethod1 " + - target.getClass().getName() + " " + + log.trace("IntrospectionUtils: callMethod1 " + target.getClass().getName() + " " + param1.getClass().getName() + " " + typeParam1); } - Class params[] = new Class[1]; + Class[] params = new Class[1]; if (typeParam1 == null) { params[0] = param1.getClass(); } else { @@ -484,20 +477,20 @@ } Method m = findMethod(target.getClass(), methodN, params); if (m == null) { - throw new NoSuchMethodException(sm.getString("introspectionUtils.noMethod", methodN, target, target.getClass())); + throw new NoSuchMethodException( + sm.getString("introspectionUtils.noMethod", methodN, target, target.getClass())); } try { - return m.invoke(target, new Object[] { param1 }); + return m.invoke(target, param1); } catch (InvocationTargetException ie) { ExceptionUtils.handleThrowable(ie.getCause()); throw ie; } } - public static Object callMethodN(Object target, String methodN, - Object params[], Class typeParams[]) throws Exception { - Method m = null; - m = findMethod(target.getClass(), methodN, typeParams); + public static Object callMethodN(Object target, String methodN, Object[] params, Class[] typeParams) + throws Exception { + Method m = findMethod(target.getClass(), methodN, typeParams); if (m == null) { if (log.isDebugEnabled()) { log.debug(sm.getString("introspectionUtils.noMethod", methodN, target, target.getClass())); @@ -530,58 +523,50 @@ Object result = null; if ("java.lang.String".equals(paramType.getName())) { result = object; - } else if ("java.lang.Integer".equals(paramType.getName()) - || "int".equals(paramType.getName())) { + } else if ("java.lang.Integer".equals(paramType.getName()) || "int".equals(paramType.getName())) { try { result = Integer.valueOf(object); } catch (NumberFormatException ex) { } // Try a setFoo ( boolean ) - } else if ("java.lang.Boolean".equals(paramType.getName()) - || "boolean".equals(paramType.getName())) { + } else if ("java.lang.Boolean".equals(paramType.getName()) || "boolean".equals(paramType.getName())) { result = Boolean.valueOf(object); // Try a setFoo ( InetAddress ) - } else if ("java.net.InetAddress".equals(paramType - .getName())) { + } else if ("java.net.InetAddress".equals(paramType.getName())) { try { result = InetAddress.getByName(object); } catch (UnknownHostException exc) { if (log.isDebugEnabled()) { - log.debug(sm.getString("introspectionUtils.hostResolutionFail", object)); + log.debug(sm.getString("introspectionUtils.hostResolutionFail", object), exc); } } // Unknown type } else { if (log.isTraceEnabled()) { - log.trace("IntrospectionUtils: Unknown type " + - paramType.getName()); + log.trace("IntrospectionUtils: Unknown type " + paramType.getName()); } } if (result == null) { - throw new IllegalArgumentException(sm.getString("introspectionUtils.conversionError", object, paramType.getName())); + throw new IllegalArgumentException( + sm.getString("introspectionUtils.conversionError", object, paramType.getName())); } return result; } /** - * Checks to see if the specified class is an instance of or assignable from - * the specified type. The class clazz, all its superclasses, - * interfaces and those superinterfaces are tested for a match against - * the type name type. - * - * This is similar to instanceof or {@link Class#isAssignableFrom} - * except that the target type will not be resolved into a Class - * object, which provides some security and memory benefits. + * Checks to see if the specified class is an instance of or assignable from the specified type. The class + * clazz, all its superclasses, interfaces and those superinterfaces are tested for a match against the + * type name type. This is similar to instanceof or {@link Class#isAssignableFrom} except + * that the target type will not be resolved into a Class object, which provides some security and memory benefits. * * @param clazz The class to test for a match. - * @param type The name of the type that clazz must be. + * @param type The name of the type that clazz must be. * - * @return true if the clazz tested is an - * instance of the specified type, - * false otherwise. + * @return true if the clazz tested is an instance of the specified type, + * false otherwise. */ public static boolean isInstance(Class clazz, String type) { if (type.equals(clazz.getName())) { @@ -615,19 +600,16 @@ public interface SecurePropertySource extends PropertySource { /** - * Obtain a property value, checking that code associated with the - * provided class loader has permission to access the property. If the - * {@code classLoader} is {@code null} or if {@code classLoader} does - * not implement {@link PermissionCheck} then the property value will be - * looked up without a call to + * Obtain a property value, checking that code associated with the provided class loader has permission to + * access the property. If the {@code classLoader} is {@code null} or if {@code classLoader} does not implement + * {@link PermissionCheck} then the property value will be looked up without a call to * {@link PermissionCheck#check(java.security.Permission)} * - * @param key The key of the requested property - * @param classLoader The class loader associated with the code that - * trigger the property lookup - * @return The property value or {@code null} if it could not be found - * or if {@link PermissionCheck#check(java.security.Permission)} - * fails + * @param key The key of the requested property + * @param classLoader The class loader associated with the code that trigger the property lookup + * + * @return The property value or {@code null} if it could not be found or if + * {@link PermissionCheck#check(java.security.Permission)} fails */ String getProperty(String key, ClassLoader classLoader); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/MultiThrowable.java tomcat10-10.1.52/java/org/apache/tomcat/util/MultiThrowable.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/MultiThrowable.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/MultiThrowable.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,8 @@ import java.util.List; /** - * Wraps a list of throwables as a single throwable. This is intended to be used - * when multiple actions are taken where each may throw an exception but all - * actions are taken before any errors are reported. + * Wraps a list of throwables as a single throwable. This is intended to be used when multiple actions are taken where + * each may throw an exception but all actions are taken before any errors are reported. *

          * This class is NOT threadsafe. */ @@ -31,7 +30,7 @@ private static final long serialVersionUID = 1L; - private List throwables = new ArrayList<>(); + private final List throwables = new ArrayList<>(); /** * Add a throwable to the list of wrapped throwables. @@ -52,9 +51,8 @@ /** - * @return {@code null} if there are no wrapped throwables, the Throwable if - * there is a single wrapped throwable or the current instance of - * there are multiple wrapped throwables + * @return {@code null} if there are no wrapped throwables, the Throwable if there is a single wrapped throwable or + * the current instance of there are multiple wrapped throwables */ public Throwable getThrowable() { if (size() == 0) { @@ -76,10 +74,9 @@ /** - * Overrides the default implementation to provide a concatenation of the - * messages associated with each of the wrapped throwables. Note that the - * format of the returned String is not guaranteed to be fixed and may - * change in a future release. + * Overrides the default implementation to provide a concatenation of the messages associated with each of the + * wrapped throwables. Note that the format of the returned String is not guaranteed to be fixed and may change in a + * future release. */ @Override public String toString() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/XReflectionIntrospectionUtils.java tomcat10-10.1.52/java/org/apache/tomcat/util/XReflectionIntrospectionUtils.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/XReflectionIntrospectionUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/XReflectionIntrospectionUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,8 @@ /** * Always throws {@link UnsupportedOperationException} * - * @param o Unused - * @param name Unused + * @param o Unused + * @param name Unused * * @return Never returns normally */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/bcel/Const.java tomcat10-10.1.52/java/org/apache/tomcat/util/bcel/Const.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/bcel/Const.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/bcel/Const.java 2026-01-23 19:33:36.000000000 +0000 @@ -269,6 +269,22 @@ public static final short MAJOR_24 = 68; /** + * Minor version number of class files for Java 25: {@value}. + * + * @see #MAJOR_25 + * @since 6.11.0 + */ + public static final short MINOR_25 = 0; + + /** + * Major version number of class files for Java 25: {@value}. + * + * @see #MINOR_25 + * @since 6.11.0 + */ + public static final short MAJOR_25 = 69; + + /** * Get the CONSTANT_NAMES entry at the given index. * * @param index index into {@code CONSTANT_NAMES}. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java tomcat10-10.1.52/java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/bcel/classfile/ConstantPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,7 +36,7 @@ * Reads constants from given input stream. * * @param input Input stream - * @throws IOException if an I/O error occurs reading the the InputStream + * @throws IOException if an I/O error occurs reading the InputStream * @throws ClassFormatException If the .class file is not valid */ ConstantPool(final DataInput input) throws IOException, ClassFormatException { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/bcel/classfile/Utility.java tomcat10-10.1.52/java/org/apache/tomcat/util/bcel/classfile/Utility.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/bcel/classfile/Utility.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/bcel/classfile/Utility.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,10 +28,7 @@ final class Utility { /** - * Shorten long class name str, i.e., chop off the prefix, - * if the - * class name starts with this string and the flag chopit is true. - * Slashes / are converted to dots .. + * Shorten long class names, java/lang/String becomes String. * * @param str The long class name * @return Compacted class name diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/AbstractChunk.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/AbstractChunk.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/AbstractChunk.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/AbstractChunk.java 2026-01-23 19:33:36.000000000 +0000 @@ -85,6 +85,7 @@ /** * Set the start position of the data in the buffer. + * * @param start the new start position */ public void setStart(int start) { @@ -105,6 +106,7 @@ /** * Set the end position of the data in the buffer. + * * @param end the new end position */ public void setEnd(int end) { @@ -114,6 +116,7 @@ /** * @return start + * * @deprecated Unused. This method will be removed in Tomcat 12. */ @Deprecated @@ -123,7 +126,9 @@ /** * Set start. + * * @param off the new start + * * @deprecated Unused. This method will be removed in Tomcat 12. */ @Deprecated @@ -155,15 +160,15 @@ /** - * Return the index of the first occurrence of the subsequence of - * the given String, or -1 if it is not found. + * Return the index of the first occurrence of the subsequence of the given String, or -1 if it is not found. * - * @param src the String to look for + * @param src the String to look for * @param srcStart the subsequence start in the String - * @param srcLen the subsequence length in the String + * @param srcLen the subsequence length in the String * @param myOffset the index on which to start the search in the buffer - * @return the position of the first character of the first occurrence - * of the subsequence in the buffer, or -1 if not found + * + * @return the position of the first character of the first occurrence of the subsequence in the buffer, or -1 if + * not found */ public int indexOf(String src, int srcStart, int srcLen, int myOffset) { char first = src.charAt(srcStart); @@ -171,7 +176,8 @@ // Look for first char int srcEnd = srcStart + srcLen; - mainLoop: for (int i = myOffset + start; i <= (end - srcLen); i++) { + mainLoop: + for (int i = myOffset + start; i <= (end - srcLen); i++) { if (getBufferElement(i) != first) { continue; } @@ -204,9 +210,7 @@ if (hasHashCode) { return hashCode; } - int code = 0; - - code = hash(); + int code = hash(); hashCode = code; hasHashCode = true; return code; @@ -227,6 +231,7 @@ /** * @param index the element location in the buffer + * * @return the element */ protected abstract int getBufferElement(int index); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/Ascii.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/Ascii.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/Ascii.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/Ascii.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,9 +18,6 @@ /** * This class implements some basic ASCII character handling functions. - * - * @author dac@eng.sun.com - * @author James Todd [gonzo@eng.sun.com] */ public final class Ascii { /* @@ -75,7 +72,7 @@ } /** - * Parses an unsigned long from the specified subarray of bytes. + * Parses an unsigned long from the specified sub array of bytes. * * @param b the bytes to parse * @param off the start offset of the bytes diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/Asn1Parser.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/Asn1Parser.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/Asn1Parser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/Asn1Parser.java 2026-01-23 19:33:36.000000000 +0000 @@ -52,7 +52,7 @@ * * See https://bz.apache.org/bugzilla/show_bug.cgi?id=67675#c24 */ - private Deque nestedSequenceEndPositions = new ArrayDeque<>(); + private final Deque nestedSequenceEndPositions = new ArrayDeque<>(); public Asn1Parser(byte[] source) { @@ -76,7 +76,7 @@ * sequences and remove those sequences from the sequence nesting tracking mechanism if they have been * completely parsed. */ - while (nestedSequenceEndPositions.size() > 0) { + while (!nestedSequenceEndPositions.isEmpty()) { if (nestedSequenceEndPositions.peekLast().intValue() <= pos) { nestedSequenceEndPositions.pollLast(); } else { @@ -118,7 +118,7 @@ } } /* - * If this is the first length parsed after a sequence has been added to the sequence nesting tracking mechansim + * If this is the first length parsed after a sequence has been added to the sequence nesting tracking mechanism * it must be the length of the sequence so update the entry to record the end position of the sequence. Note * that position recorded is actually the start of the first element after the sequence ends. */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/B2CConverter.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/B2CConverter.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/B2CConverter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/B2CConverter.java 2026-01-23 19:33:36.000000000 +0000 @@ -135,7 +135,7 @@ cb.limit(cc.getBuffer().length); cb.position(cc.getEnd()); } - CoderResult result = null; + CoderResult result; // Parse leftover if any are present if (leftovers.position() > 0) { int pos = cb.position(); @@ -204,7 +204,7 @@ cb.limit(cc.capacity()); cb.position(cc.limit()); } - CoderResult result = null; + CoderResult result; // Parse leftover if any are present if (leftovers.position() > 0) { int pos = cb.position(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/ByteBufferUtils.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ByteBufferUtils.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/ByteBufferUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ByteBufferUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,44 +16,10 @@ */ package org.apache.tomcat.util.buf; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.nio.ByteBuffer; -import org.apache.juli.logging.Log; -import org.apache.juli.logging.LogFactory; -import org.apache.tomcat.util.res.StringManager; - public class ByteBufferUtils { - private static final StringManager sm = StringManager.getManager(ByteBufferUtils.class); - private static final Log log = LogFactory.getLog(ByteBufferUtils.class); - - private static final Object unsafe; - private static final Method invokeCleanerMethod; - - static { - ByteBuffer tempBuffer = ByteBuffer.allocateDirect(0); - Object unsafeLocal = null; - Method invokeCleanerMethodLocal = null; - try { - Class clazz = Class.forName("sun.misc.Unsafe"); - Field theUnsafe = clazz.getDeclaredField("theUnsafe"); - theUnsafe.setAccessible(true); - unsafeLocal = theUnsafe.get(null); - invokeCleanerMethodLocal = clazz.getMethod("invokeCleaner", ByteBuffer.class); - invokeCleanerMethodLocal.invoke(unsafeLocal, tempBuffer); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | - SecurityException | ClassNotFoundException | NoSuchFieldException e) { - log.warn(sm.getString("byteBufferUtils.cleaner"), e); - unsafeLocal = null; - invokeCleanerMethodLocal = null; - } - unsafe = unsafeLocal; - invokeCleanerMethod = invokeCleanerMethodLocal; - } - private ByteBufferUtils() { // Hide the default constructor since this is a utility class. } @@ -94,17 +60,12 @@ return out; } + /** + * Clean specified direct buffer. This will cause an unavoidable warning on Java 24 and newer. + * + * @param buf the buffer to clean + */ public static void cleanDirectBuffer(ByteBuffer buf) { - if (invokeCleanerMethod != null) { - try { - invokeCleanerMethod.invoke(unsafe, buf); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | - SecurityException e) { - if (log.isDebugEnabled()) { - log.debug(sm.getString("byteBufferUtils.cleaner"), e); - } - } - } + ByteBufferUtilsUnsafe.cleanDirectBuffer(buf); } - } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/ByteBufferUtilsUnsafe.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ByteBufferUtilsUnsafe.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/ByteBufferUtilsUnsafe.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ByteBufferUtilsUnsafe.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,84 @@ +/* + * 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.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.res.StringManager; + +/* + * This functionality is in a separate class so it is only loaded if cleanDirectBuffer() is called. This is because the + * use of unsafe triggers an unavoidable warning with Java 24. + */ +class ByteBufferUtilsUnsafe { + + private static final StringManager sm = StringManager.getManager(ByteBufferUtilsUnsafe.class); + private static final Log log = LogFactory.getLog(ByteBufferUtilsUnsafe.class); + + private static final Object unsafe; + private static final Method invokeCleanerMethod; + + static { + ByteBuffer tempBuffer = ByteBuffer.allocateDirect(0); + Object unsafeLocal; + Method invokeCleanerMethodLocal; + try { + Class clazz = Class.forName("sun.misc.Unsafe"); + Field theUnsafe = clazz.getDeclaredField("theUnsafe"); + theUnsafe.setAccessible(true); + unsafeLocal = theUnsafe.get(null); + invokeCleanerMethodLocal = clazz.getMethod("invokeCleaner", ByteBuffer.class); + invokeCleanerMethodLocal.invoke(unsafeLocal, tempBuffer); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | + SecurityException | ClassNotFoundException | NoSuchFieldException e) { + log.warn(sm.getString("byteBufferUtils.cleaner"), e); + unsafeLocal = null; + invokeCleanerMethodLocal = null; + } + unsafe = unsafeLocal; + invokeCleanerMethod = invokeCleanerMethodLocal; + } + + private ByteBufferUtilsUnsafe() { + // Hide the default constructor since this is a utility class. + } + + + /** + * Clean specified direct buffer. This will cause an unavoidable warning on Java 24 and newer. + * + * @param buf the buffer to clean + */ + static void cleanDirectBuffer(ByteBuffer buf) { + if (invokeCleanerMethod != null) { + try { + invokeCleanerMethod.invoke(unsafe, buf); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | + SecurityException e) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("byteBufferUtils.cleaner"), e); + } + } + } + } + +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/ByteChunk.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ByteChunk.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/ByteChunk.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ByteChunk.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,22 +41,13 @@ * chars and Strings until the strings are needed. In addition, the charset is determined later, from headers or user * code. *

          - * In a server it is very important to be able to operate on - * the original byte[] without converting everything to chars. - * Some protocols are ASCII only, and some allow different - * non-UNICODE encodings. The encoding is not known beforehand, - * and can even change during the execution of the protocol. - * ( for example a multipart message may have parts with different - * encoding ) + * In a server it is very important to be able to operate on the original byte[] without converting everything to chars. + * Some protocols are ASCII only, and some allow different non-UNICODE encodings. The encoding is not known beforehand, + * and can even change during the execution of the protocol. ( for example a multipart message may have parts with + * different encoding ) *

          - * For HTTP it is not very clear how the encoding of RequestURI - * and mime values can be determined, but it is a great advantage - * to be able to parse the request without converting to string. - * - * @author dac@sun.com - * @author James Todd [gonzo@sun.com] - * @author Costin Manolache - * @author Remy Maucherat + * For HTTP, it is not very clear how the encoding of RequestURI and mime values can be determined, but it is a great + * advantage to be able to parse the request without converting to string. */ public final class ByteChunk extends AbstractChunk { @@ -92,7 +83,7 @@ * * @throws IOException If an I/O occurs while writing the bytes */ - void realWriteBytes(byte buf[], int off, int len) throws IOException; + void realWriteBytes(byte[] buf, int off, int len) throws IOException; /** @@ -175,7 +166,7 @@ /** - * Sets the buffer to the specified subarray of bytes. + * Sets the buffer to the specified sub array of bytes. * * @param b the ascii bytes * @param off the start offset of the bytes @@ -268,7 +259,7 @@ * * @throws IOException Writing overflow data to the output channel failed */ - public void append(byte src[], int off, int len) throws IOException { + public void append(byte[] src, int off, int len) throws IOException { // will grow, up to limit makeSpace(len); int limit = getLimitInternal(); @@ -393,14 +384,11 @@ } - public int subtract(byte dest[], int off, int len) throws IOException { + public int subtract(byte[] dest, int off, int len) throws IOException { if (checkEof()) { return -1; } - int n = len; - if (len > getLength()) { - n = getLength(); - } + int n = Math.min(len, getLength()); System.arraycopy(buff, start, dest, off, n); start += n; return n; @@ -436,10 +424,7 @@ if (in == null) { return true; } - int n = in.realReadBytes(); - if (n < 0) { - return true; - } + return in.realReadBytes() < 0; } return false; } @@ -469,8 +454,6 @@ * @param count The size */ public void makeSpace(int count) { - byte[] tmp = null; - int limit = getLimitInternal(); long newSize; @@ -503,12 +486,11 @@ if (newSize > limit) { newSize = limit; } - tmp = new byte[(int) newSize]; + byte[] tmp = new byte[(int) newSize]; // Compacts buffer System.arraycopy(buff, start, tmp, 0, end - start); buff = tmp; - tmp = null; end = end - start; start = 0; } @@ -575,7 +557,8 @@ // entire byte array. This is expensive if only a small subset of the // bytes will be used. The code below is from Apache Harmony. CharBuffer cb; - if (malformedInputAction == CodingErrorAction.REPLACE && unmappableCharacterAction == CodingErrorAction.REPLACE) { + if (malformedInputAction == CodingErrorAction.REPLACE && + unmappableCharacterAction == CodingErrorAction.REPLACE) { cb = charset.decode(ByteBuffer.wrap(buff, start, end - start)); } else { cb = charset.newDecoder().onMalformedInput(malformedInputAction) @@ -656,8 +639,8 @@ } - public boolean equals(byte b2[], int off2, int len2) { - byte b1[] = buff; + public boolean equals(byte[] b2, int off2, int len2) { + byte[] b1 = buff; if (b1 == null && b2 == null) { return true; } @@ -678,8 +661,8 @@ } - public boolean equalsIgnoreCase(byte b2[], int off2, int len2) { - byte b1[] = buff; + public boolean equalsIgnoreCase(byte[] b2, int off2, int len2) { + byte[] b1 = buff; if (b1 == null && b2 == null) { return true; } @@ -710,13 +693,14 @@ *

          * NOTE: This only works for characters in the range 0-127. * - * @param c2 the array to compare to + * @param c2 the array to compare to * @param off2 offset * @param len2 length + * * @return true if the comparison succeeded, false otherwise */ - public boolean equals(char c2[], int off2, int len2) { - byte b1[] = buff; + public boolean equals(char[] c2, int off2, int len2) { + byte[] b1 = buff; if (c2 == null && b1 == null) { return true; } @@ -737,7 +721,7 @@ /** - * Returns true if the buffer starts with the specified string when tested in a case sensitive manner. + * Returns true if the buffer starts with the specified string when tested in a case-sensitive manner. *

          * NOTE: This only works for characters in the range 0-127. * @@ -763,7 +747,7 @@ /** - * Returns true if the buffer starts with the specified string when tested in a case insensitive manner. + * Returns true if the buffer starts with the specified string when tested in a case-insensitive manner. *

          * NOTE: This only works for characters in the range 0-127. * @@ -823,7 +807,7 @@ * * @return The position of the first instance of the character or -1 if the character is not found. */ - public static int indexOf(byte bytes[], int start, int end, char s) { + public static int indexOf(byte[] bytes, int start, int end, char s) { int offset = start; while (offset < end) { @@ -847,7 +831,7 @@ * * @return The position of the first instance of the byte or -1 if the byte is not found. */ - public static int findByte(byte bytes[], int start, int end, byte b) { + public static int findByte(byte[] bytes, int start, int end, byte b) { int offset = start; while (offset < end) { if (bytes[offset] == b) { @@ -860,7 +844,7 @@ /** - * Returns the first instance of any of the given bytes in the byte array between the specified start and end. + * Returns the first instance of the given bytes in the byte array between the specified start and end. * * @param bytes The byte array to search * @param start The point to start searching from in the byte array @@ -869,7 +853,7 @@ * * @return The position of the first instance of the byte or -1 if the byte is not found. */ - public static int findBytes(byte bytes[], int start, int end, byte b[]) { + public static int findBytes(byte[] bytes, int start, int end, byte[] b) { int offset = start; while (offset < end) { for (byte value : b) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/C2BConverter.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/C2BConverter.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/C2BConverter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/C2BConverter.java 2026-01-23 19:33:36.000000000 +0000 @@ -66,10 +66,14 @@ leftovers.position(0); } - public boolean isUndeflow() { + public boolean isUnderflow() { return (leftovers.position() > 0); } + public boolean isUndeflow() { + return isUnderflow(); + } + /** * Convert the given characters to bytes. * @@ -95,11 +99,11 @@ cb.limit(cc.getEnd()); cb.position(cc.getStart()); } - CoderResult result = null; + CoderResult result; // Parse leftover if any are present if (leftovers.position() > 0) { int pos = bb.position(); - // Loop until one char is encoded or there is a encoder error + // Loop until one char is encoded or there is an encoder error do { leftovers.put((char) cc.subtract()); leftovers.flip(); @@ -160,11 +164,11 @@ cb.limit(cc.limit()); cb.position(cc.position()); } - CoderResult result = null; + CoderResult result; // Parse leftover if any are present if (leftovers.position() > 0) { int pos = bb.position(); - // Loop until one char is encoded or there is a encoder error + // Loop until one char is encoded or there is an encoder error do { leftovers.put(cc.get()); leftovers.flip(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/CharChunk.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/CharChunk.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/CharChunk.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/CharChunk.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,11 +21,6 @@ /** * Utilities to manipulate char chunks. While String is the easiest way to manipulate chars ( search, substrings, etc), * it is known to not be the most efficient solution - Strings are designed as immutable and secure objects. - * - * @author dac@sun.com - * @author James Todd [gonzo@sun.com] - * @author Costin Manolache - * @author Remy Maucherat */ public final class CharChunk extends AbstractChunk implements CharSequence { @@ -60,7 +55,7 @@ * * @throws IOException If an I/O occurs while writing the characters */ - void realWriteChars(char buf[], int off, int len) throws IOException; + void realWriteChars(char[] buf, int off, int len) throws IOException; } // -------------------- @@ -108,7 +103,7 @@ /** - * Sets the buffer to the specified subarray of characters. + * Sets the buffer to the specified sub array of characters. * * @param c the characters * @param off the start offset of the characters @@ -188,7 +183,7 @@ * * @throws IOException Writing overflow data to the output channel failed */ - public void append(char src[], int off, int len) throws IOException { + public void append(char[] src, int off, int len) throws IOException { // will grow, up to limit makeSpace(len); int limit = getLimitInternal(); @@ -276,7 +271,7 @@ int sOff = off; int sEnd = off + len; while (sOff < sEnd) { - int d = min(limit - end, sEnd - sOff); + int d = Math.min(limit - end, sEnd - sOff); s.getChars(sOff, sOff + d, buff, end); sOff += d; end += d; @@ -297,14 +292,11 @@ } - public int subtract(char dest[], int off, int len) throws IOException { + public int subtract(char[] dest, int off, int len) throws IOException { if (checkEof()) { return -1; } - int n = len; - if (len > getLength()) { - n = getLength(); - } + int n = Math.min(len, getLength()); System.arraycopy(buff, start, dest, off, n); start += n; return n; @@ -316,10 +308,7 @@ if (in == null) { return true; } - int n = in.realReadChars(); - if (n < 0) { - return true; - } + return in.realReadChars() < 0; } return false; } @@ -349,8 +338,6 @@ * @param count The size */ public void makeSpace(int count) { - char[] tmp = null; - int limit = getLimitInternal(); long newSize; @@ -383,12 +370,11 @@ if (newSize > limit) { newSize = limit; } - tmp = new char[(int) newSize]; + char[] tmp = new char[(int) newSize]; // Some calling code assumes buffer will not be compacted System.arraycopy(buff, 0, tmp, 0, end); buff = tmp; - tmp = null; } @@ -472,8 +458,8 @@ } - public boolean equals(char b2[], int off2, int len2) { - char b1[] = buff; + public boolean equals(char[] b2, int off2, int len2) { + char[] b1 = buff; if (b1 == null && b2 == null) { return true; } @@ -597,7 +583,7 @@ * * @return The position of the first instance of the character or -1 if the character is not found. */ - public static int indexOf(char chars[], int start, int end, char s) { + public static int indexOf(char[] chars, int start, int end, char s) { int offset = start; while (offset < end) { @@ -612,13 +598,6 @@ // -------------------- utils - private int min(int a, int b) { - if (a < b) { - return a; - } - return b; - } - // Char sequence impl diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/CharsetCache.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/CharsetCache.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/CharsetCache.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/CharsetCache.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,6 +19,7 @@ import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; +import java.nio.charset.UnsupportedCharsetException; import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -157,12 +158,12 @@ // Added from OpenJDK 21 ea18 "gb18030-2022" // If you add and entry to this list, ensure you run - // TestCharsetUtil#testIsAcsiiSupersetAll() + // TestCharsetUtil#testIsAsciiSupersetAll() }; private static final Charset DUMMY_CHARSET = new DummyCharset("Dummy", null); - private ConcurrentMap cache = new ConcurrentHashMap<>(); + private final ConcurrentMap cache = new ConcurrentHashMap<>(); public CharsetCache() { // Pre-populate the cache @@ -192,15 +193,15 @@ if (result == DUMMY_CHARSET) { // Name is known but the Charset is not in the cache - Charset charset = Charset.forName(lcCharsetName); - if (charset == null) { - // Charset not available in this JVM - remove cache entry - cache.remove(lcCharsetName); - result = null; - } else { + try { + Charset charset = Charset.forName(lcCharsetName); // Charset is available - populate cache entry addToCache(lcCharsetName, charset); result = charset; + } catch (UnsupportedCharsetException e) { + // Charset not available in this JVM - remove cache entry + cache.remove(lcCharsetName); + result = null; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/HexUtils.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/HexUtils.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/HexUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/HexUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,6 @@ /** * Tables useful when converting byte arrays to and from strings of hexadecimal digits. Code from Ajp11, from Apache's * JServ. - * - * @author Craig R. McClanahan */ public final class HexUtils { @@ -33,9 +31,9 @@ /** * Table for HEX to DEC byte translation. */ - private static final int[] DEC = { 00, 01, 02, 03, 04, 05, 06, 07, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, - 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 10, 11, 12, 13, 14, 15, }; + private static final int[] DEC = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, + 11, 12, 13, 14, 15, }; /** @@ -114,7 +112,7 @@ int upperNibble = getDec(inputChars[2 * i]); int lowerNibble = getDec(inputChars[2 * i + 1]); if (upperNibble < 0 || lowerNibble < 0) { - // Non hex character + // Non-hex character throw new IllegalArgumentException(sm.getString("hexUtils.fromHex.nonHex")); } result[i] = (byte) ((upperNibble << 4) + lowerNibble); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -45,6 +45,9 @@ uDecoder.eof=End of file (EOF) uDecoder.isHexDigit=The hexadecimal encoding is invalid +uDecoder.noBackslash=The encoded backslash character is not allowed uDecoder.noSlash=The encoded slash character is not allowed uDecoder.urlDecode.conversionError=Failed to decode [{0}] using character set [{1}] uDecoder.urlDecode.missingDigit=Failed to decode [{0}] because the % character must be followed by two hexadecimal digits +uDecoder.urlDecode.rejectEncodedReverseSolidus=Failed to decode [{0}] because it contained a %5c sequence the decoder is configured to reject +uDecoder.urlDecode.rejectEncodedSolidus=Failed to decode [{0}] because it contained a %2f sequence the decoder is configured to reject diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -45,6 +45,9 @@ uDecoder.eof=Fin de fichier (EOF) uDecoder.isHexDigit=L'encodage hexadécimal est invalide +uDecoder.noBackslash=Le caractère antislash encodé n'est pas autorisé uDecoder.noSlash=Un caractère slash encodé n'est pas autorisé uDecoder.urlDecode.conversionError=Echec de décodage [{0}] en utilisant le jeu de caractères [{1}] uDecoder.urlDecode.missingDigit=Impossible de décoder [{0}] parce que le caractère % doit être suivi de deux chiffres héxadécimaux +uDecoder.urlDecode.rejectEncodedReverseSolidus=Echec du décodage de [{0}] car il contenait une séquence %5c que le décodeur est configuré pour rejeter +uDecoder.urlDecode.rejectEncodedSolidus=Echec du décodage de [{0}] car il contenait une séquence %2f que le décodeur est configuré pour rejeter diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -45,6 +45,9 @@ uDecoder.eof=予期せぬ場所で終端に達しました。 uDecoder.isHexDigit=16進エンコーディングが無効です +uDecoder.noBackslash=エンコードされたバックスラッシュ文字は許可されていません uDecoder.noSlash="/" を符号化して含めることはできません。 uDecoder.urlDecode.conversionError=文字セット [{1}] を使用した [{0}] のデコードに失敗しました uDecoder.urlDecode.missingDigit=%文字の後ろに2つの16進数字が続く必要があるため、[{0}]のデコードに失敗しました。 +uDecoder.urlDecode.rejectEncodedReverseSolidus=デコーダが %5c シーケンスを拒否するように設定されているため、[{0}] はデコードできませんでした +uDecoder.urlDecode.rejectEncodedSolidus=デコーダが %2f シーケンスを拒否するように設定されているため、[{0}] はデコードできませんでした diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -32,6 +32,9 @@ messageBytes.illegalCharacter=代码点[{1}]处的Unicode字符[{0}]无法编码,因为它超出了允许的0到255范围。 +toStringUtil.classpath.platform=JRE提供的类 +toStringUtil.classpath.unknown=未知-非URLClassLoader实例 + uDecoder.eof=文件结尾(EOF) uDecoder.isHexDigit=十六进制编码无效 uDecoder.noSlash=不允许使用编码的斜杠字符 diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/MessageBytes.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/MessageBytes.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/MessageBytes.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/MessageBytes.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,15 +26,11 @@ import org.apache.tomcat.util.res.StringManager; /** - * This class is used to represent a subarray of bytes in an HTTP message. It represents all request/response elements. + * This class is used to represent a sub array of bytes in an HTTP message. It represents all request/response elements. * The byte/char conversions are delayed and cached. Everything is recyclable. *

          - * The object can represent a byte[], a char[], or a (sub) String. All operations can be made in case sensitive mode or + * The object can represent a byte[], a char[], or a (sub) String. All operations can be made in case-sensitive mode or * not. - * - * @author dac@eng.sun.com - * @author James Todd [gonzo@eng.sun.com] - * @author Costin Manolache */ public final class MessageBytes implements Cloneable, Serializable { @@ -47,15 +43,15 @@ public static final int T_NULL = 0; /** - * getType() is T_STR if the the object used to create the MessageBytes was a String. + * getType() is T_STR if the object used to create the MessageBytes was a String. */ public static final int T_STR = 1; /** - * getType() is T_BYTES if the the object used to create the MessageBytes was a byte[]. + * getType() is T_BYTES if the object used to create the MessageBytes was a byte[]. */ public static final int T_BYTES = 2; /** - * getType() is T_CHARS if the the object used to create the MessageBytes was a char[]. + * getType() is T_CHARS if the object used to create the MessageBytes was a char[]. */ public static final int T_CHARS = 3; @@ -112,7 +108,7 @@ /** - * Sets the content to the specified subarray of bytes. + * Sets the content to the specified sub array of bytes. * * @param b the bytes * @param off the start offset of the bytes @@ -337,7 +333,7 @@ toString(); //$FALL-THROUGH$ case T_STR: { - char cc[] = strValue.toCharArray(); + char[] cc = strValue.toCharArray(); charC.setChars(cc, 0, cc.length); } } @@ -425,9 +421,8 @@ } public boolean equals(MessageBytes mb) { - switch (type) { - case T_STR: - return mb.equals(strValue); + if (type == T_STR) { + return mb.equals(strValue); } if (mb.type != T_CHARS && mb.type != T_BYTES) { @@ -494,9 +489,7 @@ if (hasHashCode) { return hashCode; } - int code = 0; - - code = hash(); + int code = hash(); hashCode = code; hasHashCode = true; return code; @@ -628,12 +621,10 @@ return longValue; } - switch (type) { - case T_BYTES: - longValue = byteC.getLong(); - break; - default: - longValue = Long.parseLong(toString()); + if (type == T_BYTES) { + longValue = byteC.getLong(); + } else { + longValue = Long.parseLong(toString()); } hasLongValue = true; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/StringCache.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/StringCache.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/StringCache.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/StringCache.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,6 @@ /** * This class implements a String cache for ByteChunk and CharChunk. - * - * @author Remy Maucherat */ public class StringCache { @@ -445,7 +443,7 @@ * * @return -1, 0 or +1 if inferior, equal, or superior to the String. */ - protected static final int compare(ByteChunk name, byte[] compareTo) { + protected static int compare(ByteChunk name, byte[] compareTo) { int result = 0; byte[] b = name.getBuffer(); @@ -481,8 +479,8 @@ * * @return the corresponding value * - * @deprecated Unused. Will be removed in Tomcat 11. - * Use {@link #find(ByteChunk, CodingErrorAction, CodingErrorAction)} + * @deprecated Unused. Will be removed in Tomcat 11. Use + * {@link #find(ByteChunk, CodingErrorAction, CodingErrorAction)} */ @Deprecated protected static final String find(ByteChunk name) { @@ -494,13 +492,13 @@ * Find an entry given its name in the cache and return the associated String. * * @param name The name to find - * @param malformedInputAction Action to take if an malformed input is encountered + * @param malformedInputAction Action to take if a malformed input is encountered * @param unmappableCharacterAction Action to take if an unmappable character is encountered * * @return the corresponding value */ - protected static final String find(ByteChunk name, CodingErrorAction malformedInputAction, - CodingErrorAction unmappableCharacterAction) { + protected static String find(ByteChunk name, CodingErrorAction malformedInputAction, + CodingErrorAction unmappableCharacterAction) { int pos = findClosest(name, bcCache, bcCache.length); if ((pos < 0) || (compare(name, bcCache[pos].name) != 0) || !(name.getCharset().equals(bcCache[pos].charset)) || !malformedInputAction.equals(bcCache[pos].malformedInputAction) || @@ -522,7 +520,7 @@ * * @return the position of the best match */ - protected static final int findClosest(ByteChunk name, ByteEntry[] array, int len) { + protected static int findClosest(ByteChunk name, ByteEntry[] array, int len) { int a = 0; int b = len - 1; @@ -539,7 +537,7 @@ return 0; } - int i = 0; + int i; while (true) { i = (b + a) >>> 1; int result = compare(name, array[i].name); @@ -571,7 +569,7 @@ * * @return -1, 0 or +1 if inferior, equal, or superior to the String. */ - protected static final int compare(CharChunk name, char[] compareTo) { + protected static int compare(CharChunk name, char[] compareTo) { int result = 0; char[] c = name.getBuffer(); @@ -607,7 +605,7 @@ * * @return the corresponding value */ - protected static final String find(CharChunk name) { + protected static String find(CharChunk name) { int pos = findClosest(name, ccCache, ccCache.length); if ((pos < 0) || (compare(name, ccCache[pos].name) != 0)) { return null; @@ -627,7 +625,7 @@ * * @return the position of the best match */ - protected static final int findClosest(CharChunk name, CharEntry[] array, int len) { + protected static int findClosest(CharChunk name, CharEntry[] array, int len) { int a = 0; int b = len - 1; @@ -644,7 +642,7 @@ return 0; } - int i = 0; + int i; while (true) { i = (b + a) >>> 1; int result = compare(name, array[i].name); @@ -670,7 +668,7 @@ // -------------------------------------------------- ByteEntry Inner Class - private static class ByteEntry { + protected static class ByteEntry { private byte[] name = null; private Charset charset = null; @@ -710,7 +708,7 @@ // -------------------------------------------------- CharEntry Inner Class - private static class CharEntry { + protected static class CharEntry { private char[] name = null; private String value = null; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/StringUtils.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/StringUtils.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/StringUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/StringUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -101,18 +101,15 @@ } /** - * Splits a comma-separated string into an array of String values. - * - * Whitespace around the commas is removed. - * - * Null or empty values will return a zero-element array. + * Splits a comma-separated string into an array of String values. Whitespace around the commas is removed. Null or + * empty values will return a zero-element array. * * @param s The string to split by commas. * * @return An array of String values. */ public static String[] splitCommaSeparated(String s) { - if (s == null || s.length() == 0) { + if (s == null || s.isEmpty()) { return new String[0]; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/ToStringUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ToStringUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/ToStringUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/ToStringUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -75,7 +75,7 @@ // From Java 9 the internal class loaders no longer extend // URLCLassLoader String cp = System.getProperty("java.class.path"); - if (cp != null && cp.length() > 0) { + if (cp != null && !cp.isEmpty()) { String[] paths = cp.split(File.pathSeparator); for (String path : paths) { result.append(INDENT); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/UDecoder.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/UDecoder.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/UDecoder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/UDecoder.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,6 @@ /** * All URL decoding happens here. This way we can reuse, review, optimize without adding complexity to the buffers. The * conversion will modify the original buffer. - * - * @author Costin Manolache */ public final class UDecoder { @@ -58,10 +56,12 @@ /** %-encoded slash is forbidden in resource path */ private static final IOException EXCEPTION_SLASH = new DecodeException(sm.getString("uDecoder.noSlash")); + /** %-encoded backslash is forbidden in resource path */ + private static final IOException EXCEPTION_BACKSLASH = new DecodeException(sm.getString("uDecoder.noBackslash")); /** * URLDecode, will modify the source. Assumes source bytes are encoded using a superset of US-ASCII as per RFC 7230. - * "%2f" will be rejected unless the input is a query string. + * "%5c" will be decoded. "%2f" will be rejected unless the input is a query string. * * @param mb The URL encoded bytes * @param query {@code true} if this is a query string. For a query string '+' will be decoded to ' ' @@ -70,9 +70,9 @@ */ public void convert(ByteChunk mb, boolean query) throws IOException { if (query) { - convert(mb, true, EncodedSolidusHandling.DECODE); + convert(mb, true, EncodedSolidusHandling.DECODE, EncodedSolidusHandling.DECODE); } else { - convert(mb, false, EncodedSolidusHandling.REJECT); + convert(mb, false, EncodedSolidusHandling.REJECT, EncodedSolidusHandling.DECODE); } } @@ -85,17 +85,36 @@ * parameter will be ignored and the %2f sequence will be decoded * * @throws IOException Invalid %xx URL encoding + * + * @deprecated Unused. Will be removed in Tomcat 12. Use + * {@link #convert(ByteChunk, EncodedSolidusHandling, EncodedSolidusHandling)} */ + @Deprecated public void convert(ByteChunk mb, EncodedSolidusHandling encodedSolidusHandling) throws IOException { - convert(mb, false, encodedSolidusHandling); + convert(mb, false, encodedSolidusHandling, EncodedSolidusHandling.DECODE); + } + + + /** + * URLDecode, will modify the source. Assumes source bytes are encoded using a superset of US-ASCII as per RFC 7230. + * + * @param mb The URL encoded bytes + * @param encodedSolidusHandling How should the %2f sequence handled by the decoder? + * @param encodedReverseSolidusHandling How should the %5c sequence handled by the decoder? + * + * @throws IOException Invalid %xx URL encoding + */ + public void convert(ByteChunk mb, EncodedSolidusHandling encodedSolidusHandling, + EncodedSolidusHandling encodedReverseSolidusHandling) throws IOException { + convert(mb, false, encodedSolidusHandling, encodedReverseSolidusHandling); } - private void convert(ByteChunk mb, boolean query, EncodedSolidusHandling encodedSolidusHandling) - throws IOException { + private void convert(ByteChunk mb, boolean query, EncodedSolidusHandling encodedSolidusHandling, + EncodedSolidusHandling encodedReverseSolidusHandling) throws IOException { int start = mb.getStart(); - byte buff[] = mb.getBytes(); + byte[] buff = mb.getBytes(); int end = mb.getEnd(); int idx = ByteChunk.findByte(buff, start, end, (byte) '%'); @@ -145,23 +164,34 @@ buff[idx] = buff[j]; } } - } else if (res == '%') { - /* - * If encoded '/' is going to be left encoded then so must encoded '%' else the subsequent %nn - * decoding will either fail or corrupt the output. - */ - switch (encodedSolidusHandling) { - case DECODE: - case REJECT: { + } else if (res == '\\') { + switch (encodedReverseSolidusHandling) { + case DECODE: { buff[idx] = (byte) res; break; } + case REJECT: { + throw EXCEPTION_BACKSLASH; + } case PASS_THROUGH: { buff[idx++] = buff[j - 2]; buff[idx++] = buff[j - 1]; buff[idx] = buff[j]; } } + } else if (res == '%') { + /* + * If encoded '/' or '\' is going to be left encoded then so must be encoded '%' else the subsequent + * %nn decoding will either fail or corrupt the output. + */ + if (encodedSolidusHandling.equals(EncodedSolidusHandling.PASS_THROUGH) || + encodedReverseSolidusHandling.equals(EncodedSolidusHandling.PASS_THROUGH)) { + buff[idx++] = buff[j - 2]; + buff[idx++] = buff[j - 1]; + buff[idx] = buff[j]; + } else { + buff[idx] = (byte) res; + } } else { buff[idx] = (byte) res; } @@ -184,6 +214,24 @@ * @exception IllegalArgumentException if a '%' character is not followed by a valid 2-digit hexadecimal number */ public static String URLDecode(String str, Charset charset) { + return URLDecode(str, charset, EncodedSolidusHandling.DECODE, EncodedSolidusHandling.DECODE); + } + + + /** + * Decode and return the specified URL-encoded String. It is assumed the string is not a query string. + * + * @param str The url-encoded string + * @param charset The character encoding to use; if null, UTF-8 is used. + * @param encodedSolidusHandling The required handling of encoded solidus (%2f - /) + * @param encodedReverseSolidusHandling The required handling of encoded reverse solidus (%5c - \) + * + * @return the decoded string + * + * @exception IllegalArgumentException if a '%' character is not followed by a valid 2-digit hexadecimal number + */ + public static String URLDecode(String str, Charset charset, EncodedSolidusHandling encodedSolidusHandling, + EncodedSolidusHandling encodedReverseSolidusHandling) { if (str == null) { return null; } @@ -211,7 +259,7 @@ * sets and some use a variable length. */ - // This isn't perfect but it is a reasonable guess for the size of the + // This isn't perfect, but it is a reasonable guess for the size of the // array required ByteArrayOutputStream baos = new ByteArrayOutputStream(str.length() * 2); @@ -232,7 +280,60 @@ char c1 = sourceChars[ix++]; char c2 = sourceChars[ix++]; if (isHexDigit(c1) && isHexDigit(c2)) { - baos.write(x2c(c1, c2)); + int decoded = x2c(c1, c2); + switch (decoded) { + case '/': { + switch (encodedSolidusHandling) { + case DECODE: { + osw.append('/'); + break; + } + case PASS_THROUGH: { + osw.append(c); + osw.append(c1); + osw.append(c2); + break; + } + case REJECT: { + throw new IllegalArgumentException( + sm.getString("uDecoder.urlDecode.rejectEncodedSolidus", str)); + } + } + break; + } + case '\\': { + switch (encodedReverseSolidusHandling) { + case DECODE: { + osw.append('\\'); + break; + } + case PASS_THROUGH: { + osw.append(c); + osw.append(c1); + osw.append(c2); + break; + } + case REJECT: { + throw new IllegalArgumentException( + sm.getString("uDecoder.urlDecode.rejectEncodedReverseSolidus", str)); + } + } + break; + } + case '%': { + if (encodedReverseSolidusHandling == EncodedSolidusHandling.PASS_THROUGH || + encodedSolidusHandling == EncodedSolidusHandling.PASS_THROUGH) { + osw.append(c); + osw.append(c1); + osw.append(c2); + } else { + baos.write('%'); + } + break; + } + default: + baos.write(decoded); + } } else { throw new IllegalArgumentException(sm.getString("uDecoder.urlDecode.missingDigit", str)); } @@ -242,7 +343,7 @@ } osw.flush(); - return baos.toString(charset.name()); + return baos.toString(charset); } catch (IOException ioe) { throw new IllegalArgumentException(sm.getString("uDecoder.urlDecode.conversionError", str, charset.name()), ioe); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/UEncoder.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/UEncoder.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/UEncoder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/UEncoder.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,6 @@ * Efficient implementation of a UTF-8 encoder. This class is not thread safe - you need one encoder per thread. The * encoder will save and recycle the internal objects, avoiding garbage. You can add extra characters that you want * preserved, for example while encoding a URL you can add "/". - * - * @author Costin Manolache */ public final class UEncoder { @@ -49,7 +47,7 @@ // Not static - the set may differ ( it's better than adding // an extra check for "/", "+", etc - private BitSet safeChars = null; + private final BitSet safeChars; private C2BConverter c2b = null; private ByteChunk bb = null; private CharChunk cb = null; @@ -117,7 +115,7 @@ return output; } - protected void urlEncode(CharChunk out, ByteChunk bb) throws IOException { + private void urlEncode(CharChunk out, ByteChunk bb) throws IOException { byte[] bytes = bb.getBuffer(); for (int j = bb.getStart(); j < bb.getEnd(); j++) { out.append('%'); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/UriUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/UriUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/UriUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/UriUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -74,7 +74,7 @@ * * @param c The character to test * - * @return {@code true} if a the character is allowed, otherwise {@code + * @return {@code true} if the character is allowed, otherwise {@code * false} */ private static boolean isSchemeChar(char c) { @@ -196,7 +196,7 @@ * * @param path The path to test * - * @return {@code true} if the supplied path starts with once of the recognised sequences. + * @return {@code true} if the supplied path starts with one of the recognised sequences. */ public static boolean isAbsoluteURI(String path) { // Special case as only a single / @@ -217,10 +217,7 @@ } // path starts with something that might be a protocol. Look for a // following "://" - if (i + 2 < path.length() && path.charAt(i++) == ':' && path.charAt(i++) == '/' && path.charAt(i) == '/') { - return true; - } - return false; + return i + 2 < path.length() && path.charAt(i++) == ':' && path.charAt(i++) == '/' && path.charAt(i) == '/'; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/Utf8Encoder.java tomcat10-10.1.52/java/org/apache/tomcat/util/buf/Utf8Encoder.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/Utf8Encoder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/Utf8Encoder.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,11 +45,11 @@ int limit = in.limit(); byte[] bArr; char[] cArr; - int x = pos; bArr = out.array(); cArr = in.array(); int outPos = out.position(); int rem = in.remaining(); + int x; for (x = pos; x < pos + rem; x++) { int jchar = (cArr[x] & 0xFFFF); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/buf/package.html tomcat10-10.1.52/java/org/apache/tomcat/util/buf/package.html --- tomcat10-10.1.34/java/org/apache/tomcat/util/buf/package.html 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/buf/package.html 2026-01-23 19:33:36.000000000 +0000 @@ -26,12 +26,6 @@ in any VM without any garbage.

          -This package must accommodate future extensions and additional converters ( most important: the nio.charset, -which should be detected and used if available ). Also, we do have one hand-written UTF8Decoder, and -other tuned encoders could be added. - -

          -My benchmarks ( I'm costin :-) show only small differences between C2B, B2C and hand-written codders/decoders, -so UTF8Decoder may be disabled. +This package must accommodate future extensions and additional converters. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/codec/binary/Base64.java tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/Base64.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/codec/binary/Base64.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/Base64.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,7 +18,6 @@ /** * Provides Base64 encoding and decoding as defined by RFC 2045. - * *

          * This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 Multipurpose * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein. @@ -36,15 +35,15 @@ * The URL-safe parameter is only applied to encode operations. Decoding seamlessly handles both modes. *

          *

          - * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only - * encode/decode character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, - * UTF-8, etc). + * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode + * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). *

          *

          * This class is thread-safe. *

          * * @see RFC 2045 + * * @since 1.0 * * @deprecated Unused. This class will be removed in Tomcat 11 onwards. @@ -53,8 +52,7 @@ public class Base64 extends BaseNCodec { /** - * BASE64 characters are 6 bits in length. - * They are formed by taking a block of 3 octets to form a 24-bit string, + * BASE64 characters are 6 bits in length. They are formed by taking a block of 3 octets to form a 24-bit string, * which is converted into 4 BASE64 characters. */ private static final int BITS_PER_ENCODED_BYTE = 6; @@ -69,30 +67,23 @@ * https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ *

          */ - private static final byte[] STANDARD_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' - }; + private static final byte[] STANDARD_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; /** - * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / - * changed to - and _ to make the encoded Base64 results more URL-SAFE. - * This table is only used when the Base64's mode is set to URL-SAFE. - */ - private static final byte[] URL_SAFE_ENCODE_TABLE = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' - }; + * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / changed to - and _ to make the encoded Base64 + * results more URL-SAFE. This table is only used when the Base64's mode is set to URL-SAFE. + */ + private static final byte[] URL_SAFE_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', + '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' }; /** - * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified - * in Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 + * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in + * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 * alphabet but fall within the bounds of the array are translated to -1. *

          * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both @@ -103,6 +94,7 @@ * https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ *

          */ + // @formatter:off private static final byte[] STANDARD_DECODE_TABLE = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f @@ -125,7 +117,8 @@ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 // 70-7a p-z - }; + }; + // @formatter:on /* * Base64 uses 6-bit fields. @@ -141,8 +134,7 @@ // The private member fields below are used with the new streaming approach, which requires // some state be preserved between calls of encode() and decode(). - public static byte[] decodeBase64( - final byte[] base64Data, final int off, final int len) { + public static byte[] decodeBase64(final byte[] base64Data, final int off, final int len) { return new Base64().decode(base64Data, off, len); } @@ -152,9 +144,10 @@ * Note: this method seamlessly handles data encoded in URL-safe or normal mode. *

          * - * @param base64String - * String containing Base64 data + * @param base64String String containing Base64 data + * * @return Array containing decoded data. + * * @since 1.4 */ public static byte[] decodeBase64(final String base64String) { @@ -169,13 +162,13 @@ /** * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if {@code true} this encoder will chunk the base64 output into 76 character blocks + * @param binaryData Array containing binary data to encode. + * @param isChunked if {@code true} this encoder will chunk the base64 output into 76 character blocks + * * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + * + * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than + * {@link Integer#MAX_VALUE} */ public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) { return encodeBase64(binaryData, isChunked, false); @@ -184,16 +177,16 @@ /** * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if {@code true} this encoder will chunk the base64 output into 76 character blocks - * @param urlSafe - * if {@code true} this encoder will emit - and _ instead of the usual + and / characters. - * Note: no padding is added when encoding using the URL-safe alphabet. + * @param binaryData Array containing binary data to encode. + * @param isChunked if {@code true} this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe if {@code true} this encoder will emit - and _ instead of the usual + and / characters. + * Note: no padding is added when encoding using the URL-safe alphabet. + * * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE} + * + * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than + * {@link Integer#MAX_VALUE} + * * @since 1.4 */ public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) { @@ -203,22 +196,20 @@ /** * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. * - * @param binaryData - * Array containing binary data to encode. - * @param isChunked - * if {@code true} this encoder will chunk the base64 output into 76 character blocks - * @param urlSafe - * if {@code true} this encoder will emit - and _ instead of the usual + and / characters. - * Note: no padding is added when encoding using the URL-safe alphabet. - * @param maxResultSize - * The maximum result size to accept. + * @param binaryData Array containing binary data to encode. + * @param isChunked if {@code true} this encoder will chunk the base64 output into 76 character blocks + * @param urlSafe if {@code true} this encoder will emit - and _ instead of the usual + and / characters. + * Note: no padding is added when encoding using the URL-safe alphabet. + * @param maxResultSize The maximum result size to accept. + * * @return Base64-encoded data. - * @throws IllegalArgumentException - * Thrown when the input array needs an output array bigger than maxResultSize + * + * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than maxResultSize + * * @since 1.4 */ - public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, - final boolean urlSafe, final int maxResultSize) { + public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe, + final int maxResultSize) { if (binaryData == null || binaryData.length == 0) { return binaryData; } @@ -228,23 +219,22 @@ final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); final long len = b64.getEncodedLength(binaryData); if (len > maxResultSize) { - throw new IllegalArgumentException(sm.getString( - "base64.inputTooLarge", Long.valueOf(len), Integer.valueOf(maxResultSize))); + throw new IllegalArgumentException( + sm.getString("base64.inputTooLarge", Long.valueOf(len), Integer.valueOf(maxResultSize))); } return b64.encode(binaryData); } /** - * Encodes binary data using the base64 algorithm but does not chunk the output. + * Encodes binary data using the base64 algorithm but does not chunk the output. NOTE: We changed the behavior of + * this method from multi-line chunking (commons-codec-1.4) to single-line non-chunking (commons-codec-1.5). * - * NOTE: We changed the behavior of this method from multi-line chunking (commons-codec-1.4) to - * single-line non-chunking (commons-codec-1.5). + * @param binaryData binary data to encode * - * @param binaryData - * binary data to encode * @return String containing Base64 characters. - * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not). + * + * @since 1.4 (NOTE: 1.4 chunked the output, whereas 1.5 does not). */ public static String encodeBase64String(final byte[] binaryData) { return StringUtils.newStringUsAscii(encodeBase64(binaryData, false)); @@ -252,11 +242,12 @@ /** * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The - * url-safe variation emits - and _ instead of + and / characters. - * Note: no padding is added. - * @param binaryData - * binary data to encode + * url-safe variation emits - and _ instead of + and / characters. Note: no padding is added. + * + * @param binaryData binary data to encode + * * @return String containing Base64 characters + * * @since 1.4 */ public static String encodeBase64URLSafeString(final byte[] binaryData) { @@ -264,24 +255,22 @@ } /** - * Validates whether decoding the final trailing character is possible in the context - * of the set of possible base 64 values. + * Validates whether decoding the final trailing character is possible in the context of the set of possible base 64 + * values. *

          - * The character is valid if the lower bits within the provided mask are zero. This - * is used to test the final trailing base-64 digit is zero in the bits that will be discarded. + * The character is valid if the lower bits within the provided mask are zero. This is used to test the final + * trailing base-64 digit is zero in the bits that will be discarded. *

          * * @param emptyBitsMask The mask of the lower bits that should be empty - * @param context the context to be used + * @param context the context to be used * * @throws IllegalArgumentException if the bits being checked contain any non-zero value */ private static void validateCharacter(final int emptyBitsMask, final Context context) { if ((context.ibitWorkArea & emptyBitsMask) != 0) { - throw new IllegalArgumentException( - "Last encoded character (before the paddings if any) is a valid " + - "base 64 alphabet but not a possible value. " + - "Expected the discarded bits to be zero."); + throw new IllegalArgumentException("Last encoded character (before the paddings if any) is a valid " + + "base 64 alphabet but not a possible value. " + "Expected the discarded bits to be zero."); } } @@ -327,7 +316,6 @@ *

          * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE. *

          - * *

          * When decoding all variants are supported. *

          @@ -341,14 +329,12 @@ *

          * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE. *

          - * *

          * When decoding all variants are supported. *

          * - * @param urlSafe - * if {@code true}, URL-safe encoding is used. In most cases this should be set to - * {@code false}. + * @param urlSafe if {@code true}, URL-safe encoding is used. In most cases this should be set to {@code false}. + * * @since 1.4 */ public Base64(final boolean urlSafe) { @@ -368,10 +354,10 @@ * When decoding all variants are supported. *

          * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to the nearest multiple of - * 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when - * decoding. + * @param lineLength Each line of encoded data will be at most of the given length (rounded down to the nearest + * multiple of 4). If lineLength <= 0, then the output will not be divided into lines + * (chunks). Ignored when decoding. + * * @since 1.4 */ public Base64(final int lineLength) { @@ -391,14 +377,13 @@ * When decoding all variants are supported. *

          * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to the nearest multiple of - * 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when - * decoding. - * @param lineSeparator - * Each line of encoded data will end with this sequence of bytes. - * @throws IllegalArgumentException - * Thrown when the provided lineSeparator included some base64 characters. + * @param lineLength Each line of encoded data will be at most of the given length (rounded down to the nearest + * multiple of 4). If lineLength <= 0, then the output will not be divided into lines + * (chunks). Ignored when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence of bytes. + * + * @throws IllegalArgumentException Thrown when the provided lineSeparator included some base64 characters. + * * @since 1.4 */ public Base64(final int lineLength, final byte[] lineSeparator) { @@ -418,23 +403,20 @@ * When decoding all variants are supported. *

          * - * @param lineLength - * Each line of encoded data will be at most of the given length (rounded down to the nearest multiple of - * 4). If lineLength <= 0, then the output will not be divided into lines (chunks). Ignored when - * decoding. - * @param lineSeparator - * Each line of encoded data will end with this sequence of bytes. - * @param urlSafe - * Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode - * operations. Decoding seamlessly handles both modes. - * Note: no padding is added when using the URL-safe alphabet. - * @throws IllegalArgumentException - * Thrown when the {@code lineSeparator} contains Base64 characters. + * @param lineLength Each line of encoded data will be at most of the given length (rounded down to the nearest + * multiple of 4). If lineLength <= 0, then the output will not be divided into lines + * (chunks). Ignored when decoding. + * @param lineSeparator Each line of encoded data will end with this sequence of bytes. + * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to + * encode operations. Decoding seamlessly handles both modes. Note: no padding is added + * when using the URL-safe alphabet. + * + * @throws IllegalArgumentException Thrown when the {@code lineSeparator} contains Base64 characters. + * * @since 1.4 */ public Base64(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) { - super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, - lineLength, + super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, lineLength, lineSeparator == null ? 0 : lineSeparator.length); // Needs to be set early to avoid NPE during call to containsAlphabetOrPad() below this.decodeTable = urlSafe ? URL_SAFE_DECODE_TABLE : STANDARD_DECODE_TABLE; @@ -445,7 +427,7 @@ final String sep = StringUtils.newStringUtf8(lineSeparator); throw new IllegalArgumentException(sm.getString("base64.lineSeparator", sep)); } - if (lineLength > 0){ // null line-sep forces no chunking rather than throwing IAE + if (lineLength > 0) { // null line-sep forces no chunking rather than throwing IAE this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; this.lineSeparator = lineSeparator.clone(); } else { @@ -478,14 +460,10 @@ * https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ *

          * - * @param input - * byte[] array of ASCII data to base64 decode. - * @param inPos - * Position to start reading data from. - * @param inAvail - * Amount of bytes available from input for decoding. - * @param context - * the context to be used + * @param input byte[] array of ASCII data to base64 decode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for decoding. + * @param context the context to be used */ @Override void decode(final byte[] input, int inPos, final int inAvail, final Context context) { @@ -506,7 +484,7 @@ if (b >= 0 && b < decodeTable.length) { final int result = decodeTable[b]; if (result >= 0) { - context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK; + context.modulus = (context.modulus + 1) % BYTES_PER_ENCODED_BLOCK; context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result; if (context.modulus == 0) { buffer[context.pos++] = (byte) (context.ibitWorkArea >> 16 & MASK_8BITS); @@ -526,22 +504,22 @@ // We have some spare bits remaining // Output all whole multiples of 8 bits and ignore the rest switch (context.modulus) { -// case 0 : // impossible, as excluded above -// case 1 : // 6 bits - invalid - use default below - case 2 : // 12 bits = 8 + 4 + // case 0 : // impossible, as excluded above + // case 1 : // 6 bits - invalid - use default below + case 2: // 12 bits = 8 + 4 validateCharacter(MASK_4BITS, context); context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS); break; - case 3 : // 18 bits = 8 + 8 + 2 + case 3: // 18 bits = 8 + 8 + 2 validateCharacter(MASK_2BITS, context); context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits buffer[context.pos++] = (byte) (context.ibitWorkArea >> 8 & MASK_8BITS); buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS); break; default: - throw new IllegalStateException(sm.getString( - "base64.impossibleModulus", Integer.valueOf(context.modulus))); + throw new IllegalStateException( + sm.getString("base64.impossibleModulus", Integer.valueOf(context.modulus))); } } } @@ -552,20 +530,18 @@ * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, to flush last * remaining bytes (if not multiple of 3). *

          - *

          Note: no padding is added when encoding using the URL-safe alphabet.

          + *

          + * Note: no padding is added when encoding using the URL-safe alphabet. + *

          *

          * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. * https://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ *

          * - * @param in - * byte[] array of binary data to base64 encode. - * @param inPos - * Position to start reading data from. - * @param inAvail - * Amount of bytes available from input for encoding. - * @param context - * the context to be used + * @param in byte[] array of binary data to base64 encode. + * @param inPos Position to start reading data from. + * @param inAvail Amount of bytes available from input for encoding. + * @param context the context to be used */ @Override void encode(final byte[] in, int inPos, final int inAvail, final Context context) { @@ -582,9 +558,9 @@ final byte[] buffer = ensureBufferSize(encodeSize, context); final int savedPos = context.pos; switch (context.modulus) { // 0-2 - case 0 : // nothing to do here + case 0: // nothing to do here break; - case 1 : // 8 bits = 6 + 2 + case 1: // 8 bits = 6 + 2 // top 6 bits: buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 2 & MASK_6BITS]; // remaining 2: @@ -596,7 +572,7 @@ } break; - case 2 : // 16 bits = 6 + 6 + 4 + case 2: // 16 bits = 6 + 6 + 4 buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 10 & MASK_6BITS]; buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 4 & MASK_6BITS]; buffer[context.pos++] = encodeTable[context.ibitWorkArea << 2 & MASK_6BITS]; @@ -606,8 +582,8 @@ } break; default: - throw new IllegalStateException(sm.getString( - "base64.impossibleModulus", Integer.valueOf(context.modulus))); + throw new IllegalStateException( + sm.getString("base64.impossibleModulus", Integer.valueOf(context.modulus))); } context.currentLinePos += context.pos - savedPos; // keep track of current line position // if currentPos == 0 we are at the start of a line, so don't add CRLF @@ -618,12 +594,12 @@ } else { for (int i = 0; i < inAvail; i++) { final byte[] buffer = ensureBufferSize(encodeSize, context); - context.modulus = (context.modulus+1) % BYTES_PER_UNENCODED_BLOCK; + context.modulus = (context.modulus + 1) % BYTES_PER_UNENCODED_BLOCK; int b = in[inPos++]; if (b < 0) { b += 256; } - context.ibitWorkArea = (context.ibitWorkArea << 8) + b; // BITS_PER_BYTE + context.ibitWorkArea = (context.ibitWorkArea << 8) + b; // BITS_PER_BYTE if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 18 & MASK_6BITS]; buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 12 & MASK_6BITS]; @@ -643,8 +619,8 @@ /** * Returns whether or not the {@code octet} is in the Base64 alphabet. * - * @param octet - * The value to test + * @param octet The value to test + * * @return {@code true} if the value is defined in the Base64 alphabet {@code false} otherwise. */ @Override @@ -656,6 +632,7 @@ * Returns our current encode mode. True if we're URL-SAFE, false otherwise. * * @return true if we're in URL-SAFE mode, false otherwise. + * * @since 1.4 */ public boolean isUrlSafe() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/BaseNCodec.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ /** * Abstract superclass for Base-N encoders and decoders. - * *

          * This class is thread-safe. *

          @@ -36,17 +35,16 @@ protected static final StringManager sm = StringManager.getManager(BaseNCodec.class); /** - * Holds thread context so classes can be thread-safe. - * - * This class is not itself thread-safe; each thread must allocate its own copy. + * Holds thread context so classes can be thread-safe. This class is not itself thread-safe; each thread must + * allocate its own copy. * * @since 1.7 */ static class Context { /** - * Placeholder for the bytes we're dealing with for our based logic. - * Bitwise operations store and extract the encoding or decoding from this variable. + * Placeholder for the bytes we're dealing with for our based logic. Bitwise operations store and extract the + * encoding or decoding from this variable. */ int ibitWorkArea; @@ -72,8 +70,8 @@ boolean eof; /** - * Variable tracks how many characters have been written to the current line. Only used when encoding. We use - * it to make sure each encoded line never goes beyond lineLength (if lineLength > 0). + * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it + * to make sure each encoded line never goes beyond lineLength (if lineLength > 0). */ int currentLinePos; @@ -91,9 +89,10 @@ @SuppressWarnings("boxing") // OK to ignore boxing here @Override public String toString() { - return String.format("%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, " + - "modulus=%s, pos=%s, readPos=%s]", this.getClass().getSimpleName(), HexUtils.toHexString(buffer), - currentLinePos, eof, ibitWorkArea, modulus, pos, readPos); + return String.format( + "%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, " + "modulus=%s, pos=%s, readPos=%s]", + this.getClass().getSimpleName(), HexUtils.toHexString(buffer), currentLinePos, eof, ibitWorkArea, + modulus, pos, readPos); } } @@ -105,8 +104,7 @@ static final int EOF = -1; /** - * MIME chunk size per RFC 2045 section 6.8. - * + * MIME chunk size per RFC 2045 section 6.8. *

          * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any * equal signs. @@ -119,20 +117,18 @@ private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; /** - * Defines the default buffer size - currently {@value} - * - must be large enough for at least one encoded block+separator + * Defines the default buffer size - currently {@value} - must be large enough for at least one encoded + * block+separator */ private static final int DEFAULT_BUFFER_SIZE = 128; /** * The maximum size buffer to allocate. - * - *

          This is set to the same size used in the JDK {@link java.util.ArrayList}:

          - *
          - * Some VMs reserve some header words in an array. - * Attempts to allocate larger arrays may result in - * OutOfMemoryError: Requested array size exceeds VM limit. - *
          + *

          + * This is set to the same size used in the JDK {@link java.util.ArrayList}: + *

          + *
          Some VMs reserve some header words in an array. Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit.
          */ private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; @@ -149,15 +145,16 @@ * * @see RFC 2045 section 2.1 */ - static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; + static final byte[] CHUNK_SEPARATOR = { '\r', '\n' }; /** - * Create a positive capacity at least as large the minimum required capacity. - * If the minimum capacity is negative then this throws an OutOfMemoryError as no array - * can be allocated. + * Create a positive capacity at least as large the minimum required capacity. If the minimum capacity is negative + * then this throws an OutOfMemoryError as no array can be allocated. * * @param minCapacity the minimum capacity + * * @return the capacity + * * @throws OutOfMemoryError if the {@code minCapacity} is negative */ private static int createPositiveCapacity(final int minCapacity) { @@ -178,9 +175,12 @@ /** * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}. - * @param context the context to be used + * + * @param context the context to be used * @param minCapacity the minimum required capacity + * * @return the resized byte[] buffer + * * @throws OutOfMemoryError if the {@code minCapacity} is negative */ private static byte[] resizeBuffer(final Context context, final int minCapacity) { @@ -209,8 +209,7 @@ private final int encodedBlockSize; /** - * Chunksize for encoding. Not used when decoding. - * A value of zero or less implies no chunking of the encoded data. + * Chunksize for encoding. Not used when decoding. A value of zero or less implies no chunking of the encoded data. * Rounded down to the nearest multiple of encodedBlockSize. */ protected final int lineLength; @@ -221,31 +220,31 @@ private final int chunkSeparatorLength; /** - * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size. - * If {@code chunkSeparatorLength} is zero, then chunking is disabled. + * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size. If + * {@code chunkSeparatorLength} is zero, then chunking is disabled. * - * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) - * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) - * @param lineLength if > 0, use chunking with a length {@code lineLength} + * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) + * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) + * @param lineLength if > 0, use chunking with a length {@code lineLength} * @param chunkSeparatorLength the chunk separator length, if relevant */ - protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, - final int lineLength, final int chunkSeparatorLength) { + protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, final int lineLength, + final int chunkSeparatorLength) { this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, PAD_DEFAULT); } /** - * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size. - * If {@code chunkSeparatorLength} is zero, then chunking is disabled. + * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size. If + * {@code chunkSeparatorLength} is zero, then chunking is disabled. * - * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) - * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) - * @param lineLength if > 0, use chunking with a length {@code lineLength} + * @param unencodedBlockSize the size of an unencoded block (e.g. Base64 = 3) + * @param encodedBlockSize the size of an encoded block (e.g. Base64 = 4) + * @param lineLength if > 0, use chunking with a length {@code lineLength} * @param chunkSeparatorLength the chunk separator length, if relevant - * @param pad byte used as padding byte. + * @param pad byte used as padding byte. */ - protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, - final int lineLength, final int chunkSeparatorLength, final byte pad) { + protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize, final int lineLength, + final int chunkSeparatorLength, final byte pad) { this.unencodedBlockSize = unencodedBlockSize; this.encodedBlockSize = encodedBlockSize; final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0; @@ -258,19 +257,19 @@ * Returns the amount of buffered data available for reading. * * @param context the context to be used + * * @return The amount of buffered data available for reading. */ - int available(final Context context) { // package protected for access from I/O streams + int available(final Context context) { // package protected for access from I/O streams return hasData(context) ? context.pos - context.readPos : 0; } /** - * Tests a given byte array to see if it contains any characters within the alphabet or PAD. + * Tests a given byte array to see if it contains any characters within the alphabet or PAD. Intended for use in + * checking line-ending arrays * - * Intended for use in checking line-ending arrays + * @param arrayOctet byte array to test * - * @param arrayOctet - * byte array to test * @return {@code true} if any byte is a valid character in the alphabet or PAD; {@code false} otherwise */ protected boolean containsAlphabetOrPad(final byte[] arrayOctet) { @@ -288,8 +287,8 @@ /** * Decodes a byte[] containing characters in the Base-N alphabet. * - * @param pArray - * A byte array containing Base-N character data + * @param pArray A byte array containing Base-N character data + * * @return a byte array containing binary data */ public byte[] decode(final byte[] pArray) { @@ -314,8 +313,8 @@ /** * Decodes a String containing characters in the Base-N alphabet. * - * @param pArray - * A String containing Base-N character data + * @param pArray A String containing Base-N character data + * * @return a byte array containing binary data */ public byte[] decode(final String pArray) { @@ -325,8 +324,8 @@ /** * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet. * - * @param pArray - * a byte array containing binary data + * @param pArray a byte array containing binary data + * * @return A byte array containing only the base N alphabetic character data */ public byte[] encode(final byte[] pArray) { @@ -337,16 +336,14 @@ } /** - * Encodes a byte[] containing binary data, into a byte[] containing - * characters in the alphabet. + * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet. + * + * @param pArray a byte array containing binary data + * @param offset initial offset of the subarray. + * @param length length of the subarray. * - * @param pArray - * a byte array containing binary data - * @param offset - * initial offset of the subarray. - * @param length - * length of the subarray. * @return A byte array containing only the base N alphabetic character data + * * @since 1.11 */ public byte[] encode(final byte[] pArray, final int offset, final int length) { @@ -365,32 +362,35 @@ abstract void encode(byte[] pArray, int i, int length, Context context); /** - * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet. - * Uses UTF8 encoding. + * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet. Uses + * UTF8 encoding. * * @param pArray a byte array containing binary data + * * @return String containing only character data in the appropriate alphabet. + * * @since 1.5 - */ - public String encodeAsString(final byte[] pArray){ + */ + public String encodeAsString(final byte[] pArray) { return StringUtils.newStringUtf8(encode(pArray)); } /** * Ensure that the buffer has room for {@code size} bytes * - * @param size minimum spare space required + * @param size minimum spare space required * @param context the context to be used + * * @return the buffer */ - protected byte[] ensureBufferSize(final int size, final Context context){ + protected byte[] ensureBufferSize(final int size, final Context context) { if (context.buffer == null) { context.buffer = new byte[Math.max(size, getDefaultBufferSize())]; context.pos = 0; context.readPos = 0; // Overflow-conscious: - // x + y > z == x + y - z > 0 + // x + y > z == x + y - z > 0 } else if (context.pos + size - context.buffer.length > 0) { return resizeBuffer(context, context.pos + size); } @@ -411,16 +411,16 @@ * * @param pArray byte[] array which will later be encoded * - * @return amount of space needed to encode the supplied array. - * Returns a long since a max-len array will require > Integer.MAX_VALUE + * @return amount of space needed to encode the supplied array. Returns a long since a max-len array will require + * > Integer.MAX_VALUE */ public long getEncodedLength(final byte[] pArray) { // Calculate non-chunked size - rounded up to allow for padding // cast to long is needed to avoid possibility of overflow - long len = (pArray.length + unencodedBlockSize-1) / unencodedBlockSize * (long) encodedBlockSize; + long len = (pArray.length + unencodedBlockSize - 1) / unencodedBlockSize * (long) encodedBlockSize; if (lineLength > 0) { // We're using chunking // Round up to nearest multiple - len += (len + lineLength-1) / lineLength * chunkSeparatorLength; + len += (len + lineLength - 1) / lineLength * chunkSeparatorLength; } return len; } @@ -429,15 +429,15 @@ * Returns true if this object has buffered data for reading. * * @param context the context to be used + * * @return true if there is data still available for reading. */ - boolean hasData(final Context context) { // package protected for access from I/O streams + boolean hasData(final Context context) { // package protected for access from I/O streams return context.pos > context.readPos; } /** - * Returns whether or not the {@code octet} is in the current alphabet. - * Does not allow whitespace or pad. + * Returns whether or not the {@code octet} is in the current alphabet. Does not allow whitespace or pad. * * @param value The value to test * @@ -452,14 +452,11 @@ * Package private for access from I/O streams. *

          * - * @param b - * byte[] array to extract the buffered data into. - * @param bPos - * position in byte[] array to start extraction at. - * @param bAvail - * amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). - * @param context - * the context to be used + * @param b byte[] array to extract the buffered data into. + * @param bPos position in byte[] array to start extraction at. + * @param bAvail amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). + * @param context the context to be used + * * @return The number of bytes successfully extracted into the provided byte[] array. */ int readResults(final byte[] b, final int bPos, final int bAvail, final Context context) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/codec/binary/StringUtils.java tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/StringUtils.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/codec/binary/StringUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/StringUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,11 +23,13 @@ * Converts String to and from bytes using the encodings required by the Java specification. These encodings are * specified in * Standard charsets. - * - *

          This class is immutable and thread-safe.

          + *

          + * This class is immutable and thread-safe. + *

          * * @see Charset * @see StandardCharsets + * * @since 1.4 * * @deprecated Unused. This class will be removed in Tomcat 11 onwards. @@ -38,10 +40,9 @@ /** * Calls {@link String#getBytes(Charset)} * - * @param string - * The string to encode (if null, return null). - * @param charset - * The {@link Charset} to encode the {@code String} + * @param string The string to encode (if null, return null). + * @param charset The {@link Charset} to encode the {@code String} + * * @return the encoded bytes */ private static byte[] getBytes(final String string, final Charset charset) { @@ -52,13 +53,15 @@ * Encodes the given string into a sequence of bytes using the UTF-8 charset, storing the result into a new byte * array. * - * @param string - * the String to encode, may be {@code null} + * @param string the String to encode, may be {@code null} + * * @return encoded bytes, or {@code null} if the input string was {@code null} - * @throws NullPointerException - * Thrown if {@link StandardCharsets#UTF_8} is not initialized, which should never happen - * since it is required by the Java platform specification. + * + * @throws NullPointerException Thrown if {@link StandardCharsets#UTF_8} is not initialized, which should never + * happen since it is required by the Java platform specification. + * * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException + * * @see Charset */ public static byte[] getBytesUtf8(final String string) { @@ -68,14 +71,13 @@ /** * Constructs a new {@code String} by decoding the specified array of bytes using the given charset. * - * @param bytes - * The bytes to be decoded into characters - * @param charset - * The {@link Charset} to encode the {@code String}; not {@code null} - * @return A new {@code String} decoded from the specified array of bytes using the given charset, - * or {@code null} if the input byte array was {@code null}. - * @throws NullPointerException - * Thrown if charset is {@code null} + * @param bytes The bytes to be decoded into characters + * @param charset The {@link Charset} to encode the {@code String}; not {@code null} + * + * @return A new {@code String} decoded from the specified array of bytes using the given charset, or {@code null} + * if the input byte array was {@code null}. + * + * @throws NullPointerException Thrown if charset is {@code null} */ private static String newString(final byte[] bytes, final Charset charset) { return bytes == null ? null : new String(bytes, charset); @@ -84,13 +86,14 @@ /** * Constructs a new {@code String} by decoding the specified array of bytes using the US-ASCII charset. * - * @param bytes - * The bytes to be decoded into characters - * @return A new {@code String} decoded from the specified array of bytes using the US-ASCII charset, - * or {@code null} if the input byte array was {@code null}. - * @throws NullPointerException - * Thrown if {@link StandardCharsets#US_ASCII} is not initialized, which should never happen - * since it is required by the Java platform specification. + * @param bytes The bytes to be decoded into characters + * + * @return A new {@code String} decoded from the specified array of bytes using the US-ASCII charset, or + * {@code null} if the input byte array was {@code null}. + * + * @throws NullPointerException Thrown if {@link StandardCharsets#US_ASCII} is not initialized, which should never + * happen since it is required by the Java platform specification. + * * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException */ public static String newStringUsAscii(final byte[] bytes) { @@ -100,13 +103,14 @@ /** * Constructs a new {@code String} by decoding the specified array of bytes using the UTF-8 charset. * - * @param bytes - * The bytes to be decoded into characters - * @return A new {@code String} decoded from the specified array of bytes using the UTF-8 charset, - * or {@code null} if the input byte array was {@code null}. - * @throws NullPointerException - * Thrown if {@link StandardCharsets#UTF_8} is not initialized, which should never happen since it is - * required by the Java platform specification. + * @param bytes The bytes to be decoded into characters + * + * @return A new {@code String} decoded from the specified array of bytes using the UTF-8 charset, or {@code null} + * if the input byte array was {@code null}. + * + * @throws NullPointerException Thrown if {@link StandardCharsets#UTF_8} is not initialized, which should never + * happen since it is required by the Java platform specification. + * * @since As of 1.7, throws {@link NullPointerException} instead of UnsupportedEncodingException */ public static String newStringUtf8(final byte[] bytes) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/codec/binary/package-info.java tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/package-info.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/codec/binary/package-info.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/codec/binary/package-info.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,8 +16,6 @@ */ /** - * Base64 String encoding and decoding. - * - * Unused. This package will be removed in Tomcat 11 onwards. + * Base64 String encoding and decoding. Unused. This package will be removed in Tomcat 11 onwards. */ package org.apache.tomcat.util.codec.binary; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/collections/CaseInsensitiveKeyMap.java tomcat10-10.1.52/java/org/apache/tomcat/util/collections/CaseInsensitiveKeyMap.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/collections/CaseInsensitiveKeyMap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/collections/CaseInsensitiveKeyMap.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,11 +27,9 @@ import org.apache.tomcat.util.res.StringManager; /** - * A Map implementation that uses case-insensitive (using {@link - * Locale#ENGLISH}) strings as keys. + * A Map implementation that uses case-insensitive (using {@link Locale#ENGLISH}) strings as keys. *

          - * Keys must be instances of {@link String}. Note that this means that - * null keys are not permitted. + * Keys must be instances of {@link String}. Note that this means that null keys are not permitted. *

          * This implementation is not thread-safe. * @@ -39,8 +37,7 @@ */ public class CaseInsensitiveKeyMap extends AbstractMap { - private static final StringManager sm = - StringManager.getManager(CaseInsensitiveKeyMap.class); + private static final StringManager sm = StringManager.getManager(CaseInsensitiveKeyMap.class); private final Map map = new HashMap<>(); @@ -64,12 +61,11 @@ /** * {@inheritDoc} *

          - * Use this method with caution. If the input Map contains duplicate - * keys when the keys are compared in a case insensitive manner then some - * values will be lost when inserting via this method. + * Use this method with caution. If the input Map contains duplicate keys when the keys are compared in a + * case-insensitive manner then some values will be lost when inserting via this method. */ @Override - public void putAll(Map m) { + public void putAll(Map m) { super.putAll(m); } @@ -87,7 +83,7 @@ @Override - public Set> entrySet() { + public Set> entrySet() { return new EntrySet<>(map.entrySet()); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/collections/ConcurrentCache.java tomcat10-10.1.52/java/org/apache/tomcat/util/collections/ConcurrentCache.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/collections/ConcurrentCache.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/collections/ConcurrentCache.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,7 +20,7 @@ import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; -public final class ConcurrentCache { +public final class ConcurrentCache { private final int size; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/collections/ConcurrentLruCache.java tomcat10-10.1.52/java/org/apache/tomcat/util/collections/ConcurrentLruCache.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/collections/ConcurrentLruCache.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/collections/ConcurrentLruCache.java 2026-01-23 19:33:36.000000000 +0000 @@ -76,7 +76,7 @@ if (oldMap != null) { map.putAll(oldMap); } - } else { + } else { map = null; } } @@ -94,10 +94,10 @@ } - private static class LimitedLinkedHashMap extends LinkedHashMap { + private static class LimitedLinkedHashMap extends LinkedHashMap { private static final long serialVersionUID = 1L; - private volatile int limit; + private final int limit; LimitedLinkedHashMap(int limit) { super(16, 0.75F, true); @@ -106,10 +106,7 @@ @Override protected boolean removeEldestEntry(Map.Entry eldest) { - if (size() > limit) { - return true; - } - return false; + return size() > limit; } private int getLimit() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java tomcat10-10.1.52/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/collections/ManagedConcurrentWeakHashMap.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,23 +30,20 @@ import java.util.concurrent.ConcurrentMap; /** - * Concurrent hash map that holds its keys via weak references. Unlike - * WeakHashMap this class does not handle dead keys during common - * access operations, but expects you to call its {@link #maintain()} method + * Concurrent hash map that holds its keys via weak references. Unlike WeakHashMap this class does not + * handle dead keys during common access operations, but expects you to call its {@link #maintain()} method * periodically. Both keys and values are expected to be not-null. * * @param The type of keys used with the Map instance * @param The type of values used with the Map instance */ -public class ManagedConcurrentWeakHashMap extends AbstractMap implements - ConcurrentMap { +public class ManagedConcurrentWeakHashMap extends AbstractMap implements ConcurrentMap { - private final ConcurrentMap map = new ConcurrentHashMap<>(); + private final ConcurrentMap map = new ConcurrentHashMap<>(); private final ReferenceQueue queue = new ReferenceQueue<>(); /** - * Method, that has to be invoked periodically to clean dead keys from the - * map. + * Method, that has to be invoked periodically to clean dead keys from the map. */ public void maintain() { Key key; @@ -108,16 +105,14 @@ } /** - * Creates Key instance to be used to store values in the map. It is - * registered with the ReferenceQueue. + * Creates Key instance to be used to store values in the map. It is registered with the ReferenceQueue. */ private Key createStoreKey(Object key) { return new Key(key, queue); } /** - * Creates Key instance to be used only to lookup values in the map. It is - * not registered with the ReferenceQueue. + * Creates Key instance to be used only to lookup values in the map. It is not registered with the ReferenceQueue. */ private Key createLookupKey(Object key) { return new Key(key, null); @@ -213,7 +208,7 @@ } @Override - public Set> entrySet() { + public Set> entrySet() { return new AbstractSet<>() { @Override public boolean isEmpty() { @@ -226,10 +221,9 @@ } @Override - public Iterator> iterator() { + public Iterator> iterator() { return new Iterator<>() { - private final Iterator> it = map - .entrySet().iterator(); + private final Iterator> it = map.entrySet().iterator(); @Override public boolean hasNext() { @@ -237,9 +231,9 @@ } @Override - public Map.Entry next() { + public Map.Entry next() { return new Map.Entry<>() { - private final Map.Entry en = it.next(); + private final Map.Entry en = it.next(); @SuppressWarnings("unchecked") @Override diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/collections/SynchronizedQueue.java tomcat10-10.1.52/java/org/apache/tomcat/util/collections/SynchronizedQueue.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/collections/SynchronizedQueue.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/collections/SynchronizedQueue.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,11 +17,9 @@ package org.apache.tomcat.util.collections; /** - * This is intended as a (mostly) GC-free alternative to - * {@link java.util.concurrent.ConcurrentLinkedQueue} when the requirement is to - * create an unbounded queue with no requirement to shrink the queue. The aim is - * to provide the bare minimum of required functionality as quickly as possible - * with minimum garbage. + * This is intended as a (mostly) GC-free alternative to {@link java.util.concurrent.ConcurrentLinkedQueue} when the + * requirement is to create an unbounded queue with no requirement to shrink the queue. The aim is to provide the bare + * minimum of required functionality as quickly as possible with minimum garbage. * * @param The type of object managed by this queue */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/collections/SynchronizedStack.java tomcat10-10.1.52/java/org/apache/tomcat/util/collections/SynchronizedStack.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/collections/SynchronizedStack.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/collections/SynchronizedStack.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,11 +17,9 @@ package org.apache.tomcat.util.collections; /** - * This is intended as a (mostly) GC-free alternative to - * {@link java.util.Stack} when the requirement is to create a pool of re-usable - * objects with no requirement to shrink the pool. The aim is to provide the - * bare minimum of required functionality as quickly as possible with minimum - * garbage. + * This is intended as a (mostly) GC-free alternative to {@link java.util.Stack} when the requirement is to create a + * pool of re-usable objects with no requirement to shrink the pool. The aim is to provide the bare minimum of required + * functionality as quickly as possible with minimum garbage. * * @param The type of object managed by this stack */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre12Compat.java tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre12Compat.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre12Compat.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre12Compat.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,154 @@ +/* + * 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.compat; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.VarHandle; +import java.lang.management.ManagementFactory; +import java.lang.reflect.Field; +import java.lang.reflect.InaccessibleObjectException; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Optional; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.res.StringManager; + +public class Jre12Compat extends JreCompat { + + private static final Log log = LogFactory.getLog(Jre12Compat.class); + private static final StringManager sm = StringManager.getManager(Jre12Compat.class); + + private static final boolean supported; + + static { + // Don't need any Java 12 specific classes (yet) so just test for one of + // the new ones for now. + Class c1 = null; + try { + c1 = Class.forName("java.text.CompactNumberFormat"); + } catch (ClassNotFoundException cnfe) { + // Must be pre-Java 12 + log.debug(sm.getString("jre12Compat.javaPre12"), cnfe); + } + + supported = (c1 != null); + } + + static boolean isSupported() { + return supported; + } + + + /* + * The behaviour of the canonical file name cache varies by Java version. + * + * The cache was removed in Java 21 so these methods and the associated code can be removed once the minimum Java + * version is 21. + * + * For 12 <= Java <= 20, the cache was present but disabled by default. + * + * For Java < 12, the cache was enabled by default. Tomcat assumes the cache is enabled unless proven otherwise. + * + * Tomcat 10.1 has a minimum Java version of 11. + * + * The static field in java.io.FileSystem will be set before any application code gets a chance to run. Therefore, + * the value of that field can be determined by looking at the command line arguments. This enables us to determine + * the status without having using reflection. + * + * This is Java 12 and later. + */ + @Override + public boolean isCanonCachesDisabled() { + if (canonCachesDisabled != null) { + return canonCachesDisabled.booleanValue(); + } + synchronized (canonCachesDisabledLock) { + if (canonCachesDisabled != null) { + return canonCachesDisabled.booleanValue(); + } + + List args = ManagementFactory.getRuntimeMXBean().getInputArguments(); + for (String arg : args) { + // If any command line argument attempts to enable the cache, assume it is enabled. + if (arg.startsWith(USE_CANON_CACHES_CMD_ARG)) { + String value = arg.substring(USE_CANON_CACHES_CMD_ARG.length()); + boolean cacheEnabled = Boolean.valueOf(value).booleanValue(); + if (cacheEnabled) { + canonCachesDisabled = Boolean.FALSE; + return false; + } + } + } + canonCachesDisabled = Boolean.TRUE; + return true; + } + } + + + /* + * Java 12 increased security around reflection so additional code is required to disable the cache since a final + * field needs to be changed. + */ + @Override + protected void ensureUseCanonCachesFieldIsPopulated() { + if (useCanonCachesField != null) { + return; + } + synchronized (useCanonCachesFieldLock) { + if (useCanonCachesField != null) { + return; + } + + Field f = null; + try { + Class clazz = Class.forName("java.io.FileSystem"); + f = clazz.getDeclaredField("useCanonCaches"); + // Need this because the 'useCanonCaches' field is private + f.setAccessible(true); + + /* + * Need this in Java 12 to 17 (and it only works up to Java 17) because the 'useCanonCaches' field is + * final. + * + * This will fail in Java 18 to 20 but since those versions are no longer supported it is acceptable for + * the attempt to set the 'useCanonCaches' field to fail. Users that really want to use Java 18 to 20 + * will have to ensure that they do not explicitly enable the canonical file name cache. + */ + Lookup lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); + VarHandle modifiers = lookup.findVarHandle(Field.class, "modifiers", int.class); + modifiers.set(f, f.getModifiers() & ~Modifier.FINAL); + } catch (UnsupportedOperationException e) { + // Make sure field is not set. + f = null; + log.warn(sm.getString("jreCompat.useCanonCaches.java18"), e); + } catch (InaccessibleObjectException | ReflectiveOperationException | IllegalArgumentException e) { + // Make sure field is not set. + f = null; + log.warn(sm.getString("jreCompat.useCanonCaches.init"), e); + } + + if (f == null) { + useCanonCachesField = Optional.empty(); + } else { + useCanonCachesField = Optional.of(f); + } + } + } +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre16Compat.java tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre16Compat.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre16Compat.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre16Compat.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,7 +28,7 @@ import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.res.StringManager; -class Jre16Compat extends JreCompat { +class Jre16Compat extends Jre12Compat { private static final Log log = LogFactory.getLog(Jre16Compat.class); private static final StringManager sm = StringManager.getManager(Jre16Compat.class); @@ -70,8 +70,7 @@ public SocketAddress getUnixDomainSocketAddress(String path) { try { return (SocketAddress) unixDomainSocketAddressOfMethod.invoke(null, path); - } catch (IllegalAccessException | IllegalArgumentException - | InvocationTargetException e) { + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new UnsupportedOperationException(e); } } @@ -80,8 +79,8 @@ @Override public ServerSocketChannel openUnixDomainServerSocketChannel() { try { - return (ServerSocketChannel) openServerSocketChannelFamilyMethod.invoke - (null, StandardProtocolFamily.valueOf("UNIX")); + return (ServerSocketChannel) openServerSocketChannelFamilyMethod.invoke(null, + StandardProtocolFamily.valueOf("UNIX")); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new UnsupportedOperationException(e); } @@ -91,8 +90,7 @@ @Override public SocketChannel openUnixDomainSocketChannel() { try { - return (SocketChannel) openSocketChannelFamilyMethod.invoke - (null, StandardProtocolFamily.valueOf("UNIX")); + return (SocketChannel) openSocketChannelFamilyMethod.invoke(null, StandardProtocolFamily.valueOf("UNIX")); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new UnsupportedOperationException(e); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre19Compat.java tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre19Compat.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre19Compat.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre19Compat.java 2026-01-23 19:33:36.000000000 +0000 @@ -68,11 +68,9 @@ return null; } - if (task!= null && task.getClass().getCanonicalName() != null && - (task.getClass().getCanonicalName().equals( - "org.apache.tomcat.util.threads.ThreadPoolExecutor.Worker") || - task.getClass().getCanonicalName().equals( - "java.util.concurrent.ThreadPoolExecutor.Worker"))) { + if (task != null && task.getClass().getCanonicalName() != null && (task.getClass().getCanonicalName() + .equals("org.apache.tomcat.util.threads.ThreadPoolExecutor.Worker") || + task.getClass().getCanonicalName().equals("java.util.concurrent.ThreadPoolExecutor.Worker"))) { Field executorField = task.getClass().getDeclaredField("this$0"); executorField.setAccessible(true); result = executorField.get(task); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre20Compat.java tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre20Compat.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre20Compat.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre20Compat.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * 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.compat; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.res.StringManager; + +public class Jre20Compat extends Jre19Compat { + + private static final Log log = LogFactory.getLog(Jre20Compat.class); + private static final StringManager sm = StringManager.getManager(Jre20Compat.class); + + private static final boolean supported; + private static final Method setNamedGroupsMethod; + + + static { + Class c1 = null; + Method m1 = null; + + try { + c1 = Class.forName("javax.net.ssl.SSLParameters"); + m1 = c1.getMethod("setNamedGroups", String[].class); + } catch (NoSuchMethodException e) { + // Must be pre-Java 20 + log.debug(sm.getString("jre20Compat.javaPre20"), e); + } catch (ReflectiveOperationException e) { + // Should never happen + log.error(sm.getString("jre20Compat.unexpected"), e); + } + supported = (m1 != null); + setNamedGroupsMethod = m1; + } + + static boolean isSupported() { + return supported; + } + + @Override + public void setNamedGroupsMethod(Object sslParameters, String[] names) { + try { + setNamedGroupsMethod.invoke(sslParameters, (Object) names); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new UnsupportedOperationException(e); + } + } + +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre21Compat.java tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre21Compat.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre21Compat.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre21Compat.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,11 +27,12 @@ import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.res.StringManager; -public class Jre21Compat extends Jre19Compat { +public class Jre21Compat extends Jre20Compat { private static final Log log = LogFactory.getLog(Jre21Compat.class); private static final StringManager sm = StringManager.getManager(Jre21Compat.class); + private static final boolean supported; private static final Method nameMethod; private static final Method startMethod; private static final Method ofVirtualMethod; @@ -46,9 +47,14 @@ Method m4 = null; try { - c1 = Class.forName("java.lang.Thread$Builder"); - m1 = c1.getMethod("name", String.class, long.class); - m2 = c1.getMethod("start", Runnable.class); + // Note: Virtual threads is the main new feature in Java 21, but it was previously + // present as a preview. As a result, it is more accurate to test for another + // new class + c1 = Class.forName("java.util.SequencedCollection"); + + Class c2 = Class.forName("java.lang.Thread$Builder"); + m1 = c2.getMethod("name", String.class, long.class); + m2 = c2.getMethod("start", Runnable.class); m3 = Thread.class.getMethod("ofVirtual", (Class[]) null); m4 = Subject.class.getMethod("callAs", Subject.class, Callable.class); } catch (ClassNotFoundException e) { @@ -58,6 +64,7 @@ // Should never happen log.error(sm.getString("jre21Compat.unexpected"), e); } + supported = (c1 != null); nameMethod = m1; startMethod = m2; ofVirtualMethod = m3; @@ -65,7 +72,7 @@ } static boolean isSupported() { - return ofVirtualMethod != null; + return supported; } @Override @@ -104,4 +111,18 @@ throw new CompletionException(e); } } + + + @Override + public boolean isCanonCachesDisabled() { + // The cache has been removed in Java 21 + return true; + } + + + @Override + public boolean disableCanonCaches() { + // The cache has been removed in Java 21 + return true; + } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre22Compat.java tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre22Compat.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/Jre22Compat.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/Jre22Compat.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,7 +37,9 @@ c1 = Class.forName("java.text.ListFormat"); } catch (ClassNotFoundException e) { // Must be pre-Java 22 - log.debug(sm.getString("jre22Compat.javaPre22"), e); + if (log.isDebugEnabled()) { + log.debug(sm.getString("jre22Compat.javaPre22"), e); + } } supported = (c1 != null); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/JreCompat.java tomcat10-10.1.52/java/org/apache/tomcat/util/compat/JreCompat.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/JreCompat.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/JreCompat.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,32 +16,47 @@ */ package org.apache.tomcat.util.compat; +import java.lang.management.ManagementFactory; import java.lang.reflect.Field; +import java.lang.reflect.InaccessibleObjectException; import java.net.SocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.security.PrivilegedExceptionAction; +import java.util.List; +import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.CompletionException; import javax.security.auth.Subject; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.res.StringManager; /** - * This is the base implementation class for JRE compatibility and provides an - * implementation based on Java 11. Sub-classes may extend this class and provide - * alternative implementations for later JRE versions + * This is the base implementation class for JRE compatibility and provides an implementation based on Java 11. + * Subclasses may extend this class and provide alternative implementations for later JRE versions */ public class JreCompat { + private static final Log log = LogFactory.getLog(JreCompat.class); + private static final StringManager sm = StringManager.getManager(JreCompat.class); + private static final JreCompat instance; private static final boolean graalAvailable; + private static final boolean jre12Available; private static final boolean jre16Available; private static final boolean jre19Available; + private static final boolean jre20Available; private static final boolean jre21Available; private static final boolean jre22Available; - private static final StringManager sm = StringManager.getManager(JreCompat.class); + + protected static final String USE_CANON_CACHES_CMD_ARG = "-Dsun.io.useCanonCaches="; + protected static volatile Boolean canonCachesDisabled; + protected static final Object canonCachesDisabledLock = new Object(); + protected static volatile Optional useCanonCachesField; + protected static final Object useCanonCachesFieldLock = new Object(); static { boolean result = false; @@ -61,32 +76,58 @@ instance = new Jre22Compat(); jre22Available = true; jre21Available = true; + jre20Available = true; jre19Available = true; jre16Available = true; + jre12Available = true; } else if (Jre21Compat.isSupported()) { instance = new Jre21Compat(); jre22Available = false; jre21Available = true; + jre20Available = true; jre19Available = true; jre16Available = true; + jre12Available = true; + } else if (Jre20Compat.isSupported()) { + instance = new Jre20Compat(); + jre22Available = false; + jre21Available = false; + jre20Available = true; + jre19Available = true; + jre16Available = true; + jre12Available = true; } else if (Jre19Compat.isSupported()) { instance = new Jre19Compat(); jre22Available = false; jre21Available = false; + jre20Available = false; jre19Available = true; jre16Available = true; + jre12Available = true; } else if (Jre16Compat.isSupported()) { instance = new Jre16Compat(); jre22Available = false; jre21Available = false; + jre20Available = false; jre19Available = false; jre16Available = true; + jre12Available = true; + } else if (Jre12Compat.isSupported()) { + instance = new Jre12Compat(); + jre22Available = false; + jre21Available = false; + jre20Available = false; + jre19Available = false; + jre16Available = false; + jre12Available = true; } else { instance = new JreCompat(); jre22Available = false; jre21Available = false; + jre20Available = false; jre19Available = false; jre16Available = false; + jre12Available = false; } } @@ -101,6 +142,11 @@ } + public static boolean isJre12Available() { + return jre12Available; + } + + public static boolean isJre16Available() { return jre16Available; } @@ -111,6 +157,11 @@ } + public static boolean isJre20Available() { + return jre20Available; + } + + public static boolean isJre21Available() { return jre21Available; } @@ -125,7 +176,9 @@ /** * Return Unix domain socket address for given path. + * * @param path The path + * * @return the socket address */ public SocketAddress getUnixDomainSocketAddress(String path) { @@ -135,6 +188,7 @@ /** * Create server socket channel using the Unix domain socket ProtocolFamily. + * * @return the server socket channel */ public ServerSocketChannel openUnixDomainServerSocketChannel() { @@ -144,6 +198,7 @@ /** * Create socket channel using the Unix domain socket ProtocolFamily. + * * @return the socket channel */ public SocketChannel openUnixDomainSocketChannel() { @@ -258,4 +313,123 @@ throw new CompletionException(e); } } + + + /* + * The behaviour of the canonical file name cache varies by Java version. + * + * The cache was removed in Java 21 so these methods and the associated code can be removed once the minimum Java + * version is 21. + * + * For 12 <= Java <= 20, the cache was present but disabled by default. + * + * For Java < 12, the cache was enabled by default. Tomcat assumes the cache is enabled unless proven otherwise. + * + * Tomcat 10.1 has a minimum Java version of 11. + * + * The static field in java.io.FileSystem will be set before any application code gets a chance to run. Therefore, + * the value of that field can be determined by looking at the command line arguments. This enables us to determine + * the status without having using reflection. + * + * This is Java 11. + */ + public boolean isCanonCachesDisabled() { + if (canonCachesDisabled != null) { + return canonCachesDisabled.booleanValue(); + } + synchronized (canonCachesDisabledLock) { + if (canonCachesDisabled != null) { + return canonCachesDisabled.booleanValue(); + } + + boolean cacheEnabled = true; + List args = ManagementFactory.getRuntimeMXBean().getInputArguments(); + for (String arg : args) { + // To consider the cache disabled + // - there must be at least one command line argument that disables it + // - there must be no command line arguments that enable it + if (arg.startsWith(USE_CANON_CACHES_CMD_ARG)) { + String valueAsString = arg.substring(USE_CANON_CACHES_CMD_ARG.length()); + boolean valueAsBoolean = Boolean.valueOf(valueAsString).booleanValue(); + if (valueAsBoolean) { + canonCachesDisabled = Boolean.FALSE; + return false; + } else { + cacheEnabled = false; + } + } + } + if (cacheEnabled) { + canonCachesDisabled = Boolean.FALSE; + } else { + canonCachesDisabled = Boolean.TRUE; + } + return canonCachesDisabled.booleanValue(); + } + } + + + /** + * Disable the global canonical file cache. + * + * @return {@code true} if the global canonical file cache was already disabled prior to this call or was disabled + * as a result of this call, otherwise {@code false} + */ + public boolean disableCanonCaches() { + ensureUseCanonCachesFieldIsPopulated(); + if (useCanonCachesField.isEmpty()) { + log.warn(sm.getString("jreCompat.useCanonCaches.none")); + return false; + } + try { + useCanonCachesField.get().set(null, Boolean.FALSE); + } catch (ReflectiveOperationException | IllegalArgumentException e) { + log.warn(sm.getString("jreCompat.useCanonCaches.failed"), e); + return false; + } + synchronized (canonCachesDisabledLock) { + canonCachesDisabled = Boolean.TRUE; + } + return true; + } + + + protected void ensureUseCanonCachesFieldIsPopulated() { + if (useCanonCachesField != null) { + return; + } + synchronized (useCanonCachesFieldLock) { + if (useCanonCachesField != null) { + return; + } + + Field f = null; + try { + Class clazz = Class.forName("java.io.FileSystem"); + f = clazz.getDeclaredField("useCanonCaches"); + // Need this because the 'useCanonCaches' field is private final + f.setAccessible(true); + } catch (InaccessibleObjectException | ReflectiveOperationException | IllegalArgumentException e) { + // Make sure field is not set. + f = null; + log.warn(sm.getString("jreCompat.useCanonCaches.init"), e); + } + + if (f == null) { + useCanonCachesField = Optional.empty(); + } else { + useCanonCachesField = Optional.of(f); + } + } + } + + /** + * TLS groups configuration from JSSE API in Java 20. + * @param sslParameters the parameters object + * @param names the names of the groups to enable + */ + public void setNamedGroupsMethod(Object sslParameters, String[] names) { + throw new UnsupportedOperationException(sm.getString("jreCompat.noNamedGroups")); + } + } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/JrePlatform.java tomcat10-10.1.52/java/org/apache/tomcat/util/compat/JrePlatform.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/JrePlatform.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/JrePlatform.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,12 +26,11 @@ static { /* - * There are a few places where a) the behaviour of the Java API depends - * on the underlying platform and b) those behavioural differences have - * an impact on Tomcat. + * There are a few places where a) the behaviour of the Java API depends on the underlying platform and b) those + * behavioural differences have an impact on Tomcat. * - * Tomcat therefore needs to be able to determine the platform it is - * running on to account for those differences. + * Tomcat therefore needs to be able to determine the platform it is running on to account for those + * differences. * * In an ideal world this code would not exist. */ @@ -41,8 +40,8 @@ if (System.getSecurityManager() == null) { osName = System.getProperty(OS_NAME_PROPERTY); } else { - osName = AccessController.doPrivileged( - (PrivilegedAction) () -> System.getProperty(OS_NAME_PROPERTY)); + osName = AccessController + .doPrivileged((PrivilegedAction) () -> System.getProperty(OS_NAME_PROPERTY)); } IS_MAC_OS = osName.toLowerCase(Locale.ENGLISH).startsWith("mac os x"); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/JreVendor.java tomcat10-10.1.52/java/org/apache/tomcat/util/compat/JreVendor.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/JreVendor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/JreVendor.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,11 +22,9 @@ static { /* - * There are a few places where Tomcat either accesses JVM internals - * (e.g. the memory leak protection) or where feature support varies - * between JVMs (e.g. SPNEGO). These flags exist to enable Tomcat to - * adjust its behaviour based on the vendor of the JVM. In an ideal - * world this code would not exist. + * There are a few places where Tomcat either accesses JVM internals (e.g. the memory leak protection) or where + * feature support varies between JVMs (e.g. SPNEGO). These flags exist to enable Tomcat to adjust its behaviour + * based on the vendor of the JVM. In an ideal world this code would not exist. */ String vendor = System.getProperty("java.vendor", ""); vendor = vendor.toLowerCase(Locale.ENGLISH); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/compat/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/util/compat/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/compat/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/compat/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,8 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +jre12Compat.javaPre12=Method not found so assuming code is running on a pre-Java 12 JVM + jre16Compat.javaPre16=Class not found so assuming code is running on a pre-Java 16 JVM jre16Compat.unexpected=Failed to create references to Java 16 classes and methods @@ -31,3 +33,7 @@ jreCompat.noUnixDomainSocket=Java Runtime does not support Unix domain sockets. You must use Java 16 or later to use this feature. jreCompat.noVirtualThreads=Java Runtime does not support virtual threads. You must use Java 21 or later to use this feature. +jreCompat.useCanonCaches.failed=Failed to set the java.io.FileSystem.useCanonCaches static field +jreCompat.useCanonCaches.init=Unable to create a reference to the java.io.FileSystem.useCanonCaches static field +jreCompat.useCanonCaches.java18=If using Java 18 to Java 20 inclusive, you must not enabled the canonical file name cache using -Dsun.io.useCanonCaches=true on the command line as Tomcat is unable to disable the cache via reflection on those Java versions +jreCompat.useCanonCaches.none=No reference to the java.io.FileSystem.useCanonCaches static field available diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/concurrent/KeyedReentrantReadWriteLock.java tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/KeyedReentrantReadWriteLock.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/concurrent/KeyedReentrantReadWriteLock.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/KeyedReentrantReadWriteLock.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,179 @@ +/* + * 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.concurrent; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Function; + +import org.apache.tomcat.util.res.StringManager; + +/** + * Provides a reentrant read/write lock for a given key. Any locks obtained from an instance of this class using the + * same key will use the same underlying reentrant read/write lock as long as at least one lock for that key remains in + * use. Once no locks are in use for the given key, the lock is eligible for GC and the next lock obtained using that + * key will use a new underlying reentrant read/write lock. + *

          + * The class is used when Tomcat needs to manage concurrent access to components identified by a key (e.g. sessions). + *

          + * The map of keys to locks is maintained so that locks are created as required and removed when no longer used. + *

          + * The locks provided by this class only implement {@code Lock#lock()} and {@code Lock#unlock()}. All other methods will + * throw {@code UnsupportedOperationException}. + */ +public class KeyedReentrantReadWriteLock { + + private final Map locksMap = new HashMap<>(); + + + /** + * Obtain the reentrant read/write lock for the given key. + * + * @param key The key for which the lock should be obtained + * + * @return A reentrant read/write lock for the given key + */ + public ReadWriteLock getLock(String key) { + return new ReadWriteLockImpl(locksMap, key); + } + + + /* + * Reentrant read/write lock implementation that is passed back to the caller. It provides the lock wrappers that + * track usage. + */ + private static class ReadWriteLockImpl implements ReadWriteLock { + + private final Map locksMap; + private final String key; + private volatile Lock readLock; + private volatile Lock writeLock; + + ReadWriteLockImpl(Map locksMap, String key) { + this.locksMap = locksMap; + this.key = key; + } + + @Override + public Lock readLock() { + if (readLock == null) { + readLock = new LockImpl(locksMap, key, ReentrantReadWriteLock::readLock); + } + return readLock; + } + + @Override + public Lock writeLock() { + if (writeLock == null) { + writeLock = new LockImpl(locksMap, key, ReentrantReadWriteLock::writeLock); + } + return writeLock; + } + } + + + /* + * Lock wrapper implementation that provides both read locks and write locks from the underlying lock and tracks + * their usage. Most of the methods throw UnsupportedOperationException as Tomcat does not (currently) require + * implementations of those methods. + */ + private static class LockImpl implements Lock { + + private static final StringManager sm = StringManager.getManager(LockImpl.class); + + private final Map locksMap; + private final String key; + private final Function function; + + LockImpl(Map locksMap, String key, Function function) { + this.locksMap = locksMap; + this.key = key; + this.function = function; + } + + @Override + public void lock() { + CountedLock countedLock = null; + synchronized (locksMap) { + // Lookup / create the counted lock for the given key + countedLock = locksMap.compute(key, (k, v) -> v == null ? new CountedLock() : v); + // Increment usage count inside the sync block to ensure other threads are aware key is in use. + countedLock.count.incrementAndGet(); + } + // Lock outside of the sync block in case the call to lock() blocks. + function.apply(countedLock.reentrantLock).lock(); + } + + @Override + public void unlock() { + CountedLock countedLock = null; + // Unlocking so a lock should exist in the map for the given key. + synchronized (locksMap) { + countedLock = locksMap.get(key); + } + if (countedLock == null) { + throw new IllegalStateException(sm.getString("lockImpl.unlockWithoutLock")); + } + // No need to unlock inside sync block, so don't. + function.apply(countedLock.reentrantLock).unlock(); + synchronized (locksMap) { + /* + * Decrement usage count and check for zero inside the sync block to ensure usage tracking is consistent + * across multiple threads. + */ + if (countedLock.count.decrementAndGet() == 0) { + locksMap.remove(key); + } + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + } + + + /* + * Holds the underlying reentrant read/write lock and the counter that tracks usage. + */ + private static class CountedLock { + AtomicInteger count = new AtomicInteger(); + ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock(); + } +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/concurrent/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/concurrent/LocalStrings.properties 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,19 @@ +# 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. + +# Do not edit this file directly. +# To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations + +lockImpl.unlockWithoutLock=An attempt was made to release a lock without first obtaining the lock diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/concurrent/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/concurrent/LocalStrings_fr.properties 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,19 @@ +# 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. + +# Do not edit this file directly. +# To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations + +lockImpl.unlockWithoutLock=Une tentative pour rendre le verrou a été faite sans avoir d'abord obtenu le verrou diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/concurrent/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/concurrent/LocalStrings_ja.properties 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/concurrent/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,19 @@ +# 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. + +# Do not edit this file directly. +# To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations + +lockImpl.unlockWithoutLock=ロックを取得せずにロックを解除しようとしました diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/Constants.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/Constants.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,7 @@ public class Constants { - public static final String PACKAGE_NAME = - Constants.class.getPackage().getName(); + public static final String PACKAGE_NAME = Constants.class.getPackage().getName(); public static final boolean IS_SECURITY_ENABLED = (System.getSecurityManager() != null); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/DigesterFactory.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/DigesterFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/DigesterFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/DigesterFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,13 +31,11 @@ import org.xml.sax.ext.EntityResolver2; /** - * Wrapper class around the Digester that hide Digester's initialization - * details. + * Wrapper class around the Digester that hide Digester's initialization details. */ public class DigesterFactory { - private static final StringManager sm = - StringManager.getManager(Constants.PACKAGE_NAME); + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); private static final Class CLASS_SERVLET_CONTEXT; private static final Class CLASS_JSP_CONTEXT; @@ -55,20 +53,18 @@ /** - * Mapping of well-known public IDs used by the Servlet API to the matching - * local resource. + * Mapping of well-known public IDs used by the Servlet API to the matching local resource. */ public static final Map SERVLET_API_PUBLIC_IDS; /** - * Mapping of well-known system IDs used by the Servlet API to the matching - * local resource. + * Mapping of well-known system IDs used by the Servlet API to the matching local resource. */ public static final Map SERVLET_API_SYSTEM_IDS; static { - Map publicIds = new HashMap<>(); - Map systemIds = new HashMap<>(); + Map publicIds = new HashMap<>(); + Map systemIds = new HashMap<>(); // W3C add(publicIds, XmlIdentifiers.XSD_10_PUBLIC, locationFor("XMLSchema.dtd")); @@ -149,7 +145,7 @@ SERVLET_API_SYSTEM_IDS = Collections.unmodifiableMap(systemIds); } - private static void addSelf(Map ids, String id) { + private static void addSelf(Map ids, String id) { String location = locationFor(id); if (location != null) { ids.put(id, location); @@ -186,22 +182,21 @@ /** * Create a Digester parser. - * @param xmlValidation turn on/off xml validation + * + * @param xmlValidation turn on/off xml validation * @param xmlNamespaceAware turn on/off namespace validation - * @param rule an instance of RuleSet used for parsing the xml. - * @param blockExternal turn on/off the blocking of external resources + * @param rule an instance of RuleSet used for parsing the xml. + * @param blockExternal turn on/off the blocking of external resources + * * @return a new digester */ - public static Digester newDigester(boolean xmlValidation, - boolean xmlNamespaceAware, - RuleSet rule, - boolean blockExternal) { + public static Digester newDigester(boolean xmlValidation, boolean xmlNamespaceAware, RuleSet rule, + boolean blockExternal) { Digester digester = new Digester(); digester.setNamespaceAware(xmlNamespaceAware); digester.setValidating(xmlValidation); digester.setUseContextClassLoader(true); - EntityResolver2 resolver = new LocalResolver(SERVLET_API_PUBLIC_IDS, - SERVLET_API_SYSTEM_IDS, blockExternal); + EntityResolver2 resolver = new LocalResolver(SERVLET_API_PUBLIC_IDS, SERVLET_API_SYSTEM_IDS, blockExternal); digester.setEntityResolver(resolver); if (rule != null) { digester.addRuleSet(rule); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/LocalResolver.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/LocalResolver.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/LocalResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/LocalResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,14 +34,10 @@ */ public class LocalResolver implements EntityResolver2 { - private static final StringManager sm = - StringManager.getManager(Constants.PACKAGE_NAME); + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); - private static final String[] JAVA_EE_NAMESPACES = { - XmlIdentifiers.JAVAEE_1_4_NS, - XmlIdentifiers.JAVAEE_5_NS, - XmlIdentifiers.JAVAEE_7_NS, - XmlIdentifiers.JAKARTAEE_9_NS}; + private static final String[] JAVA_EE_NAMESPACES = { XmlIdentifiers.JAVAEE_1_4_NS, XmlIdentifiers.JAVAEE_5_NS, + XmlIdentifiers.JAVAEE_7_NS, XmlIdentifiers.JAKARTAEE_9_NS }; private final Map publicIds; @@ -49,19 +45,14 @@ private final boolean blockExternal; /** - * Constructor providing mappings of public and system identifiers to local - * resources. Each map contains a mapping from a well-known identifier to a - * URL for a local resource path. + * Constructor providing mappings of public and system identifiers to local resources. Each map contains a mapping + * from a well-known identifier to a URL for a local resource path. * - * @param publicIds mapping of well-known public identifiers to local - * resources - * @param systemIds mapping of well-known system identifiers to local - * resources - * @param blockExternal are external resources blocked that are not - * well-known + * @param publicIds mapping of well-known public identifiers to local resources + * @param systemIds mapping of well-known system identifiers to local resources + * @param blockExternal are external resources blocked that are not well-known */ - public LocalResolver(Map publicIds, - Map systemIds, boolean blockExternal) { + public LocalResolver(Map publicIds, Map systemIds, boolean blockExternal) { this.publicIds = publicIds; this.systemIds = systemIds; this.blockExternal = blockExternal; @@ -69,15 +60,14 @@ @Override - public InputSource resolveEntity(String publicId, String systemId) - throws SAXException, IOException { + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { return resolveEntity(null, publicId, null, systemId); } @Override - public InputSource resolveEntity(String name, String publicId, - String base, String systemId) throws SAXException, IOException { + public InputSource resolveEntity(String name, String publicId, String base, String systemId) + throws SAXException, IOException { // First try resolving using the publicId String resolved = publicIds.get(publicId); @@ -89,8 +79,7 @@ // If there is no systemId, can't try anything else if (systemId == null) { - throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity", - name, publicId, null, base)); + throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity", name, publicId, null, base)); } // Try resolving with the supplied systemId @@ -149,14 +138,12 @@ } } - throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity", - name, publicId, systemId, base)); + throw new FileNotFoundException(sm.getString("localResolver.unresolvedEntity", name, publicId, systemId, base)); } @Override - public InputSource getExternalSubset(String name, String baseURI) - throws SAXException, IOException { + public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException { return null; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/XmlErrorHandler.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/XmlErrorHandler.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/XmlErrorHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/XmlErrorHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,7 @@ public class XmlErrorHandler implements ErrorHandler { - private static final StringManager sm = - StringManager.getManager(Constants.PACKAGE_NAME); + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); private final List errors = new ArrayList<>(); @@ -64,12 +63,10 @@ public void logFindings(Log log, String source) { for (SAXParseException e : getWarnings()) { - log.warn(sm.getString( - "xmlErrorHandler.warning", e.getMessage(), source)); + log.warn(sm.getString("xmlErrorHandler.warning", e.getMessage(), source)); } for (SAXParseException e : getErrors()) { - log.warn(sm.getString( - "xmlErrorHandler.error", e.getMessage(), source)); + log.warn(sm.getString("xmlErrorHandler.error", e.getMessage(), source)); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/XmlIdentifiers.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/XmlIdentifiers.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/XmlIdentifiers.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/XmlIdentifiers.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,43 +17,32 @@ package org.apache.tomcat.util.descriptor; /** - * Defines constants for well-known Public and System identifiers documented by - * the Servlet and JSP specifications. + * Defines constants for well-known Public and System identifiers documented by the Servlet and JSP specifications. */ public final class XmlIdentifiers { // from W3C public static final String XML_2001_XSD = "http://www.w3.org/2001/xml.xsd"; public static final String DATATYPES_PUBLIC = "datatypes"; - public static final String XSD_10_PUBLIC = - "-//W3C//DTD XMLSCHEMA 200102//EN"; + public static final String XSD_10_PUBLIC = "-//W3C//DTD XMLSCHEMA 200102//EN"; // from J2EE 1.2 - public static final String WEB_22_PUBLIC = - "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"; - public static final String WEB_22_SYSTEM = - "http://java.sun.com/dtd/web-app_2_2.dtd"; - public static final String TLD_11_PUBLIC = - "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"; - public static final String TLD_11_SYSTEM = - "http://java.sun.com/dtd/web-jsptaglibrary_1_1.dtd"; + public static final String WEB_22_PUBLIC = "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"; + public static final String WEB_22_SYSTEM = "http://java.sun.com/dtd/web-app_2_2.dtd"; + public static final String TLD_11_PUBLIC = "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"; + public static final String TLD_11_SYSTEM = "http://java.sun.com/dtd/web-jsptaglibrary_1_1.dtd"; // from J2EE 1.3 - public static final String WEB_23_PUBLIC = - "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"; - public static final String WEB_23_SYSTEM = - "http://java.sun.com/dtd/web-app_2_3.dtd"; - public static final String TLD_12_PUBLIC = - "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"; - public static final String TLD_12_SYSTEM = - "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"; + public static final String WEB_23_PUBLIC = "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"; + public static final String WEB_23_SYSTEM = "http://java.sun.com/dtd/web-app_2_3.dtd"; + public static final String TLD_12_PUBLIC = "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"; + public static final String TLD_12_SYSTEM = "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd"; // from J2EE 1.4 public static final String JAVAEE_1_4_NS = "http://java.sun.com/xml/ns/j2ee"; public static final String WEB_24_XSD = JAVAEE_1_4_NS + "/web-app_2_4.xsd"; public static final String TLD_20_XSD = JAVAEE_1_4_NS + "/web-jsptaglibrary_2_0.xsd"; - public static final String WEBSERVICES_11_XSD = - "http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd"; + public static final String WEBSERVICES_11_XSD = "http://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd"; // from JavaEE 5 public static final String JAVAEE_5_NS = "http://java.sun.com/xml/ns/javaee"; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tagplugin/TagPluginParser.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tagplugin/TagPluginParser.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tagplugin/TagPluginParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tagplugin/TagPluginParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,11 +40,10 @@ private final Log log = LogFactory.getLog(TagPluginParser.class); // must not be static private static final String PREFIX = "tag-plugins/tag-plugin"; private final Digester digester; - private final Map plugins = new HashMap<>(); + private final Map plugins = new HashMap<>(); public TagPluginParser(ServletContext context, boolean blockExternal) { - digester = DigesterFactory.newDigester( - false, false, new TagPluginRuleSet(), blockExternal); + digester = DigesterFactory.newDigester(false, false, new TagPluginRuleSet(), blockExternal); digester.setClassLoader(context.getClassLoader()); } @@ -74,7 +73,7 @@ plugins.put(tagClass, pluginClass); } - public Map getPlugins() { + public Map getPlugins() { return plugins; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/ImplicitTldRuleSet.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/ImplicitTldRuleSet.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/ImplicitTldRuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/ImplicitTldRuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,7 @@ import org.xml.sax.Attributes; /** - * RulesSet for digesting implicit.tld files. - * - * Only version information used and short names are allowed. + * RulesSet for digesting implicit.tld files. Only version information used and short names are allowed. */ public class ImplicitTldRuleSet implements RuleSet { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TagFileXml.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TagFileXml.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TagFileXml.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TagFileXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,9 +17,8 @@ package org.apache.tomcat.util.descriptor.tld; /** - * Bare-bone model of a tag file loaded from a TLD. - * This does not contain the tag-specific attributes that requiring parsing - * the actual tag file to derive. + * Bare-bone model of a tag file loaded from a TLD. This does not contain the tag-specific attributes that requiring + * parsing the actual tag file to derive. */ public class TagFileXml { private String name; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TagXml.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TagXml.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TagXml.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TagXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,10 +24,8 @@ import jakarta.servlet.jsp.tagext.TagVariableInfo; /** - * Model of a tag define in a tag library descriptor. - * This represents the information as parsed from the XML but differs from - * TagInfo in that is does not provide a link back to the tag library that - * defined it. + * Model of a tag define in a tag library descriptor. This represents the information as parsed from the XML but differs + * from TagInfo in that it does not provide a link back to the tag library that defined it. */ public class TagXml { private String name; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TaglibXml.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TaglibXml.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TaglibXml.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TaglibXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,11 +24,9 @@ /** * Common representation of a Tag Library Descriptor (TLD) XML file. *

          - * This stores the raw result of parsing an TLD XML file, flattening different - * version of the descriptors to a common format. This is different to a - * TagLibraryInfo instance that would be passed to a tag validator in that it - * does not contain the uri and prefix values used by a JSP to reference this - * tag library. + * This stores the raw result of parsing an TLD XML file, flattening different version of the descriptors to a common + * format. This is different to a TagLibraryInfo instance that would be passed to a tag validator in that it does not + * contain the uri and prefix values used by a JSP to reference this tag library. */ public class TaglibXml { private String tlibVersion; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TldParser.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TldParser.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TldParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TldParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,15 +39,12 @@ private final Log log = LogFactory.getLog(TldParser.class); // must not be static private final Digester digester; - public TldParser(boolean namespaceAware, boolean validation, - boolean blockExternal) { + public TldParser(boolean namespaceAware, boolean validation, boolean blockExternal) { this(namespaceAware, validation, new TldRuleSet(), blockExternal); } - public TldParser(boolean namespaceAware, boolean validation, RuleSet ruleSet, - boolean blockExternal) { - digester = DigesterFactory.newDigester( - validation, namespaceAware, ruleSet, blockExternal); + public TldParser(boolean namespaceAware, boolean validation, RuleSet ruleSet, boolean blockExternal) { + digester = DigesterFactory.newDigester(validation, namespaceAware, ruleSet, blockExternal); } public TaglibXml parse(TldResourcePath path) throws IOException, SAXException { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TldResourcePath.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TldResourcePath.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TldResourcePath.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TldResourcePath.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,17 +28,15 @@ /** * A TLD Resource Path as defined in JSP 7.3.2. *

          - * This encapsulates references to Tag Library Descriptors that can be located - * in different places: + * This encapsulates references to Tag Library Descriptors that can be located in different places: *

            *
          • As resources within an application
          • *
          • As entries in JAR files included in the application
          • *
          • As resources provided by the container
          • *
          - * When configuring a mapping from a well-known URI to a TLD, a user is allowed - * to specify just the name of a JAR file that implicitly contains a TLD in - * META-INF/taglib.tld. Such a mapping must be explicitly converted - * to a URL and entryName when using this implementation. + * When configuring a mapping from a well-known URI to a TLD, a user is allowed to specify just the name of a JAR file + * that implicitly contains a TLD in META-INF/taglib.tld. Such a mapping must be explicitly converted to a + * URL and entryName when using this implementation. */ public class TldResourcePath { private final URL url; @@ -78,19 +76,18 @@ } /** - * Returns the path within the web application, if any, that the resource - * returned by {@link #getUrl()} was obtained from. + * Returns the path within the web application, if any, that the resource returned by {@link #getUrl()} was obtained + * from. * - * @return the web application path or @null if the the resource is not - * located within a web application + * @return the web application path or @null if the resource is not located within a web application */ public String getWebappPath() { return webappPath; } /** - * Returns the name of the JAR entry that contains the TLD. - * May be null to indicate the URL refers directly to the TLD itself. + * Returns the name of the JAR entry that contains the TLD. May be null to indicate the URL refers directly to the + * TLD itself. * * @return the name of the JAR entry that contains the TLD */ @@ -99,9 +96,8 @@ } /** - * Return the external form of the URL representing this TLD. - * This can be used as a canonical location for the TLD itself, for example, - * as the systemId to use when parsing its XML. + * Return the external form of the URL representing this TLD. This can be used as a canonical location for the TLD + * itself, for example, as the systemId to use when parsing its XML. * * @return the external form of the URL representing this TLD */ @@ -117,6 +113,7 @@ * Opens a stream to access the TLD. * * @return a stream containing the TLD content + * * @throws IOException if there was a problem opening the stream */ public InputStream openStream() throws IOException { @@ -156,8 +153,7 @@ TldResourcePath other = (TldResourcePath) o; - return url.equals(other.url) && - Objects.equals(webappPath, other.webappPath) && + return url.equals(other.url) && Objects.equals(webappPath, other.webappPath) && Objects.equals(entryName, other.entryName); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TldRuleSet.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TldRuleSet.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/TldRuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/TldRuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -90,29 +90,23 @@ digester.addRule(TAG_PREFIX + "/variable", new ScriptVariableRule()); digester.addCallMethod(TAG_PREFIX + "/variable/name-given", "setNameGiven", 0); - digester.addCallMethod(TAG_PREFIX + "/variable/name-from-attribute", - "setNameFromAttribute", 0); + digester.addCallMethod(TAG_PREFIX + "/variable/name-from-attribute", "setNameFromAttribute", 0); digester.addCallMethod(TAG_PREFIX + "/variable/variable-class", "setClassName", 0); - digester.addRule(TAG_PREFIX + "/variable/declare", - new GenericBooleanRule(Variable.class, "setDeclare")); + digester.addRule(TAG_PREFIX + "/variable/declare", new GenericBooleanRule(Variable.class, "setDeclare")); digester.addCallMethod(TAG_PREFIX + "/variable/scope", "setScope", 0); digester.addRule(TAG_PREFIX + "/attribute", new TagAttributeRule()); digester.addCallMethod(TAG_PREFIX + "/attribute/description", "setDescription", 0); digester.addCallMethod(TAG_PREFIX + "/attribute/name", "setName", 0); - digester.addRule(TAG_PREFIX + "/attribute/required", - new GenericBooleanRule(Attribute.class, "setRequired")); + digester.addRule(TAG_PREFIX + "/attribute/required", new GenericBooleanRule(Attribute.class, "setRequired")); digester.addRule(TAG_PREFIX + "/attribute/rtexprvalue", new GenericBooleanRule(Attribute.class, "setRequestTime")); digester.addCallMethod(TAG_PREFIX + "/attribute/type", "setType", 0); digester.addCallMethod(TAG_PREFIX + "/attribute/deferred-value", "setDeferredValue"); - digester.addCallMethod(TAG_PREFIX + "/attribute/deferred-value/type", - "setExpectedTypeName", 0); + digester.addCallMethod(TAG_PREFIX + "/attribute/deferred-value/type", "setExpectedTypeName", 0); digester.addCallMethod(TAG_PREFIX + "/attribute/deferred-method", "setDeferredMethod"); - digester.addCallMethod(TAG_PREFIX + "/attribute/deferred-method/method-signature", - "setMethodSignature", 0); - digester.addRule(TAG_PREFIX + "/attribute/fragment", - new GenericBooleanRule(Attribute.class, "setFragment")); + digester.addCallMethod(TAG_PREFIX + "/attribute/deferred-method/method-signature", "setMethodSignature", 0); + digester.addRule(TAG_PREFIX + "/attribute/fragment", new GenericBooleanRule(Attribute.class, "setFragment")); digester.addRule(TAG_PREFIX + "/dynamic-attributes", new GenericBooleanRule(TagXml.class, "setDynamicAttributes")); @@ -144,19 +138,20 @@ } private static class TagAttributeRule extends Rule { - private boolean allowShortNames = false; @Override public void begin(String namespace, String name, Attributes attributes) throws Exception { TaglibXml taglibXml = (TaglibXml) digester.peek(digester.getCount() - 1); - allowShortNames = "1.2".equals(taglibXml.getJspVersion()); + boolean allowShortNames = "1.2".equals(taglibXml.getJspVersion()); Attribute attribute = new Attribute(allowShortNames); digester.push(attribute); StringBuilder code = digester.getGeneratedCode(); if (code != null) { code.append(System.lineSeparator()); - code.append(TldRuleSet.class.getName()).append(".Attribute ").append(digester.toVariableName(attribute)).append(" = new "); - code.append(TldRuleSet.class.getName()).append(".Attribute").append('(').append(Boolean.toString(allowShortNames)); + code.append(TldRuleSet.class.getName()).append(".Attribute ").append(digester.toVariableName(attribute)) + .append(" = new "); + code.append(TldRuleSet.class.getName()).append(".Attribute").append('(') + .append(Boolean.toString(allowShortNames)); code.append(");").append(System.lineSeparator()); } } @@ -294,17 +289,8 @@ type = "java.lang.String"; } - return new TagAttributeInfo( - name, - required, - type, - requestTime, - fragment, - description, - deferredValue, - deferredMethod, - expectedTypeName, - methodSignature); + return new TagAttributeInfo(name, required, type, requestTime, fragment, description, deferredValue, + deferredMethod, expectedTypeName, methodSignature); } } @@ -317,8 +303,10 @@ StringBuilder code = digester.getGeneratedCode(); if (code != null) { code.append(System.lineSeparator()); - code.append(TldRuleSet.class.getName()).append(".Variable ").append(digester.toVariableName(variable)).append(" = new "); - code.append(TldRuleSet.class.getName()).append(".Variable").append("();").append(System.lineSeparator()); + code.append(TldRuleSet.class.getName()).append(".Variable ").append(digester.toVariableName(variable)) + .append(" = new "); + code.append(TldRuleSet.class.getName()).append(".Variable").append("();") + .append(System.lineSeparator()); } } @@ -392,7 +380,7 @@ @Override public void body(String namespace, String name, String text) throws Exception { - if(null != text) { + if (null != text) { text = text.trim(); } boolean value = "true".equalsIgnoreCase(text) || "yes".equalsIgnoreCase(text); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/ValidatorXml.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/ValidatorXml.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/tld/ValidatorXml.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/tld/ValidatorXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,7 @@ */ public class ValidatorXml { private String validatorClass; - private final Map initParams = new HashMap<>(); + private final Map initParams = new HashMap<>(); public String getValidatorClass() { return validatorClass; @@ -38,7 +38,7 @@ initParams.put(name, value); } - public Map getInitParams() { + public Map getInitParams() { return initParams; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ApplicationParameter.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ApplicationParameter.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ApplicationParameter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ApplicationParameter.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,13 +20,9 @@ /** - * Representation of a context initialization parameter that is configured - * in the server configuration file, rather than the application deployment - * descriptor. This is convenient for establishing default values (which - * may be configured to allow application overrides or not) without having - * to modify the application deployment descriptor itself. - * - * @author Craig R. McClanahan + * Representation of a context initialization parameter that is configured in the server configuration file, rather than + * the application deployment descriptor. This is convenient for establishing default values (which may be configured to + * allow application overrides or not) without having to modify the application deployment descriptor itself. */ public class ApplicationParameter implements Serializable { @@ -64,8 +60,7 @@ /** - * Does this application parameter allow overrides by the application - * deployment descriptor? + * Does this application parameter allow overrides by the application deployment descriptor? */ private boolean override = true; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/Constants.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/Constants.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,7 @@ public class Constants { - public static final String PACKAGE_NAME = - Constants.class.getPackage().getName(); + public static final String PACKAGE_NAME = Constants.class.getPackage().getName(); public static final String WEB_XML_LOCATION = "/WEB-INF/web.xml"; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextEjb.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,14 +17,9 @@ package org.apache.tomcat.util.descriptor.web; - /** - * Representation of an EJB resource reference for a web application, as - * represented in a <ejb-ref> element in the - * deployment descriptor. - * - * @author Craig R. McClanahan - * @author Peter Rossbach (pero@apache.org) + * Representation of an EJB resource reference for a web application, as represented in a <ejb-ref> + * element in the deployment descriptor. */ public class ContextEjb extends ResourceBase { @@ -150,12 +145,9 @@ return false; } if (remote == null) { - if (other.remote != null) { - return false; - } - } else if (!remote.equals(other.remote)) { - return false; + return other.remote == null; + } else { + return remote.equals(other.remote); } - return true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextEnvironment.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,12 +17,9 @@ package org.apache.tomcat.util.descriptor.web; - /** - * Representation of an application environment entry, as represented in - * an <env-entry> element in the deployment descriptor. - * - * @author Craig R. McClanahan + * Representation of an application environment entry, as represented in an <env-entry> element in + * the deployment descriptor. */ public class ContextEnvironment extends ResourceBase { @@ -33,8 +30,7 @@ /** - * Does this environment entry allow overrides by the application - * deployment descriptor? + * Does this environment entry allow overrides by the application deployment descriptor? */ private boolean override = true; @@ -119,12 +115,9 @@ return false; } if (value == null) { - if (other.value != null) { - return false; - } - } else if (!value.equals(other.value)) { - return false; + return other.value == null; + } else { + return value.equals(other.value); } - return true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,11 +24,8 @@ /** - * Representation of a handler reference for a web service, as - * represented in a <handler> element in the - * deployment descriptor. - * - * @author Fabien Carrion + * Representation of a handler reference for a web service, as represented in a <handler> element in + * the deployment descriptor. */ public class ContextHandler extends ResourceBase { @@ -51,15 +48,14 @@ } /** - * A list of QName specifying the SOAP Headers the handler will work on. - * -namespace and localpart values must be found inside the WSDL. - * + * A list of QName specifying the SOAP Headers the handler will work on. -namespace and localpart values must be + * found inside the WSDL. + *

          * A service-qname is composed by a namespaceURI and a localpart. - * - * soapHeader[0] : namespaceURI - * soapHeader[1] : localpart + *

          + * soapHeader[0] : namespaceURI soapHeader[1] : localpart */ - private final Map soapHeaders = new HashMap<>(); + private final Map soapHeaders = new HashMap<>(); public Iterator getLocalparts() { return soapHeaders.keySet().iterator(); @@ -75,7 +71,8 @@ /** * Set a configured property. - * @param name The property name + * + * @param name The property name * @param value The property value */ public void setProperty(String name, String value) { @@ -132,7 +129,7 @@ sb.append(", class="); sb.append(handlerclass); } - if (this.soapHeaders != null) { + if (!soapHeaders.isEmpty()) { sb.append(", soap-headers="); sb.append(this.soapHeaders); } @@ -157,14 +154,10 @@ public int hashCode() { final int prime = 31; int result = super.hashCode(); - result = prime * result + - ((handlerclass == null) ? 0 : handlerclass.hashCode()); - result = prime * result + - ((portNames == null) ? 0 : portNames.hashCode()); - result = prime * result + - ((soapHeaders == null) ? 0 : soapHeaders.hashCode()); - result = prime * result + - ((soapRoles == null) ? 0 : soapRoles.hashCode()); + result = prime * result + ((handlerclass == null) ? 0 : handlerclass.hashCode()); + result = prime * result + portNames.hashCode(); + result = prime * result + soapHeaders.hashCode(); + result = prime * result + soapRoles.hashCode(); return result; } @@ -188,27 +181,12 @@ } else if (!handlerclass.equals(other.handlerclass)) { return false; } - if (portNames == null) { - if (other.portNames != null) { - return false; - } - } else if (!portNames.equals(other.portNames)) { + if (!portNames.equals(other.portNames)) { return false; } - if (soapHeaders == null) { - if (other.soapHeaders != null) { - return false; - } - } else if (!soapHeaders.equals(other.soapHeaders)) { - return false; - } - if (soapRoles == null) { - if (other.soapRoles != null) { - return false; - } - } else if (!soapRoles.equals(other.soapRoles)) { + if (!soapHeaders.equals(other.soapHeaders)) { return false; } - return true; + return soapRoles.equals(other.soapRoles); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextLocalEjb.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,14 +17,9 @@ package org.apache.tomcat.util.descriptor.web; - /** - * Representation of a local EJB resource reference for a web application, as - * represented in a <ejb-local-ref> element in the - * deployment descriptor. - * - * @author Craig R. McClanahan - * @author Peter Rossbach (pero@apache.org) + * Representation of a local EJB resource reference for a web application, as represented in a + * <ejb-local-ref> element in the deployment descriptor. */ public class ContextLocalEjb extends ResourceBase { @@ -149,12 +144,9 @@ return false; } if (local == null) { - if (other.local != null) { - return false; - } - } else if (!local.equals(other.local)) { - return false; + return other.local == null; + } else { + return local.equals(other.local); } - return true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextResource.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextResource.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextResource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextResource.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,14 +17,9 @@ package org.apache.tomcat.util.descriptor.web; - /** - * Representation of a resource reference for a web application, as - * represented in a <resource-ref> element in the - * deployment descriptor. - * - * @author Craig R. McClanahan - * @author Peter Rossbach (pero@apache.org) + * Representation of a resource reference for a web application, as represented in a <resource-ref> + * element in the deployment descriptor. */ public class ContextResource extends ResourceBase { @@ -34,8 +29,7 @@ /** - * The authorization requirement for this resource - * (Application or Container). + * The authorization requirement for this resource (Application or Container). */ private String auth = null; @@ -48,8 +42,7 @@ } /** - * The sharing scope of this resource factory (Shareable - * or Unshareable). + * The sharing scope of this resource factory (Shareable or Unshareable). */ private String scope = "Shareable"; @@ -63,9 +56,8 @@ /** - * Is this resource known to be a singleton resource. The default value is - * true since this is what users expect although the Jakarta EE spec implies - * that the default should be false. + * Is this resource known to be a singleton resource. The default value is true since this is what users expect + * although the Jakarta EE spec implies that the default should be false. */ private boolean singleton = true; @@ -79,10 +71,8 @@ /** - * The name of the zero argument method to be called when the resource is - * no longer required to clean-up resources. This method must only speed up - * the clean-up of resources that would otherwise happen via garbage - * collection. + * The name of the zero argument method to be called when the resource is no longer required to clean-up resources. + * This method must only speed up the clean-up of resources that would otherwise happen via garbage collection. */ private String closeMethod = null; private boolean closeMethodConfigured = false; @@ -139,8 +129,7 @@ final int prime = 31; int result = super.hashCode(); result = prime * result + ((auth == null) ? 0 : auth.hashCode()); - result = prime * result + - ((closeMethod == null) ? 0 : closeMethod.hashCode()); + result = prime * result + ((closeMethod == null) ? 0 : closeMethod.hashCode()); result = prime * result + ((scope == null) ? 0 : scope.hashCode()); result = prime * result + (singleton ? 1231 : 1237); return result; @@ -180,9 +169,6 @@ } else if (!scope.equals(other.scope)) { return false; } - if (singleton != other.singleton) { - return false; - } - return true; + return singleton == other.singleton; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextResourceEnvRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,13 +17,9 @@ package org.apache.tomcat.util.descriptor.web; - /** - * Representation of an application resource reference, as represented in - * an <res-env-refy> element in the deployment descriptor. - * - * @author Craig R. McClanahan - * @author Peter Rossbach (pero@apache.org) + * Representation of an application resource reference, as represented in an <res-env-ref> element in + * the deployment descriptor. */ public class ContextResourceEnvRef extends ResourceBase { @@ -32,8 +28,7 @@ // ------------------------------------------------------------- Properties /** - * Does this environment entry allow overrides by the application - * deployment descriptor? + * Does this environment entry allow overrides by the application deployment descriptor? */ private boolean override = true; @@ -89,9 +84,6 @@ return false; } ContextResourceEnvRef other = (ContextResourceEnvRef) obj; - if (override != other.override) { - return false; - } - return true; + return override == other.override; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextResourceLink.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,14 +17,9 @@ package org.apache.tomcat.util.descriptor.web; - /** - * Representation of a resource link for a web application, as - * represented in a <ResourceLink> element in the - * server configuration file. - * - * @author Remy Maucherat - * @author Peter Rossbach (Peter Rossbach (pero@apache.org)) + * Representation of a resource link for a web application, as represented in a <ResourceLink> + * element in the server configuration file. */ public class ContextResourceLink extends ResourceBase { @@ -32,7 +27,7 @@ // ------------------------------------------------------------- Properties - /** + /** * The global name of this resource. */ private String global = null; @@ -111,12 +106,9 @@ return false; } if (global == null) { - if (other.global != null) { - return false; - } - } else if (!global.equals(other.global)) { - return false; + return other.global == null; + } else { + return global.equals(other.global); } - return true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextService.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextService.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextService.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextService.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,11 +23,8 @@ /** - * Representation of a web service reference for a web application, as - * represented in a <service-ref> element in the - * deployment descriptor. - * - * @author Fabien Carrion + * Representation of a web service reference for a web application, as represented in a <service-ref> + * element in the deployment descriptor. */ public class ContextService extends ResourceBase { @@ -76,8 +73,7 @@ } /** - * The fully qualified class name of the JAX-WS Service interface that the - * client depends on. + * The fully qualified class name of the JAX-WS Service interface that the client depends on. */ private String serviceInterface = null; @@ -90,8 +86,7 @@ } /** - * Contains the location (relative to the root of - * the module) of the web service WSDL description. + * Contains the location (relative to the root of the module) of the web service WSDL description. */ private String wsdlfile = null; @@ -104,8 +99,8 @@ } /** - * A file specifying the correlation of the WSDL definition - * to the interfaces (Service Endpoint Interface, Service Interface). + * A file specifying the correlation of the WSDL definition to the interfaces (Service Endpoint Interface, Service + * Interface). */ private String jaxrpcmappingfile = null; @@ -118,15 +113,13 @@ } /** - * Declares the specific WSDL service element that is being referred to. - * It is not specified if no wsdl-file is declared or if WSDL contains only - * 1 service element. - * - * A service-qname is composed by a namespaceURI and a localpart. - * It must be defined if more than 1 service is declared in the WSDL. - * - * serviceqname[0] : namespaceURI - * serviceqname[1] : localpart + * Declares the specific WSDL service element that is being referred to. It is not specified if no wsdl-file is + * declared or if WSDL contains only 1 service element. + *

          + * A service-qname is composed by a namespaceURI and a localpart. It must be defined if more than 1 service is + * declared in the WSDL. + *

          + * serviceqname[0] : namespaceURI serviceqname[1] : localpart */ private String[] serviceqname = new String[2]; @@ -163,9 +156,9 @@ } /** - * Declares a client dependency on the container to resolving a Service Endpoint Interface - * to a WSDL port. It optionally associates the Service Endpoint Interface with a - * particular port-component. + * Declares a client dependency on the container to resolving a Service Endpoint Interface to a WSDL port. It + * optionally associates the Service Endpoint Interface with a particular port-component. + * * @return the endpoint names */ public Iterator getServiceendpoints() { @@ -184,11 +177,9 @@ } /** - * A list of Handlers to use for this service-ref. - * - * The instantiation of the handler have to be done. + * A list of Handlers to use for this service-ref. The instantiation of the handler have to be done. */ - private final Map handlers = new HashMap<>(); + private final Map handlers = new HashMap<>(); public Iterator getHandlers() { return handlers.keySet().iterator(); @@ -255,7 +246,7 @@ sb.append(", port-component/service-endpoint-interface="); sb.append(this.getServiceendpoints()); } - if (handlers != null) { + if (!handlers.isEmpty()) { sb.append(", handler="); sb.append(handlers); } @@ -268,22 +259,14 @@ public int hashCode() { final int prime = 31; int result = super.hashCode(); - result = prime * result + - ((displayname == null) ? 0 : displayname.hashCode()); - result = prime * result + - ((handlers == null) ? 0 : handlers.hashCode()); - result = prime * - result + - ((jaxrpcmappingfile == null) ? 0 : jaxrpcmappingfile.hashCode()); - result = prime * result + - ((largeIcon == null) ? 0 : largeIcon.hashCode()); - result = prime * result + - ((serviceInterface == null) ? 0 : serviceInterface.hashCode()); + result = prime * result + ((displayname == null) ? 0 : displayname.hashCode()); + result = prime * result + handlers.hashCode(); + result = prime * result + ((jaxrpcmappingfile == null) ? 0 : jaxrpcmappingfile.hashCode()); + result = prime * result + ((largeIcon == null) ? 0 : largeIcon.hashCode()); + result = prime * result + ((serviceInterface == null) ? 0 : serviceInterface.hashCode()); result = prime * result + Arrays.hashCode(serviceqname); - result = prime * result + - ((smallIcon == null) ? 0 : smallIcon.hashCode()); - result = prime * result + - ((wsdlfile == null) ? 0 : wsdlfile.hashCode()); + result = prime * result + ((smallIcon == null) ? 0 : smallIcon.hashCode()); + result = prime * result + ((wsdlfile == null) ? 0 : wsdlfile.hashCode()); return result; } @@ -307,11 +290,7 @@ } else if (!displayname.equals(other.displayname)) { return false; } - if (handlers == null) { - if (other.handlers != null) { - return false; - } - } else if (!handlers.equals(other.handlers)) { + if (!handlers.equals(other.handlers)) { return false; } if (jaxrpcmappingfile == null) { @@ -346,12 +325,9 @@ return false; } if (wsdlfile == null) { - if (other.wsdlfile != null) { - return false; - } - } else if (!wsdlfile.equals(other.wsdlfile)) { - return false; + return other.wsdlfile == null; + } else { + return wsdlfile.equals(other.wsdlfile); } - return true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextTransaction.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextTransaction.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ContextTransaction.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ContextTransaction.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,10 +23,8 @@ /** - * Representation of an application resource reference, as represented in - * an <res-env-refy> element in the deployment descriptor. - * - * @author Craig R. McClanahan + * Representation of an application resource reference, as represented in an <res-env-ref> element in + * the deployment descriptor. */ public class ContextTransaction implements Serializable { @@ -38,10 +36,11 @@ /** * Holder for our configured properties. */ - private final Map properties = new HashMap<>(); + private final Map properties = new HashMap<>(); /** * @param name The property name + * * @return a configured property. */ public Object getProperty(String name) { @@ -50,7 +49,8 @@ /** * Set a configured property. - * @param name The property name + * + * @param name The property name * @param value The property value */ public void setProperty(String name, Object value) { @@ -59,6 +59,7 @@ /** * Remove a configured property. + * * @param name The property name */ public void removeProperty(String name) { @@ -67,6 +68,7 @@ /** * List properties. + * * @return the property names iterator */ public Iterator listProperties() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ErrorPage.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ErrorPage.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ErrorPage.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ErrorPage.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,15 +17,13 @@ package org.apache.tomcat.util.descriptor.web; import java.io.Serializable; +import java.util.Objects; import org.apache.tomcat.util.buf.UDecoder; /** - * Representation of an error page element for a web application, - * as represented in a <error-page> element in the - * deployment descriptor. - * - * @author Craig R. McClanahan + * Representation of an error page element for a web application, as represented in a <error-page> + * element in the deployment descriptor. */ public class ErrorPage extends XmlEncodingBase implements Serializable { @@ -35,8 +33,8 @@ // ----------------------------------------------------- Instance Variables /** - * The error (status) code for which this error page is active. Note that - * status code 0 is used for the default error page. + * The error (status) code for which this error page is active. Note that status code 0 is used for the default + * error page. */ private int errorCode = 0; @@ -121,12 +119,7 @@ * @param location The new location */ public void setLocation(String location) { - - // if ((location == null) || !location.startsWith("/")) - // throw new IllegalArgumentException - // ("Error Page Location must start with a '/'"); this.location = UDecoder.URLDecode(location, getCharset()); - } @@ -153,11 +146,7 @@ } public String getName() { - if (exceptionType == null) { - return Integer.toString(errorCode); - } else { - return exceptionType; - } + return Objects.requireNonNullElseGet(exceptionType, () -> Integer.toString(errorCode)); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/FilterDef.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/FilterDef.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/FilterDef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/FilterDef.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,17 +26,14 @@ /** - * Representation of a filter definition for a web application, as represented - * in a <filter> element in the deployment descriptor. - * - * @author Craig R. McClanahan + * Representation of a filter definition for a web application, as represented in a <filter> element + * in the deployment descriptor. */ public class FilterDef implements Serializable { private static final long serialVersionUID = 1L; - private static final StringManager sm = - StringManager.getManager(Constants.PACKAGE_NAME); + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); // ------------------------------------------------------------- Properties @@ -98,8 +95,7 @@ /** - * The name of this filter, which must be unique among the filters - * defined for a particular web application. + * The name of this filter, which must be unique among the filters defined for a particular web application. */ private String filterName = null; @@ -108,9 +104,8 @@ } public void setFilterName(String filterName) { - if (filterName == null || filterName.equals("")) { - throw new IllegalArgumentException( - sm.getString("filterDef.invalidFilterName", filterName)); + if (filterName == null || filterName.isEmpty()) { + throw new IllegalArgumentException(sm.getString("filterDef.invalidFilterName", filterName)); } this.filterName = filterName; } @@ -131,12 +126,11 @@ /** - * The set of initialization parameters for this filter, keyed by - * parameter name. + * The set of initialization parameters for this filter, keyed by parameter name. */ - private final Map parameters = new HashMap<>(); + private final Map parameters = new HashMap<>(); - public Map getParameterMap() { + public Map getParameterMap() { return this.parameters; } @@ -175,10 +169,9 @@ /** - * Add an initialization parameter to the set of parameters associated - * with this filter. + * Add an initialization parameter to the set of parameters associated with this filter. * - * @param name The initialization parameter name + * @param name The initialization parameter name * @param value The initialization parameter value */ public void addInitParameter(String name, String value) { @@ -198,13 +191,7 @@ */ @Override public String toString() { - StringBuilder sb = new StringBuilder("FilterDef["); - sb.append("filterName="); - sb.append(this.filterName); - sb.append(", filterClass="); - sb.append(this.filterClass); - sb.append(']'); - return sb.toString(); + return "FilterDef[" + "filterName=" + this.filterName + ", filterClass=" + this.filterClass + ']'; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/FilterMap.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/FilterMap.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/FilterMap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/FilterMap.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,12 +26,9 @@ import org.apache.tomcat.util.buf.UDecoder; /** - * Representation of a filter mapping for a web application, as represented - * in a <filter-mapping> element in the deployment - * descriptor. Each filter mapping must contain a filter name plus either - * a URL pattern or a servlet name. - * - * @author Craig R. McClanahan + * Representation of a filter mapping for a web application, as represented in a <filter-mapping> + * element in the deployment descriptor. Each filter mapping must contain a filter name plus either a URL pattern or a + * servlet name. */ public class FilterMap extends XmlEncodingBase implements Serializable { @@ -42,8 +39,7 @@ private static final long serialVersionUID = 1L; /** - * The name of this filter to be executed when this mapping matches - * a particular request. + * The name of this filter to be executed when this mapping matches a particular request. */ public static final int ERROR = 1; @@ -130,6 +126,7 @@ public void addURLPattern(String urlPattern) { addURLPatternDecoded(UDecoder.URLDecode(urlPattern, getCharset())); } + public void addURLPatternDecoded(String urlPattern) { if ("*".equals(urlPattern)) { this.matchAllUrlPatterns = true; @@ -142,10 +139,10 @@ } /** - * This method will be used to set the current state of the FilterMap - * representing the state of when filters should be applied. - * @param dispatcherString the dispatcher type which should - * match this filter + * This method will be used to set the current state of the FilterMap representing the state of when filters should + * be applied. + * + * @param dispatcherString the dispatcher type which should match this filter */ public void setDispatcher(String dispatcherString) { String dispatcher = dispatcherString.toUpperCase(Locale.ENGLISH); @@ -159,10 +156,10 @@ } else if (dispatcher.equals(DispatcherType.REQUEST.name())) { // apply REQUEST to the global dispatcherMapping. dispatcherMapping |= REQUEST; - } else if (dispatcher.equals(DispatcherType.ERROR.name())) { + } else if (dispatcher.equals(DispatcherType.ERROR.name())) { // apply ERROR to the global dispatcherMapping. dispatcherMapping |= ERROR; - } else if (dispatcher.equals(DispatcherType.ASYNC.name())) { + } else if (dispatcher.equals(DispatcherType.ASYNC.name())) { // apply ERROR to the global dispatcherMapping. dispatcherMapping |= ASYNC; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/FragmentJarScannerCallback.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,16 +33,14 @@ */ public class FragmentJarScannerCallback implements JarScannerCallback { - private static final String FRAGMENT_LOCATION = - "META-INF/web-fragment.xml"; + private static final String FRAGMENT_LOCATION = "META-INF/web-fragment.xml"; private final WebXmlParser webXmlParser; private final boolean delegate; private final boolean parseRequired; private final Map fragments = new HashMap<>(); - private boolean ok = true; + private boolean ok = true; - public FragmentJarScannerCallback(WebXmlParser webXmlParser, boolean delegate, - boolean parseRequired) { + public FragmentJarScannerCallback(WebXmlParser webXmlParser, boolean delegate, boolean parseRequired) { this.webXmlParser = webXmlParser; this.delegate = delegate; this.parseRequired = parseRequired; @@ -107,8 +105,7 @@ try { if (fragmentFile.isFile()) { try (InputStream stream = new FileInputStream(fragmentFile)) { - InputSource source = - new InputSource(fragmentFile.toURI().toURL().toString()); + InputSource source = new InputSource(fragmentFile.toURI().toURL().toString()); source.setByteStream(stream); if (!webXmlParser.parseWebXml(source, fragment, true)) { ok = false; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/Injectable.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/Injectable.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/Injectable.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/Injectable.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,6 +20,8 @@ public interface Injectable { String getName(); + void addInjectionTarget(String injectionTargetName, String jndiName); + List getInjectionTargets(); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/JspConfigDescriptorImpl.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/JspConfigDescriptorImpl.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/JspConfigDescriptorImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/JspConfigDescriptorImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,7 +29,7 @@ private final Collection taglibs; public JspConfigDescriptorImpl(Collection jspPropertyGroups, - Collection taglibs) { + Collection taglibs) { this.jspPropertyGroups = jspPropertyGroups; this.taglibs = taglibs; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroup.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroup.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroup.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroup.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,86 +29,135 @@ public class JspPropertyGroup extends XmlEncodingBase { private Boolean deferredSyntax = null; + public void setDeferredSyntax(String deferredSyntax) { this.deferredSyntax = Boolean.valueOf(deferredSyntax); } - public Boolean getDeferredSyntax() { return deferredSyntax; } + + public Boolean getDeferredSyntax() { + return deferredSyntax; + } private Boolean errorOnELNotFound = null; + public void setErrorOnELNotFound(String errorOnELNotFound) { this.errorOnELNotFound = Boolean.valueOf(errorOnELNotFound); } - public Boolean getErrorOnELNotFound() { return errorOnELNotFound; } + + public Boolean getErrorOnELNotFound() { + return errorOnELNotFound; + } private Boolean elIgnored = null; + public void setElIgnored(String elIgnored) { this.elIgnored = Boolean.valueOf(elIgnored); } - public Boolean getElIgnored() { return elIgnored; } + + public Boolean getElIgnored() { + return elIgnored; + } private final Collection includeCodas = new ArrayList<>(); + public void addIncludeCoda(String includeCoda) { includeCodas.add(includeCoda); } - public Collection getIncludeCodas() { return includeCodas; } + + public Collection getIncludeCodas() { + return includeCodas; + } private final Collection includePreludes = new ArrayList<>(); + public void addIncludePrelude(String includePrelude) { includePreludes.add(includePrelude); } - public Collection getIncludePreludes() { return includePreludes; } + + public Collection getIncludePreludes() { + return includePreludes; + } private Boolean isXml = null; + public void setIsXml(String isXml) { this.isXml = Boolean.valueOf(isXml); } - public Boolean getIsXml() { return isXml; } + + public Boolean getIsXml() { + return isXml; + } private String pageEncoding = null; + public void setPageEncoding(String pageEncoding) { this.pageEncoding = pageEncoding; } - public String getPageEncoding() { return this.pageEncoding; } + + public String getPageEncoding() { + return this.pageEncoding; + } private Boolean scriptingInvalid = null; + public void setScriptingInvalid(String scriptingInvalid) { this.scriptingInvalid = Boolean.valueOf(scriptingInvalid); } - public Boolean getScriptingInvalid() { return scriptingInvalid; } + + public Boolean getScriptingInvalid() { + return scriptingInvalid; + } private Boolean trimWhitespace = null; + public void setTrimWhitespace(String trimWhitespace) { this.trimWhitespace = Boolean.valueOf(trimWhitespace); } - public Boolean getTrimWhitespace() { return trimWhitespace; } - private LinkedHashSet urlPattern = new LinkedHashSet<>(); + public Boolean getTrimWhitespace() { + return trimWhitespace; + } + + private final LinkedHashSet urlPattern = new LinkedHashSet<>(); + public void addUrlPattern(String urlPattern) { addUrlPatternDecoded(UDecoder.URLDecode(urlPattern, getCharset())); } + public void addUrlPatternDecoded(String urlPattern) { this.urlPattern.add(urlPattern); } - public Set getUrlPatterns() { return this.urlPattern; } + + public Set getUrlPatterns() { + return this.urlPattern; + } private String defaultContentType = null; + public void setDefaultContentType(String defaultContentType) { this.defaultContentType = defaultContentType; } - public String getDefaultContentType() { return this.defaultContentType; } + + public String getDefaultContentType() { + return this.defaultContentType; + } private String buffer = null; + public void setBuffer(String buffer) { this.buffer = buffer; } - public String getBuffer() { return this.buffer; } + + public String getBuffer() { + return this.buffer; + } private Boolean errorOnUndeclaredNamespace = null; - public void setErrorOnUndeclaredNamespace( - String errorOnUndeclaredNamespace) { - this.errorOnUndeclaredNamespace = - Boolean.valueOf(errorOnUndeclaredNamespace); + + public void setErrorOnUndeclaredNamespace(String errorOnUndeclaredNamespace) { + this.errorOnUndeclaredNamespace = Boolean.valueOf(errorOnUndeclaredNamespace); } + public Boolean getErrorOnUndeclaredNamespace() { return this.errorOnUndeclaredNamespace; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/JspPropertyGroupDescriptorImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,15 +22,12 @@ import jakarta.servlet.descriptor.JspPropertyGroupDescriptor; - -public class JspPropertyGroupDescriptorImpl - implements JspPropertyGroupDescriptor{ +public class JspPropertyGroupDescriptorImpl implements JspPropertyGroupDescriptor { private final JspPropertyGroup jspPropertyGroup; - public JspPropertyGroupDescriptorImpl( - JspPropertyGroup jspPropertyGroup) { + public JspPropertyGroupDescriptorImpl(JspPropertyGroup jspPropertyGroup) { this.jspPropertyGroup = jspPropertyGroup; } @@ -88,8 +85,7 @@ String result = null; if (jspPropertyGroup.getErrorOnUndeclaredNamespace() != null) { - result = - jspPropertyGroup.getErrorOnUndeclaredNamespace().toString(); + result = jspPropertyGroup.getErrorOnUndeclaredNamespace().toString(); } return result; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/LocalStrings_es.properties tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/LocalStrings_es.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/LocalStrings_es.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/LocalStrings_es.properties 2026-01-23 19:33:36.000000000 +0000 @@ -26,6 +26,7 @@ webXml.duplicateFilter=Filtro de nombre duplicado [{0}]\n webXml.duplicateServletMapping=Los servlets llamados [{0}] y [{1}] estan ambos mapeados al patrón de URL [{2}] el cual no esta permitido +webXml.mergeConflictFilter=El filtro [{0}] se definió de forma inconsistente en varios fragmentos, incluido el fragmento con nombre [{1}] ubicado en [{2}] webXml.mergeConflictSessionTrackingMode=Los modos de seguimiento fueron definidos inconsistentemente en multiples fragmentos, incluyendo fragmentos con el nombre [{0}] localizado en [{1}]\n webXml.reservedName=Un archivo web.xml fue detectado usando un nombre reservado [{0}]. El nombre será ignorado para este fragmento. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/LoginConfig.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/LoginConfig.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/LoginConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/LoginConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,11 +21,8 @@ import org.apache.tomcat.util.buf.UDecoder; /** - * Representation of a login configuration element for a web application, - * as represented in a <login-config> element in the - * deployment descriptor. - * - * @author Craig R. McClanahan + * Representation of a login configuration element for a web application, as represented in a + * <login-config> element in the deployment descriptor. */ public class LoginConfig extends XmlEncodingBase implements Serializable { @@ -49,12 +46,11 @@ * Construct a new LoginConfig with the specified properties. * * @param authMethod The authentication method - * @param realmName The realm name - * @param loginPage The login page URI - * @param errorPage The error page URI + * @param realmName The realm name + * @param loginPage The login page URI + * @param errorPage The error page URI */ - public LoginConfig(String authMethod, String realmName, - String loginPage, String errorPage) { + public LoginConfig(String authMethod, String realmName, String loginPage, String errorPage) { super(); setAuthMethod(authMethod); @@ -69,8 +65,7 @@ /** - * The authentication method to use for application login. Must be - * BASIC, DIGEST, FORM, or CLIENT-CERT. + * The authentication method to use for application login. Must be BASIC, DIGEST, FORM, or CLIENT-CERT. */ private String authMethod = null; @@ -93,9 +88,6 @@ } public void setErrorPage(String errorPage) { - // if ((errorPage == null) || !errorPage.startsWith("/")) - // throw new IllegalArgumentException - // ("Error Page resource path must start with a '/'"); this.errorPage = UDecoder.URLDecode(errorPage, getCharset()); } @@ -110,16 +102,12 @@ } public void setLoginPage(String loginPage) { - // if ((loginPage == null) || !loginPage.startsWith("/")) - // throw new IllegalArgumentException - // ("Login Page resource path must start with a '/'"); this.loginPage = UDecoder.URLDecode(loginPage, getCharset()); } /** - * The realm name used when challenging the user for authentication - * credentials. + * The realm name used when challenging the user for authentication credentials. */ private String realmName = null; @@ -160,26 +148,26 @@ } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = 1; - result = prime * result - + ((authMethod == null) ? 0 : authMethod.hashCode()); - result = prime * result - + ((errorPage == null) ? 0 : errorPage.hashCode()); - result = prime * result - + ((loginPage == null) ? 0 : loginPage.hashCode()); - result = prime * result - + ((realmName == null) ? 0 : realmName.hashCode()); + result = prime * result + ((authMethod == null) ? 0 : authMethod.hashCode()); + result = prime * result + ((errorPage == null) ? 0 : errorPage.hashCode()); + result = prime * result + ((loginPage == null) ? 0 : loginPage.hashCode()); + result = prime * result + ((realmName == null) ? 0 : realmName.hashCode()); return result; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see java.lang.Object#equals(java.lang.Object) */ @Override @@ -213,13 +201,10 @@ return false; } if (realmName == null) { - if (other.realmName != null) { - return false; - } - } else if (!realmName.equals(other.realmName)) { - return false; + return other.realmName == null; + } else { + return realmName.equals(other.realmName); } - return true; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/MessageDestination.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,11 +18,11 @@ /** - *

          Representation of a message destination for a web application, as - * represented in a <message-destination> element - * in the deployment descriptor.

          + *

          + * Representation of a message destination for a web application, as represented in a + * <message-destination> element in the deployment descriptor. + *

          * - * @author Craig R. McClanahan * @since Tomcat 5.0 */ public class MessageDestination extends ResourceBase { @@ -110,12 +110,9 @@ public int hashCode() { final int prime = 31; int result = super.hashCode(); - result = prime * result + - ((displayName == null) ? 0 : displayName.hashCode()); - result = prime * result + - ((largeIcon == null) ? 0 : largeIcon.hashCode()); - result = prime * result + - ((smallIcon == null) ? 0 : smallIcon.hashCode()); + result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); + result = prime * result + ((largeIcon == null) ? 0 : largeIcon.hashCode()); + result = prime * result + ((smallIcon == null) ? 0 : smallIcon.hashCode()); return result; } @@ -147,12 +144,9 @@ return false; } if (smallIcon == null) { - if (other.smallIcon != null) { - return false; - } - } else if (!smallIcon.equals(other.smallIcon)) { - return false; + return other.smallIcon == null; + } else { + return smallIcon.equals(other.smallIcon); } - return true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/MessageDestinationRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,11 +18,11 @@ /** - *

          Representation of a message destination reference for a web application, - * as represented in a <message-destination-ref> element - * in the deployment descriptor.

          + *

          + * Representation of a message destination reference for a web application, as represented in a + * <message-destination-ref> element in the deployment descriptor. + *

          * - * @author Craig R. McClanahan * @since Tomcat 5.0 */ public class MessageDestinationRef extends ResourceBase { @@ -121,12 +121,9 @@ return false; } if (usage == null) { - if (other.usage != null) { - return false; - } - } else if (!usage.equals(other.usage)) { - return false; + return other.usage == null; + } else { + return usage.equals(other.usage); } - return true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/MultipartDef.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/MultipartDef.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/MultipartDef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/MultipartDef.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,7 +20,7 @@ /** - * Representation of a the multipart configuration for a servlet. + * Representation of the multipart configuration for a servlet. */ public class MultipartDef implements Serializable { @@ -77,16 +77,10 @@ public int hashCode() { final int prime = 31; int result = 1; - result = prime - * result - + ((fileSizeThreshold == null) ? 0 : fileSizeThreshold - .hashCode()); - result = prime * result - + ((location == null) ? 0 : location.hashCode()); - result = prime * result - + ((maxFileSize == null) ? 0 : maxFileSize.hashCode()); - result = prime * result - + ((maxRequestSize == null) ? 0 : maxRequestSize.hashCode()); + result = prime * result + ((fileSizeThreshold == null) ? 0 : fileSizeThreshold.hashCode()); + result = prime * result + ((location == null) ? 0 : location.hashCode()); + result = prime * result + ((maxFileSize == null) ? 0 : maxFileSize.hashCode()); + result = prime * result + ((maxRequestSize == null) ? 0 : maxRequestSize.hashCode()); return result; } @@ -124,13 +118,10 @@ return false; } if (maxRequestSize == null) { - if (other.maxRequestSize != null) { - return false; - } - } else if (!maxRequestSize.equals(other.maxRequestSize)) { - return false; + return other.maxRequestSize == null; + } else { + return maxRequestSize.equals(other.maxRequestSize); } - return true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/NamingResources.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/NamingResources.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/NamingResources.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/NamingResources.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,13 +17,10 @@ package org.apache.tomcat.util.descriptor.web; - /** - * Defines an interface for the object that is added to the representation of a - * JNDI resource in web.xml to enable it to also be the implementation of that - * JNDI resource. Only Catalina implements this interface but because the - * web.xml representation is shared this interface has to be visible to Catalina - * and Jasper. + * Defines an interface for the object that is added to the representation of a JNDI resource in web.xml to enable it to + * also be the implementation of that JNDI resource. Only Catalina implements this interface but because the web.xml + * representation is shared this interface has to be visible to Catalina and Jasper. */ public interface NamingResources { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ResourceBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,9 +25,7 @@ /** - * Representation of an Context element - * - * @author Peter Rossbach (pero@apache.org) + * Representation of a Context element. */ public class ResourceBase implements Serializable, Injectable { @@ -50,7 +48,6 @@ } - /** * The name of this resource. */ @@ -87,7 +84,7 @@ } public void setLookupName(String lookupName) { - if (lookupName == null || lookupName.length() == 0) { + if (lookupName == null || lookupName.isEmpty()) { this.lookupName = null; return; } @@ -98,10 +95,11 @@ /** * Holder for our configured properties. */ - private final Map properties = new HashMap<>(); + private final Map properties = new HashMap<>(); /** * @param name The property name + * * @return a configured property. */ public Object getProperty(String name) { @@ -110,7 +108,8 @@ /** * Set a configured property. - * @param name The property name + * + * @param name The property name * @param value The property value */ public void setProperty(String name, Object value) { @@ -119,6 +118,7 @@ /** * Remove a configured property. + * * @param name The property name */ public void removeProperty(String name) { @@ -127,6 +127,7 @@ /** * List properties. + * * @return the property names iterator */ public Iterator listProperties() { @@ -152,9 +153,9 @@ final int prime = 31; int result = 1; result = prime * result + ((description == null) ? 0 : description.hashCode()); - result = prime * result + ((injectionTargets == null) ? 0 : injectionTargets.hashCode()); + result = prime * result + injectionTargets.hashCode(); result = prime * result + ((name == null) ? 0 : name.hashCode()); - result = prime * result + ((properties == null) ? 0 : properties.hashCode()); + result = prime * result + properties.hashCode(); result = prime * result + ((type == null) ? 0 : type.hashCode()); result = prime * result + ((lookupName == null) ? 0 : lookupName.hashCode()); return result; @@ -180,11 +181,7 @@ } else if (!description.equals(other.description)) { return false; } - if (injectionTargets == null) { - if (other.injectionTargets != null) { - return false; - } - } else if (!injectionTargets.equals(other.injectionTargets)) { + if (!injectionTargets.equals(other.injectionTargets)) { return false; } if (name == null) { @@ -194,11 +191,7 @@ } else if (!name.equals(other.name)) { return false; } - if (properties == null) { - if (other.properties != null) { - return false; - } - } else if (!properties.equals(other.properties)) { + if (!properties.equals(other.properties)) { return false; } if (type == null) { @@ -209,13 +202,10 @@ return false; } if (lookupName == null) { - if (other.lookupName != null) { - return false; - } - } else if (!lookupName.equals(other.lookupName)) { - return false; + return other.lookupName == null; + } else { + return lookupName.equals(other.lookupName); } - return true; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/SecurityCollection.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SecurityCollection.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/SecurityCollection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SecurityCollection.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,17 +24,12 @@ /** - * Representation of a web resource collection for a web application's security - * constraint, as represented in a <web-resource-collection> - * element in the deployment descriptor. + * Representation of a web resource collection for a web application's security constraint, as represented in a + * <web-resource-collection> element in the deployment descriptor. *

          - * WARNING: It is assumed that instances of this class will be created - * and modified only within the context of a single thread, before the instance - * is made visible to the remainder of the application. After that, only read - * access is expected. Therefore, none of the read and write access within - * this class is synchronized. - * - * @author Craig R. McClanahan + * WARNING: It is assumed that instances of this class will be created and modified only within the context of a + * single thread, before the instance is made visible to the remainder of the application. After that, only read access + * is expected. Therefore, none of the read and write access within this class is synchronized. */ public class SecurityCollection extends XmlEncodingBase implements Serializable { @@ -56,7 +51,7 @@ /** * Construct a new security collection instance with specified values. * - * @param name Name of this security collection + * @param name Name of this security collection * @param description Description of this security collection */ public SecurityCollection(String name, String description) { @@ -80,13 +75,13 @@ /** * The HTTP methods explicitly covered by this web resource collection. */ - private String methods[] = new String[0]; + private String[] methods = new String[0]; /** * The HTTP methods explicitly excluded from this web resource collection. */ - private String omittedMethods[] = new String[0]; + private String[] omittedMethods = new String[0]; /** * The name of this web resource collection. @@ -97,12 +92,11 @@ /** * The URL patterns protected by this security collection. */ - private String patterns[] = new String[0]; + private String[] patterns = new String[0]; /** - * This security collection was established by a deployment descriptor. - * Defaults to true. + * This security collection was established by a deployment descriptor. Defaults to true. */ private boolean isFromDescriptor = true; @@ -155,6 +149,7 @@ /** * Set if this constraint was defined in a deployment descriptor. + * * @param isFromDescriptor true was declared in a descriptor */ public void setFromDescriptor(boolean isFromDescriptor) { @@ -166,8 +161,8 @@ /** - * Add an HTTP request method to be explicitly part of this web resource - * collection. + * Add an HTTP request method to be explicitly part of this web resource collection. + * * @param method The method */ public void addMethod(String method) { @@ -183,8 +178,8 @@ /** - * Add an HTTP request method to the methods explicitly excluded from this - * web resource collection. + * Add an HTTP request method to the methods explicitly excluded from this web resource collection. + * * @param method The method */ public void addOmittedMethod(String method) { @@ -198,11 +193,13 @@ /** * Add a URL pattern to be part of this web resource collection. + * * @param pattern The pattern */ public void addPattern(String pattern) { addPatternDecoded(UDecoder.URLDecode(pattern, StandardCharsets.UTF_8)); } + public void addPatternDecoded(String pattern) { if (pattern == null) { @@ -218,9 +215,10 @@ /** * Check if the collection applies to the specified method. + * * @param method Request method to check - * @return true if the specified HTTP request method is - * part of this web resource collection. + * + * @return true if the specified HTTP request method is part of this web resource collection. */ public boolean findMethod(String method) { @@ -235,11 +233,9 @@ } return false; } - if (omittedMethods.length > 0) { - for (String omittedMethod : omittedMethods) { - if (omittedMethod.equals(method)) { - return false; - } + for (String omittedMethod : omittedMethods) { + if (omittedMethod.equals(method)) { + return false; } } return true; @@ -247,9 +243,8 @@ /** - * @return the set of HTTP request methods that are part of this web - * resource collection, or a zero-length array if no methods have been - * explicitly included. + * @return the array of HTTP request methods that are part of this web resource collection, or a zero-length array + * if no methods have been explicitly included. */ public String[] findMethods() { return methods; @@ -257,9 +252,8 @@ /** - * @return the set of HTTP request methods that are explicitly excluded from - * this web resource collection, or a zero-length array if no request - * methods are excluded. + * @return the array of HTTP request methods that are explicitly excluded from this web resource collection, or a + * zero-length array if no request methods are excluded. */ public String[] findOmittedMethods() { return omittedMethods; @@ -270,6 +264,7 @@ * Is the specified pattern part of this web resource collection? * * @param pattern Pattern to be compared + * * @return true if the pattern is part of the collection */ public boolean findPattern(String pattern) { @@ -283,9 +278,8 @@ /** - * @return the set of URL patterns that are part of this web resource - * collection. If none have been specified, a zero-length array is - * returned. + * @return the array of URL patterns that are part of this web resource collection. If none have been specified, a + * zero-length array is returned. */ public String[] findPatterns() { return patterns; @@ -293,8 +287,7 @@ /** - * Remove the specified HTTP request method from those that are part - * of this web resource collection. + * Remove the specified HTTP request method from those that are part of this web resource collection. * * @param method Request method to be removed */ @@ -312,7 +305,7 @@ } if (n >= 0) { int j = 0; - String results[] = new String[methods.length - 1]; + String[] results = new String[methods.length - 1]; for (int i = 0; i < methods.length; i++) { if (i != n) { results[j++] = methods[i]; @@ -325,8 +318,8 @@ /** - * Remove the specified HTTP request method from those that are explicitly - * excluded from this web resource collection. + * Remove the specified HTTP request method from those that are explicitly excluded from this web resource + * collection. * * @param method Request method to be removed */ @@ -344,7 +337,7 @@ } if (n >= 0) { int j = 0; - String results[] = new String[omittedMethods.length - 1]; + String[] results = new String[omittedMethods.length - 1]; for (int i = 0; i < omittedMethods.length; i++) { if (i != n) { results[j++] = omittedMethods[i]; @@ -357,8 +350,7 @@ /** - * Remove the specified URL pattern from those that are part of this - * web resource collection. + * Remove the specified URL pattern from those that are part of this web resource collection. * * @param pattern Pattern to be removed */ @@ -376,7 +368,7 @@ } if (n >= 0) { int j = 0; - String results[] = new String[patterns.length - 1]; + String[] results = new String[patterns.length - 1]; for (int i = 0; i < patterns.length; i++) { if (i != n) { results[j++] = patterns[i]; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/SecurityConstraint.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SecurityConstraint.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/SecurityConstraint.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SecurityConstraint.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,17 +38,12 @@ /** - * Representation of a security constraint element for a web application, - * as represented in a <security-constraint> element in the - * deployment descriptor. + * Representation of a security constraint element for a web application, as represented in a + * <security-constraint> element in the deployment descriptor. *

          - * WARNING: It is assumed that instances of this class will be created - * and modified only within the context of a single thread, before the instance - * is made visible to the remainder of the application. After that, only read - * access is expected. Therefore, none of the read and write access within - * this class is synchronized. - * - * @author Craig R. McClanahan + * WARNING: It is assumed that instances of this class will be created and modified only within the context of a + * single thread, before the instance is made visible to the remainder of the application. After that, only read access + * is expected. Therefore, none of the read and write access within this class is synchronized. */ public class SecurityConstraint extends XmlEncodingBase implements Serializable { @@ -57,8 +52,7 @@ public static final String ROLE_ALL_ROLES = "*"; public static final String ROLE_ALL_AUTHENTICATED_USERS = "**"; - private static final StringManager sm = - StringManager.getManager(Constants.PACKAGE_NAME); + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); // ----------------------------------------------------------- Constructors @@ -75,41 +69,37 @@ /** - * Was the "all roles" wildcard - {@link #ROLE_ALL_ROLES} - included in the - * authorization constraints for this security constraint? + * Was the "all roles" wildcard - {@link #ROLE_ALL_ROLES} - included in the authorization constraints for this + * security constraint? */ private boolean allRoles = false; /** - * Was the "all authenticated users" wildcard - - * {@link #ROLE_ALL_AUTHENTICATED_USERS} - included in the authorization - * constraints for this security constraint? + * Was the "all authenticated users" wildcard - {@link #ROLE_ALL_AUTHENTICATED_USERS} - included in the + * authorization constraints for this security constraint? */ private boolean authenticatedUsers = false; /** - * Was an authorization constraint included in this security constraint? - * This is necessary to distinguish the case where an auth-constraint with - * no roles (signifying no direct access at all) was requested, versus - * a lack of auth-constraint which implies no access control checking. + * Was an authorization constraint included in this security constraint? This is necessary to distinguish the case + * where an auth-constraint with no roles (signifying no direct access at all) was requested, versus a lack of + * auth-constraint which implies no access control checking. */ private boolean authConstraint = false; /** - * The set of roles permitted to access resources protected by this - * security constraint. + * The set of roles permitted to access resources protected by this security constraint. */ - private String authRoles[] = new String[0]; + private String[] authRoles = new String[0]; /** - * The set of web resource collections protected by this security - * constraint. + * The set of web resource collections protected by this security constraint. */ - private SecurityCollection collections[] = new SecurityCollection[0]; + private SecurityCollection[] collections = new SecurityCollection[0]; /** @@ -119,8 +109,7 @@ /** - * The user data constraint for this security constraint. Must be NONE, - * INTEGRAL, or CONFIDENTIAL. + * The user data constraint for this security constraint. Must be NONE, INTEGRAL, or CONFIDENTIAL. */ private String userConstraint = "NONE"; @@ -129,8 +118,8 @@ /** - * Was the "all roles" wildcard included in this authentication - * constraint? + * Was the "all roles" wildcard included in this authentication constraint? + * * @return true if all roles */ public boolean getAllRoles() { @@ -141,8 +130,8 @@ /** - * Was the "all authenticated users" wildcard included in this - * authentication constraint? + * Was the "all authenticated users" wildcard included in this authentication constraint? + * * @return true if all authenticated users */ public boolean getAuthenticatedUsers() { @@ -151,8 +140,8 @@ /** - * Return the authorization constraint present flag for this security - * constraint. + * Return the authorization constraint present flag for this security constraint. + * * @return true if this needs authorization */ public boolean getAuthConstraint() { @@ -163,8 +152,8 @@ /** - * Set the authorization constraint present flag for this security - * constraint. + * Set the authorization constraint present flag for this security constraint. + * * @param authConstraint The new value */ public void setAuthConstraint(boolean authConstraint) { @@ -186,6 +175,7 @@ /** * Set the display name of this security constraint. + * * @param displayName The new value */ public void setDisplayName(String displayName) { @@ -197,6 +187,7 @@ /** * Return the user data constraint for this security constraint. + * * @return the user constraint */ public String getUserConstraint() { @@ -221,8 +212,7 @@ /** - * Called in the unlikely event that an application defines a role named - * "**". + * Called in the unlikely event that an application defines a role named "**". */ public void treatAllAuthenticatedUsersAsApplicationRole() { if (authenticatedUsers) { @@ -240,8 +230,8 @@ /** - * Add an authorization role, which is a role name that will be - * permitted access to the resources protected by this security constraint. + * Add an authorization role, which is a role name that will be permitted access to the resources protected by this + * security constraint. * * @param authRole Role name to be added */ @@ -278,8 +268,7 @@ /** - * Add a new web resource collection to those protected by this - * security constraint. + * Add a new web resource collection to those protected by this security constraint. * * @param collection The new web resource collection */ @@ -291,7 +280,7 @@ collection.setCharset(getCharset()); - SecurityCollection results[] = Arrays.copyOf(collections, collections.length + 1); + SecurityCollection[] results = Arrays.copyOf(collections, collections.length + 1); results[collections.length] = collection; collections = results; @@ -302,8 +291,9 @@ * Check a role. * * @param role Role name to be checked - * @return true if the specified role is permitted access to - * the resources protected by this security constraint. + * + * @return true if the specified role is permitted access to the resources protected by this security + * constraint. */ public boolean findAuthRole(String role) { @@ -321,10 +311,10 @@ /** - * Return the set of roles that are permitted access to the resources - * protected by this security constraint. If none have been defined, - * a zero-length array is returned (which implies that all authenticated - * users are permitted access). + * Return the set of roles that are permitted access to the resources protected by this security constraint. If none + * have been defined, a zero-length array is returned (which implies that all authenticated users are permitted + * access). + * * @return the roles array */ public String[] findAuthRoles() { @@ -333,10 +323,10 @@ /** - * Return the web resource collection for the specified name, if any; - * otherwise, return null. + * Return the web resource collection for the specified name, if any; otherwise, return null. * * @param name Web resource collection name to return + * * @return the collection */ public SecurityCollection findCollection(String name) { @@ -353,9 +343,9 @@ /** - * Return all of the web resource collections protected by this - * security constraint. If there are none, a zero-length array is - * returned. + * Return all of the web resource collections protected by this security constraint. If there are none, a + * zero-length array is returned. + * * @return the collections array */ public SecurityCollection[] findCollections() { @@ -365,10 +355,12 @@ /** * Check if the constraint applies to a URI and method. - * @param uri Context-relative URI to check + * + * @param uri Context-relative URI to check * @param method Request method being used - * @return true if the specified context-relative URI (and - * associated HTTP method) are protected by this security constraint. + * + * @return true if the specified context-relative URI (and associated HTTP method) are protected by + * this security constraint. */ public boolean included(String uri, String method) { @@ -382,7 +374,7 @@ if (!collection.findMethod(method)) { continue; } - String patterns[] = collection.findPatterns(); + String[] patterns = collection.findPatterns(); for (String pattern : patterns) { if (matchPattern(uri, pattern)) { return true; @@ -397,8 +389,8 @@ /** - * Remove the specified role from the set of roles permitted to access - * the resources protected by this security constraint. + * Remove the specified role from the set of roles permitted to access the resources protected by this security + * constraint. * * @param authRole Role name to be removed */ @@ -427,7 +419,7 @@ } if (n >= 0) { int j = 0; - String results[] = new String[authRoles.length - 1]; + String[] results = new String[authRoles.length - 1]; for (int i = 0; i < authRoles.length; i++) { if (i != n) { results[j++] = authRoles[i]; @@ -439,8 +431,7 @@ /** - * Remove the specified web resource collection from those protected by - * this security constraint. + * Remove the specified web resource collection from those protected by this security constraint. * * @param collection Web resource collection to be removed */ @@ -458,8 +449,7 @@ } if (n >= 0) { int j = 0; - SecurityCollection results[] = - new SecurityCollection[collections.length - 1]; + SecurityCollection[] results = new SecurityCollection[collections.length - 1]; for (int i = 0; i < collections.length; i++) { if (i != n) { results[j++] = collections[i]; @@ -492,21 +482,19 @@ /** - * Does the specified request path match the specified URL pattern? - * This method follows the same rules (in the same order) as those used - * for mapping requests to servlets. + * Does the specified request path match the specified URL pattern? This method follows the same rules (in the same + * order) as those used for mapping requests to servlets. * - * @param path Context-relative request path to be checked - * (must start with '/') + * @param path Context-relative request path to be checked (must start with '/') * @param pattern URL pattern to be compared against */ private boolean matchPattern(String path, String pattern) { // Normalize the argument strings - if (path == null || path.length() == 0) { + if (path == null || path.isEmpty()) { path = "/"; } - if (pattern == null || pattern.length() == 0) { + if (pattern == null || pattern.isEmpty()) { pattern = "/"; } @@ -518,9 +506,8 @@ // Check for path prefix matching if (pattern.startsWith("/") && pattern.endsWith("/*")) { pattern = pattern.substring(0, pattern.length() - 2); - if (pattern.length() == 0) - { - return true; // "/*" is the same as "/" + if (pattern.isEmpty()) { + return true; // "/*" is the same as "/" } if (path.endsWith("/")) { path = path.substring(0, path.length() - 1); @@ -542,43 +529,30 @@ if (pattern.startsWith("*.")) { int slash = path.lastIndexOf('/'); int period = path.lastIndexOf('.'); - if ((slash >= 0) && (period > slash) && - path.endsWith(pattern.substring(1))) { - return true; - } - return false; + return (slash >= 0) && (period > slash) && path.endsWith(pattern.substring(1)); } // Check for universal mapping - if (pattern.equals("/")) { - return true; - } - - return false; + return pattern.equals("/"); } /** - * Convert a {@link ServletSecurityElement} to an array of - * {@link SecurityConstraint}(s). + * Convert a {@link ServletSecurityElement} to an array of {@link SecurityConstraint}(s). + * + * @param element The element to be converted + * @param urlPattern The url pattern that the element should be applied to * - * @param element The element to be converted - * @param urlPattern The url pattern that the element should be applied - * to - * @return The (possibly zero length) array of constraints that - * are the equivalent to the input + * @return The (possibly zero length) array of constraints that are the equivalent to the input */ - public static SecurityConstraint[] createConstraints( - ServletSecurityElement element, String urlPattern) { + public static SecurityConstraint[] createConstraints(ServletSecurityElement element, String urlPattern) { Set result = new HashSet<>(); // Add the per method constraints - Collection methods = - element.getHttpMethodConstraints(); + Collection methods = element.getHttpMethodConstraints(); for (HttpMethodConstraintElement methodElement : methods) { - SecurityConstraint constraint = - createConstraint(methodElement, urlPattern, true); + SecurityConstraint constraint = createConstraint(methodElement, urlPattern, true); // There will always be a single collection SecurityCollection collection = constraint.findCollections()[0]; collection.addMethod(methodElement.getMethodName()); @@ -601,15 +575,14 @@ return result.toArray(new SecurityConstraint[0]); } - private static SecurityConstraint createConstraint( - HttpConstraintElement element, String urlPattern, boolean alwaysCreate) { + private static SecurityConstraint createConstraint(HttpConstraintElement element, String urlPattern, + boolean alwaysCreate) { SecurityConstraint constraint = new SecurityConstraint(); SecurityCollection collection = new SecurityCollection(); boolean create = alwaysCreate; - if (element.getTransportGuarantee() != - ServletSecurity.TransportGuarantee.NONE) { + if (element.getTransportGuarantee() != ServletSecurity.TransportGuarantee.NONE) { constraint.setUserConstraint(element.getTransportGuarantee().name()); create = true; } @@ -635,8 +608,7 @@ } - public static SecurityConstraint[] findUncoveredHttpMethods( - SecurityConstraint[] constraints, + public static SecurityConstraint[] findUncoveredHttpMethods(SecurityConstraint[] constraints, boolean denyUncoveredHttpMethods, Log log) { Set coveredPatterns = new HashSet<>(); @@ -690,7 +662,7 @@ } // Now check the potentially uncovered patterns - for (Map.Entry> entry : urlMethodMap.entrySet()) { + for (Map.Entry> entry : urlMethodMap.entrySet()) { String pattern = entry.getKey(); if (coveredPatterns.contains(pattern)) { // Fully covered. Ignore any partial coverage @@ -708,9 +680,7 @@ msg.append(' '); } if (denyUncoveredHttpMethods) { - log.info(sm.getString( - "securityConstraint.uncoveredHttpMethodFix", - pattern, msg.toString().trim())); + log.info(sm.getString("securityConstraint.uncoveredHttpMethodFix", pattern, msg.toString().trim())); SecurityCollection collection = new SecurityCollection(); for (String method : methods) { collection.addOmittedMethod(method); @@ -722,9 +692,7 @@ constraint.addCollection(collection); newConstraints.add(constraint); } else { - log.error(sm.getString( - "securityConstraint.uncoveredHttpMethod", - pattern, msg.toString().trim())); + log.error(sm.getString("securityConstraint.uncoveredHttpMethod", pattern, msg.toString().trim())); } continue; } @@ -733,19 +701,16 @@ // pattern is fully covered. omittedMethods.removeAll(methods); - handleOmittedMethods(omittedMethods, pattern, denyUncoveredHttpMethods, - newConstraints, log); + handleOmittedMethods(omittedMethods, pattern, denyUncoveredHttpMethods, newConstraints, log); } - for (Map.Entry> entry : - urlOmittedMethodMap.entrySet()) { + for (Map.Entry> entry : urlOmittedMethodMap.entrySet()) { String pattern = entry.getKey(); if (coveredPatterns.contains(pattern)) { // Fully covered. Ignore any partial coverage continue; } - handleOmittedMethods(entry.getValue(), pattern, denyUncoveredHttpMethods, - newConstraints, log); + handleOmittedMethods(entry.getValue(), pattern, denyUncoveredHttpMethods, newConstraints, log); } return newConstraints.toArray(new SecurityConstraint[0]); @@ -754,16 +719,15 @@ private static void handleOmittedMethods(Set omittedMethods, String pattern, boolean denyUncoveredHttpMethods, List newConstraints, Log log) { - if (omittedMethods.size() > 0) { + if (!omittedMethods.isEmpty()) { StringBuilder msg = new StringBuilder(); for (String method : omittedMethods) { msg.append(method); msg.append(' '); } if (denyUncoveredHttpMethods) { - log.info(sm.getString( - "securityConstraint.uncoveredHttpOmittedMethodFix", - pattern, msg.toString().trim())); + log.info(sm.getString("securityConstraint.uncoveredHttpOmittedMethodFix", pattern, + msg.toString().trim())); SecurityCollection collection = new SecurityCollection(); for (String method : omittedMethods) { collection.addMethod(method); @@ -775,9 +739,8 @@ constraint.addCollection(collection); newConstraints.add(constraint); } else { - log.error(sm.getString( - "securityConstraint.uncoveredHttpOmittedMethod", - pattern, msg.toString().trim())); + log.error( + sm.getString("securityConstraint.uncoveredHttpOmittedMethod", pattern, msg.toString().trim())); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/SecurityRoleRef.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SecurityRoleRef.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/SecurityRoleRef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SecurityRoleRef.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,9 +20,10 @@ /** - *

          Representation of a security role reference for a web application, as - * represented in a <security-role-ref> element - * in the deployment descriptor.

          + *

          + * Representation of a security role reference for a web application, as represented in a + * <security-role-ref> element in the deployment descriptor. + *

          * * @since Tomcat 5.5 */ @@ -61,7 +62,6 @@ } - // --------------------------------------------------------- Public Methods diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ServletDef.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ServletDef.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/ServletDef.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/ServletDef.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,16 +26,15 @@ /** - * Representation of a servlet definition for a web application, as represented - * in a <servlet> element in the deployment descriptor. + * Representation of a servlet definition for a web application, as represented in a <servlet> + * element in the deployment descriptor. */ public class ServletDef implements Serializable { private static final long serialVersionUID = 1L; - private static final StringManager sm = - StringManager.getManager(Constants.PACKAGE_NAME); + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); // ------------------------------------------------------------- Properties @@ -96,8 +95,7 @@ /** - * The name of this servlet, which must be unique among the servlets - * defined for a particular web application. + * The name of this servlet, which must be unique among the servlets defined for a particular web application. */ private String servletName = null; @@ -106,9 +104,8 @@ } public void setServletName(String servletName) { - if (servletName == null || servletName.equals("")) { - throw new IllegalArgumentException( - sm.getString("servletDef.invalidServletName", servletName)); + if (servletName == null || servletName.isEmpty()) { + throw new IllegalArgumentException(sm.getString("servletDef.invalidServletName", servletName)); } this.servletName = servletName; } @@ -143,20 +140,18 @@ /** - * The set of initialization parameters for this servlet, keyed by - * parameter name. + * The set of initialization parameters for this servlet, keyed by parameter name. */ - private final Map parameters = new HashMap<>(); + private final Map parameters = new HashMap<>(); - public Map getParameterMap() { + public Map getParameterMap() { return this.parameters; } /** - * Add an initialization parameter to the set of parameters associated - * with this servlet. + * Add an initialization parameter to the set of parameters associated with this servlet. * - * @param name The initialisation parameter name + * @param name The initialisation parameter name * @param value The initialisation parameter value */ public void addInitParameter(String name, String value) { @@ -208,8 +203,8 @@ } /** - * Add a security-role-ref to the set of security-role-refs associated - * with this servlet. + * Add a security-role-ref to the set of security-role-refs associated with this servlet. + * * @param securityRoleRef The security role */ public void addSecurityRoleRef(SecurityRoleRef securityRoleRef) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/SessionConfig.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SessionConfig.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/SessionConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/SessionConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,21 +23,20 @@ import jakarta.servlet.SessionTrackingMode; /** - * Representation of a session configuration element for a web application, - * as represented in a <session-config> element in the - * deployment descriptor. + * Representation of a session configuration element for a web application, as represented in a + * <session-config> element in the deployment descriptor. */ public class SessionConfig { private Integer sessionTimeout; private String cookieName; private final Map cookieAttributes = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - private final EnumSet sessionTrackingModes = - EnumSet.noneOf(SessionTrackingMode.class); + private final EnumSet sessionTrackingModes = EnumSet.noneOf(SessionTrackingMode.class); public Integer getSessionTimeout() { return sessionTimeout; } + public void setSessionTimeout(String sessionTimeout) { this.sessionTimeout = Integer.valueOf(sessionTimeout); } @@ -45,6 +44,7 @@ public String getCookieName() { return cookieName; } + public void setCookieName(String cookieName) { this.cookieName = cookieName; } @@ -52,6 +52,7 @@ public String getCookieDomain() { return getCookieAttribute(Constants.COOKIE_DOMAIN_ATTR); } + public void setCookieDomain(String cookieDomain) { setCookieAttribute(Constants.COOKIE_DOMAIN_ATTR, cookieDomain); } @@ -59,6 +60,7 @@ public String getCookiePath() { return getCookieAttribute(Constants.COOKIE_PATH_ATTR); } + public void setCookiePath(String cookiePath) { setCookieAttribute(Constants.COOKIE_PATH_ATTR, cookiePath); } @@ -66,6 +68,7 @@ public String getCookieComment() { return getCookieAttribute(Constants.COOKIE_COMMENT_ATTR); } + public void setCookieComment(String cookieComment) { setCookieAttribute(Constants.COOKIE_COMMENT_ATTR, cookieComment); } @@ -77,6 +80,7 @@ } return Boolean.valueOf(httpOnly); } + public void setCookieHttpOnly(String cookieHttpOnly) { setCookieAttribute(Constants.COOKIE_HTTP_ONLY_ATTR, cookieHttpOnly); } @@ -88,6 +92,7 @@ } return Boolean.valueOf(secure); } + public void setCookieSecure(String cookieSecure) { setCookieAttribute(Constants.COOKIE_SECURE_ATTR, cookieSecure); } @@ -99,6 +104,7 @@ } return Integer.valueOf(maxAge); } + public void setCookieMaxAge(String cookieMaxAge) { setCookieAttribute(Constants.COOKIE_MAX_AGE_ATTR, cookieMaxAge); } @@ -106,9 +112,11 @@ public Map getCookieAttributes() { return cookieAttributes; } + public void setCookieAttribute(String name, String value) { cookieAttributes.put(name, value); } + public String getCookieAttribute(String name) { return cookieAttributes.get(name); } @@ -116,8 +124,8 @@ public EnumSet getSessionTrackingModes() { return sessionTrackingModes; } + public void addSessionTrackingMode(String sessionTrackingMode) { - sessionTrackingModes.add( - SessionTrackingMode.valueOf(sessionTrackingMode)); + sessionTrackingModes.add(SessionTrackingMode.valueOf(sessionTrackingMode)); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/TaglibDescriptorImpl.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/TaglibDescriptorImpl.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/TaglibDescriptorImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/TaglibDescriptorImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,8 +42,7 @@ public int hashCode() { final int prime = 31; int result = 1; - result = prime * result - + ((location == null) ? 0 : location.hashCode()); + result = prime * result + ((location == null) ? 0 : location.hashCode()); result = prime * result + ((uri == null) ? 0 : uri.hashCode()); return result; } @@ -65,13 +64,10 @@ return false; } if (uri == null) { - if (other.uri != null) { - return false; - } - } else if (!uri.equals(other.uri)) { - return false; + return other.uri == null; + } else { + return uri.equals(other.uri); } - return true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/WebRuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,18 +30,17 @@ import org.xml.sax.Attributes; /** - *

          RuleSet for processing the contents of a web application - * deployment descriptor (/WEB-INF/web.xml) resource.

          - * - * @author Craig R. McClanahan + *

          + * RuleSet for processing the contents of a web application deployment descriptor + * (/WEB-INF/web.xml) resource. + *

          */ public class WebRuleSet implements RuleSet { /** * The string resources for this package. */ - protected static final StringManager sm = - StringManager.getManager(Constants.PACKAGE_NAME); + protected static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); // ----------------------------------------------------- Instance Variables @@ -52,14 +51,12 @@ protected final String prefix; /** - * The full pattern matching prefix, including the webapp or web-fragment - * component, to use for matching elements + * The full pattern matching prefix, including the webapp or web-fragment component, to use for matching elements */ protected final String fullPrefix; /** - * Flag that indicates if this ruleset is for a web-fragment.xml file or for - * a web.xml file. + * Flag that indicates if this ruleset is for a web-fragment.xml file or for a web.xml file. */ protected final boolean fragment; @@ -99,13 +96,12 @@ protected final RelativeOrderingRule relativeOrdering; - // ------------------------------------------------------------ Constructor /** - * Construct an instance of this RuleSet with the default - * matching pattern prefix and default fragment setting. + * Construct an instance of this RuleSet with the default matching pattern prefix and default fragment + * setting. */ public WebRuleSet() { @@ -115,8 +111,8 @@ /** - * Construct an instance of this RuleSet with the default - * matching pattern prefix. + * Construct an instance of this RuleSet with the default matching pattern prefix. + * * @param fragment true if this is a web fragment */ public WebRuleSet(boolean fragment) { @@ -127,18 +123,16 @@ /** - * Construct an instance of this RuleSet with the specified - * matching pattern prefix. + * Construct an instance of this RuleSet with the specified matching pattern prefix. * - * @param prefix Prefix for matching pattern rules (including the - * trailing slash character) + * @param prefix Prefix for matching pattern rules (including the trailing slash character) * @param fragment true if this is a web fragment */ public WebRuleSet(String prefix, boolean fragment) { this.prefix = prefix; this.fragment = fragment; - if(fragment) { + if (fragment) { fullPrefix = prefix + "web-fragment"; } else { fullPrefix = prefix + "web-app"; @@ -152,22 +146,18 @@ // --------------------------------------------------------- Public Methods /** - *

          Add the set of Rule instances defined in this RuleSet to the - * specified Digester instance, associating them with - * our namespace URI (if any). This method should only be called - * by a Digester instance.

          + *

          + * Add the set of Rule instances defined in this RuleSet to the specified Digester instance, + * associating them with our namespace URI (if any). This method should only be called by a Digester instance. + *

          * - * @param digester Digester instance to which the new Rule instances - * should be added. + * @param digester Digester instance to which the new Rule instances should be added. */ @Override public void addRuleInstances(Digester digester) { - digester.addRule(fullPrefix, - new SetPublicIdRule("setPublicId")); - digester.addRule(fullPrefix, - new IgnoreAnnotationsRule()); - digester.addRule(fullPrefix, - new VersionRule()); + digester.addRule(fullPrefix, new SetPublicIdRule("setPublicId")); + digester.addRule(fullPrefix, new IgnoreAnnotationsRule()); + digester.addRule(fullPrefix, new VersionRule()); // Required for both fragments and non-fragments digester.addRule(fullPrefix + "/absolute-ordering", absoluteOrdering); @@ -176,515 +166,344 @@ if (fragment) { // web-fragment.xml digester.addRule(fullPrefix + "/name", name); - digester.addCallMethod(fullPrefix + "/ordering/after/name", - "addAfterOrdering", 0); - digester.addCallMethod(fullPrefix + "/ordering/after/others", - "addAfterOrderingOthers"); - digester.addCallMethod(fullPrefix + "/ordering/before/name", - "addBeforeOrdering", 0); - digester.addCallMethod(fullPrefix + "/ordering/before/others", - "addBeforeOrderingOthers"); + digester.addCallMethod(fullPrefix + "/ordering/after/name", "addAfterOrdering", 0); + digester.addCallMethod(fullPrefix + "/ordering/after/others", "addAfterOrderingOthers"); + digester.addCallMethod(fullPrefix + "/ordering/before/name", "addBeforeOrdering", 0); + digester.addCallMethod(fullPrefix + "/ordering/before/others", "addBeforeOrderingOthers"); } else { // web.xml - digester.addCallMethod(fullPrefix + "/absolute-ordering/name", - "addAbsoluteOrdering", 0); - digester.addCallMethod(fullPrefix + "/absolute-ordering/others", - "addAbsoluteOrderingOthers"); - digester.addRule(fullPrefix + "/deny-uncovered-http-methods", - new SetDenyUncoveredHttpMethodsRule()); - digester.addCallMethod(fullPrefix + "/request-character-encoding", - "setRequestCharacterEncoding", 0); - digester.addCallMethod(fullPrefix + "/response-character-encoding", - "setResponseCharacterEncoding", 0); + digester.addCallMethod(fullPrefix + "/absolute-ordering/name", "addAbsoluteOrdering", 0); + digester.addCallMethod(fullPrefix + "/absolute-ordering/others", "addAbsoluteOrderingOthers"); + digester.addRule(fullPrefix + "/deny-uncovered-http-methods", new SetDenyUncoveredHttpMethodsRule()); + digester.addCallMethod(fullPrefix + "/request-character-encoding", "setRequestCharacterEncoding", 0); + digester.addCallMethod(fullPrefix + "/response-character-encoding", "setResponseCharacterEncoding", 0); } - digester.addCallMethod(fullPrefix + "/context-param", - "addContextParam", 2); + digester.addCallMethod(fullPrefix + "/context-param", "addContextParam", 2); digester.addCallParam(fullPrefix + "/context-param/param-name", 0); digester.addCallParam(fullPrefix + "/context-param/param-value", 1); - digester.addCallMethod(fullPrefix + "/display-name", - "setDisplayName", 0); + digester.addCallMethod(fullPrefix + "/display-name", "setDisplayName", 0); - digester.addRule(fullPrefix + "/distributable", - new SetDistributableRule()); + digester.addRule(fullPrefix + "/distributable", new SetDistributableRule()); configureNamingRules(digester); - digester.addObjectCreate(fullPrefix + "/error-page", - "org.apache.tomcat.util.descriptor.web.ErrorPage"); - digester.addSetNext(fullPrefix + "/error-page", - "addErrorPage", - "org.apache.tomcat.util.descriptor.web.ErrorPage"); - - digester.addCallMethod(fullPrefix + "/error-page/error-code", - "setErrorCode", 0); - digester.addCallMethod(fullPrefix + "/error-page/exception-type", - "setExceptionType", 0); - digester.addCallMethod(fullPrefix + "/error-page/location", - "setLocation", 0); - - digester.addObjectCreate(fullPrefix + "/filter", - "org.apache.tomcat.util.descriptor.web.FilterDef"); - digester.addSetNext(fullPrefix + "/filter", - "addFilter", - "org.apache.tomcat.util.descriptor.web.FilterDef"); - - digester.addCallMethod(fullPrefix + "/filter/description", - "setDescription", 0); - digester.addCallMethod(fullPrefix + "/filter/display-name", - "setDisplayName", 0); - digester.addCallMethod(fullPrefix + "/filter/filter-class", - "setFilterClass", 0); - digester.addCallMethod(fullPrefix + "/filter/filter-name", - "setFilterName", 0); - digester.addCallMethod(fullPrefix + "/filter/icon/large-icon", - "setLargeIcon", 0); - digester.addCallMethod(fullPrefix + "/filter/icon/small-icon", - "setSmallIcon", 0); - digester.addCallMethod(fullPrefix + "/filter/async-supported", - "setAsyncSupported", 0); - - digester.addCallMethod(fullPrefix + "/filter/init-param", - "addInitParameter", 2); - digester.addCallParam(fullPrefix + "/filter/init-param/param-name", - 0); - digester.addCallParam(fullPrefix + "/filter/init-param/param-value", - 1); - - digester.addObjectCreate(fullPrefix + "/filter-mapping", - "org.apache.tomcat.util.descriptor.web.FilterMap"); - digester.addSetNext(fullPrefix + "/filter-mapping", - "addFilterMapping", - "org.apache.tomcat.util.descriptor.web.FilterMap"); - - digester.addCallMethod(fullPrefix + "/filter-mapping/filter-name", - "setFilterName", 0); - digester.addCallMethod(fullPrefix + "/filter-mapping/servlet-name", - "addServletName", 0); - digester.addCallMethod(fullPrefix + "/filter-mapping/url-pattern", - "addURLPattern", 0); + digester.addObjectCreate(fullPrefix + "/error-page", "org.apache.tomcat.util.descriptor.web.ErrorPage"); + digester.addSetNext(fullPrefix + "/error-page", "addErrorPage", + "org.apache.tomcat.util.descriptor.web.ErrorPage"); + + digester.addCallMethod(fullPrefix + "/error-page/error-code", "setErrorCode", 0); + digester.addCallMethod(fullPrefix + "/error-page/exception-type", "setExceptionType", 0); + digester.addCallMethod(fullPrefix + "/error-page/location", "setLocation", 0); + + digester.addObjectCreate(fullPrefix + "/filter", "org.apache.tomcat.util.descriptor.web.FilterDef"); + digester.addSetNext(fullPrefix + "/filter", "addFilter", "org.apache.tomcat.util.descriptor.web.FilterDef"); + + digester.addCallMethod(fullPrefix + "/filter/description", "setDescription", 0); + digester.addCallMethod(fullPrefix + "/filter/display-name", "setDisplayName", 0); + digester.addCallMethod(fullPrefix + "/filter/filter-class", "setFilterClass", 0); + digester.addCallMethod(fullPrefix + "/filter/filter-name", "setFilterName", 0); + digester.addCallMethod(fullPrefix + "/filter/icon/large-icon", "setLargeIcon", 0); + digester.addCallMethod(fullPrefix + "/filter/icon/small-icon", "setSmallIcon", 0); + digester.addCallMethod(fullPrefix + "/filter/async-supported", "setAsyncSupported", 0); + + digester.addCallMethod(fullPrefix + "/filter/init-param", "addInitParameter", 2); + digester.addCallParam(fullPrefix + "/filter/init-param/param-name", 0); + digester.addCallParam(fullPrefix + "/filter/init-param/param-value", 1); + + digester.addObjectCreate(fullPrefix + "/filter-mapping", "org.apache.tomcat.util.descriptor.web.FilterMap"); + digester.addSetNext(fullPrefix + "/filter-mapping", "addFilterMapping", + "org.apache.tomcat.util.descriptor.web.FilterMap"); + + digester.addCallMethod(fullPrefix + "/filter-mapping/filter-name", "setFilterName", 0); + digester.addCallMethod(fullPrefix + "/filter-mapping/servlet-name", "addServletName", 0); + digester.addCallMethod(fullPrefix + "/filter-mapping/url-pattern", "addURLPattern", 0); - digester.addCallMethod(fullPrefix + "/filter-mapping/dispatcher", - "setDispatcher", 0); + digester.addCallMethod(fullPrefix + "/filter-mapping/dispatcher", "setDispatcher", 0); - digester.addCallMethod(fullPrefix + "/listener/listener-class", - "addListener", 0); + digester.addCallMethod(fullPrefix + "/listener/listener-class", "addListener", 0); - digester.addRule(fullPrefix + "/jsp-config", - jspConfig); + digester.addRule(fullPrefix + "/jsp-config", jspConfig); digester.addObjectCreate(fullPrefix + "/jsp-config/jsp-property-group", - "org.apache.tomcat.util.descriptor.web.JspPropertyGroup"); - digester.addSetNext(fullPrefix + "/jsp-config/jsp-property-group", - "addJspPropertyGroup", - "org.apache.tomcat.util.descriptor.web.JspPropertyGroup"); + "org.apache.tomcat.util.descriptor.web.JspPropertyGroup"); + digester.addSetNext(fullPrefix + "/jsp-config/jsp-property-group", "addJspPropertyGroup", + "org.apache.tomcat.util.descriptor.web.JspPropertyGroup"); digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/deferred-syntax-allowed-as-literal", - "setDeferredSyntax", 0); - digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/el-ignored", - "setElIgnored", 0); + "setDeferredSyntax", 0); + digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/el-ignored", "setElIgnored", 0); digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/error-on-el-not-found", - "setErrorOnELNotFound", 0); - digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-coda", - "addIncludeCoda", 0); - digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-prelude", - "addIncludePrelude", 0); - digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/is-xml", - "setIsXml", 0); - digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/page-encoding", - "setPageEncoding", 0); - digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/scripting-invalid", - "setScriptingInvalid", 0); + "setErrorOnELNotFound", 0); + digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-coda", "addIncludeCoda", 0); + digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/include-prelude", "addIncludePrelude", 0); + digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/is-xml", "setIsXml", 0); + digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/page-encoding", "setPageEncoding", 0); + digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/scripting-invalid", "setScriptingInvalid", + 0); digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/trim-directive-whitespaces", - "setTrimWhitespace", 0); - digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/url-pattern", - "addUrlPattern", 0); + "setTrimWhitespace", 0); + digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/url-pattern", "addUrlPattern", 0); digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/default-content-type", - "setDefaultContentType", 0); - digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/buffer", - "setBuffer", 0); + "setDefaultContentType", 0); + digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/buffer", "setBuffer", 0); digester.addCallMethod(fullPrefix + "/jsp-config/jsp-property-group/error-on-undeclared-namespace", - "setErrorOnUndeclaredNamespace", 0); + "setErrorOnUndeclaredNamespace", 0); + + digester.addRule(fullPrefix + "/login-config", loginConfig); - digester.addRule(fullPrefix + "/login-config", - loginConfig); + digester.addObjectCreate(fullPrefix + "/login-config", "org.apache.tomcat.util.descriptor.web.LoginConfig"); + digester.addSetNext(fullPrefix + "/login-config", "setLoginConfig", + "org.apache.tomcat.util.descriptor.web.LoginConfig"); - digester.addObjectCreate(fullPrefix + "/login-config", - "org.apache.tomcat.util.descriptor.web.LoginConfig"); - digester.addSetNext(fullPrefix + "/login-config", - "setLoginConfig", - "org.apache.tomcat.util.descriptor.web.LoginConfig"); - - digester.addCallMethod(fullPrefix + "/login-config/auth-method", - "setAuthMethod", 0); - digester.addCallMethod(fullPrefix + "/login-config/realm-name", - "setRealmName", 0); - digester.addCallMethod(fullPrefix + "/login-config/form-login-config/form-error-page", - "setErrorPage", 0); - digester.addCallMethod(fullPrefix + "/login-config/form-login-config/form-login-page", - "setLoginPage", 0); + digester.addCallMethod(fullPrefix + "/login-config/auth-method", "setAuthMethod", 0); + digester.addCallMethod(fullPrefix + "/login-config/realm-name", "setRealmName", 0); + digester.addCallMethod(fullPrefix + "/login-config/form-login-config/form-error-page", "setErrorPage", 0); + digester.addCallMethod(fullPrefix + "/login-config/form-login-config/form-login-page", "setLoginPage", 0); - digester.addCallMethod(fullPrefix + "/mime-mapping", - "addMimeMapping", 2); + digester.addCallMethod(fullPrefix + "/mime-mapping", "addMimeMapping", 2); digester.addCallParam(fullPrefix + "/mime-mapping/extension", 0); digester.addCallParam(fullPrefix + "/mime-mapping/mime-type", 1); digester.addObjectCreate(fullPrefix + "/security-constraint", - "org.apache.tomcat.util.descriptor.web.SecurityConstraint"); - digester.addSetNext(fullPrefix + "/security-constraint", - "addSecurityConstraint", - "org.apache.tomcat.util.descriptor.web.SecurityConstraint"); - - digester.addRule(fullPrefix + "/security-constraint/auth-constraint", - new SetAuthConstraintRule()); - digester.addCallMethod(fullPrefix + "/security-constraint/auth-constraint/role-name", - "addAuthRole", 0); - digester.addCallMethod(fullPrefix + "/security-constraint/display-name", - "setDisplayName", 0); + "org.apache.tomcat.util.descriptor.web.SecurityConstraint"); + digester.addSetNext(fullPrefix + "/security-constraint", "addSecurityConstraint", + "org.apache.tomcat.util.descriptor.web.SecurityConstraint"); + + digester.addRule(fullPrefix + "/security-constraint/auth-constraint", new SetAuthConstraintRule()); + digester.addCallMethod(fullPrefix + "/security-constraint/auth-constraint/role-name", "addAuthRole", 0); + digester.addCallMethod(fullPrefix + "/security-constraint/display-name", "setDisplayName", 0); digester.addCallMethod(fullPrefix + "/security-constraint/user-data-constraint/transport-guarantee", - "setUserConstraint", 0); + "setUserConstraint", 0); digester.addObjectCreate(fullPrefix + "/security-constraint/web-resource-collection", - "org.apache.tomcat.util.descriptor.web.SecurityCollection"); - digester.addSetNext(fullPrefix + "/security-constraint/web-resource-collection", - "addCollection", - "org.apache.tomcat.util.descriptor.web.SecurityCollection"); - digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/http-method", - "addMethod", 0); + "org.apache.tomcat.util.descriptor.web.SecurityCollection"); + digester.addSetNext(fullPrefix + "/security-constraint/web-resource-collection", "addCollection", + "org.apache.tomcat.util.descriptor.web.SecurityCollection"); + digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/http-method", "addMethod", 0); digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/http-method-omission", - "addOmittedMethod", 0); - digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/url-pattern", - "addPattern", 0); - digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/web-resource-name", - "setName", 0); - - digester.addCallMethod(fullPrefix + "/security-role/role-name", - "addSecurityRole", 0); - - digester.addRule(fullPrefix + "/servlet", - new ServletDefCreateRule()); - digester.addSetNext(fullPrefix + "/servlet", - "addServlet", - "org.apache.tomcat.util.descriptor.web.ServletDef"); - - digester.addCallMethod(fullPrefix + "/servlet/init-param", - "addInitParameter", 2); - digester.addCallParam(fullPrefix + "/servlet/init-param/param-name", - 0); - digester.addCallParam(fullPrefix + "/servlet/init-param/param-value", - 1); - - digester.addCallMethod(fullPrefix + "/servlet/jsp-file", - "setJspFile", 0); - digester.addCallMethod(fullPrefix + "/servlet/load-on-startup", - "setLoadOnStartup", 0); - digester.addCallMethod(fullPrefix + "/servlet/run-as/role-name", - "setRunAs", 0); + "addOmittedMethod", 0); + digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/url-pattern", "addPattern", + 0); + digester.addCallMethod(fullPrefix + "/security-constraint/web-resource-collection/web-resource-name", "setName", + 0); + + digester.addCallMethod(fullPrefix + "/security-role/role-name", "addSecurityRole", 0); + + digester.addRule(fullPrefix + "/servlet", new ServletDefCreateRule()); + digester.addSetNext(fullPrefix + "/servlet", "addServlet", "org.apache.tomcat.util.descriptor.web.ServletDef"); + + digester.addCallMethod(fullPrefix + "/servlet/init-param", "addInitParameter", 2); + digester.addCallParam(fullPrefix + "/servlet/init-param/param-name", 0); + digester.addCallParam(fullPrefix + "/servlet/init-param/param-value", 1); + + digester.addCallMethod(fullPrefix + "/servlet/jsp-file", "setJspFile", 0); + digester.addCallMethod(fullPrefix + "/servlet/load-on-startup", "setLoadOnStartup", 0); + digester.addCallMethod(fullPrefix + "/servlet/run-as/role-name", "setRunAs", 0); digester.addObjectCreate(fullPrefix + "/servlet/security-role-ref", - "org.apache.tomcat.util.descriptor.web.SecurityRoleRef"); - digester.addSetNext(fullPrefix + "/servlet/security-role-ref", - "addSecurityRoleRef", - "org.apache.tomcat.util.descriptor.web.SecurityRoleRef"); - digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-link", - "setLink", 0); - digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-name", - "setName", 0); - - digester.addCallMethod(fullPrefix + "/servlet/servlet-class", - "setServletClass", 0); - digester.addCallMethod(fullPrefix + "/servlet/servlet-name", - "setServletName", 0); + "org.apache.tomcat.util.descriptor.web.SecurityRoleRef"); + digester.addSetNext(fullPrefix + "/servlet/security-role-ref", "addSecurityRoleRef", + "org.apache.tomcat.util.descriptor.web.SecurityRoleRef"); + digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-link", "setLink", 0); + digester.addCallMethod(fullPrefix + "/servlet/security-role-ref/role-name", "setName", 0); + + digester.addCallMethod(fullPrefix + "/servlet/servlet-class", "setServletClass", 0); + digester.addCallMethod(fullPrefix + "/servlet/servlet-name", "setServletName", 0); digester.addObjectCreate(fullPrefix + "/servlet/multipart-config", - "org.apache.tomcat.util.descriptor.web.MultipartDef"); - digester.addSetNext(fullPrefix + "/servlet/multipart-config", - "setMultipartDef", - "org.apache.tomcat.util.descriptor.web.MultipartDef"); - digester.addCallMethod(fullPrefix + "/servlet/multipart-config/location", - "setLocation", 0); - digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-file-size", - "setMaxFileSize", 0); - digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-request-size", - "setMaxRequestSize", 0); - digester.addCallMethod(fullPrefix + "/servlet/multipart-config/file-size-threshold", - "setFileSizeThreshold", 0); - - digester.addCallMethod(fullPrefix + "/servlet/async-supported", - "setAsyncSupported", 0); - digester.addCallMethod(fullPrefix + "/servlet/enabled", - "setEnabled", 0); + "org.apache.tomcat.util.descriptor.web.MultipartDef"); + digester.addSetNext(fullPrefix + "/servlet/multipart-config", "setMultipartDef", + "org.apache.tomcat.util.descriptor.web.MultipartDef"); + digester.addCallMethod(fullPrefix + "/servlet/multipart-config/location", "setLocation", 0); + digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-file-size", "setMaxFileSize", 0); + digester.addCallMethod(fullPrefix + "/servlet/multipart-config/max-request-size", "setMaxRequestSize", 0); + digester.addCallMethod(fullPrefix + "/servlet/multipart-config/file-size-threshold", "setFileSizeThreshold", 0); + + digester.addCallMethod(fullPrefix + "/servlet/async-supported", "setAsyncSupported", 0); + digester.addCallMethod(fullPrefix + "/servlet/enabled", "setEnabled", 0); - digester.addRule(fullPrefix + "/servlet-mapping", - new CallMethodMultiRule("addServletMapping", 2, 0)); + digester.addRule(fullPrefix + "/servlet-mapping", new CallMethodMultiRule("addServletMapping", 2, 0)); digester.addCallParam(fullPrefix + "/servlet-mapping/servlet-name", 1); digester.addRule(fullPrefix + "/servlet-mapping/url-pattern", new CallParamMultiRule(0)); digester.addRule(fullPrefix + "/session-config", sessionConfig); - digester.addObjectCreate(fullPrefix + "/session-config", - "org.apache.tomcat.util.descriptor.web.SessionConfig"); + digester.addObjectCreate(fullPrefix + "/session-config", "org.apache.tomcat.util.descriptor.web.SessionConfig"); digester.addSetNext(fullPrefix + "/session-config", "setSessionConfig", - "org.apache.tomcat.util.descriptor.web.SessionConfig"); - digester.addCallMethod(fullPrefix + "/session-config/session-timeout", - "setSessionTimeout", 0); - digester.addCallMethod(fullPrefix + "/session-config/cookie-config/name", - "setCookieName", 0); - digester.addCallMethod(fullPrefix + "/session-config/cookie-config/domain", - "setCookieDomain", 0); - digester.addCallMethod(fullPrefix + "/session-config/cookie-config/path", - "setCookiePath", 0); - digester.addCallMethod(fullPrefix + "/session-config/cookie-config/comment", - "setCookieComment", 0); - digester.addCallMethod(fullPrefix + "/session-config/cookie-config/http-only", - "setCookieHttpOnly", 0); - digester.addCallMethod(fullPrefix + "/session-config/cookie-config/secure", - "setCookieSecure", 0); - digester.addCallMethod(fullPrefix + "/session-config/cookie-config/max-age", - "setCookieMaxAge", 0); - digester.addCallMethod(fullPrefix + "/session-config/cookie-config/attribute", - "setCookieAttribute", 2); + "org.apache.tomcat.util.descriptor.web.SessionConfig"); + digester.addCallMethod(fullPrefix + "/session-config/session-timeout", "setSessionTimeout", 0); + digester.addCallMethod(fullPrefix + "/session-config/cookie-config/name", "setCookieName", 0); + digester.addCallMethod(fullPrefix + "/session-config/cookie-config/domain", "setCookieDomain", 0); + digester.addCallMethod(fullPrefix + "/session-config/cookie-config/path", "setCookiePath", 0); + digester.addCallMethod(fullPrefix + "/session-config/cookie-config/comment", "setCookieComment", 0); + digester.addCallMethod(fullPrefix + "/session-config/cookie-config/http-only", "setCookieHttpOnly", 0); + digester.addCallMethod(fullPrefix + "/session-config/cookie-config/secure", "setCookieSecure", 0); + digester.addCallMethod(fullPrefix + "/session-config/cookie-config/max-age", "setCookieMaxAge", 0); + digester.addCallMethod(fullPrefix + "/session-config/cookie-config/attribute", "setCookieAttribute", 2); digester.addCallParam(fullPrefix + "/session-config/cookie-config/attribute/attribute-name", 0); digester.addCallParam(fullPrefix + "/session-config/cookie-config/attribute/attribute-value", 1); - digester.addCallMethod(fullPrefix + "/session-config/tracking-mode", - "addSessionTrackingMode", 0); + digester.addCallMethod(fullPrefix + "/session-config/tracking-mode", "addSessionTrackingMode", 0); // Taglibs pre Servlet 2.4 digester.addRule(fullPrefix + "/taglib", new TaglibLocationRule(false)); - digester.addCallMethod(fullPrefix + "/taglib", - "addTaglib", 2); + digester.addCallMethod(fullPrefix + "/taglib", "addTaglib", 2); digester.addCallParam(fullPrefix + "/taglib/taglib-location", 1); digester.addCallParam(fullPrefix + "/taglib/taglib-uri", 0); // Taglibs Servlet 2.4 onwards digester.addRule(fullPrefix + "/jsp-config/taglib", new TaglibLocationRule(true)); - digester.addCallMethod(fullPrefix + "/jsp-config/taglib", - "addTaglib", 2); + digester.addCallMethod(fullPrefix + "/jsp-config/taglib", "addTaglib", 2); digester.addCallParam(fullPrefix + "/jsp-config/taglib/taglib-location", 1); digester.addCallParam(fullPrefix + "/jsp-config/taglib/taglib-uri", 0); - digester.addCallMethod(fullPrefix + "/welcome-file-list/welcome-file", - "addWelcomeFile", 0); + digester.addCallMethod(fullPrefix + "/welcome-file-list/welcome-file", "addWelcomeFile", 0); digester.addCallMethod(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping", - "addLocaleEncodingMapping", 2); + "addLocaleEncodingMapping", 2); digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/locale", 0); digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/encoding", 1); - digester.addRule(fullPrefix + "/post-construct", - new LifecycleCallbackRule("addPostConstructMethods", 2, true)); + digester.addRule(fullPrefix + "/post-construct", new LifecycleCallbackRule("addPostConstructMethods", 2, true)); digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-class", 0); digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-method", 1); - digester.addRule(fullPrefix + "/pre-destroy", - new LifecycleCallbackRule("addPreDestroyMethods", 2, false)); + digester.addRule(fullPrefix + "/pre-destroy", new LifecycleCallbackRule("addPreDestroyMethods", 2, false)); digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-class", 0); digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-method", 1); } protected void configureNamingRules(Digester digester) { - //ejb-local-ref + // ejb-local-ref digester.addObjectCreate(fullPrefix + "/ejb-local-ref", - "org.apache.tomcat.util.descriptor.web.ContextLocalEjb"); - digester.addSetNext(fullPrefix + "/ejb-local-ref", - "addEjbLocalRef", - "org.apache.tomcat.util.descriptor.web.ContextLocalEjb"); - digester.addCallMethod(fullPrefix + "/ejb-local-ref/description", - "setDescription", 0); - digester.addCallMethod(fullPrefix + "/ejb-local-ref/ejb-link", - "setLink", 0); - digester.addCallMethod(fullPrefix + "/ejb-local-ref/ejb-ref-name", - "setName", 0); - digester.addCallMethod(fullPrefix + "/ejb-local-ref/ejb-ref-type", - "setType", 0); - digester.addCallMethod(fullPrefix + "/ejb-local-ref/local", - "setLocal", 0); - digester.addCallMethod(fullPrefix + "/ejb-local-ref/local-home", - "setHome", 0); - digester.addRule(fullPrefix + "/ejb-local-ref/mapped-name", - new MappedNameRule()); + "org.apache.tomcat.util.descriptor.web.ContextLocalEjb"); + digester.addSetNext(fullPrefix + "/ejb-local-ref", "addEjbLocalRef", + "org.apache.tomcat.util.descriptor.web.ContextLocalEjb"); + digester.addCallMethod(fullPrefix + "/ejb-local-ref/description", "setDescription", 0); + digester.addCallMethod(fullPrefix + "/ejb-local-ref/ejb-link", "setLink", 0); + digester.addCallMethod(fullPrefix + "/ejb-local-ref/ejb-ref-name", "setName", 0); + digester.addCallMethod(fullPrefix + "/ejb-local-ref/ejb-ref-type", "setType", 0); + digester.addCallMethod(fullPrefix + "/ejb-local-ref/local", "setLocal", 0); + digester.addCallMethod(fullPrefix + "/ejb-local-ref/local-home", "setHome", 0); + digester.addRule(fullPrefix + "/ejb-local-ref/mapped-name", new MappedNameRule()); digester.addCallMethod(fullPrefix + "/ejb-local-ref/lookup-name", "setLookupName", 0); configureInjectionRules(digester, "web-app/ejb-local-ref/"); - //ejb-ref - digester.addObjectCreate(fullPrefix + "/ejb-ref", - "org.apache.tomcat.util.descriptor.web.ContextEjb"); - digester.addSetNext(fullPrefix + "/ejb-ref", - "addEjbRef", - "org.apache.tomcat.util.descriptor.web.ContextEjb"); - digester.addCallMethod(fullPrefix + "/ejb-ref/description", - "setDescription", 0); - digester.addCallMethod(fullPrefix + "/ejb-ref/ejb-link", - "setLink", 0); - digester.addCallMethod(fullPrefix + "/ejb-ref/ejb-ref-name", - "setName", 0); - digester.addCallMethod(fullPrefix + "/ejb-ref/ejb-ref-type", - "setType", 0); - digester.addCallMethod(fullPrefix + "/ejb-ref/home", - "setHome", 0); - digester.addCallMethod(fullPrefix + "/ejb-ref/remote", - "setRemote", 0); - digester.addRule(fullPrefix + "/ejb-ref/mapped-name", - new MappedNameRule()); + // ejb-ref + digester.addObjectCreate(fullPrefix + "/ejb-ref", "org.apache.tomcat.util.descriptor.web.ContextEjb"); + digester.addSetNext(fullPrefix + "/ejb-ref", "addEjbRef", "org.apache.tomcat.util.descriptor.web.ContextEjb"); + digester.addCallMethod(fullPrefix + "/ejb-ref/description", "setDescription", 0); + digester.addCallMethod(fullPrefix + "/ejb-ref/ejb-link", "setLink", 0); + digester.addCallMethod(fullPrefix + "/ejb-ref/ejb-ref-name", "setName", 0); + digester.addCallMethod(fullPrefix + "/ejb-ref/ejb-ref-type", "setType", 0); + digester.addCallMethod(fullPrefix + "/ejb-ref/home", "setHome", 0); + digester.addCallMethod(fullPrefix + "/ejb-ref/remote", "setRemote", 0); + digester.addRule(fullPrefix + "/ejb-ref/mapped-name", new MappedNameRule()); digester.addCallMethod(fullPrefix + "/ejb-ref/lookup-name", "setLookupName", 0); configureInjectionRules(digester, "web-app/ejb-ref/"); - //env-entry - digester.addObjectCreate(fullPrefix + "/env-entry", - "org.apache.tomcat.util.descriptor.web.ContextEnvironment"); - digester.addSetNext(fullPrefix + "/env-entry", - "addEnvEntry", - "org.apache.tomcat.util.descriptor.web.ContextEnvironment"); + // env-entry + digester.addObjectCreate(fullPrefix + "/env-entry", "org.apache.tomcat.util.descriptor.web.ContextEnvironment"); + digester.addSetNext(fullPrefix + "/env-entry", "addEnvEntry", + "org.apache.tomcat.util.descriptor.web.ContextEnvironment"); digester.addRule(fullPrefix + "/env-entry", new SetOverrideRule()); - digester.addCallMethod(fullPrefix + "/env-entry/description", - "setDescription", 0); - digester.addCallMethod(fullPrefix + "/env-entry/env-entry-name", - "setName", 0); - digester.addCallMethod(fullPrefix + "/env-entry/env-entry-type", - "setType", 0); - digester.addCallMethod(fullPrefix + "/env-entry/env-entry-value", - "setValue", 0); - digester.addRule(fullPrefix + "/env-entry/mapped-name", - new MappedNameRule()); + digester.addCallMethod(fullPrefix + "/env-entry/description", "setDescription", 0); + digester.addCallMethod(fullPrefix + "/env-entry/env-entry-name", "setName", 0); + digester.addCallMethod(fullPrefix + "/env-entry/env-entry-type", "setType", 0); + digester.addCallMethod(fullPrefix + "/env-entry/env-entry-value", "setValue", 0); + digester.addRule(fullPrefix + "/env-entry/mapped-name", new MappedNameRule()); digester.addCallMethod(fullPrefix + "/env-entry/lookup-name", "setLookupName", 0); configureInjectionRules(digester, "web-app/env-entry/"); - //resource-env-ref + // resource-env-ref digester.addObjectCreate(fullPrefix + "/resource-env-ref", - "org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef"); - digester.addSetNext(fullPrefix + "/resource-env-ref", - "addResourceEnvRef", - "org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef"); - digester.addCallMethod(fullPrefix + "/resource-env-ref/resource-env-ref-name", - "setName", 0); - digester.addCallMethod(fullPrefix + "/resource-env-ref/resource-env-ref-type", - "setType", 0); - digester.addRule(fullPrefix + "/resource-env-ref/mapped-name", - new MappedNameRule()); + "org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef"); + digester.addSetNext(fullPrefix + "/resource-env-ref", "addResourceEnvRef", + "org.apache.tomcat.util.descriptor.web.ContextResourceEnvRef"); + digester.addCallMethod(fullPrefix + "/resource-env-ref/resource-env-ref-name", "setName", 0); + digester.addCallMethod(fullPrefix + "/resource-env-ref/resource-env-ref-type", "setType", 0); + digester.addRule(fullPrefix + "/resource-env-ref/mapped-name", new MappedNameRule()); digester.addCallMethod(fullPrefix + "/resource-env-ref/lookup-name", "setLookupName", 0); configureInjectionRules(digester, "web-app/resource-env-ref/"); - //message-destination + // message-destination digester.addObjectCreate(fullPrefix + "/message-destination", - "org.apache.tomcat.util.descriptor.web.MessageDestination"); - digester.addSetNext(fullPrefix + "/message-destination", - "addMessageDestination", - "org.apache.tomcat.util.descriptor.web.MessageDestination"); - digester.addCallMethod(fullPrefix + "/message-destination/description", - "setDescription", 0); - digester.addCallMethod(fullPrefix + "/message-destination/display-name", - "setDisplayName", 0); - digester.addCallMethod(fullPrefix + "/message-destination/icon/large-icon", - "setLargeIcon", 0); - digester.addCallMethod(fullPrefix + "/message-destination/icon/small-icon", - "setSmallIcon", 0); - digester.addCallMethod(fullPrefix + "/message-destination/message-destination-name", - "setName", 0); - digester.addRule(fullPrefix + "/message-destination/mapped-name", - new MappedNameRule()); + "org.apache.tomcat.util.descriptor.web.MessageDestination"); + digester.addSetNext(fullPrefix + "/message-destination", "addMessageDestination", + "org.apache.tomcat.util.descriptor.web.MessageDestination"); + digester.addCallMethod(fullPrefix + "/message-destination/description", "setDescription", 0); + digester.addCallMethod(fullPrefix + "/message-destination/display-name", "setDisplayName", 0); + digester.addCallMethod(fullPrefix + "/message-destination/icon/large-icon", "setLargeIcon", 0); + digester.addCallMethod(fullPrefix + "/message-destination/icon/small-icon", "setSmallIcon", 0); + digester.addCallMethod(fullPrefix + "/message-destination/message-destination-name", "setName", 0); + digester.addRule(fullPrefix + "/message-destination/mapped-name", new MappedNameRule()); digester.addCallMethod(fullPrefix + "/message-destination/lookup-name", "setLookupName", 0); - //message-destination-ref + // message-destination-ref digester.addObjectCreate(fullPrefix + "/message-destination-ref", - "org.apache.tomcat.util.descriptor.web.MessageDestinationRef"); - digester.addSetNext(fullPrefix + "/message-destination-ref", - "addMessageDestinationRef", - "org.apache.tomcat.util.descriptor.web.MessageDestinationRef"); - digester.addCallMethod(fullPrefix + "/message-destination-ref/description", - "setDescription", 0); - digester.addCallMethod(fullPrefix + "/message-destination-ref/message-destination-link", - "setLink", 0); - digester.addCallMethod(fullPrefix + "/message-destination-ref/message-destination-ref-name", - "setName", 0); - digester.addCallMethod(fullPrefix + "/message-destination-ref/message-destination-type", - "setType", 0); - digester.addCallMethod(fullPrefix + "/message-destination-ref/message-destination-usage", - "setUsage", 0); - digester.addRule(fullPrefix + "/message-destination-ref/mapped-name", - new MappedNameRule()); - digester.addCallMethod(fullPrefix + "/message-destination-ref/lookup-name", - "setLookupName", 0); + "org.apache.tomcat.util.descriptor.web.MessageDestinationRef"); + digester.addSetNext(fullPrefix + "/message-destination-ref", "addMessageDestinationRef", + "org.apache.tomcat.util.descriptor.web.MessageDestinationRef"); + digester.addCallMethod(fullPrefix + "/message-destination-ref/description", "setDescription", 0); + digester.addCallMethod(fullPrefix + "/message-destination-ref/message-destination-link", "setLink", 0); + digester.addCallMethod(fullPrefix + "/message-destination-ref/message-destination-ref-name", "setName", 0); + digester.addCallMethod(fullPrefix + "/message-destination-ref/message-destination-type", "setType", 0); + digester.addCallMethod(fullPrefix + "/message-destination-ref/message-destination-usage", "setUsage", 0); + digester.addRule(fullPrefix + "/message-destination-ref/mapped-name", new MappedNameRule()); + digester.addCallMethod(fullPrefix + "/message-destination-ref/lookup-name", "setLookupName", 0); configureInjectionRules(digester, "web-app/message-destination-ref/"); - //resource-ref - digester.addObjectCreate(fullPrefix + "/resource-ref", - "org.apache.tomcat.util.descriptor.web.ContextResource"); - digester.addSetNext(fullPrefix + "/resource-ref", - "addResourceRef", - "org.apache.tomcat.util.descriptor.web.ContextResource"); - digester.addCallMethod(fullPrefix + "/resource-ref/description", - "setDescription", 0); - digester.addCallMethod(fullPrefix + "/resource-ref/res-auth", - "setAuth", 0); - digester.addCallMethod(fullPrefix + "/resource-ref/res-ref-name", - "setName", 0); - digester.addCallMethod(fullPrefix + "/resource-ref/res-sharing-scope", - "setScope", 0); - digester.addCallMethod(fullPrefix + "/resource-ref/res-type", - "setType", 0); - digester.addRule(fullPrefix + "/resource-ref/mapped-name", - new MappedNameRule()); + // resource-ref + digester.addObjectCreate(fullPrefix + "/resource-ref", "org.apache.tomcat.util.descriptor.web.ContextResource"); + digester.addSetNext(fullPrefix + "/resource-ref", "addResourceRef", + "org.apache.tomcat.util.descriptor.web.ContextResource"); + digester.addCallMethod(fullPrefix + "/resource-ref/description", "setDescription", 0); + digester.addCallMethod(fullPrefix + "/resource-ref/res-auth", "setAuth", 0); + digester.addCallMethod(fullPrefix + "/resource-ref/res-ref-name", "setName", 0); + digester.addCallMethod(fullPrefix + "/resource-ref/res-sharing-scope", "setScope", 0); + digester.addCallMethod(fullPrefix + "/resource-ref/res-type", "setType", 0); + digester.addRule(fullPrefix + "/resource-ref/mapped-name", new MappedNameRule()); digester.addCallMethod(fullPrefix + "/resource-ref/lookup-name", "setLookupName", 0); configureInjectionRules(digester, "web-app/resource-ref/"); - //service-ref - digester.addObjectCreate(fullPrefix + "/service-ref", - "org.apache.tomcat.util.descriptor.web.ContextService"); - digester.addSetNext(fullPrefix + "/service-ref", - "addServiceRef", - "org.apache.tomcat.util.descriptor.web.ContextService"); - digester.addCallMethod(fullPrefix + "/service-ref/description", - "setDescription", 0); - digester.addCallMethod(fullPrefix + "/service-ref/display-name", - "setDisplayname", 0); - digester.addCallMethod(fullPrefix + "/service-ref/icon/large-icon", - "setLargeIcon", 0); - digester.addCallMethod(fullPrefix + "/service-ref/icon/small-icon", - "setSmallIcon", 0); - digester.addCallMethod(fullPrefix + "/service-ref/service-ref-name", - "setName", 0); - digester.addCallMethod(fullPrefix + "/service-ref/service-interface", - "setInterface", 0); - digester.addCallMethod(fullPrefix + "/service-ref/service-ref-type", - "setType", 0); - digester.addCallMethod(fullPrefix + "/service-ref/wsdl-file", - "setWsdlfile", 0); - digester.addCallMethod(fullPrefix + "/service-ref/jaxrpc-mapping-file", - "setJaxrpcmappingfile", 0); + // service-ref + digester.addObjectCreate(fullPrefix + "/service-ref", "org.apache.tomcat.util.descriptor.web.ContextService"); + digester.addSetNext(fullPrefix + "/service-ref", "addServiceRef", + "org.apache.tomcat.util.descriptor.web.ContextService"); + digester.addCallMethod(fullPrefix + "/service-ref/description", "setDescription", 0); + digester.addCallMethod(fullPrefix + "/service-ref/display-name", "setDisplayname", 0); + digester.addCallMethod(fullPrefix + "/service-ref/icon/large-icon", "setLargeIcon", 0); + digester.addCallMethod(fullPrefix + "/service-ref/icon/small-icon", "setSmallIcon", 0); + digester.addCallMethod(fullPrefix + "/service-ref/service-ref-name", "setName", 0); + digester.addCallMethod(fullPrefix + "/service-ref/service-interface", "setInterface", 0); + digester.addCallMethod(fullPrefix + "/service-ref/service-ref-type", "setType", 0); + digester.addCallMethod(fullPrefix + "/service-ref/wsdl-file", "setWsdlfile", 0); + digester.addCallMethod(fullPrefix + "/service-ref/jaxrpc-mapping-file", "setJaxrpcmappingfile", 0); digester.addRule(fullPrefix + "/service-ref/service-qname", new ServiceQnameRule()); digester.addRule(fullPrefix + "/service-ref/port-component-ref", - new CallMethodMultiRule("addPortcomponent", 2, 1)); + new CallMethodMultiRule("addPortcomponent", 2, 1)); digester.addCallParam(fullPrefix + "/service-ref/port-component-ref/service-endpoint-interface", 0); digester.addRule(fullPrefix + "/service-ref/port-component-ref/port-component-link", new CallParamMultiRule(1)); digester.addObjectCreate(fullPrefix + "/service-ref/handler", - "org.apache.tomcat.util.descriptor.web.ContextHandler"); + "org.apache.tomcat.util.descriptor.web.ContextHandler"); digester.addRule(fullPrefix + "/service-ref/handler", - new SetNextRule("addHandler", - "org.apache.tomcat.util.descriptor.web.ContextHandler")); + new SetNextRule("addHandler", "org.apache.tomcat.util.descriptor.web.ContextHandler")); + + digester.addCallMethod(fullPrefix + "/service-ref/handler/handler-name", "setName", 0); + digester.addCallMethod(fullPrefix + "/service-ref/handler/handler-class", "setHandlerclass", 0); - digester.addCallMethod(fullPrefix + "/service-ref/handler/handler-name", - "setName", 0); - digester.addCallMethod(fullPrefix + "/service-ref/handler/handler-class", - "setHandlerclass", 0); - - digester.addCallMethod(fullPrefix + "/service-ref/handler/init-param", - "setProperty", 2); - digester.addCallParam(fullPrefix + "/service-ref/handler/init-param/param-name", - 0); - digester.addCallParam(fullPrefix + "/service-ref/handler/init-param/param-value", - 1); + digester.addCallMethod(fullPrefix + "/service-ref/handler/init-param", "setProperty", 2); + digester.addCallParam(fullPrefix + "/service-ref/handler/init-param/param-name", 0); + digester.addCallParam(fullPrefix + "/service-ref/handler/init-param/param-value", 1); digester.addRule(fullPrefix + "/service-ref/handler/soap-header", new SoapHeaderRule()); - digester.addCallMethod(fullPrefix + "/service-ref/handler/soap-role", - "addSoapRole", 0); - digester.addCallMethod(fullPrefix + "/service-ref/handler/port-name", - "addPortName", 0); - digester.addRule(fullPrefix + "/service-ref/mapped-name", - new MappedNameRule()); + digester.addCallMethod(fullPrefix + "/service-ref/handler/soap-role", "addSoapRole", 0); + digester.addCallMethod(fullPrefix + "/service-ref/handler/port-name", "addPortName", 0); + digester.addRule(fullPrefix + "/service-ref/mapped-name", new MappedNameRule()); digester.addCallMethod(fullPrefix + "/service-ref/lookup-name", "setLookupName", 0); configureInjectionRules(digester, "web-app/service-ref/"); } @@ -701,7 +520,7 @@ /** * Reset counter used for validating the web.xml file. */ - public void recycle(){ + public void recycle() { jspConfig.isJspConfigSet = false; sessionConfig.isSessionConfigSet = false; loginConfig.isLoginConfigSet = false; @@ -716,21 +535,19 @@ /** - * Rule to check that the login-config is occurring - * only 1 time within the web.xml + * Rule to check that the login-config is occurring only 1 time within the web.xml */ final class SetLoginConfig extends Rule { boolean isLoginConfigSet = false; + SetLoginConfig() { // NO-OP } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { - if (isLoginConfigSet){ - throw new IllegalArgumentException( - " element is limited to 1 occurrence"); + public void begin(String namespace, String name, Attributes attributes) throws Exception { + if (isLoginConfigSet) { + throw new IllegalArgumentException(" element is limited to 1 occurrence"); } isLoginConfigSet = true; } @@ -739,21 +556,19 @@ /** - * Rule to check that the jsp-config is occurring - * only 1 time within the web.xml + * Rule to check that the jsp-config is occurring only 1 time within the web.xml */ final class SetJspConfig extends Rule { boolean isJspConfigSet = false; + SetJspConfig() { // NO-OP } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { - if (isJspConfigSet){ - throw new IllegalArgumentException( - " element is limited to 1 occurrence"); + public void begin(String namespace, String name, Attributes attributes) throws Exception { + if (isJspConfigSet) { + throw new IllegalArgumentException(" element is limited to 1 occurrence"); } isJspConfigSet = true; } @@ -762,21 +577,19 @@ /** - * Rule to check that the session-config is occurring - * only 1 time within the web.xml + * Rule to check that the session-config is occurring only 1 time within the web.xml */ final class SetSessionConfig extends Rule { boolean isSessionConfigSet = false; + SetSessionConfig() { // NO-OP } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { - if (isSessionConfigSet){ - throw new IllegalArgumentException( - " element is limited to 1 occurrence"); + public void begin(String namespace, String name, Attributes attributes) throws Exception { + if (isSessionConfigSet) { + throw new IllegalArgumentException(" element is limited to 1 occurrence"); } isSessionConfigSet = true; } @@ -784,8 +597,7 @@ } /** - * A Rule that calls the setAuthConstraint(true) method of - * the top item on the stack, which must be of type + * A Rule that calls the setAuthConstraint(true) method of the top item on the stack, which must be of type * org.apache.tomcat.util.descriptor.web.SecurityConstraint. */ @@ -796,14 +608,11 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { - SecurityConstraint securityConstraint = - (SecurityConstraint) digester.peek(); + public void begin(String namespace, String name, Attributes attributes) throws Exception { + SecurityConstraint securityConstraint = (SecurityConstraint) digester.peek(); securityConstraint.setAuthConstraint(true); if (digester.getLogger().isTraceEnabled()) { - digester.getLogger() - .trace("Calling SecurityConstraint.setAuthConstraint(true)"); + digester.getLogger().trace("Calling SecurityConstraint.setAuthConstraint(true)"); } StringBuilder code = digester.getGeneratedCode(); @@ -818,8 +627,8 @@ /** - * Class that calls setDistributable(true) for the top object - * on the stack, which must be a {@link WebXml} instance. + * Class that calls setDistributable(true) for the top object on the stack, which must be a {@link WebXml} + * instance. */ final class SetDistributableRule extends Rule { @@ -828,13 +637,11 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { WebXml webXml = (WebXml) digester.peek(); webXml.setDistributable(true); if (digester.getLogger().isTraceEnabled()) { - digester.getLogger().trace - (webXml.getClass().getName() + ".setDistributable(true)"); + digester.getLogger().trace(webXml.getClass().getName() + ".setDistributable(true)"); } StringBuilder code = digester.getGeneratedCode(); @@ -848,8 +655,8 @@ /** - * Class that calls setDenyUncoveredHttpMethods(true) for the top - * object on the stack, which must be a {@link WebXml} instance. + * Class that calls setDenyUncoveredHttpMethods(true) for the top object on the stack, which must be a + * {@link WebXml} instance. */ final class SetDenyUncoveredHttpMethodsRule extends Rule { @@ -858,13 +665,11 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { WebXml webXml = (WebXml) digester.peek(); webXml.setDenyUncoveredHttpMethods(true); if (digester.getLogger().isTraceEnabled()) { - digester.getLogger().trace(webXml.getClass().getName() + - ".setDenyUncoveredHttpMethods(true)"); + digester.getLogger().trace(webXml.getClass().getName() + ".setDenyUncoveredHttpMethods(true)"); } StringBuilder code = digester.getGeneratedCode(); @@ -878,8 +683,8 @@ /** - * Class that calls a property setter for the top object on the stack, - * passing the public ID of the entity we are currently processing. + * Class that calls a property setter for the top object on the stack, passing the public ID of the entity we are + * currently processing. */ final class SetPublicIdRule extends Rule { @@ -888,19 +693,18 @@ this.method = method; } - private String method = null; + private final String method; @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { Object top = digester.peek(); - Class paramClasses[] = new Class[1]; + Class[] paramClasses = new Class[1]; paramClasses[0] = "String".getClass(); - String paramValues[] = new String[1]; + String[] paramValues = new String[1]; paramValues[0] = digester.getPublicId(); - Method m = null; + Method m; try { m = top.getClass().getMethod(method, paramClasses); } catch (NoSuchMethodException e) { @@ -908,10 +712,9 @@ return; } - m.invoke(top, (Object [])paramValues); + m.invoke(top, (Object[]) paramValues); if (digester.getLogger().isTraceEnabled()) { - digester.getLogger().trace("" + top.getClass().getName() + "." - + method + "(" + paramValues[0] + ")"); + digester.getLogger().trace(top.getClass().getName() + "." + method + "(" + paramValues[0] + ")"); } StringBuilder code = digester.getGeneratedCode(); @@ -927,8 +730,7 @@ /** - * A Rule that calls the factory method on the specified Context to - * create the object that is to be added to the stack. + * A Rule that calls the factory method on the specified Context to create the object that is to be added to the stack. */ final class ServletDefCreateRule extends Rule { @@ -938,8 +740,7 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { ServletDef servletDef = new ServletDef(); digester.push(servletDef); if (digester.getLogger().isTraceEnabled()) { @@ -949,14 +750,14 @@ StringBuilder code = digester.getGeneratedCode(); if (code != null) { code.append(System.lineSeparator()); - code.append(ServletDef.class.getName()).append(' ').append(digester.toVariableName(servletDef)).append(" = new "); + code.append(ServletDef.class.getName()).append(' ').append(digester.toVariableName(servletDef)) + .append(" = new "); code.append(ServletDef.class.getName()).append("();").append(System.lineSeparator()); } } @Override - public void end(String namespace, String name) - throws Exception { + public void end(String namespace, String name) throws Exception { ServletDef servletDef = (ServletDef) digester.pop(); if (digester.getLogger().isTraceEnabled()) { digester.getLogger().trace("pop " + servletDef.getClass().getName()); @@ -972,8 +773,7 @@ /** - * A Rule that can be used to call multiple times a method as many times as needed - * (used for addServletMapping). + * A Rule that can be used to call multiple times a method as many times as needed (used for addServletMapping). */ final class CallParamMultiRule extends CallParamRule { @@ -985,7 +785,7 @@ public void end(String namespace, String name) { if (bodyTextStack != null && !bodyTextStack.empty()) { // what we do now is push one parameter onto the top set of parameters - Object parameters[] = (Object[]) digester.peekParams(); + Object[] parameters = (Object[]) digester.peekParams(); @SuppressWarnings("unchecked") ArrayList params = (ArrayList) parameters[paramIndex]; if (params == null) { @@ -1000,8 +800,7 @@ /** - * A Rule that can be used to call multiple times a method as many times as needed - * (used for addServletMapping). + * A Rule that can be used to call multiple times a method as many times as needed (used for addServletMapping). */ final class CallMethodMultiRule extends CallMethodRule { @@ -1015,17 +814,15 @@ /** * Process the end of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise */ @Override public void end(String namespace, String name) throws Exception { // Retrieve or construct the parameter values array - Object parameters[] = null; + Object[] parameters; if (paramCount > 0) { parameters = (Object[]) digester.popParams(); } else { @@ -1038,15 +835,14 @@ // Construct the parameter values array we will need // We only do the conversion if the param value is a String and // the specified paramType is not String. - Object paramValues[] = new Object[paramTypes.length]; + Object[] paramValues = new Object[paramTypes.length]; for (int i = 0; i < paramTypes.length; i++) { if (i != multiParamIndex) { // convert nulls and convert stringy parameters // for non-stringy param types - if(parameters[i] == null || (parameters[i] instanceof String - && !String.class.isAssignableFrom(paramTypes[i]))) { - paramValues[i] = - IntrospectionUtils.convert((String) parameters[i], paramTypes[i]); + if (parameters[i] == null || + (parameters[i] instanceof String && !String.class.isAssignableFrom(paramTypes[i]))) { + paramValues[i] = IntrospectionUtils.convert((String) parameters[i], paramTypes[i]); } else { paramValues[i] = parameters[i]; } @@ -1062,35 +858,25 @@ } if (target == null) { - StringBuilder sb = new StringBuilder(); - sb.append("[CallMethodRule]{"); - sb.append(""); - sb.append("} Call target is null ("); - sb.append("targetOffset="); - sb.append(targetOffset); - sb.append(",stackdepth="); - sb.append(digester.getCount()); - sb.append(')'); - throw new org.xml.sax.SAXException(sb.toString()); + String sb = "[CallMethodRule]{" + "} Call target is null (" + "targetOffset=" + targetOffset + + ",stackdepth=" + digester.getCount() + ')'; + throw new org.xml.sax.SAXException(sb); } if (multiParams == null) { paramValues[multiParamIndex] = null; - IntrospectionUtils.callMethodN(target, methodName, paramValues, - paramTypes); + IntrospectionUtils.callMethodN(target, methodName, paramValues, paramTypes); return; } for (Object param : multiParams) { - if (param == null || (param instanceof String - && !String.class.isAssignableFrom(paramTypes[multiParamIndex]))) { - paramValues[multiParamIndex] = - IntrospectionUtils.convert((String) param, paramTypes[multiParamIndex]); + if (param == null || + (param instanceof String && !String.class.isAssignableFrom(paramTypes[multiParamIndex]))) { + paramValues[multiParamIndex] = IntrospectionUtils.convert((String) param, paramTypes[multiParamIndex]); } else { paramValues[multiParamIndex] = param; } - IntrospectionUtils.callMethodN(target, methodName, paramValues, - paramTypes); + IntrospectionUtils.callMethodN(target, methodName, paramValues, paramTypes); StringBuilder code = digester.getGeneratedCode(); if (code != null) { @@ -1116,10 +902,8 @@ } - /** * A Rule that check if the annotations have to be loaded. - * */ final class IgnoreAnnotationsRule extends Rule { @@ -1129,8 +913,7 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { WebXml webXml = (WebXml) digester.peek(digester.getCount() - 1); String value = attributes.getValue("metadata-complete"); if ("true".equals(value)) { @@ -1141,9 +924,8 @@ value = null; } if (digester.getLogger().isTraceEnabled()) { - digester.getLogger().trace - (webXml.getClass().getName() + ".setMetadataComplete( " + - webXml.isMetadataComplete() + ")"); + digester.getLogger() + .trace(webXml.getClass().getName() + ".setMetadataComplete( " + webXml.isMetadataComplete() + ")"); } StringBuilder code = digester.getGeneratedCode(); @@ -1159,7 +941,6 @@ /** * A Rule that records the spec version of the web.xml being parsed - * */ final class VersionRule extends Rule { @@ -1169,15 +950,12 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { WebXml webXml = (WebXml) digester.peek(digester.getCount() - 1); webXml.setVersion(attributes.getValue("version")); if (digester.getLogger().isTraceEnabled()) { - digester.getLogger().trace - (webXml.getClass().getName() + ".setVersion( " + - webXml.getVersion() + ")"); + digester.getLogger().trace(webXml.getClass().getName() + ".setVersion( " + webXml.getVersion() + ")"); } StringBuilder code = digester.getGeneratedCode(); @@ -1204,18 +982,15 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { - if (isNameSet){ - throw new IllegalArgumentException(WebRuleSet.sm.getString( - "webRuleSet.nameCount")); + public void begin(String namespace, String name, Attributes attributes) throws Exception { + if (isNameSet) { + throw new IllegalArgumentException(WebRuleSet.sm.getString("webRuleSet.nameCount")); } isNameSet = true; } @Override - public void body(String namespace, String name, String text) - throws Exception { + public void body(String namespace, String name, String text) throws Exception { ((WebXml) digester.peek()).setName(text); StringBuilder code = digester.getGeneratedCode(); @@ -1230,8 +1005,8 @@ /** - * A rule that logs a warning if absolute ordering is configured for a fragment - * and fails if multiple absolute orders are configured. + * A rule that logs a warning if absolute ordering is configured for a fragment and fails if multiple absolute orders + * are configured. */ final class AbsoluteOrderingRule extends Rule { @@ -1243,22 +1018,18 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { if (fragment) { - digester.getLogger().warn( - WebRuleSet.sm.getString("webRuleSet.absoluteOrdering")); + digester.getLogger().warn(WebRuleSet.sm.getString("webRuleSet.absoluteOrdering")); } if (isAbsoluteOrderingSet) { - throw new IllegalArgumentException(WebRuleSet.sm.getString( - "webRuleSet.absoluteOrderingCount")); + throw new IllegalArgumentException(WebRuleSet.sm.getString("webRuleSet.absoluteOrderingCount")); } else { isAbsoluteOrderingSet = true; WebXml webXml = (WebXml) digester.peek(); webXml.createAbsoluteOrdering(); if (digester.getLogger().isTraceEnabled()) { - digester.getLogger().trace( - webXml.getClass().getName() + ".setAbsoluteOrdering()"); + digester.getLogger().trace(webXml.getClass().getName() + ".setAbsoluteOrdering()"); } StringBuilder code = digester.getGeneratedCode(); @@ -1284,15 +1055,12 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { if (!fragment) { - digester.getLogger().warn( - WebRuleSet.sm.getString("webRuleSet.relativeOrdering")); + digester.getLogger().warn(WebRuleSet.sm.getString("webRuleSet.relativeOrdering")); } if (isRelativeOrderingSet) { - throw new IllegalArgumentException(WebRuleSet.sm.getString( - "webRuleSet.relativeOrderingCount")); + throw new IllegalArgumentException(WebRuleSet.sm.getString("webRuleSet.relativeOrderingCount")); } else { isRelativeOrderingSet = true; } @@ -1301,7 +1069,6 @@ /** * A Rule that sets soap headers on the ContextHandler. - * */ final class SoapHeaderRule extends Rule { @@ -1312,23 +1079,20 @@ /** * Process the body text of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise - * @param text The body text of this element + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise + * @param text The body text of this element */ @Override - public void body(String namespace, String name, String text) - throws Exception { + public void body(String namespace, String name, String text) throws Exception { String namespaceuri = null; String localpart = text; int colon = text.indexOf(':'); if (colon >= 0) { - String prefix = text.substring(0,colon); + String prefix = text.substring(0, colon); namespaceuri = digester.findNamespaceURI(prefix); - localpart = text.substring(colon+1); + localpart = text.substring(colon + 1); } ContextHandler contextHandler = (ContextHandler) digester.peek(); contextHandler.addSoapHeaders(localpart, namespaceuri); @@ -1345,7 +1109,6 @@ /** * A Rule that sets service qname on the ContextService. - * */ final class ServiceQnameRule extends Rule { @@ -1356,23 +1119,20 @@ /** * Process the body text of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise - * @param text The body text of this element + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise + * @param text The body text of this element */ @Override - public void body(String namespace, String name, String text) - throws Exception { + public void body(String namespace, String name, String text) throws Exception { String namespaceuri = null; String localpart = text; int colon = text.indexOf(':'); if (colon >= 0) { - String prefix = text.substring(0,colon); + String prefix = text.substring(0, colon); namespaceuri = digester.findNamespaceURI(prefix); - localpart = text.substring(colon+1); + localpart = text.substring(colon + 1); } ContextService contextService = (ContextService) digester.peek(); contextService.setServiceqnameLocalpart(localpart); @@ -1404,15 +1164,13 @@ } @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { WebXml webXml = (WebXml) digester.peek(digester.getCount() - 1); // If we have a public ID, this is not a 2.4 or later webapp boolean havePublicId = (webXml.getPublicId() != null); // havePublicId and isServlet24OrLater should be mutually exclusive if (havePublicId == isServlet24OrLater) { - throw new IllegalArgumentException( - "taglib definition not consistent with specification version"); + throw new IllegalArgumentException("taglib definition not consistent with specification version"); } } } @@ -1429,16 +1187,13 @@ /** * Process the body text of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise - * @param text The body text of this element + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise + * @param text The body text of this element */ @Override - public void body(String namespace, String name, String text) - throws Exception { + public void body(String namespace, String name, String text) throws Exception { ResourceBase resourceBase = (ResourceBase) digester.peek(); resourceBase.setProperty("mappedName", text.trim()); @@ -1453,15 +1208,13 @@ } /** - * A rule that fails if more than one post construct or pre destroy methods - * are configured per class. + * A rule that fails if more than one post construct or pre destroy methods are configured per class. */ final class LifecycleCallbackRule extends CallMethodRule { private final boolean postConstruct; - LifecycleCallbackRule(String methodName, int paramCount, - boolean postConstruct) { + LifecycleCallbackRule(String methodName, int paramCount, boolean postConstruct) { super(methodName, paramCount); this.postConstruct = postConstruct; } @@ -1473,13 +1226,13 @@ WebXml webXml = (WebXml) digester.peek(); if (postConstruct) { if (webXml.getPostConstructMethods().containsKey(params[0])) { - throw new IllegalArgumentException(WebRuleSet.sm.getString( - "webRuleSet.postconstruct.duplicate", params[0])); + throw new IllegalArgumentException( + WebRuleSet.sm.getString("webRuleSet.postconstruct.duplicate", params[0])); } } else { if (webXml.getPreDestroyMethods().containsKey(params[0])) { - throw new IllegalArgumentException(WebRuleSet.sm.getString( - "webRuleSet.predestroy.duplicate", params[0])); + throw new IllegalArgumentException( + WebRuleSet.sm.getString("webRuleSet.predestroy.duplicate", params[0])); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/WebXml.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/WebXml.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/WebXml.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/WebXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,6 +19,7 @@ import java.io.UnsupportedEncodingException; import java.net.URL; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; @@ -50,103 +51,112 @@ import org.apache.tomcat.util.security.Escape; /** - * Representation of common elements of web.xml and web-fragment.xml. Provides - * a repository for parsed data before the elements are merged. - * Validation is spread between multiple classes: - * The digester checks for structural correctness (eg single login-config) - * This class checks for invalid duplicates (eg filter/servlet names) - * StandardContext will check validity of values (eg URL formats etc) + * Representation of common elements of web.xml and web-fragment.xml. Provides a repository for parsed data before the + * elements are merged. Validation is spread between multiple classes: The digester checks for structural correctness + * (e.g. single login-config) This class checks for invalid duplicates (e.g. filter/servlet names) StandardContext will + * check validity of values (e.g. URL formats etc) */ public class WebXml extends XmlEncodingBase implements DocumentProperties.Charset { - protected static final String ORDER_OTHERS = - "org.apache.catalina.order.others"; + protected static final String ORDER_OTHERS = "org.apache.catalina.order.others"; - private static final StringManager sm = - StringManager.getManager(Constants.PACKAGE_NAME); + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); private final Log log = LogFactory.getLog(WebXml.class); // must not be static /** - * Global defaults are overridable but Servlets and Servlet mappings need to - * be unique. Duplicates normally trigger an error. This flag indicates if - * newly added Servlet elements are marked as overridable. + * Global defaults are overridable but Servlets and Servlet mappings need to be unique. Duplicates normally trigger + * an error. This flag indicates if newly added Servlet elements are marked as overridable. */ private boolean overridable = false; + public boolean isOverridable() { return overridable; } + public void setOverridable(boolean overridable) { this.overridable = overridable; } /* - * Ideally, fragment names will be unique. If they are not, Tomcat needs - * to know as the action that the specification requires (see 8.2.2 1.e and - * 2.c) varies depending on the ordering method used. + * Ideally, fragment names will be unique. If they are not, Tomcat needs to know as the action that the + * specification requires (see 8.2.2 1.e and 2.c) varies depending on the ordering method used. */ private final List duplicates = new ArrayList<>(); + public boolean isDuplicated() { return !duplicates.isEmpty(); } + public void addDuplicate(String duplicate) { this.duplicates.add(duplicate); } + public List getDuplicates() { return new ArrayList<>(this.duplicates); } /** - * web.xml only elements - * Absolute Ordering + * web.xml only elements Absolute Ordering */ private Set absoluteOrdering = null; + public void createAbsoluteOrdering() { if (absoluteOrdering == null) { absoluteOrdering = new LinkedHashSet<>(); } } + public void addAbsoluteOrdering(String fragmentName) { createAbsoluteOrdering(); absoluteOrdering.add(fragmentName); } + public void addAbsoluteOrderingOthers() { createAbsoluteOrdering(); absoluteOrdering.add(ORDER_OTHERS); } + public Set getAbsoluteOrdering() { return absoluteOrdering; } /** - * web-fragment.xml only elements - * Relative ordering + * web-fragment.xml only elements Relative ordering */ private final Set after = new LinkedHashSet<>(); + public void addAfterOrdering(String fragmentName) { after.add(fragmentName); } + public void addAfterOrderingOthers() { if (before.contains(ORDER_OTHERS)) { - throw new IllegalArgumentException(sm.getString( - "webXml.multipleOther")); + throw new IllegalArgumentException(sm.getString("webXml.multipleOther")); } after.add(ORDER_OTHERS); } - public Set getAfterOrdering() { return after; } + + public Set getAfterOrdering() { + return after; + } private final Set before = new LinkedHashSet<>(); + public void addBeforeOrdering(String fragmentName) { before.add(fragmentName); } + public void addBeforeOrderingOthers() { if (after.contains(ORDER_OTHERS)) { - throw new IllegalArgumentException(sm.getString( - "webXml.multipleOther")); + throw new IllegalArgumentException(sm.getString("webXml.multipleOther")); } before.add(ORDER_OTHERS); } - public Set getBeforeOrdering() { return before; } + + public Set getBeforeOrdering() { + return before; + } // Common elements and attributes // Required attribute of web-app element @@ -157,9 +167,11 @@ sb.append(minorVersion); return sb.toString(); } + /** * Set the version for this web.xml file - * @param version Values of null will be ignored + * + * @param version Values of null will be ignored */ public void setVersion(String version) { if (version == null) { @@ -200,10 +212,13 @@ } - // Optional publicId attribute private String publicId = null; - public String getPublicId() { return publicId; } + + public String getPublicId() { + return publicId; + } + public void setPublicId(String publicId) { // Update major and minor version if (publicId == null) { @@ -228,13 +243,22 @@ // Optional metadata-complete attribute private boolean metadataComplete = false; - public boolean isMetadataComplete() { return metadataComplete; } + + public boolean isMetadataComplete() { + return metadataComplete; + } + public void setMetadataComplete(boolean metadataComplete) { - this.metadataComplete = metadataComplete; } + this.metadataComplete = metadataComplete; + } // Optional name element private String name = null; - public String getName() { return name; } + + public String getName() { + return name; + } + public void setName(String name) { if (ORDER_OTHERS.equalsIgnoreCase(name)) { // This is unusual. This name will be ignored. Log the fact. @@ -247,8 +271,14 @@ // Derived major and minor version attributes private int majorVersion = 6; private int minorVersion = 0; - public int getMajorVersion() { return majorVersion; } - public int getMinorVersion() { return minorVersion; } + + public int getMajorVersion() { + return majorVersion; + } + + public int getMinorVersion() { + return minorVersion; + } // web-app elements // TODO: Ignored elements: @@ -257,23 +287,33 @@ // display-name - TODO should support multiple with language private String displayName = null; - public String getDisplayName() { return displayName; } + + public String getDisplayName() { + return displayName; + } + public void setDisplayName(String displayName) { this.displayName = displayName; } // distributable private boolean distributable = false; - public boolean isDistributable() { return distributable; } + + public boolean isDistributable() { + return distributable; + } + public void setDistributable(boolean distributable) { this.distributable = distributable; } // deny-uncovered-http-methods private boolean denyUncoveredHttpMethods = false; + public boolean getDenyUncoveredHttpMethods() { return denyUncoveredHttpMethods; } + public void setDenyUncoveredHttpMethods(boolean denyUncoveredHttpMethods) { this.denyUncoveredHttpMethods = denyUncoveredHttpMethods; } @@ -281,10 +321,14 @@ // context-param // TODO: description (multiple with language) is ignored private final Map contextParams = new HashMap<>(); + public void addContextParam(String param, String value) { contextParams.put(param, value); } - public Map getContextParams() { return contextParams; } + + public Map getContextParams() { + return contextParams; + } // filter // TODO: Should support multiple description elements with language @@ -292,36 +336,46 @@ // TODO: Should support multiple icon elements // TODO: Description for init-param is ignored private final Map filters = new LinkedHashMap<>(); + public void addFilter(FilterDef filter) { if (filters.containsKey(filter.getFilterName())) { // Filter names must be unique within a web(-fragment).xml - throw new IllegalArgumentException( - sm.getString("webXml.duplicateFilter", - filter.getFilterName())); + throw new IllegalArgumentException(sm.getString("webXml.duplicateFilter", filter.getFilterName())); } filters.put(filter.getFilterName(), filter); } - public Map getFilters() { return filters; } + + public Map getFilters() { + return filters; + } // filter-mapping private final Set filterMaps = new LinkedHashSet<>(); private final Set filterMappingNames = new HashSet<>(); + public void addFilterMapping(FilterMap filterMap) { filterMap.setCharset(getCharset()); filterMaps.add(filterMap); filterMappingNames.add(filterMap.getFilterName()); } - public Set getFilterMappings() { return filterMaps; } + + public Set getFilterMappings() { + return filterMaps; + } // listener // TODO: description (multiple with language) is ignored // TODO: display-name (multiple with language) is ignored // TODO: icon (multiple) is ignored private final Set listeners = new LinkedHashSet<>(); + public void addListener(String className) { listeners.add(className); } - public Set getListeners() { return listeners; } + + public Set getListeners() { + return listeners; + } // servlet // TODO: description (multiple with language) is ignored @@ -330,65 +384,83 @@ // TODO: init-param/description (multiple with language) is ignored // TODO: security-role-ref/description (multiple with language) is ignored private final Map servlets = new HashMap<>(); + public void addServlet(ServletDef servletDef) { servlets.put(servletDef.getServletName(), servletDef); if (overridable) { - servletDef.setOverridable(overridable); + servletDef.setOverridable(true); } } - public Map getServlets() { return servlets; } + + public Map getServlets() { + return servlets; + } // servlet-mapping // Note: URLPatterns from web.xml may be URL encoded - // (https://svn.apache.org/r285186) + // (https://svn.apache.org/r285186) private final Map servletMappings = new HashMap<>(); private final Set servletMappingNames = new HashSet<>(); + public void addServletMapping(String urlPattern, String servletName) { addServletMappingDecoded(UDecoder.URLDecode(urlPattern, getCharset()), servletName); } + public void addServletMappingDecoded(String urlPattern, String servletName) { String oldServletName = servletMappings.put(urlPattern, servletName); if (oldServletName != null) { // Duplicate mapping. As per clarification from the Servlet EG, // deployment should fail. - throw new IllegalArgumentException(sm.getString( - "webXml.duplicateServletMapping", oldServletName, - servletName, urlPattern)); + throw new IllegalArgumentException( + sm.getString("webXml.duplicateServletMapping", oldServletName, servletName, urlPattern)); } servletMappingNames.add(servletName); } - public Map getServletMappings() { return servletMappings; } + + public Map getServletMappings() { + return servletMappings; + } // session-config // Digester will check there is only one of these private SessionConfig sessionConfig = new SessionConfig(); + public void setSessionConfig(SessionConfig sessionConfig) { this.sessionConfig = sessionConfig; } - public SessionConfig getSessionConfig() { return sessionConfig; } + + public SessionConfig getSessionConfig() { + return sessionConfig; + } // mime-mapping private final Map mimeMappings = new HashMap<>(); + public void addMimeMapping(String extension, String mimeType) { mimeMappings.put(extension, mimeType); } - public Map getMimeMappings() { return mimeMappings; } + + public Map getMimeMappings() { + return mimeMappings; + } // welcome-file-list merge control private boolean replaceWelcomeFiles = false; private boolean alwaysAddWelcomeFiles = true; + /** - * When merging/parsing web.xml files into this web.xml should the current - * set be completely replaced? - * @param replaceWelcomeFiles true to replace welcome files - * rather than add to the list + * When merging/parsing web.xml files into this web.xml should the current set be completely replaced? + * + * @param replaceWelcomeFiles true to replace welcome files rather than add to the list */ public void setReplaceWelcomeFiles(boolean replaceWelcomeFiles) { this.replaceWelcomeFiles = replaceWelcomeFiles; } + /** - * When merging from this web.xml, should the welcome files be added to the - * target web.xml even if it already contains welcome file definitions. + * When merging from this web.xml, should the welcome files be added to the target web.xml even if it already + * contains welcome file definitions. + * * @param alwaysAddWelcomeFiles true to add welcome files */ public void setAlwaysAddWelcomeFiles(boolean alwaysAddWelcomeFiles) { @@ -397,6 +469,7 @@ // welcome-file-list private final Set welcomeFiles = new LinkedHashSet<>(); + public void addWelcomeFile(String welcomeFile) { if (replaceWelcomeFiles) { welcomeFiles.clear(); @@ -404,35 +477,47 @@ } welcomeFiles.add(welcomeFile); } - public Set getWelcomeFiles() { return welcomeFiles; } + + public Set getWelcomeFiles() { + return welcomeFiles; + } // error-page private final Map errorPages = new HashMap<>(); + public void addErrorPage(ErrorPage errorPage) { errorPage.setCharset(getCharset()); errorPages.put(errorPage.getName(), errorPage); } - public Map getErrorPages() { return errorPages; } + + public Map getErrorPages() { + return errorPages; + } // Digester will check there is only one jsp-config // jsp-config/taglib or taglib (2.3 and earlier) private final Map taglibs = new HashMap<>(); + public void addTaglib(String uri, String location) { if (taglibs.containsKey(uri)) { // Taglib URIs must be unique within a web(-fragment).xml - throw new IllegalArgumentException( - sm.getString("webXml.duplicateTaglibUri", uri)); + throw new IllegalArgumentException(sm.getString("webXml.duplicateTaglibUri", uri)); } taglibs.put(uri, location); } - public Map getTaglibs() { return taglibs; } + + public Map getTaglibs() { + return taglibs; + } // jsp-config/jsp-property-group private final Set jspPropertyGroups = new LinkedHashSet<>(); + public void addJspPropertyGroup(JspPropertyGroup propertyGroup) { propertyGroup.setCharset(getCharset()); jspPropertyGroups.add(propertyGroup); } + public Set getJspPropertyGroups() { return jspPropertyGroups; } @@ -441,10 +526,12 @@ // TODO: Should support multiple display-name elements with language // TODO: Should support multiple description elements with language private final Set securityConstraints = new HashSet<>(); + public void addSecurityConstraint(SecurityConstraint securityConstraint) { securityConstraint.setCharset(getCharset()); securityConstraints.add(securityConstraint); } + public Set getSecurityConstraints() { return securityConstraints; } @@ -452,48 +539,64 @@ // login-config // Digester will check there is only one of these private LoginConfig loginConfig = null; + public void setLoginConfig(LoginConfig loginConfig) { loginConfig.setCharset(getCharset()); this.loginConfig = loginConfig; } - public LoginConfig getLoginConfig() { return loginConfig; } + + public LoginConfig getLoginConfig() { + return loginConfig; + } // security-role // TODO: description (multiple with language) is ignored private final Set securityRoles = new HashSet<>(); + public void addSecurityRole(String securityRole) { securityRoles.add(securityRole); } - public Set getSecurityRoles() { return securityRoles; } + + public Set getSecurityRoles() { + return securityRoles; + } // env-entry // TODO: Should support multiple description elements with language private final Map envEntries = new HashMap<>(); + public void addEnvEntry(ContextEnvironment envEntry) { if (envEntries.containsKey(envEntry.getName())) { // env-entry names must be unique within a web(-fragment).xml - throw new IllegalArgumentException( - sm.getString("webXml.duplicateEnvEntry", - envEntry.getName())); + throw new IllegalArgumentException(sm.getString("webXml.duplicateEnvEntry", envEntry.getName())); } - envEntries.put(envEntry.getName(),envEntry); + envEntries.put(envEntry.getName(), envEntry); + } + + public Map getEnvEntries() { + return envEntries; } - public Map getEnvEntries() { return envEntries; } // ejb-ref // TODO: Should support multiple description elements with language private final Map ejbRefs = new HashMap<>(); + public void addEjbRef(ContextEjb ejbRef) { - ejbRefs.put(ejbRef.getName(),ejbRef); + ejbRefs.put(ejbRef.getName(), ejbRef); + } + + public Map getEjbRefs() { + return ejbRefs; } - public Map getEjbRefs() { return ejbRefs; } // ejb-local-ref // TODO: Should support multiple description elements with language private final Map ejbLocalRefs = new HashMap<>(); + public void addEjbLocalRef(ContextLocalEjb ejbLocalRef) { - ejbLocalRefs.put(ejbLocalRef.getName(),ejbLocalRef); + ejbLocalRefs.put(ejbLocalRef.getName(), ejbLocalRef); } + public Map getEjbLocalRefs() { return ejbLocalRefs; } @@ -503,23 +606,27 @@ // TODO: Should support multiple display-names elements with language // TODO: Should support multiple icon elements ??? private final Map serviceRefs = new HashMap<>(); + public void addServiceRef(ContextService serviceRef) { serviceRefs.put(serviceRef.getName(), serviceRef); } - public Map getServiceRefs() { return serviceRefs; } + + public Map getServiceRefs() { + return serviceRefs; + } // resource-ref // TODO: Should support multiple description elements with language private final Map resourceRefs = new HashMap<>(); + public void addResourceRef(ContextResource resourceRef) { if (resourceRefs.containsKey(resourceRef.getName())) { // resource-ref names must be unique within a web(-fragment).xml - throw new IllegalArgumentException( - sm.getString("webXml.duplicateResourceRef", - resourceRef.getName())); + throw new IllegalArgumentException(sm.getString("webXml.duplicateResourceRef", resourceRef.getName())); } resourceRefs.put(resourceRef.getName(), resourceRef); } + public Map getResourceRefs() { return resourceRefs; } @@ -527,36 +634,34 @@ // resource-env-ref // TODO: Should support multiple description elements with language private final Map resourceEnvRefs = new HashMap<>(); + public void addResourceEnvRef(ContextResourceEnvRef resourceEnvRef) { if (resourceEnvRefs.containsKey(resourceEnvRef.getName())) { // resource-env-ref names must be unique within a web(-fragment).xml throw new IllegalArgumentException( - sm.getString("webXml.duplicateResourceEnvRef", - resourceEnvRef.getName())); + sm.getString("webXml.duplicateResourceEnvRef", resourceEnvRef.getName())); } resourceEnvRefs.put(resourceEnvRef.getName(), resourceEnvRef); } + public Map getResourceEnvRefs() { return resourceEnvRefs; } // message-destination-ref // TODO: Should support multiple description elements with language - private final Map messageDestinationRefs = - new HashMap<>(); - public void addMessageDestinationRef( - MessageDestinationRef messageDestinationRef) { - if (messageDestinationRefs.containsKey( - messageDestinationRef.getName())) { + private final Map messageDestinationRefs = new HashMap<>(); + + public void addMessageDestinationRef(MessageDestinationRef messageDestinationRef) { + if (messageDestinationRefs.containsKey(messageDestinationRef.getName())) { // message-destination-ref names must be unique within a // web(-fragment).xml - throw new IllegalArgumentException(sm.getString( - "webXml.duplicateMessageDestinationRef", - messageDestinationRef.getName())); + throw new IllegalArgumentException( + sm.getString("webXml.duplicateMessageDestinationRef", messageDestinationRef.getName())); } - messageDestinationRefs.put(messageDestinationRef.getName(), - messageDestinationRef); + messageDestinationRefs.put(messageDestinationRef.getName(), messageDestinationRef); } + public Map getMessageDestinationRefs() { return messageDestinationRefs; } @@ -565,53 +670,56 @@ // TODO: Should support multiple description elements with language // TODO: Should support multiple display-names elements with language // TODO: Should support multiple icon elements ??? - private final Map messageDestinations = - new HashMap<>(); - public void addMessageDestination( - MessageDestination messageDestination) { - if (messageDestinations.containsKey( - messageDestination.getName())) { + private final Map messageDestinations = new HashMap<>(); + + public void addMessageDestination(MessageDestination messageDestination) { + if (messageDestinations.containsKey(messageDestination.getName())) { // message-destination names must be unique within a // web(-fragment).xml throw new IllegalArgumentException( - sm.getString("webXml.duplicateMessageDestination", - messageDestination.getName())); + sm.getString("webXml.duplicateMessageDestination", messageDestination.getName())); } - messageDestinations.put(messageDestination.getName(), - messageDestination); + messageDestinations.put(messageDestination.getName(), messageDestination); } + public Map getMessageDestinations() { return messageDestinations; } // locale-encoding-mapping-list private final Map localeEncodingMappings = new HashMap<>(); + public void addLocaleEncodingMapping(String locale, String encoding) { localeEncodingMappings.put(locale, encoding); } + public Map getLocaleEncodingMappings() { return localeEncodingMappings; } // post-construct elements - private Map postConstructMethods = new HashMap<>(); + private final Map postConstructMethods = new HashMap<>(); + public void addPostConstructMethods(String clazz, String method) { if (!postConstructMethods.containsKey(clazz)) { postConstructMethods.put(clazz, method); } } - public Map getPostConstructMethods() { + + public Map getPostConstructMethods() { return postConstructMethods; } // pre-destroy elements - private Map preDestroyMethods = new HashMap<>(); + private final Map preDestroyMethods = new HashMap<>(); + public void addPreDestroyMethods(String clazz, String method) { if (!preDestroyMethods.containsKey(clazz)) { preDestroyMethods.put(clazz, method); } } - public Map getPreDestroyMethods() { + + public Map getPreDestroyMethods() { return preDestroyMethods; } @@ -620,28 +728,27 @@ return null; } - Collection descriptors = - new ArrayList<>(jspPropertyGroups.size()); + Collection descriptors = new ArrayList<>(jspPropertyGroups.size()); for (JspPropertyGroup jspPropertyGroup : jspPropertyGroups) { - JspPropertyGroupDescriptor descriptor = - new JspPropertyGroupDescriptorImpl(jspPropertyGroup); + JspPropertyGroupDescriptor descriptor = new JspPropertyGroupDescriptorImpl(jspPropertyGroup); descriptors.add(descriptor); } Collection tlds = new HashSet<>(taglibs.size()); - for (Entry entry : taglibs.entrySet()) { - TaglibDescriptor descriptor = new TaglibDescriptorImpl( - entry.getValue(), entry.getKey()); + for (Entry entry : taglibs.entrySet()) { + TaglibDescriptor descriptor = new TaglibDescriptorImpl(entry.getValue(), entry.getKey()); tlds.add(descriptor); } return new JspConfigDescriptorImpl(descriptors, tlds); } private String requestCharacterEncoding; + public String getRequestCharacterEncoding() { return requestCharacterEncoding; } + public void setRequestCharacterEncoding(String requestCharacterEncoding) { if (requestCharacterEncoding != null) { try { @@ -654,9 +761,11 @@ } private String responseCharacterEncoding; + public String getResponseCharacterEncoding() { return responseCharacterEncoding; } + public void setResponseCharacterEncoding(String responseCharacterEncoding) { if (responseCharacterEncoding != null) { try { @@ -672,24 +781,48 @@ // URL of JAR / exploded JAR for this web-fragment private URL uRL = null; - public void setURL(URL url) { this.uRL = url; } - public URL getURL() { return uRL; } + + public void setURL(URL url) { + this.uRL = url; + } + + public URL getURL() { + return uRL; + } // Name of jar file private String jarName = null; - public void setJarName(String jarName) { this.jarName = jarName; } - public String getJarName() { return jarName; } + + public void setJarName(String jarName) { + this.jarName = jarName; + } + + public String getJarName() { + return jarName; + } // Is this JAR part of the application or is it a container JAR? Assume it // is. private boolean webappJar = true; - public void setWebappJar(boolean webappJar) { this.webappJar = webappJar; } - public boolean getWebappJar() { return webappJar; } + + public void setWebappJar(boolean webappJar) { + this.webappJar = webappJar; + } + + public boolean getWebappJar() { + return webappJar; + } // Does this web application delegate first for class loading? private boolean delegate = false; - public boolean getDelegate() { return delegate; } - public void setDelegate(boolean delegate) { this.delegate = delegate; } + + public boolean getDelegate() { + return delegate; + } + + public void setDelegate(boolean delegate) { + this.delegate = delegate; + } @Override public String toString() { @@ -706,23 +839,22 @@ private static final String INDENT6 = " "; /** - * Generate a web.xml in String form that matches the representation stored - * in this object. + * Generate a web.xml in String form that matches the representation stored in this object. * * @return The complete contents of web.xml as a String */ public String toXml() { StringBuilder sb = new StringBuilder(2048); // TODO - Various, icon, description etc elements are skipped - mainly - // because they are ignored when web.xml is parsed - see above + // because they are ignored when web.xml is parsed - see above // NOTE - Elements need to be written in the order defined in the 2.3 - // DTD else validation of the merged web.xml will fail + // DTD else validation of the merged web.xml will fail // NOTE - Some elements need to be skipped based on the version of the - // specification being used. Version is validated and starts at - // 2.2. The version tests used in this method take advantage of - // this. + // specification being used. Version is validated and starts at + // 2.2. The version tests used in this method take advantage of + // this. // Declaration sb.append("\n"); @@ -793,7 +925,7 @@ sb.append(" \n\n"); } - for (Map.Entry entry : contextParams.entrySet()) { + for (Map.Entry entry : contextParams.entrySet()) { sb.append(" \n"); appendElement(sb, INDENT4, "param-name", entry.getKey()); appendElement(sb, INDENT4, "param-value", entry.getValue()); @@ -803,24 +935,18 @@ // Filters were introduced in Servlet 2.3 if (getMajorVersion() > 2 || getMinorVersion() > 2) { - for (Map.Entry entry : filters.entrySet()) { + for (Map.Entry entry : filters.entrySet()) { FilterDef filterDef = entry.getValue(); sb.append(" \n"); - appendElement(sb, INDENT4, "description", - filterDef.getDescription()); - appendElement(sb, INDENT4, "display-name", - filterDef.getDisplayName()); - appendElement(sb, INDENT4, "filter-name", - filterDef.getFilterName()); - appendElement(sb, INDENT4, "filter-class", - filterDef.getFilterClass()); + appendElement(sb, INDENT4, "description", filterDef.getDescription()); + appendElement(sb, INDENT4, "display-name", filterDef.getDisplayName()); + appendElement(sb, INDENT4, "filter-name", filterDef.getFilterName()); + appendElement(sb, INDENT4, "filter-class", filterDef.getFilterClass()); // Async support was introduced for Servlet 3.0 onwards if (getMajorVersion() != 2) { - appendElement(sb, INDENT4, "async-supported", - filterDef.getAsyncSupported()); + appendElement(sb, INDENT4, "async-supported", filterDef.getAsyncSupported()); } - for (Map.Entry param : - filterDef.getParameterMap().entrySet()) { + for (Map.Entry param : filterDef.getParameterMap().entrySet()) { sb.append(" \n"); appendElement(sb, INDENT6, "param-name", param.getKey()); appendElement(sb, INDENT6, "param-value", param.getValue()); @@ -832,8 +958,7 @@ for (FilterMap filterMap : filterMaps) { sb.append(" \n"); - appendElement(sb, INDENT4, "filter-name", - filterMap.getFilterName()); + appendElement(sb, INDENT4, "filter-name", filterMap.getFilterName()); if (filterMap.getMatchAllServletNames()) { sb.append(" *\n"); } else { @@ -851,8 +976,7 @@ // dispatcher was added in Servlet 2.4 if (getMajorVersion() > 2 || getMinorVersion() > 3) { for (String dispatcher : filterMap.getDispatcherNames()) { - if (getMajorVersion() == 2 && - DispatcherType.ASYNC.name().equals(dispatcher)) { + if (getMajorVersion() == 2 && DispatcherType.ASYNC.name().equals(dispatcher)) { continue; } appendElement(sb, INDENT4, "dispatcher", dispatcher); @@ -873,31 +997,25 @@ sb.append('\n'); } - for (Map.Entry entry : servlets.entrySet()) { + for (Map.Entry entry : servlets.entrySet()) { ServletDef servletDef = entry.getValue(); sb.append(" \n"); - appendElement(sb, INDENT4, "description", - servletDef.getDescription()); - appendElement(sb, INDENT4, "display-name", - servletDef.getDisplayName()); + appendElement(sb, INDENT4, "description", servletDef.getDescription()); + appendElement(sb, INDENT4, "display-name", servletDef.getDisplayName()); appendElement(sb, INDENT4, "servlet-name", entry.getKey()); - appendElement(sb, INDENT4, "servlet-class", - servletDef.getServletClass()); + appendElement(sb, INDENT4, "servlet-class", servletDef.getServletClass()); appendElement(sb, INDENT4, "jsp-file", servletDef.getJspFile()); - for (Map.Entry param : - servletDef.getParameterMap().entrySet()) { + for (Map.Entry param : servletDef.getParameterMap().entrySet()) { sb.append(" \n"); appendElement(sb, INDENT6, "param-name", param.getKey()); appendElement(sb, INDENT6, "param-value", param.getValue()); sb.append(" \n"); } - appendElement(sb, INDENT4, "load-on-startup", - servletDef.getLoadOnStartup()); + appendElement(sb, INDENT4, "load-on-startup", servletDef.getLoadOnStartup()); appendElement(sb, INDENT4, "enabled", servletDef.getEnabled()); // Async support was introduced for Servlet 3.0 onwards if (getMajorVersion() != 2) { - appendElement(sb, INDENT4, "async-supported", - servletDef.getAsyncSupported()); + appendElement(sb, INDENT4, "async-supported", servletDef.getAsyncSupported()); } // servlet/run-as was introduced in Servlet 2.3 if (getMajorVersion() > 2 || getMinorVersion() > 2) { @@ -918,14 +1036,10 @@ MultipartDef multipartDef = servletDef.getMultipartDef(); if (multipartDef != null) { sb.append(" \n"); - appendElement(sb, INDENT6, "location", - multipartDef.getLocation()); - appendElement(sb, INDENT6, "max-file-size", - multipartDef.getMaxFileSize()); - appendElement(sb, INDENT6, "max-request-size", - multipartDef.getMaxRequestSize()); - appendElement(sb, INDENT6, "file-size-threshold", - multipartDef.getFileSizeThreshold()); + appendElement(sb, INDENT6, "location", multipartDef.getLocation()); + appendElement(sb, INDENT6, "max-file-size", multipartDef.getMaxFileSize()); + appendElement(sb, INDENT6, "max-request-size", multipartDef.getMaxRequestSize()); + appendElement(sb, INDENT6, "file-size-threshold", multipartDef.getFileSizeThreshold()); sb.append(" \n"); } } @@ -933,7 +1047,7 @@ } sb.append('\n'); - for (Map.Entry entry : servletMappings.entrySet()) { + for (Map.Entry entry : servletMappings.entrySet()) { sb.append(" \n"); appendElement(sb, INDENT4, "servlet-name", entry.getValue()); appendElement(sb, INDENT4, "url-pattern", encodeUrl(entry.getKey())); @@ -943,32 +1057,25 @@ if (sessionConfig != null) { sb.append(" \n"); - appendElement(sb, INDENT4, "session-timeout", - sessionConfig.getSessionTimeout()); + appendElement(sb, INDENT4, "session-timeout", sessionConfig.getSessionTimeout()); if (majorVersion >= 3) { sb.append(" \n"); appendElement(sb, INDENT6, "name", sessionConfig.getCookieName()); - appendElement(sb, INDENT6, "domain", - sessionConfig.getCookieDomain()); + appendElement(sb, INDENT6, "domain", sessionConfig.getCookieDomain()); appendElement(sb, INDENT6, "path", sessionConfig.getCookiePath()); - appendElement(sb, INDENT6, "comment", - sessionConfig.getCookieComment()); - appendElement(sb, INDENT6, "http-only", - sessionConfig.getCookieHttpOnly()); - appendElement(sb, INDENT6, "secure", - sessionConfig.getCookieSecure()); - appendElement(sb, INDENT6, "max-age", - sessionConfig.getCookieMaxAge()); + appendElement(sb, INDENT6, "comment", sessionConfig.getCookieComment()); + appendElement(sb, INDENT6, "http-only", sessionConfig.getCookieHttpOnly()); + appendElement(sb, INDENT6, "secure", sessionConfig.getCookieSecure()); + appendElement(sb, INDENT6, "max-age", sessionConfig.getCookieMaxAge()); sb.append(" \n"); - for (SessionTrackingMode stm : - sessionConfig.getSessionTrackingModes()) { + for (SessionTrackingMode stm : sessionConfig.getSessionTrackingModes()) { appendElement(sb, INDENT4, "tracking-mode", stm.name()); } } sb.append(" \n\n"); } - for (Map.Entry entry : mimeMappings.entrySet()) { + for (Map.Entry entry : mimeMappings.entrySet()) { sb.append(" \n"); appendElement(sb, INDENT4, "extension", entry.getKey()); appendElement(sb, INDENT4, "mime-type", entry.getValue()); @@ -976,7 +1083,7 @@ } sb.append('\n'); - if (welcomeFiles.size() > 0) { + if (!welcomeFiles.isEmpty()) { sb.append(" \n"); for (String welcomeFile : welcomeFiles) { appendElement(sb, INDENT4, "welcome-file", welcomeFile); @@ -996,8 +1103,7 @@ if (errorPage.getExceptionType() != null) { appendElement(sb, INDENT4, "exception-type", exceptionType); } else if (errorPage.getErrorCode() > 0) { - appendElement(sb, INDENT4, "error-code", - Integer.toString(errorCode)); + appendElement(sb, INDENT4, "error-code", Integer.toString(errorCode)); } appendElement(sb, INDENT4, "location", errorPage.getLocation()); sb.append(" \n"); @@ -1006,11 +1112,11 @@ // jsp-config was added in Servlet 2.4. Prior to that, tag-libs was used // directly and jsp-property-group did not exist - if (taglibs.size() > 0 || jspPropertyGroups.size() > 0) { + if (!taglibs.isEmpty() || !jspPropertyGroups.isEmpty()) { if (getMajorVersion() > 2 || getMinorVersion() > 3) { sb.append(" \n"); } - for (Map.Entry entry : taglibs.entrySet()) { + for (Map.Entry entry : taglibs.entrySet()) { sb.append(" \n"); appendElement(sb, INDENT6, "taglib-uri", entry.getKey()); appendElement(sb, INDENT6, "taglib-location", entry.getValue()); @@ -1023,10 +1129,8 @@ appendElement(sb, INDENT6, "url-pattern", encodeUrl(urlPattern)); } appendElement(sb, INDENT6, "el-ignored", jpg.getElIgnored()); - appendElement(sb, INDENT6, "page-encoding", - jpg.getPageEncoding()); - appendElement(sb, INDENT6, "scripting-invalid", - jpg.getScriptingInvalid()); + appendElement(sb, INDENT6, "page-encoding", jpg.getPageEncoding()); + appendElement(sb, INDENT6, "scripting-invalid", jpg.getScriptingInvalid()); appendElement(sb, INDENT6, "is-xml", jpg.getIsXml()); for (String prelude : jpg.getIncludePreludes()) { appendElement(sb, INDENT6, "include-prelude", prelude); @@ -1034,15 +1138,11 @@ for (String coda : jpg.getIncludeCodas()) { appendElement(sb, INDENT6, "include-coda", coda); } - appendElement(sb, INDENT6, "deferred-syntax-allowed-as-literal", - jpg.getDeferredSyntax()); - appendElement(sb, INDENT6, "trim-directive-whitespaces", - jpg.getTrimWhitespace()); - appendElement(sb, INDENT6, "default-content-type", - jpg.getDefaultContentType()); + appendElement(sb, INDENT6, "deferred-syntax-allowed-as-literal", jpg.getDeferredSyntax()); + appendElement(sb, INDENT6, "trim-directive-whitespaces", jpg.getTrimWhitespace()); + appendElement(sb, INDENT6, "default-content-type", jpg.getDefaultContentType()); appendElement(sb, INDENT6, "buffer", jpg.getBuffer()); - appendElement(sb, INDENT6, "error-on-undeclared-namespace", - jpg.getErrorOnUndeclaredNamespace()); + appendElement(sb, INDENT6, "error-on-undeclared-namespace", jpg.getErrorOnUndeclaredNamespace()); sb.append(" \n"); } sb.append(" \n\n"); @@ -1053,21 +1153,14 @@ if (getMajorVersion() > 2 || getMinorVersion() > 2) { for (ContextResourceEnvRef resourceEnvRef : resourceEnvRefs.values()) { sb.append(" \n"); - appendElement(sb, INDENT4, "description", - resourceEnvRef.getDescription()); - appendElement(sb, INDENT4, "resource-env-ref-name", - resourceEnvRef.getName()); - appendElement(sb, INDENT4, "resource-env-ref-type", - resourceEnvRef.getType()); - appendElement(sb, INDENT4, "mapped-name", - resourceEnvRef.getProperty("mappedName")); - for (InjectionTarget target : - resourceEnvRef.getInjectionTargets()) { + appendElement(sb, INDENT4, "description", resourceEnvRef.getDescription()); + appendElement(sb, INDENT4, "resource-env-ref-name", resourceEnvRef.getName()); + appendElement(sb, INDENT4, "resource-env-ref-type", resourceEnvRef.getType()); + appendElement(sb, INDENT4, "mapped-name", resourceEnvRef.getProperty("mappedName")); + for (InjectionTarget target : resourceEnvRef.getInjectionTargets()) { sb.append(" \n"); - appendElement(sb, INDENT6, "injection-target-class", - target.getTargetClass()); - appendElement(sb, INDENT6, "injection-target-name", - target.getTargetName()); + appendElement(sb, INDENT6, "injection-target-class", target.getTargetClass()); + appendElement(sb, INDENT6, "injection-target-name", target.getTargetName()); sb.append(" \n"); } appendElement(sb, INDENT4, "lookup-name", resourceEnvRef.getLookupName()); @@ -1078,8 +1171,7 @@ for (ContextResource resourceRef : resourceRefs.values()) { sb.append(" \n"); - appendElement(sb, INDENT4, "description", - resourceRef.getDescription()); + appendElement(sb, INDENT4, "description", resourceRef.getDescription()); appendElement(sb, INDENT4, "res-ref-name", resourceRef.getName()); appendElement(sb, INDENT4, "res-type", resourceRef.getType()); appendElement(sb, INDENT4, "res-auth", resourceRef.getAuth()); @@ -1090,10 +1182,8 @@ appendElement(sb, INDENT4, "mapped-name", resourceRef.getProperty("mappedName")); for (InjectionTarget target : resourceRef.getInjectionTargets()) { sb.append(" \n"); - appendElement(sb, INDENT6, "injection-target-class", - target.getTargetClass()); - appendElement(sb, INDENT6, "injection-target-name", - target.getTargetName()); + appendElement(sb, INDENT6, "injection-target-class", target.getTargetClass()); + appendElement(sb, INDENT6, "injection-target-name", target.getTargetName()); sb.append(" \n"); } appendElement(sb, INDENT4, "lookup-name", resourceRef.getLookupName()); @@ -1105,15 +1195,12 @@ sb.append(" \n"); // security-constraint/display-name was introduced in Servlet 2.3 if (getMajorVersion() > 2 || getMinorVersion() > 2) { - appendElement(sb, INDENT4, "display-name", - constraint.getDisplayName()); + appendElement(sb, INDENT4, "display-name", constraint.getDisplayName()); } for (SecurityCollection collection : constraint.findCollections()) { sb.append(" \n"); - appendElement(sb, INDENT6, "web-resource-name", - collection.getName()); - appendElement(sb, INDENT6, "description", - collection.getDescription()); + appendElement(sb, INDENT6, "web-resource-name", collection.getName()); + appendElement(sb, INDENT6, "description", collection.getDescription()); for (String urlPattern : collection.findPatterns()) { appendElement(sb, INDENT6, "url-pattern", encodeUrl(urlPattern)); } @@ -1134,8 +1221,7 @@ } if (constraint.getUserConstraint() != null) { sb.append(" \n"); - appendElement(sb, INDENT6, "transport-guarantee", - constraint.getUserConstraint()); + appendElement(sb, INDENT6, "transport-guarantee", constraint.getUserConstraint()); sb.append(" \n"); } sb.append(" \n"); @@ -1144,17 +1230,12 @@ if (loginConfig != null) { sb.append(" \n"); - appendElement(sb, INDENT4, "auth-method", - loginConfig.getAuthMethod()); - appendElement(sb,INDENT4, "realm-name", - loginConfig.getRealmName()); - if (loginConfig.getErrorPage() != null || - loginConfig.getLoginPage() != null) { + appendElement(sb, INDENT4, "auth-method", loginConfig.getAuthMethod()); + appendElement(sb, INDENT4, "realm-name", loginConfig.getRealmName()); + if (loginConfig.getErrorPage() != null || loginConfig.getLoginPage() != null) { sb.append(" \n"); - appendElement(sb, INDENT6, "form-login-page", - loginConfig.getLoginPage()); - appendElement(sb, INDENT6, "form-error-page", - loginConfig.getErrorPage()); + appendElement(sb, INDENT6, "form-login-page", loginConfig.getLoginPage()); + appendElement(sb, INDENT6, "form-error-page", loginConfig.getErrorPage()); sb.append(" \n"); } sb.append(" \n\n"); @@ -1168,18 +1249,15 @@ for (ContextEnvironment envEntry : envEntries.values()) { sb.append(" \n"); - appendElement(sb, INDENT4, "description", - envEntry.getDescription()); + appendElement(sb, INDENT4, "description", envEntry.getDescription()); appendElement(sb, INDENT4, "env-entry-name", envEntry.getName()); appendElement(sb, INDENT4, "env-entry-type", envEntry.getType()); appendElement(sb, INDENT4, "env-entry-value", envEntry.getValue()); appendElement(sb, INDENT4, "mapped-name", envEntry.getProperty("mappedName")); for (InjectionTarget target : envEntry.getInjectionTargets()) { sb.append(" \n"); - appendElement(sb, INDENT6, "injection-target-class", - target.getTargetClass()); - appendElement(sb, INDENT6, "injection-target-name", - target.getTargetName()); + appendElement(sb, INDENT6, "injection-target-class", target.getTargetClass()); + appendElement(sb, INDENT6, "injection-target-name", target.getTargetName()); sb.append(" \n"); } appendElement(sb, INDENT4, "lookup-name", envEntry.getLookupName()); @@ -1198,10 +1276,8 @@ appendElement(sb, INDENT4, "mapped-name", ejbRef.getProperty("mappedName")); for (InjectionTarget target : ejbRef.getInjectionTargets()) { sb.append(" \n"); - appendElement(sb, INDENT6, "injection-target-class", - target.getTargetClass()); - appendElement(sb, INDENT6, "injection-target-name", - target.getTargetName()); + appendElement(sb, INDENT6, "injection-target-class", target.getTargetClass()); + appendElement(sb, INDENT6, "injection-target-name", target.getTargetName()); sb.append(" \n"); } appendElement(sb, INDENT4, "lookup-name", ejbRef.getLookupName()); @@ -1213,8 +1289,7 @@ if (getMajorVersion() > 2 || getMinorVersion() > 2) { for (ContextLocalEjb ejbLocalRef : ejbLocalRefs.values()) { sb.append(" \n"); - appendElement(sb, INDENT4, "description", - ejbLocalRef.getDescription()); + appendElement(sb, INDENT4, "description", ejbLocalRef.getDescription()); appendElement(sb, INDENT4, "ejb-ref-name", ejbLocalRef.getName()); appendElement(sb, INDENT4, "ejb-ref-type", ejbLocalRef.getType()); appendElement(sb, INDENT4, "local-home", ejbLocalRef.getHome()); @@ -1223,10 +1298,8 @@ appendElement(sb, INDENT4, "mapped-name", ejbLocalRef.getProperty("mappedName")); for (InjectionTarget target : ejbLocalRef.getInjectionTargets()) { sb.append(" \n"); - appendElement(sb, INDENT6, "injection-target-class", - target.getTargetClass()); - appendElement(sb, INDENT6, "injection-target-name", - target.getTargetName()); + appendElement(sb, INDENT6, "injection-target-class", target.getTargetClass()); + appendElement(sb, INDENT6, "injection-target-name", target.getTargetName()); sb.append(" \n"); } appendElement(sb, INDENT4, "lookup-name", ejbLocalRef.getLookupName()); @@ -1239,19 +1312,13 @@ if (getMajorVersion() > 2 || getMinorVersion() > 3) { for (ContextService serviceRef : serviceRefs.values()) { sb.append(" \n"); - appendElement(sb, INDENT4, "description", - serviceRef.getDescription()); - appendElement(sb, INDENT4, "display-name", - serviceRef.getDisplayname()); - appendElement(sb, INDENT4, "service-ref-name", - serviceRef.getName()); - appendElement(sb, INDENT4, "service-interface", - serviceRef.getInterface()); - appendElement(sb, INDENT4, "service-ref-type", - serviceRef.getType()); + appendElement(sb, INDENT4, "description", serviceRef.getDescription()); + appendElement(sb, INDENT4, "display-name", serviceRef.getDisplayname()); + appendElement(sb, INDENT4, "service-ref-name", serviceRef.getName()); + appendElement(sb, INDENT4, "service-interface", serviceRef.getInterface()); + appendElement(sb, INDENT4, "service-ref-type", serviceRef.getType()); appendElement(sb, INDENT4, "wsdl-file", serviceRef.getWsdlfile()); - appendElement(sb, INDENT4, "jaxrpc-mapping-file", - serviceRef.getJaxrpcmappingfile()); + appendElement(sb, INDENT4, "jaxrpc-mapping-file", serviceRef.getJaxrpcmappingfile()); String qname = serviceRef.getServiceqnameNamespaceURI(); if (qname != null) { qname = qname + ":"; @@ -1262,10 +1329,8 @@ while (endpointIter.hasNext()) { String endpoint = endpointIter.next(); sb.append(" \n"); - appendElement(sb, INDENT6, "service-endpoint-interface", - endpoint); - appendElement(sb, INDENT6, "port-component-link", - serviceRef.getProperty(endpoint)); + appendElement(sb, INDENT6, "service-endpoint-interface", endpoint); + appendElement(sb, INDENT6, "port-component-link", serviceRef.getProperty(endpoint)); sb.append(" \n"); } Iterator handlerIter = serviceRef.getHandlers(); @@ -1274,18 +1339,15 @@ sb.append(" \n"); ContextHandler ch = serviceRef.getHandler(handler); appendElement(sb, INDENT6, "handler-name", ch.getName()); - appendElement(sb, INDENT6, "handler-class", - ch.getHandlerclass()); + appendElement(sb, INDENT6, "handler-class", ch.getHandlerclass()); sb.append(" \n"); } // TODO handler-chains appendElement(sb, INDENT4, "mapped-name", serviceRef.getProperty("mappedName")); for (InjectionTarget target : serviceRef.getInjectionTargets()) { sb.append(" \n"); - appendElement(sb, INDENT6, "injection-target-class", - target.getTargetClass()); - appendElement(sb, INDENT6, "injection-target-name", - target.getTargetName()); + appendElement(sb, INDENT6, "injection-target-class", target.getTargetClass()); + appendElement(sb, INDENT6, "injection-target-name", target.getTargetName()); sb.append(" \n"); } appendElement(sb, INDENT4, "lookup-name", serviceRef.getLookupName()); @@ -1295,26 +1357,20 @@ } if (!postConstructMethods.isEmpty()) { - for (Entry entry : postConstructMethods - .entrySet()) { + for (Entry entry : postConstructMethods.entrySet()) { sb.append(" \n"); - appendElement(sb, INDENT4, "lifecycle-callback-class", - entry.getKey()); - appendElement(sb, INDENT4, "lifecycle-callback-method", - entry.getValue()); + appendElement(sb, INDENT4, "lifecycle-callback-class", entry.getKey()); + appendElement(sb, INDENT4, "lifecycle-callback-method", entry.getValue()); sb.append(" \n"); } sb.append('\n'); } if (!preDestroyMethods.isEmpty()) { - for (Entry entry : preDestroyMethods - .entrySet()) { + for (Entry entry : preDestroyMethods.entrySet()) { sb.append(" \n"); - appendElement(sb, INDENT4, "lifecycle-callback-class", - entry.getKey()); - appendElement(sb, INDENT4, "lifecycle-callback-method", - entry.getValue()); + appendElement(sb, INDENT4, "lifecycle-callback-class", entry.getKey()); + appendElement(sb, INDENT4, "lifecycle-callback-method", entry.getValue()); sb.append(" \n"); } sb.append('\n'); @@ -1326,21 +1382,15 @@ for (MessageDestinationRef mdr : messageDestinationRefs.values()) { sb.append(" \n"); appendElement(sb, INDENT4, "description", mdr.getDescription()); - appendElement(sb, INDENT4, "message-destination-ref-name", - mdr.getName()); - appendElement(sb, INDENT4, "message-destination-type", - mdr.getType()); - appendElement(sb, INDENT4, "message-destination-usage", - mdr.getUsage()); - appendElement(sb, INDENT4, "message-destination-link", - mdr.getLink()); + appendElement(sb, INDENT4, "message-destination-ref-name", mdr.getName()); + appendElement(sb, INDENT4, "message-destination-type", mdr.getType()); + appendElement(sb, INDENT4, "message-destination-usage", mdr.getUsage()); + appendElement(sb, INDENT4, "message-destination-link", mdr.getLink()); appendElement(sb, INDENT4, "mapped-name", mdr.getProperty("mappedName")); for (InjectionTarget target : mdr.getInjectionTargets()) { sb.append(" \n"); - appendElement(sb, INDENT6, "injection-target-class", - target.getTargetClass()); - appendElement(sb, INDENT6, "injection-target-name", - target.getTargetName()); + appendElement(sb, INDENT6, "injection-target-class", target.getTargetClass()); + appendElement(sb, INDENT6, "injection-target-name", target.getTargetName()); sb.append(" \n"); } appendElement(sb, INDENT4, "lookup-name", mdr.getLookupName()); @@ -1352,8 +1402,7 @@ sb.append(" \n"); appendElement(sb, INDENT4, "description", md.getDescription()); appendElement(sb, INDENT4, "display-name", md.getDisplayName()); - appendElement(sb, INDENT4, "message-destination-name", - md.getName()); + appendElement(sb, INDENT4, "message-destination-name", md.getName()); appendElement(sb, INDENT4, "mapped-name", md.getProperty("mappedName")); appendElement(sb, INDENT4, "lookup-name", md.getLookupName()); sb.append(" \n"); @@ -1363,10 +1412,9 @@ // locale-encoding-mapping-list was introduced in Servlet 2.4 if (getMajorVersion() > 2 || getMinorVersion() > 3) { - if (localeEncodingMappings.size() > 0) { + if (!localeEncodingMappings.isEmpty()) { sb.append(" \n"); - for (Map.Entry entry : - localeEncodingMappings.entrySet()) { + for (Map.Entry entry : localeEncodingMappings.entrySet()) { sb.append(" \n"); appendElement(sb, INDENT6, "locale", entry.getKey()); appendElement(sb, INDENT6, "encoding", entry.getValue()); @@ -1378,8 +1426,7 @@ } // deny-uncovered-http-methods was introduced in Servlet 3.1 - if (getMajorVersion() > 3 || - (getMajorVersion() == 3 && getMinorVersion() > 0)) { + if (getMajorVersion() > 3 || (getMajorVersion() == 3 && getMinorVersion() > 0)) { if (denyUncoveredHttpMethods) { sb.append(" "); sb.append("\n"); @@ -1397,21 +1444,15 @@ private String encodeUrl(String input) { - try { - return URLEncoder.encode(input, "UTF-8"); - } catch (UnsupportedEncodingException e) { - // Impossible. UTF-8 is a required character set - return null; - } + return URLEncoder.encode(input, StandardCharsets.UTF_8); } - private static void appendElement(StringBuilder sb, String indent, - String elementName, String value) { + private static void appendElement(StringBuilder sb, String indent, String elementName, String value) { if (value == null) { return; } - if (value.length() == 0) { + if (value.isEmpty()) { sb.append(indent); sb.append('<'); sb.append(elementName); @@ -1428,8 +1469,7 @@ } } - private static void appendElement(StringBuilder sb, String indent, - String elementName, Object value) { + private static void appendElement(StringBuilder sb, String indent, String elementName, Object value) { if (value == null) { return; } @@ -1440,9 +1480,9 @@ /** * Merge the supplied web fragments into this main web.xml. * - * @param fragments The fragments to merge in - * @return true if merge is successful, else - * false + * @param fragments The fragments to merge in + * + * @return true if merge is successful, else false */ public boolean merge(Set fragments) { // As far as possible, process in alphabetical order so it is easy to @@ -1453,8 +1493,8 @@ WebXml temp = new WebXml(); for (WebXml fragment : fragments) { - if (!mergeMap(fragment.getContextParams(), contextParams, - temp.getContextParams(), fragment, "Context Parameter")) { + if (!mergeMap(fragment.getContextParams(), contextParams, temp.getContextParams(), fragment, + "Context Parameter")) { return false; } } @@ -1467,10 +1507,8 @@ if (temp.getDisplayName() == null) { temp.setDisplayName(value); } else { - log.error(sm.getString( - "webXml.mergeConflictDisplayName", - fragment.getName(), - fragment.getURL())); + log.error( + sm.getString("webXml.mergeConflictDisplayName", fragment.getName(), fragment.getURL())); return false; } } @@ -1478,8 +1516,8 @@ displayName = temp.getDisplayName(); } - // Note: Not permitted in fragments but we also use fragments for - // per-Host and global defaults so they may appear there + // Note: Not permitted in fragments, but we also use fragments for + // per-Host and global defaults so they may appear there if (!denyUncoveredHttpMethods) { for (WebXml fragment : fragments) { if (fragment.getDenyUncoveredHttpMethods()) { @@ -1513,32 +1551,28 @@ } for (WebXml fragment : fragments) { - if (!mergeResourceMap(fragment.getEjbLocalRefs(), ejbLocalRefs, - temp.getEjbLocalRefs(), fragment)) { + if (!mergeResourceMap(fragment.getEjbLocalRefs(), ejbLocalRefs, temp.getEjbLocalRefs(), fragment)) { return false; } } ejbLocalRefs.putAll(temp.getEjbLocalRefs()); for (WebXml fragment : fragments) { - if (!mergeResourceMap(fragment.getEjbRefs(), ejbRefs, - temp.getEjbRefs(), fragment)) { + if (!mergeResourceMap(fragment.getEjbRefs(), ejbRefs, temp.getEjbRefs(), fragment)) { return false; } } ejbRefs.putAll(temp.getEjbRefs()); for (WebXml fragment : fragments) { - if (!mergeResourceMap(fragment.getEnvEntries(), envEntries, - temp.getEnvEntries(), fragment)) { + if (!mergeResourceMap(fragment.getEnvEntries(), envEntries, temp.getEnvEntries(), fragment)) { return false; } } envEntries.putAll(temp.getEnvEntries()); for (WebXml fragment : fragments) { - if (!mergeMap(fragment.getErrorPages(), errorPages, - temp.getErrorPages(), fragment, "Error Page")) { + if (!mergeMap(fragment.getErrorPages(), errorPages, temp.getErrorPages(), fragment, "Error Page")) { return false; } } @@ -1561,19 +1595,13 @@ } for (WebXml fragment : fragments) { - for (Map.Entry entry : - fragment.getFilters().entrySet()) { + for (Map.Entry entry : fragment.getFilters().entrySet()) { if (filters.containsKey(entry.getKey())) { - mergeFilter(entry.getValue(), - filters.get(entry.getKey()), false); + mergeFilter(entry.getValue(), filters.get(entry.getKey()), false); } else { if (temp.getFilters().containsKey(entry.getKey())) { - if (!(mergeFilter(entry.getValue(), - temp.getFilters().get(entry.getKey()), true))) { - log.error(sm.getString( - "webXml.mergeConflictFilter", - entry.getKey(), - fragment.getName(), + if (!(mergeFilter(entry.getValue(), temp.getFilters().get(entry.getKey()), true))) { + log.error(sm.getString("webXml.mergeConflictFilter", entry.getKey(), fragment.getName(), fragment.getURL())); return false; @@ -1587,8 +1615,7 @@ filters.putAll(temp.getFilters()); for (WebXml fragment : fragments) { - for (JspPropertyGroup jspPropertyGroup : - fragment.getJspPropertyGroups()) { + for (JspPropertyGroup jspPropertyGroup : fragment.getJspPropertyGroups()) { // Always additive addJspPropertyGroup(jspPropertyGroup); } @@ -1602,9 +1629,8 @@ } for (WebXml fragment : fragments) { - if (!mergeMap(fragment.getLocaleEncodingMappings(), - localeEncodingMappings, temp.getLocaleEncodingMappings(), - fragment, "Locale Encoding Mapping")) { + if (!mergeMap(fragment.getLocaleEncodingMappings(), localeEncodingMappings, + temp.getLocaleEncodingMappings(), fragment, "Locale Encoding Mapping")) { return false; } } @@ -1615,14 +1641,11 @@ for (WebXml fragment : fragments) { LoginConfig fragmentLoginConfig = fragment.loginConfig; if (fragmentLoginConfig != null) { - if (tempLoginConfig == null || - fragmentLoginConfig.equals(tempLoginConfig)) { + if (tempLoginConfig == null || fragmentLoginConfig.equals(tempLoginConfig)) { tempLoginConfig = fragmentLoginConfig; } else { - log.error(sm.getString( - "webXml.mergeConflictLoginConfig", - fragment.getName(), - fragment.getURL())); + log.error( + sm.getString("webXml.mergeConflictLoginConfig", fragment.getName(), fragment.getURL())); } } } @@ -1638,32 +1661,30 @@ messageDestinationRefs.putAll(temp.getMessageDestinationRefs()); for (WebXml fragment : fragments) { - if (!mergeResourceMap(fragment.getMessageDestinations(), messageDestinations, - temp.getMessageDestinations(), fragment)) { + if (!mergeResourceMap(fragment.getMessageDestinations(), messageDestinations, temp.getMessageDestinations(), + fragment)) { return false; } } messageDestinations.putAll(temp.getMessageDestinations()); for (WebXml fragment : fragments) { - if (!mergeMap(fragment.getMimeMappings(), mimeMappings, - temp.getMimeMappings(), fragment, "Mime Mapping")) { + if (!mergeMap(fragment.getMimeMappings(), mimeMappings, temp.getMimeMappings(), fragment, "Mime Mapping")) { return false; } } mimeMappings.putAll(temp.getMimeMappings()); for (WebXml fragment : fragments) { - if (!mergeResourceMap(fragment.getResourceEnvRefs(), resourceEnvRefs, - temp.getResourceEnvRefs(), fragment)) { + if (!mergeResourceMap(fragment.getResourceEnvRefs(), resourceEnvRefs, temp.getResourceEnvRefs(), + fragment)) { return false; } } resourceEnvRefs.putAll(temp.getResourceEnvRefs()); for (WebXml fragment : fragments) { - if (!mergeResourceMap(fragment.getResourceRefs(), resourceRefs, - temp.getResourceRefs(), fragment)) { + if (!mergeResourceMap(fragment.getResourceRefs(), resourceRefs, temp.getResourceRefs(), fragment)) { return false; } } @@ -1684,8 +1705,7 @@ } for (WebXml fragment : fragments) { - if (!mergeResourceMap(fragment.getServiceRefs(), serviceRefs, - temp.getServiceRefs(), fragment)) { + if (!mergeResourceMap(fragment.getServiceRefs(), serviceRefs, temp.getServiceRefs(), fragment)) { return false; } } @@ -1698,8 +1718,7 @@ // defined in web.xml List> servletMappingsToAdd = new ArrayList<>(); for (WebXml fragment : fragments) { - for (Map.Entry servletMap : - fragment.getServletMappings().entrySet()) { + for (Map.Entry servletMap : fragment.getServletMappings().entrySet()) { if (!servletMappingNames.contains(servletMap.getValue()) && !servletMappings.containsKey(servletMap.getKey())) { servletMappingsToAdd.add(servletMap); @@ -1713,19 +1732,13 @@ } for (WebXml fragment : fragments) { - for (Map.Entry entry : - fragment.getServlets().entrySet()) { + for (Map.Entry entry : fragment.getServlets().entrySet()) { if (servlets.containsKey(entry.getKey())) { - mergeServlet(entry.getValue(), - servlets.get(entry.getKey()), false); + mergeServlet(entry.getValue(), servlets.get(entry.getKey()), false); } else { if (temp.getServlets().containsKey(entry.getKey())) { - if (!(mergeServlet(entry.getValue(), - temp.getServlets().get(entry.getKey()), true))) { - log.error(sm.getString( - "webXml.mergeConflictServlet", - entry.getKey(), - fragment.getName(), + if (!(mergeServlet(entry.getValue(), temp.getServlets().get(entry.getKey()), true))) { + log.error(sm.getString("webXml.mergeConflictServlet", entry.getKey(), fragment.getName(), fragment.getURL())); return false; @@ -1744,21 +1757,17 @@ if (value != null) { if (temp.getSessionConfig().getSessionTimeout() == null) { temp.getSessionConfig().setSessionTimeout(value.toString()); - } else if (value.equals( - temp.getSessionConfig().getSessionTimeout())) { + } else if (value.equals(temp.getSessionConfig().getSessionTimeout())) { // Fragments use same value - no conflict } else { - log.error(sm.getString( - "webXml.mergeConflictSessionTimeout", - fragment.getName(), + log.error(sm.getString("webXml.mergeConflictSessionTimeout", fragment.getName(), fragment.getURL())); return false; } } } if (temp.getSessionConfig().getSessionTimeout() != null) { - sessionConfig.setSessionTimeout( - temp.getSessionConfig().getSessionTimeout().toString()); + sessionConfig.setSessionTimeout(temp.getSessionConfig().getSessionTimeout().toString()); } } @@ -1768,20 +1777,16 @@ if (value != null) { if (temp.getSessionConfig().getCookieName() == null) { temp.getSessionConfig().setCookieName(value); - } else if (value.equals( - temp.getSessionConfig().getCookieName())) { + } else if (value.equals(temp.getSessionConfig().getCookieName())) { // Fragments use same value - no conflict } else { - log.error(sm.getString( - "webXml.mergeConflictSessionCookieName", - fragment.getName(), + log.error(sm.getString("webXml.mergeConflictSessionCookieName", fragment.getName(), fragment.getURL())); return false; } } } - sessionConfig.setCookieName( - temp.getSessionConfig().getCookieName()); + sessionConfig.setCookieName(temp.getSessionConfig().getCookieName()); } Map mainAttributes = getSessionConfig().getCookieAttributes(); @@ -1795,9 +1800,7 @@ // If values are the same, NO-OP. If they are different // trigger a merge error if (!mergedFragmentAttributes.get(attribute.getKey()).equals(attribute.getValue())) { - log.error(sm.getString( - "webXml.mergeConflictSessionCookieAttributes", - fragment.getName(), + log.error(sm.getString("webXml.mergeConflictSessionCookieAttributes", fragment.getName(), fragment.getURL())); return false; } @@ -1810,39 +1813,33 @@ } mainAttributes.putAll(mergedFragmentAttributes); - if (sessionConfig.getSessionTrackingModes().size() == 0) { + if (sessionConfig.getSessionTrackingModes().isEmpty()) { for (WebXml fragment : fragments) { - EnumSet value = - fragment.getSessionConfig().getSessionTrackingModes(); - if (value.size() > 0) { - if (temp.getSessionConfig().getSessionTrackingModes().size() == 0) { + EnumSet value = fragment.getSessionConfig().getSessionTrackingModes(); + if (!value.isEmpty()) { + if (temp.getSessionConfig().getSessionTrackingModes().isEmpty()) { temp.getSessionConfig().getSessionTrackingModes().addAll(value); - } else if (value.equals( - temp.getSessionConfig().getSessionTrackingModes())) { + } else if (value.equals(temp.getSessionConfig().getSessionTrackingModes())) { // Fragments use same value - no conflict } else { - log.error(sm.getString( - "webXml.mergeConflictSessionTrackingMode", - fragment.getName(), + log.error(sm.getString("webXml.mergeConflictSessionTrackingMode", fragment.getName(), fragment.getURL())); return false; } } } - sessionConfig.getSessionTrackingModes().addAll( - temp.getSessionConfig().getSessionTrackingModes()); + sessionConfig.getSessionTrackingModes().addAll(temp.getSessionConfig().getSessionTrackingModes()); } for (WebXml fragment : fragments) { - if (!mergeMap(fragment.getTaglibs(), taglibs, - temp.getTaglibs(), fragment, "Taglibs")) { + if (!mergeMap(fragment.getTaglibs(), taglibs, temp.getTaglibs(), fragment, "Taglibs")) { return false; } } taglibs.putAll(temp.getTaglibs()); for (WebXml fragment : fragments) { - if (fragment.alwaysAddWelcomeFiles || welcomeFiles.size() == 0) { + if (fragment.alwaysAddWelcomeFiles || welcomeFiles.isEmpty()) { for (String welcomeFile : fragment.getWelcomeFiles()) { addWelcomeFile(welcomeFile); } @@ -1851,9 +1848,8 @@ if (postConstructMethods.isEmpty()) { for (WebXml fragment : fragments) { - if (!mergeLifecycleCallback(fragment.getPostConstructMethods(), - temp.getPostConstructMethods(), fragment, - "Post Construct Methods")) { + if (!mergeLifecycleCallback(fragment.getPostConstructMethods(), temp.getPostConstructMethods(), + fragment, "Post Construct Methods")) { return false; } } @@ -1862,8 +1858,7 @@ if (preDestroyMethods.isEmpty()) { for (WebXml fragment : fragments) { - if (!mergeLifecycleCallback(fragment.getPreDestroyMethods(), - temp.getPreDestroyMethods(), fragment, + if (!mergeLifecycleCallback(fragment.getPreDestroyMethods(), temp.getPreDestroyMethods(), fragment, "Pre Destroy Methods")) { return false; } @@ -1874,23 +1869,18 @@ return true; } - private boolean mergeResourceMap( - Map fragmentResources, Map mainResources, - Map tempResources, WebXml fragment) { + private boolean mergeResourceMap(Map fragmentResources, + Map mainResources, Map tempResources, WebXml fragment) { for (T resource : fragmentResources.values()) { String resourceName = resource.getName(); if (mainResources.containsKey(resourceName)) { - mainResources.get(resourceName).getInjectionTargets().addAll( - resource.getInjectionTargets()); + mainResources.get(resourceName).getInjectionTargets().addAll(resource.getInjectionTargets()); } else { // Not defined in main web.xml T existingResource = tempResources.get(resourceName); if (existingResource != null) { if (!existingResource.equals(resource)) { - log.error(sm.getString( - "webXml.mergeConflictResource", - resourceName, - fragment.getName(), + log.error(sm.getString("webXml.mergeConflictResource", resourceName, fragment.getName(), fragment.getURL())); return false; } @@ -1902,22 +1892,16 @@ return true; } - private boolean mergeMap(Map fragmentMap, - Map mainMap, Map tempMap, WebXml fragment, - String mapName) { - for (Entry entry : fragmentMap.entrySet()) { + private boolean mergeMap(Map fragmentMap, Map mainMap, Map tempMap, + WebXml fragment, String mapName) { + for (Entry entry : fragmentMap.entrySet()) { final String key = entry.getKey(); if (!mainMap.containsKey(key)) { // Not defined in main web.xml T value = entry.getValue(); if (tempMap.containsKey(key)) { - if (value != null && !value.equals( - tempMap.get(key))) { - log.error(sm.getString( - "webXml.mergeConflictString", - mapName, - key, - fragment.getName(), + if (value != null && !value.equals(tempMap.get(key))) { + log.error(sm.getString("webXml.mergeConflictString", mapName, key, fragment.getName(), fragment.getURL())); return false; } @@ -1929,31 +1913,26 @@ return true; } - private static boolean mergeFilter(FilterDef src, FilterDef dest, - boolean failOnConflict) { + private static boolean mergeFilter(FilterDef src, FilterDef dest, boolean failOnConflict) { if (dest.getAsyncSupported() == null) { dest.setAsyncSupported(src.getAsyncSupported()); } else if (src.getAsyncSupported() != null) { - if (failOnConflict && - !src.getAsyncSupported().equals(dest.getAsyncSupported())) { + if (failOnConflict && !src.getAsyncSupported().equals(dest.getAsyncSupported())) { return false; } } - if (dest.getFilterClass() == null) { + if (dest.getFilterClass() == null) { dest.setFilterClass(src.getFilterClass()); } else if (src.getFilterClass() != null) { - if (failOnConflict && - !src.getFilterClass().equals(dest.getFilterClass())) { + if (failOnConflict && !src.getFilterClass().equals(dest.getFilterClass())) { return false; } } - for (Map.Entry srcEntry : - src.getParameterMap().entrySet()) { + for (Map.Entry srcEntry : src.getParameterMap().entrySet()) { if (dest.getParameterMap().containsKey(srcEntry.getKey())) { - if (failOnConflict && !dest.getParameterMap().get( - srcEntry.getKey()).equals(srcEntry.getValue())) { + if (failOnConflict && !dest.getParameterMap().get(srcEntry.getKey()).equals(srcEntry.getValue())) { return false; } } else { @@ -1963,8 +1942,7 @@ return true; } - private static boolean mergeServlet(ServletDef src, ServletDef dest, - boolean failOnConflict) { + private static boolean mergeServlet(ServletDef src, ServletDef dest, boolean failOnConflict) { // These tests should be unnecessary... if (dest.getServletClass() != null && dest.getJspFile() != null) { return false; @@ -1979,13 +1957,11 @@ dest.setJspFile(src.getJspFile()); } else if (failOnConflict) { if (src.getServletClass() != null && - (dest.getJspFile() != null || - !src.getServletClass().equals(dest.getServletClass()))) { + (dest.getJspFile() != null || !src.getServletClass().equals(dest.getServletClass()))) { return false; } if (src.getJspFile() != null && - (dest.getServletClass() != null || - !src.getJspFile().equals(dest.getJspFile()))) { + (dest.getServletClass() != null || !src.getJspFile().equals(dest.getJspFile()))) { return false; } } @@ -2000,8 +1976,7 @@ dest.setLoadOnStartup(src.getLoadOnStartup().toString()); } } else if (src.getLoadOnStartup() != null) { - if (failOnConflict && - !src.getLoadOnStartup().equals(dest.getLoadOnStartup())) { + if (failOnConflict && !src.getLoadOnStartup().equals(dest.getLoadOnStartup())) { return false; } } @@ -2011,17 +1986,14 @@ dest.setEnabled(src.getEnabled().toString()); } } else if (src.getEnabled() != null) { - if (failOnConflict && - !src.getEnabled().equals(dest.getEnabled())) { + if (failOnConflict && !src.getEnabled().equals(dest.getEnabled())) { return false; } } - for (Map.Entry srcEntry : - src.getParameterMap().entrySet()) { + for (Map.Entry srcEntry : src.getParameterMap().entrySet()) { if (dest.getParameterMap().containsKey(srcEntry.getKey())) { - if (failOnConflict && !dest.getParameterMap().get( - srcEntry.getKey()).equals(srcEntry.getValue())) { + if (failOnConflict && !dest.getParameterMap().get(srcEntry.getKey()).equals(srcEntry.getValue())) { return false; } } else { @@ -2032,8 +2004,7 @@ if (dest.getMultipartDef() == null) { dest.setMultipartDef(src.getMultipartDef()); } else if (src.getMultipartDef() != null) { - return mergeMultipartDef(src.getMultipartDef(), - dest.getMultipartDef(), failOnConflict); + return mergeMultipartDef(src.getMultipartDef(), dest.getMultipartDef(), failOnConflict); } if (dest.getAsyncSupported() == null) { @@ -2041,23 +2012,18 @@ dest.setAsyncSupported(src.getAsyncSupported().toString()); } } else if (src.getAsyncSupported() != null) { - if (failOnConflict && - !src.getAsyncSupported().equals(dest.getAsyncSupported())) { - return false; - } + return !failOnConflict || src.getAsyncSupported().equals(dest.getAsyncSupported()); } return true; } - private static boolean mergeMultipartDef(MultipartDef src, MultipartDef dest, - boolean failOnConflict) { + private static boolean mergeMultipartDef(MultipartDef src, MultipartDef dest, boolean failOnConflict) { if (dest.getLocation() == null) { dest.setLocation(src.getLocation()); } else if (src.getLocation() != null) { - if (failOnConflict && - !src.getLocation().equals(dest.getLocation())) { + if (failOnConflict && !src.getLocation().equals(dest.getLocation())) { return false; } } @@ -2065,9 +2031,7 @@ if (dest.getFileSizeThreshold() == null) { dest.setFileSizeThreshold(src.getFileSizeThreshold()); } else if (src.getFileSizeThreshold() != null) { - if (failOnConflict && - !src.getFileSizeThreshold().equals( - dest.getFileSizeThreshold())) { + if (failOnConflict && !src.getFileSizeThreshold().equals(dest.getFileSizeThreshold())) { return false; } } @@ -2075,8 +2039,7 @@ if (dest.getMaxFileSize() == null) { dest.setMaxFileSize(src.getMaxFileSize()); } else if (src.getMaxFileSize() != null) { - if (failOnConflict && - !src.getMaxFileSize().equals(dest.getMaxFileSize())) { + if (failOnConflict && !src.getMaxFileSize().equals(dest.getMaxFileSize())) { return false; } } @@ -2084,27 +2047,22 @@ if (dest.getMaxRequestSize() == null) { dest.setMaxRequestSize(src.getMaxRequestSize()); } else if (src.getMaxRequestSize() != null) { - if (failOnConflict && - !src.getMaxRequestSize().equals( - dest.getMaxRequestSize())) { - return false; - } + return !failOnConflict || src.getMaxRequestSize().equals(dest.getMaxRequestSize()); } return true; } - private boolean mergeLifecycleCallback( - Map fragmentMap, Map tempMap, - WebXml fragment, String mapName) { - for (Entry entry : fragmentMap.entrySet()) { + private boolean mergeLifecycleCallback(Map fragmentMap, Map tempMap, WebXml fragment, + String mapName) { + for (Entry entry : fragmentMap.entrySet()) { final String key = entry.getKey(); final String value = entry.getValue(); if (tempMap.containsKey(key)) { if (value != null && !value.equals(tempMap.get(key))) { - log.error(sm.getString("webXml.mergeConflictString", - mapName, key, fragment.getName(), fragment.getURL())); + log.error(sm.getString("webXml.mergeConflictString", mapName, key, fragment.getName(), + fragment.getURL())); return false; } } else { @@ -2116,24 +2074,22 @@ /** - * Generates the sub-set of the web-fragment.xml files to be processed in - * the order that the fragments must be processed as per the rules in the - * Servlet spec. + * Generates the sub-set of the web-fragment.xml files to be processed in the order that the fragments must be + * processed as per the rules in the Servlet spec. * * @param application The application web.xml file * @param fragments The map of fragment names to web fragments - * @param servletContext The servlet context the fragments are associated - * with + * @param servletContext The servlet context the fragments are associated with + * * @return Ordered list of web-fragment.xml files to process */ - public static Set orderWebFragments(WebXml application, - Map fragments, ServletContext servletContext) { + public static Set orderWebFragments(WebXml application, Map fragments, + ServletContext servletContext) { return application.orderWebFragments(fragments, servletContext); } - private Set orderWebFragments(Map fragments, - ServletContext servletContext) { + private Set orderWebFragments(Map fragments, ServletContext servletContext) { Set orderedFragments = new LinkedHashSet<>(); @@ -2148,7 +2104,7 @@ for (String requestedName : requestedOrder) { if (ORDER_OTHERS.equals(requestedName)) { // Add all fragments not named explicitly at this point - for (Entry entry : fragments.entrySet()) { + for (Entry entry : fragments.entrySet()) { if (!requestedOrder.contains(entry.getKey())) { WebXml fragment = entry.getValue(); if (fragment != null) { @@ -2161,7 +2117,7 @@ if (fragment != null) { orderedFragments.add(fragment); } else { - log.warn(sm.getString("webXml.wrongFragmentName",requestedName)); + log.warn(sm.getString("webXml.wrongFragmentName", requestedName)); } } } @@ -2175,11 +2131,10 @@ sm.getString("webXml.duplicateFragment", fragment.getName(), duplicates)); } } - // Stage 1. Make all dependencies bi-directional - this makes the - // next stage simpler. + // Stage 1. Make all dependencies bidirectional - this makes the + // next stage simpler. for (WebXml fragment : fragments.values()) { - Iterator before = - fragment.getBeforeOrdering().iterator(); + Iterator before = fragment.getBeforeOrdering().iterator(); while (before.hasNext()) { orderingPresent = true; String beforeEntry = before.next(); @@ -2208,8 +2163,8 @@ } // Stage 2. Make all fragments that are implicitly before/after - // others explicitly so. This is iterative so the next - // stage doesn't have to be. + // others explicitly so. This is iterative so the next + // stage doesn't have to be. for (WebXml fragment : fragments.values()) { if (fragment.getBeforeOrdering().contains(ORDER_OTHERS)) { makeBeforeOthersExplicit(fragment.getAfterOrdering(), fragments); @@ -2237,18 +2192,18 @@ } // Stage 4. Decouple the groups so the ordering requirements for - // each fragment in the group only refer to other fragments - // in the group. Ordering requirements outside the group - // will be handled by processing the groups in order. - // Note: Only after ordering requirements are considered. - // This is OK because of the processing in stage 1. + // each fragment in the group only refer to other fragments + // in the group. Ordering requirements outside the group + // will be handled by processing the groups in order. + // Note: Only after ordering requirements are considered. + // This is OK because of the processing in stage 1. decoupleOtherGroups(beforeSet); decoupleOtherGroups(othersSet); decoupleOtherGroups(afterSet); // Stage 5. Order each group - // Note: Only after ordering requirements are considered. - // This is OK because of the processing in stage 1. + // Note: Only after ordering requirements are considered. + // This is OK because of the processing in stage 1. orderFragments(orderedFragments, beforeSet); orderFragments(orderedFragments, othersSet); orderFragments(orderedFragments, afterSet); @@ -2272,17 +2227,16 @@ List orderedJarFileNames = null; if (orderingPresent) { orderedJarFileNames = new ArrayList<>(); - for (WebXml fragment: orderedFragments) { + for (WebXml fragment : orderedFragments) { orderedJarFileNames.add(fragment.getJarName()); } } - servletContext.setAttribute(ServletContext.ORDERED_LIBS, - orderedJarFileNames); + servletContext.setAttribute(ServletContext.ORDERED_LIBS, orderedJarFileNames); } // The remainder of the processing needs to know about container // fragments - if (containerFragments.size() > 0) { + if (!containerFragments.isEmpty()) { Set result = new LinkedHashSet<>(); if (containerFragments.iterator().next().getDelegate()) { result.addAll(containerFragments); @@ -2306,11 +2260,11 @@ fragment.getAfterOrdering().removeIf(entry -> !names.contains(entry)); } } - private static void orderFragments(Set orderedFragments, - Set unordered) { + + private static void orderFragments(Set orderedFragments, Set unordered) { Set addedThisRound = new HashSet<>(); Set addedLastRound = new HashSet<>(); - while (unordered.size() > 0) { + while (!unordered.isEmpty()) { Iterator source = unordered.iterator(); while (source.hasNext()) { WebXml fragment = source.next(); @@ -2323,10 +2277,9 @@ source.remove(); } } - if (addedThisRound.size() == 0) { + if (addedThisRound.isEmpty()) { // Circular - throw new IllegalArgumentException( - sm.getString("webXml.mergeConflictOrder")); + throw new IllegalArgumentException(sm.getString("webXml.mergeConflictOrder")); } addedLastRound.clear(); addedLastRound.addAll(addedThisRound); @@ -2334,8 +2287,7 @@ } } - private static void makeBeforeOthersExplicit(Set beforeOrdering, - Map fragments) { + private static void makeBeforeOthersExplicit(Set beforeOrdering, Map fragments) { for (String before : beforeOrdering) { if (!before.equals(ORDER_OTHERS)) { WebXml webXml = fragments.get(before); @@ -2347,8 +2299,7 @@ } } - private static void makeAfterOthersExplicit(Set afterOrdering, - Map fragments) { + private static void makeAfterOthersExplicit(Set afterOrdering, Map fragments) { for (String after : afterOrdering) { if (!after.equals(ORDER_OTHERS)) { WebXml webXml = fragments.get(after); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/WebXmlParser.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/WebXmlParser.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/WebXmlParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/WebXmlParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,44 +36,41 @@ /** * The string resources for this package. */ - private static final StringManager sm = - StringManager.getManager(Constants.PACKAGE_NAME); + private static final StringManager sm = StringManager.getManager(Constants.PACKAGE_NAME); /** - * The Digester we will use to process web application - * deployment descriptor files. + * The Digester we will use to process web application deployment descriptor files. */ private final Digester webDigester; private final WebRuleSet webRuleSet; /** - * The Digester we will use to process web fragment - * deployment descriptor files. + * The Digester we will use to process web fragment deployment descriptor files. */ private final Digester webFragmentDigester; private final WebRuleSet webFragmentRuleSet; - public WebXmlParser(boolean namespaceAware, boolean validation, - boolean blockExternal) { + public WebXmlParser(boolean namespaceAware, boolean validation, boolean blockExternal) { webRuleSet = new WebRuleSet(false); - webDigester = DigesterFactory.newDigester(validation, - namespaceAware, webRuleSet, blockExternal); + webDigester = DigesterFactory.newDigester(validation, namespaceAware, webRuleSet, blockExternal); webDigester.getParser(); webFragmentRuleSet = new WebRuleSet(true); - webFragmentDigester = DigesterFactory.newDigester(validation, - namespaceAware, webFragmentRuleSet, blockExternal); + webFragmentDigester = + DigesterFactory.newDigester(validation, namespaceAware, webFragmentRuleSet, blockExternal); webFragmentDigester.getParser(); } /** * Parse a web descriptor at a location. * - * @param url the location; if null no load will be attempted - * @param dest the instance to be populated by the parse operation + * @param url the location; if null no load will be attempted + * @param dest the instance to be populated by the parse operation * @param fragment indicate if the descriptor is a web-app or web-fragment + * * @return true if the descriptor was successfully parsed + * * @throws IOException if there was a problem reading from the URL */ public boolean parseWebXml(URL url, WebXml dest, boolean fragment) throws IOException { @@ -86,13 +83,10 @@ } - public boolean parseWebXml(InputSource source, WebXml dest, - boolean fragment) { - - boolean ok = true; + public boolean parseWebXml(InputSource source, WebXml dest, boolean fragment) { if (source == null) { - return ok; + return true; } XmlErrorHandler handler = new XmlErrorHandler(); @@ -110,29 +104,26 @@ digester.push(dest); digester.setErrorHandler(handler); - if(log.isDebugEnabled()) { - log.debug(sm.getString("webXmlParser.applicationStart", - source.getSystemId())); + if (log.isDebugEnabled()) { + log.debug(sm.getString("webXmlParser.applicationStart", source.getSystemId())); } + boolean ok = true; + try { digester.parse(source); - if (handler.getWarnings().size() > 0 || - handler.getErrors().size() > 0) { + if (!handler.getWarnings().isEmpty() || !handler.getErrors().isEmpty()) { ok = false; handler.logFindings(log, source.getSystemId()); } } catch (SAXParseException e) { - log.error(sm.getString("webXmlParser.applicationParse", - source.getSystemId()), e); - log.error(sm.getString("webXmlParser.applicationPosition", - "" + e.getLineNumber(), - "" + e.getColumnNumber())); + log.error(sm.getString("webXmlParser.applicationParse", source.getSystemId()), e); + log.error( + sm.getString("webXmlParser.applicationPosition", "" + e.getLineNumber(), "" + e.getColumnNumber())); ok = false; } catch (Exception e) { - log.error(sm.getString("webXmlParser.applicationParse", - source.getSystemId()), e); + log.error(sm.getString("webXmlParser.applicationParse", source.getSystemId()), e); ok = false; } finally { InputSourceUtil.close(source); @@ -146,6 +137,7 @@ /** * Sets the ClassLoader to be used for creating descriptor objects. + * * @param classLoader the ClassLoader to be used for creating descriptor objects */ public void setClassLoader(ClassLoader classLoader) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/XmlEncodingBase.java tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/XmlEncodingBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/descriptor/web/XmlEncodingBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/descriptor/web/XmlEncodingBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,7 @@ import java.nio.charset.StandardCharsets; /** - * Base class for those elements that need to track the encoding used in the - * source XML. + * Base class for those elements that need to track the encoding used in the source XML. */ public abstract class XmlEncodingBase { @@ -34,11 +33,10 @@ /** - * Obtain the character encoding of the XML source that was used to - * populated this object. + * Obtain the character encoding of the XML source that was used to populate this object. * - * @return The character encoding of the associated XML source or - * UTF-8 if the encoding could not be determined + * @return The character encoding of the associated XML source or UTF-8 if the encoding could not be + * determined */ public Charset getCharset() { return charset; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/AbstractObjectCreationFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,19 +21,18 @@ /** - *

          Abstract base class for ObjectCreationFactory - * implementations.

          + *

          + * Abstract base class for ObjectCreationFactory implementations. + *

          */ -public abstract class AbstractObjectCreationFactory - implements ObjectCreationFactory { +public abstract class AbstractObjectCreationFactory implements ObjectCreationFactory { // ----------------------------------------------------- Instance Variables /** - * The associated Digester instance that was set up by - * {@link FactoryCreateRule} upon initialization. + * The associated Digester instance that was set up by {@link FactoryCreateRule} upon initialization. */ private Digester digester = null; @@ -42,8 +41,8 @@ /** - *

          Factory method called by {@link FactoryCreateRule} to supply an - * object based on the element's attributes. + *

          + * Factory method called by {@link FactoryCreateRule} to supply an object based on the element's attributes. * * @param attributes the element's attributes * @@ -54,8 +53,8 @@ /** - *

          Returns the {@link Digester} that was set by the - * {@link FactoryCreateRule} upon initialization. + *

          + * Returns the {@link Digester} that was set by the {@link FactoryCreateRule} upon initialization. */ @Override public Digester getDigester() { @@ -64,8 +63,9 @@ /** - *

          Set the {@link Digester} to allow the implementation to do logging, - * classloading based on the digester's classloader, etc. + *

          + * Set the {@link Digester} to allow the implementation to do logging, classloading based on the digester's + * classloader, etc. * * @param digester parent Digester object */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/ArrayStack.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ArrayStack.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/ArrayStack.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ArrayStack.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,27 +20,28 @@ import java.util.EmptyStackException; /** - *

          Imported copy of the ArrayStack class from - * Commons Collections, which was the only direct dependency from Digester.

          - * - *

          WARNING - This class is public solely to allow it to be - * used from subpackages of org.apache.commons.digester. - * It should not be considered part of the public API of Commons Digester. - * If you want to use such a class yourself, you should use the one from - * Commons Collections directly.

          - * - *

          An implementation of the {@link java.util.Stack} API that is based on an - * ArrayList instead of a Vector, so it is not - * synchronized to protect against multi-threaded access. The implementation - * is therefore operates faster in environments where you do not need to - * worry about multiple thread contention.

          - * - *

          Unlike Stack, ArrayStack accepts null entries. + *

          + * Imported copy of the ArrayStack class from Commons Collections, which was the only direct dependency + * from Digester. + *

          + *

          + * WARNING - This class is public solely to allow it to be used from subpackages of + * org.apache.commons.digester. It should not be considered part of the public API of Commons Digester. If + * you want to use such a class yourself, you should use the one from Commons Collections directly. + *

          + *

          + * An implementation of the {@link java.util.Stack} API that is based on an ArrayList instead of a + * Vector, so it is not synchronized to protect against multi-threaded access. The implementation is + * therefore operates faster in environments where you do not need to worry about multiple thread contention. + *

          + *

          + * Unlike Stack, ArrayStack accepts null entries. *

          * * @param Type of object in this stack * * @see java.util.Stack + * * @since Digester 1.6 (from Commons Collections 1.0) */ public class ArrayStack extends ArrayList { @@ -49,8 +50,8 @@ private static final long serialVersionUID = 2130079159931574599L; /** - * Constructs a new empty ArrayStack. The initial size - * is controlled by ArrayList and is currently 10. + * Constructs a new empty ArrayStack. The initial size is controlled by ArrayList and is + * currently 10. */ public ArrayStack() { super(); @@ -59,9 +60,9 @@ /** * Constructs a new empty ArrayStack with an initial size. * - * @param initialSize the initial size to use - * @throws IllegalArgumentException if the specified initial size - * is negative + * @param initialSize the initial size to use + * + * @throws IllegalArgumentException if the specified initial size is negative */ public ArrayStack(int initialSize) { super(initialSize); @@ -70,8 +71,8 @@ /** * Return true if this stack is currently empty. *

          - * This method exists for compatibility with java.util.Stack. - * New users of this class should use isEmpty instead. + * This method exists for compatibility with java.util.Stack. New users of this class should use + * isEmpty instead. * * @return true if the stack is currently empty */ @@ -83,11 +84,12 @@ * Returns the top item off of this stack without removing it. * * @return the top item on the stack - * @throws EmptyStackException if the stack is empty + * + * @throws EmptyStackException if the stack is empty */ public E peek() throws EmptyStackException { int n = size(); - if (n <= 0) { + if (n == 0) { throw new EmptyStackException(); } else { return get(n - 1); @@ -95,13 +97,13 @@ } /** - * Returns the n'th item down (zero-relative) from the top of this - * stack without removing it. + * Returns the n'th item down (zero-relative) from the top of this stack without removing it. + * + * @param n the number of items down to go * - * @param n the number of items down to go * @return the n'th item on the stack, zero relative - * @throws EmptyStackException if there are not enough items on the - * stack to satisfy this request + * + * @throws EmptyStackException if there are not enough items on the stack to satisfy this request */ public E peek(int n) throws EmptyStackException { int m = (size() - n) - 1; @@ -116,11 +118,12 @@ * Pops the top item off of this stack and return it. * * @return the top item on the stack - * @throws EmptyStackException if the stack is empty + * + * @throws EmptyStackException if the stack is empty */ public E pop() throws EmptyStackException { int n = size(); - if (n <= 0) { + if (n == 0) { throw new EmptyStackException(); } else { return remove(n - 1); @@ -128,10 +131,11 @@ } /** - * Pushes a new item onto the top of this stack. The pushed item is also - * returned. This is equivalent to calling add. + * Pushes a new item onto the top of this stack. The pushed item is also returned. This is equivalent to calling + * add. + * + * @param item the item to be added * - * @param item the item to be added * @return the item just pushed */ public E push(E item) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/CallMethodRule.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/CallMethodRule.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/CallMethodRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/CallMethodRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,56 +22,48 @@ import org.xml.sax.Attributes; /** - *

          Rule implementation that calls a method on an object on the stack - * (normally the top/parent object), passing arguments collected from - * subsequent CallParamRule rules or from the body of this - * element.

          - * - *

          By using {@link #CallMethodRule(String methodName)} - * a method call can be made to a method which accepts no - * arguments.

          - * - *

          Incompatible method parameter types are converted - * using org.apache.commons.beanutils.ConvertUtils. + *

          + * Rule implementation that calls a method on an object on the stack (normally the top/parent object), passing arguments + * collected from subsequent CallParamRule rules or from the body of this element. *

          - * - *

          This rule now uses + *

          + * By using {@link #CallMethodRule(String methodName)} a method call can be made to a method which accepts no arguments. + *

          + *

          + * Incompatible method parameter types are converted using org.apache.commons.beanutils.ConvertUtils. + *

          + *

          + * This rule now uses * - * org.apache.commons.beanutils.MethodUtils#invokeMethod - * by default. - * This increases the kinds of methods successfully and allows primitives - * to be matched by passing in wrapper classes. - * There are rare cases when org.apache.commons.beanutils.MethodUtils#invokeExactMethod - * (the old default) is required. - * This method is much stricter in its reflection. - * Setting the UseExactMatch to true reverts to the use of this - * method.

          - * - *

          Note that the target method is invoked when the end of - * the tag the CallMethodRule fired on is encountered, not when the - * last parameter becomes available. This implies that rules which fire on - * tags nested within the one associated with the CallMethodRule will - * fire before the CallMethodRule invokes the target method. This behaviour is - * not configurable.

          - * - *

          Note also that if a CallMethodRule is expecting exactly one parameter - * and that parameter is not available (eg CallParamRule is used with an - * attribute name but the attribute does not exist) then the method will - * not be invoked. If a CallMethodRule is expecting more than one parameter, - * then it is always invoked, regardless of whether the parameters were - * available or not (missing parameters are passed as null values).

          + * org.apache.commons.beanutils.MethodUtils#invokeMethod by default. This increases the kinds of methods + * successfully and allows primitives to be matched by passing in wrapper classes. There are rare cases when + * org.apache.commons.beanutils.MethodUtils#invokeExactMethod (the old default) is required. This method is much + * stricter in its reflection. Setting the UseExactMatch to true reverts to the use of this method. + *

          + *

          + * Note that the target method is invoked when the end of the tag the CallMethodRule fired on is encountered, + * not when the last parameter becomes available. This implies that rules which fire on tags nested within the + * one associated with the CallMethodRule will fire before the CallMethodRule invokes the target method. This behaviour + * is not configurable. + *

          + *

          + * Note also that if a CallMethodRule is expecting exactly one parameter and that parameter is not available (e.g. + * CallParamRule is used with an attribute name but the attribute does not exist) then the method will not be invoked. + * If a CallMethodRule is expecting more than one parameter, then it is always invoked, regardless of whether the + * parameters were available or not (missing parameters are passed as null values). + *

          */ public class CallMethodRule extends Rule { // ----------------------------------------------------------- Constructors /** - * Construct a "call method" rule with the specified method name. The - * parameter types (if any) default to java.lang.String. + * Construct a "call method" rule with the specified method name. The parameter types (if any) default to + * java.lang.String. * * @param methodName Method name of the parent method to call - * @param paramCount The number of parameters to collect, or - * zero for a single argument from the body of this element. + * @param paramCount The number of parameters to collect, or zero for a single argument from the body of this + * element. */ public CallMethodRule(String methodName, int paramCount) { this(0, methodName, paramCount); @@ -79,16 +71,15 @@ /** - * Construct a "call method" rule with the specified method name. The - * parameter types (if any) default to java.lang.String. + * Construct a "call method" rule with the specified method name. The parameter types (if any) default to + * java.lang.String. * - * @param targetOffset location of the target object. Positive numbers are - * relative to the top of the digester object stack. Negative numbers - * are relative to the bottom of the stack. Zero implies the top - * object on the stack. - * @param methodName Method name of the parent method to call - * @param paramCount The number of parameters to collect, or - * zero for a single argument from the body of this element. + * @param targetOffset location of the target object. Positive numbers are relative to the top of the digester + * object stack. Negative numbers are relative to the bottom of the stack. Zero implies the + * top object on the stack. + * @param methodName Method name of the parent method to call + * @param paramCount The number of parameters to collect, or zero for a single argument from the body of this + * element. */ public CallMethodRule(int targetOffset, String methodName, int paramCount) { this.targetOffset = targetOffset; @@ -104,8 +95,7 @@ /** - * Construct a "call method" rule with the specified method name. - * The method should accept no parameters. + * Construct a "call method" rule with the specified method name. The method should accept no parameters. * * @param methodName Method name of the parent method to call */ @@ -115,27 +105,21 @@ /** - * Construct a "call method" rule with the specified method name and - * parameter types. If paramCount is set to zero the rule - * will use the body of this element as the single argument of the - * method, unless paramTypes is null or empty, in this - * case the rule will call the specified method with no arguments. + * Construct a "call method" rule with the specified method name and parameter types. If paramCount is + * set to zero the rule will use the body of this element as the single argument of the method, unless + * paramTypes is null or empty, in this case the rule will call the specified method with no arguments. * - * @param targetOffset location of the target object. Positive numbers are - * relative to the top of the digester object stack. Negative numbers - * are relative to the bottom of the stack. Zero implies the top - * object on the stack. - * @param methodName Method name of the parent method to call - * @param paramCount The number of parameters to collect, or - * zero for a single argument from the body of this element - * @param paramTypes The Java classes that represent the - * parameter types of the method arguments - * (if you wish to use a primitive type, specify the corresponding - * Java wrapper class instead, such as java.lang.Boolean.TYPE - * for a boolean parameter) + * @param targetOffset location of the target object. Positive numbers are relative to the top of the digester + * object stack. Negative numbers are relative to the bottom of the stack. Zero implies the + * top object on the stack. + * @param methodName Method name of the parent method to call + * @param paramCount The number of parameters to collect, or zero for a single argument from the body of this + * element + * @param paramTypes The Java classes that represent the parameter types of the method arguments (if you wish to + * use a primitive type, specify the corresponding Java wrapper class instead, such as + * java.lang.Boolean.TYPE for a boolean parameter) */ - public CallMethodRule(int targetOffset, String methodName, int paramCount, - Class[] paramTypes) { + public CallMethodRule(int targetOffset, String methodName, int paramCount, Class[] paramTypes) { this.targetOffset = targetOffset; this.methodName = methodName; @@ -159,9 +143,8 @@ /** - * location of the target object for the call, relative to the - * top of the digester object stack. The default value of zero - * means the target object is the one on top of the stack. + * location of the target object for the call, relative to the top of the digester object stack. The default value + * of zero means the target object is the one on top of the stack. */ protected final int targetOffset; @@ -173,9 +156,8 @@ /** - * The number of parameters to collect from MethodParam rules. - * If this value is zero, a single parameter will be collected from the - * body of this element. + * The number of parameters to collect from MethodParam rules. If this value is zero, a single + * parameter will be collected from the body of this element. */ protected final int paramCount; @@ -183,7 +165,7 @@ /** * The parameter types of the parameters to be collected. */ - protected Class paramTypes[] = null; + protected final Class[] paramTypes; /** @@ -195,8 +177,8 @@ // --------------------------------------------------------- Public Methods /** - * Should MethodUtils.invokeExactMethod - * be used for the reflection. + * Should MethodUtils.invokeExactMethod be used for the reflection. + * * @return true if invokeExactMethod is used */ public boolean getUseExactMatch() { @@ -205,8 +187,8 @@ /** - * Set whether MethodUtils.invokeExactMethod - * should be used for the reflection. + * Set whether MethodUtils.invokeExactMethod should be used for the reflection. + * * @param useExactMatch The flag value */ public void setUseExactMatch(boolean useExactMatch) { @@ -217,16 +199,13 @@ /** * Process the start of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise * @param attributes The attribute list for this element */ @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { // Push an array to capture the parameter values if necessary if (paramCount > 0) { @@ -240,16 +219,13 @@ /** * Process the body text of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise - * @param bodyText The body text of this element + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise + * @param bodyText The body text of this element */ @Override - public void body(String namespace, String name, String bodyText) - throws Exception { + public void body(String namespace, String name, String bodyText) throws Exception { if (paramCount == 0) { this.bodyText = bodyText.trim().intern(); @@ -261,25 +237,23 @@ /** * Process the end of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise */ @SuppressWarnings("null") // parameters can't trigger NPE @Override public void end(String namespace, String name) throws Exception { // Retrieve or construct the parameter values array - Object parameters[] = null; + Object[] parameters = null; if (paramCount > 0) { parameters = (Object[]) digester.popParams(); if (digester.log.isTraceEnabled()) { - for (int i=0,size=parameters.length;i= 0) { target = digester.peek(targetOffset); } else { - target = digester.peek( digester.getCount() + targetOffset ); + target = digester.peek(digester.getCount() + targetOffset); } if (target == null) { - StringBuilder sb = new StringBuilder(); - sb.append("[CallMethodRule]{"); - sb.append(digester.match); - sb.append("} Call target is null ("); - sb.append("targetOffset="); - sb.append(targetOffset); - sb.append(",stackdepth="); - sb.append(digester.getCount()); - sb.append(')'); - throw new org.xml.sax.SAXException(sb.toString()); + String sb = "[CallMethodRule]{" + digester.match + "} Call target is null (" + "targetOffset=" + + targetOffset + ",stackdepth=" + digester.getCount() + ')'; + throw new org.xml.sax.SAXException(sb); } // Invoke the required method on the top object @@ -375,8 +340,7 @@ sb.append(')'); digester.log.trace(sb.toString()); } - Object result = IntrospectionUtils.callMethodN(target, methodName, - paramValues, paramTypes); + Object result = IntrospectionUtils.callMethodN(target, methodName, paramValues, paramTypes); processMethodCallResult(result); StringBuilder code = digester.getGeneratedCode(); @@ -411,8 +375,7 @@ /** - * Subclasses may override this method to perform additional processing of the - * invoked method's result. + * Subclasses may override this method to perform additional processing of the invoked method's result. * * @param result the Object returned by the method invoked, possibly null */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/CallParamRule.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/CallParamRule.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/CallParamRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/CallParamRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,15 +19,15 @@ import org.xml.sax.Attributes; /** - *

          Rule implementation that saves a parameter for use by a surrounding - * CallMethodRule.

          - * - *

          This parameter may be:

          + *

          + * Rule implementation that saves a parameter for use by a surrounding CallMethodRule. + *

          + *

          + * This parameter may be: + *

          *
            - *
          • from an attribute of the current element - * See {@link #CallParamRule(int paramIndex, String attributeName)} - *
          • from current the element body - * See {@link #CallParamRule(int paramIndex)} + *
          • from an attribute of the current element See {@link #CallParamRule(int paramIndex, String attributeName)} + *
          • from current the element body See {@link #CallParamRule(int paramIndex)} *
          */ public class CallParamRule extends Rule { @@ -35,8 +35,7 @@ // ----------------------------------------------------------- Constructors /** - * Construct a "call parameter" rule that will save the body text of this - * element as the parameter value. + * Construct a "call parameter" rule that will save the body text of this element as the parameter value. * * @param paramIndex The zero-relative parameter number */ @@ -46,20 +45,17 @@ /** - * Construct a "call parameter" rule that will save the value of the - * specified attribute as the parameter value. + * Construct a "call parameter" rule that will save the value of the specified attribute as the parameter value. * - * @param paramIndex The zero-relative parameter number + * @param paramIndex The zero-relative parameter number * @param attributeName The name of the attribute to save */ - public CallParamRule(int paramIndex, - String attributeName) { + public CallParamRule(int paramIndex, String attributeName) { this(attributeName, paramIndex, 0, false); } - private CallParamRule(String attributeName, int paramIndex, int stackIndex, - boolean fromStack) { + private CallParamRule(String attributeName, int paramIndex, int stackIndex, boolean fromStack) { this.attributeName = attributeName; this.paramIndex = paramIndex; this.stackIndex = stackIndex; @@ -93,8 +89,7 @@ protected final int stackIndex; /** - * Stack is used to allow nested body text to be processed. - * Lazy creation. + * Stack is used to allow nested body text to be processed. Lazy creation. */ protected ArrayStack bodyTextStack; @@ -104,16 +99,13 @@ /** * Process the start of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise * @param attributes The attribute list for this element */ @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { Object param = null; @@ -121,17 +113,14 @@ param = attributes.getValue(attributeName); - } else if(fromStack) { + } else if (fromStack) { param = digester.peek(stackIndex); if (digester.log.isTraceEnabled()) { - - StringBuilder sb = new StringBuilder("[CallParamRule]{"); - sb.append(digester.match); - sb.append("} Save from stack; from stack?").append(fromStack); - sb.append("; object=").append(param); - digester.log.trace(sb.toString()); + String sb = "[CallParamRule]{" + digester.match + "} Save from stack; from stack? " + true + + "; object=" + param; + digester.log.trace(sb); } } @@ -141,8 +130,8 @@ // the instance variables will be overwritten // if this CallParamRule is reused in subsequent nesting. - if(param != null) { - Object parameters[] = (Object[]) digester.peekParams(); + if (param != null) { + Object[] parameters = (Object[]) digester.peekParams(); parameters[paramIndex] = param; } } @@ -151,16 +140,13 @@ /** * Process the body text of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise - * @param bodyText The body text of this element + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise + * @param bodyText The body text of this element */ @Override - public void body(String namespace, String name, String bodyText) - throws Exception { + public void body(String namespace, String name, String bodyText) throws Exception { if (attributeName == null && !fromStack) { // We must wait to set the parameter until end @@ -181,7 +167,7 @@ public void end(String namespace, String name) { if (bodyTextStack != null && !bodyTextStack.empty()) { // what we do now is push one parameter onto the top set of parameters - Object parameters[] = (Object[]) digester.peekParams(); + Object[] parameters = (Object[]) digester.peekParams(); parameters[paramIndex] = bodyTextStack.pop(); } } @@ -191,15 +177,8 @@ */ @Override public String toString() { - StringBuilder sb = new StringBuilder("CallParamRule["); - sb.append("paramIndex="); - sb.append(paramIndex); - sb.append(", attributeName="); - sb.append(attributeName); - sb.append(", from stack="); - sb.append(fromStack); - sb.append(']'); - return sb.toString(); + return "CallParamRule[" + "paramIndex=" + paramIndex + ", attributeName=" + attributeName + ", from stack=" + + fromStack + ']'; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/Digester.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/Digester.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/Digester.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/Digester.java 2026-01-23 19:33:36.000000000 +0000 @@ -63,23 +63,23 @@ /** - *

          A Digester processes an XML input stream by matching a - * series of element nesting patterns to execute Rules that have been added - * prior to the start of parsing. This package was inspired by the - * XmlMapper class that was part of Tomcat 3.0 and 3.1, - * but is organized somewhat differently.

          - * - *

          See the Digester - * Developer Guide for more information.

          - * - *

          IMPLEMENTATION NOTE - A single Digester instance may - * only be used within the context of a single thread at a time, and a call - * to parse() must be completed before another can be initiated - * even from the same thread.

          - * - *

          IMPLEMENTATION NOTE - A bug in Xerces 2.0.2 prevents - * the support of XML schema. You need Xerces 2.1/2.3 and up to make - * this class working with XML schema

          + *

          + * A Digester processes an XML input stream by matching a series of element nesting patterns to execute + * Rules that have been added prior to the start of parsing. This package was inspired by the XmlMapper + * class that was part of Tomcat 3.0 and 3.1, but is organized somewhat differently. + *

          + *

          + * See the Digester Developer Guide for more information. + *

          + *

          + * IMPLEMENTATION NOTE - A single Digester instance may only be used within the context of a single + * thread at a time, and a call to parse() must be completed before another can be initiated even from the + * same thread. + *

          + *

          + * IMPLEMENTATION NOTE - A bug in Xerces 2.0.2 prevents the support of XML schema. You need Xerces + * 2.1/2.3 and up to make this class working with XML schema + *

          */ public class Digester extends DefaultHandler2 { @@ -106,7 +106,8 @@ break; } catch (Throwable t) { ExceptionUtils.handleThrowable(t); - LogFactory.getLog(Digester.class).error(sm.getString("digester.propertySourceLoadError", className), t); + LogFactory.getLog(Digester.class) + .error(sm.getString("digester.propertySourceLoadError", className), t); } } } @@ -188,11 +189,9 @@ /** - * Stack whose elements are List objects, each containing a list of - * Rule objects as returned from Rules.getMatch(). As each xml element - * in the input is entered, the matching rules are pushed onto this - * stack. After the end tag is reached, the matches are popped again. - * The depth of is stack is therefore exactly the same as the current + * Stack whose elements are List objects, each containing a list of Rule objects as returned from Rules.getMatch(). + * As each xml element in the input is entered, the matching rules are pushed onto this stack. After the end tag is + * reached, the matches are popped again. The depth of is stack is therefore exactly the same as the current * "nesting" level of the input xml. * * @since 1.6 @@ -200,10 +199,9 @@ protected ArrayStack> matches = new ArrayStack<>(10); /** - * The class loader to use for instantiating application objects. - * If not specified, the context class loader, or the class loader - * used to load Digester itself, is used, based on the value of the - * useContextClassLoader variable. + * The class loader to use for instantiating application objects. If not specified, the context class loader, or the + * class loader used to load Digester itself, is used, based on the value of the useContextClassLoader + * variable. */ protected ClassLoader classLoader = null; @@ -215,20 +213,18 @@ /** - * The EntityResolver used by the SAX parser. By default it use this class + * The EntityResolver used by the SAX parser. By default, it uses this class */ protected EntityResolver entityResolver; /** - * The URLs of entityValidator that have been registered, keyed by the public - * identifier that corresponds. + * The URLs of entityValidator that have been registered, keyed by the public identifier that corresponds. */ - protected HashMap entityValidator = new HashMap<>(); + protected HashMap entityValidator = new HashMap<>(); /** - * The application-supplied error handler that is notified when parsing - * warnings, errors, or fatal errors occur. + * The application-supplied error handler that is notified when parsing warnings, errors, or fatal errors occur. */ protected ErrorHandler errorHandler = null; @@ -257,19 +253,16 @@ /** - * Registered namespaces we are currently processing. The key is the - * namespace prefix that was declared in the document. The value is an - * ArrayStack of the namespace URIs this prefix has been mapped to -- - * the top Stack element is the most current one. (This architecture - * is required because documents can declare nested uses of the same - * prefix for different Namespace URIs). + * Registered namespaces we are currently processing. The key is the namespace prefix that was declared in the + * document. The value is an ArrayStack of the namespace URIs this prefix has been mapped to -- the top Stack + * element is the most current one. (This architecture is required because documents can declare nested uses of the + * same prefix for different Namespace URIs). */ - protected HashMap> namespaces = new HashMap<>(); + protected HashMap> namespaces = new HashMap<>(); /** - * The parameters stack being utilized by CallMethodRule and - * CallParamRule rules. + * The parameters stack being utilized by CallMethodRule and CallParamRule rules. */ protected ArrayStack params = new ArrayStack<>(); @@ -281,8 +274,7 @@ /** - * The public identifier of the DTD we are currently parsing under - * (if any). + * The public identifier of the DTD we are currently parsing under (if any). */ protected String publicId = null; @@ -294,17 +286,14 @@ /** - * The "root" element of the stack (in other words, the last object - * that was popped. + * The "root" element of the stack (in other words, the last object that was popped). */ protected Object root = null; /** - * The Rules implementation containing our collection of - * Rule instances and associated matching policy. If not - * established before the first rule is added, a default implementation - * will be provided. + * The Rules implementation containing our collection of Rule instances and associated + * matching policy. If not established before the first rule is added, a default implementation will be provided. */ protected Rules rules = null; @@ -315,8 +304,8 @@ /** - * Do we want to use the Context ClassLoader when loading classes - * for instantiating new objects. Default is false. + * Do we want to use the Context ClassLoader when loading classes for instantiating new objects. Default is + * false. */ protected boolean useContextClassLoader = false; @@ -336,7 +325,7 @@ /** * Fake attributes map (attributes are often used for object creation). */ - protected Map, List> fakeAttributes = null; + protected Map,List> fakeAttributes = null; /** @@ -409,13 +398,15 @@ } protected ArrayList known = new ArrayList<>(); + public void setKnown(Object object) { known.add(object); } + public String toVariableName(Object object) { boolean found = false; int pos = 0; - if (known.size() > 0) { + if (!known.isEmpty()) { for (int i = known.size() - 1; i >= 0; i--) { if (known.get(i) == object) { pos = i; @@ -434,11 +425,11 @@ // ------------------------------------------------------------- Properties /** - * Return the currently mapped namespace URI for the specified prefix, - * if any; otherwise return null. These mappings come and - * go dynamically as the document is parsed. + * Return the currently mapped namespace URI for the specified prefix, if any; otherwise return null. + * These mappings come and go dynamically as the document is parsed. * * @param prefix Prefix to look up + * * @return the namespace URI */ public String findNamespaceURI(String prefix) { @@ -455,14 +446,15 @@ /** - * Return the class loader to be used for instantiating application objects - * when required. This is determined based upon the following rules: + * Return the class loader to be used for instantiating application objects when required. This is determined based + * upon the following rules: *
            *
          • The class loader set by setClassLoader(), if any
          • - *
          • The thread context class loader, if it exists and the - * useContextClassLoader property is set to true
          • + *
          • The thread context class loader, if it exists and the useContextClassLoader property is set to + * true
          • *
          • The class loader used to load the Digester class itself. *
          + * * @return the classloader */ public ClassLoader getClassLoader() { @@ -480,11 +472,9 @@ /** - * Set the class loader to be used for instantiating application objects - * when required. + * Set the class loader to be used for instantiating application objects when required. * - * @param classLoader The new class loader to use, or null - * to revert to the standard rules + * @param classLoader The new class loader to use, or null to revert to the standard rules */ public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; @@ -532,13 +522,15 @@ /** * SAX parser factory method. + * * @return the SAXParserFactory we will use, creating one if necessary. + * * @throws ParserConfigurationException Error creating parser - * @throws SAXNotSupportedException Error creating parser - * @throws SAXNotRecognizedException Error creating parser + * @throws SAXNotSupportedException Error creating parser + * @throws SAXNotRecognizedException Error creating parser */ - public SAXParserFactory getFactory() throws SAXNotRecognizedException, SAXNotSupportedException, - ParserConfigurationException { + public SAXParserFactory getFactory() + throws SAXNotRecognizedException, SAXNotSupportedException, ParserConfigurationException { if (factory == null) { factory = SAXParserFactory.newInstance(); @@ -562,27 +554,22 @@ /** - * Sets a flag indicating whether the requested feature is supported - * by the underlying implementation of org.xml.sax.XMLReader. - * See - * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description - * for information about the standard SAX2 feature flags. In order to be - * effective, this method must be called before the - * getParser() method is called for the first time, either - * directly or indirectly. + * Sets a flag indicating whether the requested feature is supported by the underlying implementation of + * org.xml.sax.XMLReader. See + * + * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description for information about the + * standard SAX2 feature flags. In order to be effective, this method must be called before the + * getParser() method is called for the first time, either directly or indirectly. * * @param feature Name of the feature to set the status for - * @param value The new value for this feature + * @param value The new value for this feature * - * @exception ParserConfigurationException if a parser configuration error - * occurs - * @exception SAXNotRecognizedException if the property name is - * not recognized - * @exception SAXNotSupportedException if the property name is - * recognized but not supported + * @exception ParserConfigurationException if a parser configuration error occurs + * @exception SAXNotRecognizedException if the property name is not recognized + * @exception SAXNotSupportedException if the property name is recognized but not supported */ - public void setFeature(String feature, boolean value) throws ParserConfigurationException, - SAXNotRecognizedException, SAXNotSupportedException { + public void setFeature(String feature, boolean value) + throws ParserConfigurationException, SAXNotRecognizedException, SAXNotSupportedException { getFactory().setFeature(feature, value); @@ -601,6 +588,7 @@ /** * Set the current logger for this Digester. + * * @param log The logger that will be used */ public void setLogger(Log log) { @@ -610,10 +598,10 @@ } /** - * Gets the logger used for logging SAX-related information. - * Note the output is finely grained. + * Gets the logger used for logging SAX-related information. Note the output is finely grained. * * @since 1.6 + * * @return the SAX logger */ public Log getSAXLogger() { @@ -623,8 +611,8 @@ /** - * Sets the logger used for logging SAX-related information. - * Note the output is finely grained. + * Sets the logger used for logging SAX-related information. Note the output is finely grained. + * * @param saxLog Log, not null * * @since 1.6 @@ -663,7 +651,8 @@ /** - * Set the public id of the current file being parse. + * Set the public id of the current file being parsed. + * * @param publicId the DTD/Schema public's id. */ public void setPublicId(String publicId) { @@ -672,8 +661,7 @@ /** - * @return the public identifier of the DTD we are currently - * parsing under, if any. + * @return the public identifier of the DTD we are currently parsing under, if any. */ public String getPublicId() { return this.publicId; @@ -681,8 +669,8 @@ /** - * @return the SAXParser we will use to parse the input stream. If there - * is a problem creating the parser, return null. + * @return the SAXParser we will use to parse the input stream. If there is a problem creating the parser, return + * null. */ public SAXParser getParser() { @@ -704,30 +692,28 @@ /** - * Return the current value of the specified property for the underlying - * XMLReader implementation. - * See - * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description - * for information about the standard SAX2 properties. + * Return the current value of the specified property for the underlying XMLReader implementation. See + * + * http://www.saxproject.org/apidoc/xml/sax/package-summary.html#package-description for information about the + * standard SAX2 properties. * * @param property Property name to be retrieved + * * @return the property value - * @exception SAXNotRecognizedException if the property name is - * not recognized - * @exception SAXNotSupportedException if the property name is - * recognized but not supported + * + * @exception SAXNotRecognizedException if the property name is not recognized + * @exception SAXNotSupportedException if the property name is recognized but not supported */ - public Object getProperty(String property) - throws SAXNotRecognizedException, SAXNotSupportedException { + public Object getProperty(String property) throws SAXNotRecognizedException, SAXNotSupportedException { return getParser().getProperty(property); } /** - * Return the Rules implementation object containing our - * rules collection and associated matching policy. If none has been - * established, a default implementation will be created and returned. + * Return the Rules implementation object containing our rules collection and associated matching + * policy. If none has been established, a default implementation will be created and returned. + * * @return the rules */ public Rules getRules() { @@ -740,8 +726,7 @@ /** - * Set the Rules implementation object containing our - * rules collection and associated matching policy. + * Set the Rules implementation object containing our rules collection and associated matching policy. * * @param rules New Rules implementation */ @@ -752,7 +737,7 @@ /** - * @return the boolean as to whether the context classloader should be used. + * @return a boolean to indicate if the context classloader should be used. */ public boolean getUseContextClassLoader() { return useContextClassLoader; @@ -760,11 +745,9 @@ /** - * Determine whether to use the Context ClassLoader (the one found by - * calling Thread.currentThread().getContextClassLoader()) - * to resolve/load classes that are defined in various rules. If not - * using Context ClassLoader, then the class-loading defaults to - * using the calling-class' ClassLoader. + * Determine whether to use the Context ClassLoader (the one found by calling + * Thread.currentThread().getContextClassLoader()) to resolve/load classes that are defined in various + * rules. If not using Context ClassLoader, then the class-loading defaults to using the calling class' ClassLoader. * * @param use determines whether to use Context ClassLoader. */ @@ -784,8 +767,7 @@ /** - * Set the validating parser flag. This must be called before - * parse() is called the first time. + * Set the validating parser flag. This must be called before parse() is called the first time. * * @param validating The new validating parser flag. */ @@ -803,8 +785,7 @@ /** - * Set the rules validation flag. This must be called before - * parse() is called the first time. + * Set the rules validation flag. This must be called before parse() is called the first time. * * @param rulesValidation The new rules validation flag. */ @@ -816,15 +797,17 @@ /** * @return the fake attributes list. */ - public Map, List> getFakeAttributes() { + public Map,List> getFakeAttributes() { return this.fakeAttributes; } /** * Determine if an attribute is a fake attribute. + * * @param object The object - * @param name The attribute name + * @param name The attribute name + * * @return true if this is a fake attribute */ public boolean isFakeAttribute(Object object, String name) { @@ -848,7 +831,7 @@ * * @param fakeAttributes The new fake attributes. */ - public void setFakeAttributes(Map, List> fakeAttributes) { + public void setFakeAttributes(Map,List> fakeAttributes) { this.fakeAttributes = fakeAttributes; @@ -856,11 +839,11 @@ /** - * Return the XMLReader to be used for parsing the input document. + * Return the XMLReader to be used for parsing the input document. FIX ME: there is a bug in JAXP/XERCES that + * prevent the use of a parser that contains a schema with a DTD. * - * FIX ME: there is a bug in JAXP/XERCES that prevent the use of a - * parser that contains a schema with a DTD. * @return the XML reader + * * @exception SAXException if no XMLReader can be instantiated */ public XMLReader getXMLReader() throws SAXException { @@ -895,17 +878,16 @@ /** - * Process notification of character data received from the body of - * an XML element. + * Process notification of character data received from the body of an XML element. * * @param buffer The characters from the XML document - * @param start Starting offset into the buffer + * @param start Starting offset into the buffer * @param length Number of characters from the buffer * * @exception SAXException if a parsing error is to be reported */ @Override - public void characters(char buffer[], int start, int length) throws SAXException { + public void characters(char[] buffer, int start, int length) throws SAXException { if (saxLog.isTraceEnabled()) { saxLog.trace("characters(" + new String(buffer, start, length) + ")"); @@ -958,18 +940,17 @@ /** * Process notification of the end of an XML element being reached. * - * @param namespaceURI - The Namespace URI, or the empty string if the - * element has no Namespace URI or if Namespace processing is not - * being performed. - * @param localName - The local name (without prefix), or the empty - * string if Namespace processing is not being performed. - * @param qName - The qualified XML 1.0 name (with prefix), or the - * empty string if qualified names are not available. + * @param namespaceURI - The Namespace URI, or the empty string if the element has no Namespace URI or if Namespace + * processing is not being performed. + * @param localName - The local name (without prefix), or the empty string if Namespace processing is not being + * performed. + * @param qName - The qualified XML 1.0 name (with prefix), or the empty string if qualified names are not + * available. + * * @exception SAXException if a parsing error is to be reported */ @Override - public void endElement(String namespaceURI, String localName, String qName) - throws SAXException { + public void endElement(String namespaceURI, String localName, String qName) throws SAXException { boolean debug = log.isTraceEnabled(); @@ -987,17 +968,16 @@ // the actual element name is either in localName or qName, depending // on whether the parser is namespace aware String name = localName; - if ((name == null) || (name.length() < 1)) { + if ((name == null) || (name.isEmpty())) { name = qName; } // Fire "body" events for all relevant rules List rules = matches.pop(); - if ((rules != null) && (rules.size() > 0)) { + if ((rules != null) && (!rules.isEmpty())) { String bodyText = this.bodyText.toString().intern(); - for (Rule value : rules) { + for (Rule rule : rules) { try { - Rule rule = value; if (debug) { log.trace(" Fire body() for " + rule); } @@ -1085,17 +1065,16 @@ /** - * Process notification of ignorable whitespace received from the body of - * an XML element. + * Process notification of ignorable whitespace received from the body of an XML element. * * @param buffer The characters from the XML document - * @param start Starting offset into the buffer - * @param len Number of characters from the buffer + * @param start Starting offset into the buffer + * @param len Number of characters from the buffer * * @exception SAXException if a parsing error is to be reported */ @Override - public void ignorableWhitespace(char buffer[], int start, int len) throws SAXException { + public void ignorableWhitespace(char[] buffer, int start, int len) throws SAXException { if (saxLog.isTraceEnabled()) { saxLog.trace("ignorableWhitespace(" + new String(buffer, start, len) + ")"); @@ -1110,7 +1089,7 @@ * Process notification of a processing instruction that was encountered. * * @param target The processing instruction target - * @param data The processing instruction data (if any) + * @param data The processing instruction data (if any) * * @exception SAXException if a parsing error is to be reported */ @@ -1208,19 +1187,18 @@ /** * Process notification of the start of an XML element being reached. * - * @param namespaceURI The Namespace URI, or the empty string if the element - * has no Namespace URI or if Namespace processing is not being performed. - * @param localName The local name (without prefix), or the empty - * string if Namespace processing is not being performed. - * @param qName The qualified name (with prefix), or the empty - * string if qualified names are not available.\ - * @param list The attributes attached to the element. If there are - * no attributes, it shall be an empty Attributes object. + * @param namespaceURI The Namespace URI, or the empty string if the element has no Namespace URI or if Namespace + * processing is not being performed. + * @param localName The local name (without prefix), or the empty string if Namespace processing is not being + * performed. + * @param qName The qualified name (with prefix), or the empty string if qualified names are not available.\ + * @param list The attributes attached to the element. If there are no attributes, it shall be an empty + * Attributes object. + * * @exception SAXException if a parsing error is to be reported */ @Override - public void startElement(String namespaceURI, String localName, String qName, Attributes list) - throws SAXException { + public void startElement(String namespaceURI, String localName, String qName, Attributes list) throws SAXException { boolean debug = log.isTraceEnabled(); if (saxLog.isTraceEnabled()) { @@ -1237,13 +1215,13 @@ // the actual element name is either in localName or qName, depending // on whether the parser is namespace aware String name = localName; - if ((name == null) || (name.length() < 1)) { + if ((name == null) || (name.isEmpty())) { name = qName; } // Compute the current matching rule StringBuilder sb = new StringBuilder(match); - if (match.length() > 0) { + if (!match.isEmpty()) { sb.append('/'); } sb.append(name); @@ -1255,10 +1233,9 @@ // Fire "begin" events for all relevant rules List rules = getRules().match(namespaceURI, match); matches.push(rules); - if ((rules != null) && (rules.size() > 0)) { - for (Rule value : rules) { + if ((rules != null) && (!rules.isEmpty())) { + for (Rule rule : rules) { try { - Rule rule = value; if (debug) { log.trace(" Fire begin() for " + rule); } @@ -1289,7 +1266,7 @@ /** * Process notification that a namespace prefix is coming in to scope. * - * @param prefix Prefix that is being declared + * @param prefix Prefix that is being declared * @param namespaceURI Corresponding namespace URI being mapped to * * @exception SAXException if a parsing error is to be reported @@ -1318,7 +1295,7 @@ /** * Receive notification of a notation declaration event. * - * @param name The notation name + * @param name The notation name * @param publicId The public identifier (if any) * @param systemId The system identifier (if any) */ @@ -1335,7 +1312,7 @@ /** * Receive notification of an unparsed entity declaration event. * - * @param name The unparsed entity name + * @param name The unparsed entity name * @param publicId The public identifier (if any) * @param systemId The system identifier (if any) * @param notation The name of the associated notation @@ -1344,8 +1321,7 @@ public void unparsedEntityDecl(String name, String publicId, String systemId, String notation) { if (saxLog.isTraceEnabled()) { - saxLog.trace("unparsedEntityDecl(" + name + "," + publicId + "," + systemId + "," - + notation + ")"); + saxLog.trace("unparsedEntityDecl(" + name + "," + publicId + "," + systemId + "," + notation + ")"); } } @@ -1354,9 +1330,9 @@ // ----------------------------------------------- EntityResolver Methods /** - * Set the EntityResolver used by SAX when resolving - * public id and system id. - * This must be called before the first call to parse(). + * Set the EntityResolver used by SAX when resolving public id and system id. This must be called + * before the first call to parse(). + * * @param entityResolver a class that implement the EntityResolver interface. */ public void setEntityResolver(EntityResolver entityResolver) { @@ -1366,6 +1342,7 @@ /** * Return the Entity Resolver used by the SAX parser. + * * @return Return the Entity Resolver used by the SAX parser. */ public EntityResolver getEntityResolver() { @@ -1377,8 +1354,7 @@ throws SAXException, IOException { if (saxLog.isTraceEnabled()) { - saxLog.trace( - "resolveEntity('" + publicId + "', '" + systemId + "', '" + baseURI + "')"); + saxLog.trace("resolveEntity('" + publicId + "', '" + systemId + "', '" + baseURI + "')"); } // Has this system identifier been registered? @@ -1410,7 +1386,7 @@ } } catch (URISyntaxException e) { if (log.isDebugEnabled()) { - log.debug(sm.getString("digester.invalidURI", baseURI, systemId)); + log.debug(sm.getString("digester.invalidURI", baseURI, systemId), e); } } } @@ -1441,8 +1417,7 @@ // ------------------------------------------------- ErrorHandler Methods /** - * Forward notification of a parsing error to the application supplied - * error handler (if any). + * Forward notification of a parsing error to the application supplied error handler (if any). * * @param exception The error information * @@ -1459,8 +1434,7 @@ /** - * Forward notification of a fatal parsing error to the application - * supplied error handler (if any). + * Forward notification of a fatal parsing error to the application supplied error handler (if any). * * @param exception The fatal error information * @@ -1477,8 +1451,7 @@ /** - * Forward notification of a parse warning to the application supplied - * error handler (if any). + * Forward notification of a parse warning to the application supplied error handler (if any). * * @param exception The warning information * @@ -1498,12 +1471,14 @@ // ------------------------------------------------------- Public Methods /** - * Parse the content of the specified file using this Digester. Returns - * the root element from the object stack (if any). + * Parse the content of the specified file using this Digester. Returns the root element from the object stack (if + * any). * * @param file File containing the XML data to be parsed + * * @return the root object - * @exception IOException if an input/output error occurs + * + * @exception IOException if an input/output error occurs * @exception SAXException if a parsing exception occurs */ public Object parse(File file) throws IOException, SAXException { @@ -1516,12 +1491,14 @@ /** - * Parse the content of the specified input source using this Digester. - * Returns the root element from the object stack (if any). + * Parse the content of the specified input source using this Digester. Returns the root element from the object + * stack (if any). * * @param input Input source containing the XML data to be parsed + * * @return the root object - * @exception IOException if an input/output error occurs + * + * @exception IOException if an input/output error occurs * @exception SAXException if a parsing exception occurs */ public Object parse(InputSource input) throws IOException, SAXException { @@ -1532,12 +1509,14 @@ /** - * Parse the content of the specified input stream using this Digester. - * Returns the root element from the object stack (if any). + * Parse the content of the specified input stream using this Digester. Returns the root element from the object + * stack (if any). * * @param input Input stream containing the XML data to be parsed + * * @return the root object - * @exception IOException if an input/output error occurs + * + * @exception IOException if an input/output error occurs * @exception SAXException if a parsing exception occurs */ public Object parse(InputStream input) throws IOException, SAXException { @@ -1549,24 +1528,24 @@ /** - *

          Register the specified DTD URL for the specified public identifier. - * This must be called before the first call to parse(). - *

          - * Digester contains an internal EntityResolver - * implementation. This maps PUBLICID's to URLs - * (from which the resource will be loaded). A common use case for this - * method is to register local URLs (possibly computed at runtime by a - * classloader) for DTDs. This allows the performance advantage of using - * a local version without having to ensure every SYSTEM - * URI on every processed xml document is local. This implementation provides - * only basic functionality. If more sophisticated features are required, - * using {@link #setEntityResolver} to set a custom resolver is recommended. - *

          - * Note: This method will have no effect when a custom - * EntityResolver has been set. (Setting a custom - * EntityResolver overrides the internal implementation.) + *

          + * Register the specified DTD URL for the specified public identifier. This must be called before the first call to + * parse(). *

          - * @param publicId Public identifier of the DTD to be resolved + *

          + * Digester contains an internal EntityResolver implementation. This maps + * PUBLICID's to URLs (from which the resource will be loaded). A common use case for this method is to + * register local URLs (possibly computed at runtime by a classloader) for DTDs. This allows the performance + * advantage of using a local version without having to ensure every SYSTEM URI on every processed xml + * document is local. This implementation provides only basic functionality. If more sophisticated features are + * required, using {@link #setEntityResolver} to set a custom resolver is recommended. + *

          + *

          + * Note: This method will have no effect when a custom EntityResolver has been set. + * (Setting a custom EntityResolver overrides the internal implementation.) + *

          + * + * @param publicId Public identifier of the DTD to be resolved * @param entityURL The URL to use for reading this DTD */ public void register(String publicId, String entityURL) { @@ -1583,11 +1562,13 @@ /** - *

          Register a new Rule matching the specified pattern. - * This method sets the Digester property on the rule.

          + *

          + * Register a new Rule matching the specified pattern. This method sets the Digester property on the + * rule. + *

          * * @param pattern Element matching pattern - * @param rule Rule to be registered + * @param rule Rule to be registered */ public void addRule(String pattern, Rule rule) { @@ -1608,10 +1589,11 @@ /** - * Add an "call method" rule for a method which accepts no arguments. + * Add a "call method" rule for a method which accepts no arguments. * - * @param pattern Element matching pattern + * @param pattern Element matching pattern * @param methodName Method name to be called + * * @see CallMethodRule */ public void addCallMethod(String pattern, String methodName) { @@ -1621,12 +1603,12 @@ } /** - * Add an "call method" rule for the specified parameters. + * Add a "call method" rule for the specified parameters. * - * @param pattern Element matching pattern + * @param pattern Element matching pattern * @param methodName Method name to be called - * @param paramCount Number of expected parameters (or zero - * for a single parameter from the body of this element) + * @param paramCount Number of expected parameters (or zero for a single parameter from the body of this element) + * * @see CallMethodRule */ public void addCallMethod(String pattern, String methodName, int paramCount) { @@ -1639,9 +1621,9 @@ /** * Add a "call parameter" rule for the specified parameters. * - * @param pattern Element matching pattern - * @param paramIndex Zero-relative parameter index to set - * (from the body of this element) + * @param pattern Element matching pattern + * @param paramIndex Zero-relative parameter index to set (from the body of this element) + * * @see CallParamRule */ public void addCallParam(String pattern, int paramIndex) { @@ -1654,11 +1636,11 @@ /** * Add a "factory create" rule for the specified parameters. * - * @param pattern Element matching pattern - * @param creationFactory Previously instantiated ObjectCreationFactory - * to be utilized - * @param ignoreCreateExceptions when true any exceptions thrown during - * object creation will be ignored. + * @param pattern Element matching pattern + * @param creationFactory Previously instantiated ObjectCreationFactory to be utilized + * @param ignoreCreateExceptions when true any exceptions thrown during object creation will be + * ignored. + * * @see FactoryCreateRule */ public void addFactoryCreate(String pattern, ObjectCreationFactory creationFactory, @@ -1672,8 +1654,9 @@ /** * Add an "object create" rule for the specified parameters. * - * @param pattern Element matching pattern + * @param pattern Element matching pattern * @param className Java class name to be created + * * @see ObjectCreateRule */ public void addObjectCreate(String pattern, String className) { @@ -1686,10 +1669,10 @@ /** * Add an "object create" rule for the specified parameters. * - * @param pattern Element matching pattern - * @param className Default Java class name to be created - * @param attributeName Attribute name that optionally overrides - * the default Java class name to be created + * @param pattern Element matching pattern + * @param className Default Java class name to be created + * @param attributeName Attribute name that optionally overrides the default Java class name to be created + * * @see ObjectCreateRule */ public void addObjectCreate(String pattern, String className, String attributeName) { @@ -1702,12 +1685,12 @@ /** * Add a "set next" rule for the specified parameters. * - * @param pattern Element matching pattern + * @param pattern Element matching pattern * @param methodName Method name to call on the parent element - * @param paramType Java class name of the expected parameter type - * (if you wish to use a primitive type, specify the corresponding - * Java wrapper class instead, such as java.lang.Boolean - * for a boolean parameter) + * @param paramType Java class name of the expected parameter type (if you wish to use a primitive type, specify + * the corresponding Java wrapper class instead, such as java.lang.Boolean for a + * boolean parameter) + * * @see SetNextRule */ public void addSetNext(String pattern, String methodName, String paramType) { @@ -1721,6 +1704,7 @@ * Add a "set properties" rule for the specified parameters. * * @param pattern Element matching pattern + * * @see SetPropertiesRule */ public void addSetProperties(String pattern) { @@ -1743,10 +1727,9 @@ /** * Clear the current contents of the object stack. *

          - * Calling this method might allow another document of the same type - * to be correctly parsed. However this method was not intended for this - * purpose. In general, a separate Digester object should be created for - * each document to be parsed. + * Calling this method might allow another document of the same type to be correctly parsed. However, this + * method was not intended for this purpose. In general, a separate Digester object should be created for each + * document to be parsed. */ public void clear() { @@ -1770,8 +1753,9 @@ /** - * Return the top object on the stack without removing it. If there are - * no objects on the stack, return null. + * Return the top object on the stack without removing it. If there are no objects on the stack, return + * null. + * * @return the top object */ public Object peek() { @@ -1785,12 +1769,11 @@ /** - * Return the n'th object down the stack, where 0 is the top element - * and [getCount()-1] is the bottom element. If the specified index - * is out of range, return null. + * Return the n'th object down the stack, where 0 is the top element and [getCount()-1] is the bottom element. If + * the specified index is out of range, return null. + * + * @param n Index of the desired element, where 0 is the top of the stack, 1 is the next element down, and so on. * - * @param n Index of the desired element, where 0 is the top of the stack, - * 1 is the next element down, and so on. * @return the specified object */ public Object peek(int n) { @@ -1804,8 +1787,9 @@ /** - * Pop the top object off of the stack, and return it. If there are - * no objects on the stack, return null. + * Pop the top object off of the stack, and return it. If there are no objects on the stack, return + * null. + * * @return the top object */ public Object pop() { @@ -1825,7 +1809,7 @@ */ public void push(Object object) { - if (stack.size() == 0) { + if (stack.isEmpty()) { root = object; } stack.push(object); @@ -1833,12 +1817,10 @@ } /** - * When the Digester is being used as a SAXContentHandler, - * this method allows you to access the root object that has been - * created after parsing. + * When the Digester is being used as a SAXContentHandler, this method allows you to access the root object that has + * been created after parsing. * - * @return the root object that has been created after parsing - * or null if the digester has not parsed any XML yet. + * @return the root object that has been created after parsing or null if the digester has not parsed any XML yet. */ public Object getRoot() { return root; @@ -1853,11 +1835,9 @@ /** *

          - * Provide a hook for lazy configuration of this Digester - * instance. The default implementation does nothing, but subclasses - * can override as needed. + * Provide a hook for lazy configuration of this Digester instance. The default implementation does + * nothing, but subclasses can override as needed. *

          - * *

          * Note This method may be called more than once. *

          @@ -1878,11 +1858,14 @@ /** - *

          Return the top object on the parameters stack without removing it. If there are - * no objects on the stack, return null.

          + *

          + * Return the top object on the parameters stack without removing it. If there are no objects on the stack, return + * null. + *

          + *

          + * The parameters stack is used to store CallMethodRule parameters. See {@link #params}. + *

          * - *

          The parameters stack is used to store CallMethodRule parameters. - * See {@link #params}.

          * @return the top object on the parameters stack */ public Object peekParams() { @@ -1896,11 +1879,14 @@ /** - *

          Pop the top object off of the parameters stack, and return it. If there are - * no objects on the stack, return null.

          + *

          + * Pop the top object off of the parameters stack, and return it. If there are no objects on the stack, return + * null. + *

          + *

          + * The parameters stack is used to store CallMethodRule parameters. See {@link #params}. + *

          * - *

          The parameters stack is used to store CallMethodRule parameters. - * See {@link #params}.

          * @return the top object on the parameters stack */ public Object popParams() { @@ -1917,10 +1903,12 @@ /** - *

          Push a new object onto the top of the parameters stack.

          - * - *

          The parameters stack is used to store CallMethodRule parameters. - * See {@link #params}.

          + *

          + * Push a new object onto the top of the parameters stack. + *

          + *

          + * The parameters stack is used to store CallMethodRule parameters. See {@link #params}. + *

          * * @param object The new object */ @@ -1933,14 +1921,15 @@ } /** - * Create a SAX exception which also understands about the location in - * the digester file where the exception occurs + * Create a SAX exception which also understands about the location in the digester file where the exception occurs + * * @param message The error message - * @param e The root cause + * @param e The root cause + * * @return the new exception */ public SAXException createSAXException(String message, Exception e) { - if ((e != null) && (e instanceof InvocationTargetException)) { + if ((e instanceof InvocationTargetException)) { Throwable t = e.getCause(); if (t instanceof ThreadDeath) { throw (ThreadDeath) t; @@ -1953,8 +1942,7 @@ } } if (locator != null) { - String error = sm.getString("digester.errorLocation", - Integer.valueOf(locator.getLineNumber()), + String error = sm.getString("digester.errorLocation", Integer.valueOf(locator.getLineNumber()), Integer.valueOf(locator.getColumnNumber()), message); if (e != null) { return new SAXParseException(error, locator, e); @@ -1971,9 +1959,10 @@ } /** - * Create a SAX exception which also understands about the location in - * the digester file where the exception occurs + * Create a SAX exception which also understands about the location in the digester file where the exception occurs + * * @param e The root cause + * * @return the new exception */ public SAXException createSAXException(Exception e) { @@ -1993,9 +1982,10 @@ } /** - * Create a SAX exception which also understands about the location in - * the digester file where the exception occurs + * Create a SAX exception which also understands about the location in the digester file where the exception occurs + * * @param message The error message + * * @return the new exception */ public SAXException createSAXException(String message) { @@ -2006,10 +1996,9 @@ // ------------------------------------------------------- Private Methods - /** - * Returns an attributes list which contains all the attributes - * passed in, with any text of form "${xxx}" in an attribute value - * replaced by the appropriate value from the system property. + /** + * Returns an attributes list which contains all the attributes passed in, with any text of form "${xxx}" in an + * attribute value replaced by the appropriate value from the system property. */ private Attributes updateAttributes(Attributes list) { @@ -2022,7 +2011,8 @@ for (int i = 0; i < nAttributes; ++i) { String value = newAttrs.getValue(i); try { - newAttrs.setValue(i, IntrospectionUtils.replaceProperties(value, null, source, getClassLoader()).intern()); + newAttrs.setValue(i, + IntrospectionUtils.replaceProperties(value, null, source, getClassLoader()).intern()); } catch (Exception e) { log.warn(sm.getString("digester.failedToUpdateAttributes", newAttrs.getLocalName(i), value), e); } @@ -2033,9 +2023,8 @@ /** - * Return a new StringBuilder containing the same contents as the - * input buffer, except that data of form ${varname} have been - * replaced by the value of that var as defined in the system property. + * Return a new StringBuilder containing the same contents as the input buffer, except that data of form ${varname} + * have been replaced by the value of that var as defined in the system property. */ private StringBuilder updateBodyText(StringBuilder bodyText) { String in = bodyText.toString(); @@ -2069,8 +2058,7 @@ } @Override - public InputSource resolveEntity(String publicId, String systemId) - throws SAXException, IOException { + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { publicId = replace(publicId); systemId = replace(systemId); return entityResolver.resolveEntity(publicId, systemId); @@ -2090,23 +2078,21 @@ private final EntityResolver2 entityResolver2; - EntityResolver2Wrapper(EntityResolver2 entityResolver, PropertySource[] source, - ClassLoader classLoader) { + EntityResolver2Wrapper(EntityResolver2 entityResolver, PropertySource[] source, ClassLoader classLoader) { super(entityResolver, source, classLoader); this.entityResolver2 = entityResolver; } @Override - public InputSource getExternalSubset(String name, String baseURI) - throws SAXException, IOException { + public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException { name = replace(name); baseURI = replace(baseURI); return entityResolver2.getExternalSubset(name, baseURI); } @Override - public InputSource resolveEntity(String name, String publicId, String baseURI, - String systemId) throws SAXException, IOException { + public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) + throws SAXException, IOException { name = replace(name); publicId = replace(publicId); baseURI = replace(baseURI); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/DocumentProperties.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/DocumentProperties.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/DocumentProperties.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/DocumentProperties.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,10 +17,8 @@ package org.apache.tomcat.util.digester; /** - * A collection of interfaces, one per property, that enables the object being - * populated by the digester to signal to the digester that it supports the - * given property and that the digester should populate that property if - * available. + * A collection of interfaces, one per property, that enables the object being populated by the digester to signal to + * the digester that it supports the given property and that the digester should populate that property if available. */ public interface DocumentProperties { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/EnvironmentPropertySource.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/EnvironmentPropertySource.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/EnvironmentPropertySource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/EnvironmentPropertySource.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,11 +22,11 @@ import org.apache.tomcat.util.security.PermissionCheck; /** - * A {@link org.apache.tomcat.util.IntrospectionUtils.SecurePropertySource} - * that uses environment variables to resolve expressions. - * - *

          Usage example:

          - * + * A {@link org.apache.tomcat.util.IntrospectionUtils.SecurePropertySource} that uses environment variables to resolve + * expressions. + *

          + * Usage example: + *

          * Configure the certificate with environment variables. * *
          @@ -40,10 +40,12 @@
            * 
          * * How to configure: + * *
            * {@code
            *   echo "org.apache.tomcat.util.digester.PROPERTY_SOURCE=org.apache.tomcat.util.digester.EnvironmentPropertySource" >> conf/catalina.properties}
            * 
          + * * or add this to {@code CATALINA_OPTS} * *
          @@ -51,12 +53,11 @@
            *   -Dorg.apache.tomcat.util.digester.PROPERTY_SOURCE=org.apache.tomcat.util.digester.EnvironmentPropertySource}
            * 
          * - * NOTE: When configured the PropertySource for resolving expressions - * from system properties is still active. + * NOTE: When configured the PropertySource for resolving expressions from system properties is still active. * * @see Digester - * - * @see Tomcat Configuration Reference System Properties + * @see Tomcat + * Configuration Reference System Properties */ public class EnvironmentPropertySource implements IntrospectionUtils.SecurePropertySource { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/FactoryCreateRule.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/FactoryCreateRule.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/FactoryCreateRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/FactoryCreateRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,14 +20,14 @@ /** - *

          Rule implementation that uses an {@link ObjectCreationFactory} to create - * a new object which it pushes onto the object stack. When the element is - * complete, the object will be popped.

          - * - *

          This rule is intended in situations where the element's attributes are - * needed before the object can be created. A common scenario is for the - * ObjectCreationFactory implementation to use the attributes as parameters - * in a call to either a factory method or to a non-empty constructor. + *

          + * Rule implementation that uses an {@link ObjectCreationFactory} to create a new object which it pushes onto the object + * stack. When the element is complete, the object will be popped. + *

          + *

          + * This rule is intended in situations where the element's attributes are needed before the object can be created. A + * common scenario is for the ObjectCreationFactory implementation to use the attributes as parameters in a call to + * either a factory method or to a non-empty constructor. */ public class FactoryCreateRule extends Rule { @@ -35,7 +35,7 @@ // ----------------------------------------------------------- Fields /** Should exceptions thrown by the factory be ignored? */ - private boolean ignoreCreateExceptions; + private final boolean ignoreCreateExceptions; /** Stock to manage */ private ArrayStack exceptionIgnoredStack; @@ -43,16 +43,12 @@ // ----------------------------------------------------------- Constructors /** - * Construct a factory create rule using the given, already instantiated, - * {@link ObjectCreationFactory}. + * Construct a factory create rule using the given, already instantiated, {@link ObjectCreationFactory}. * - * @param creationFactory called on to create the object. - * @param ignoreCreateExceptions if true, exceptions thrown by the object - * creation factory will be ignored. + * @param creationFactory called on to create the object. + * @param ignoreCreateExceptions if true, exceptions thrown by the object creation factory will be ignored. */ - public FactoryCreateRule( - ObjectCreationFactory creationFactory, - boolean ignoreCreateExceptions) { + public FactoryCreateRule(ObjectCreationFactory creationFactory, boolean ignoreCreateExceptions) { this.creationFactory = creationFactory; this.ignoreCreateExceptions = ignoreCreateExceptions; @@ -62,11 +58,10 @@ // ----------------------------------------------------- Instance Variables /** - * The object creation factory we will use to instantiate objects - * as required based on the attributes specified in the matched XML - * element. + * The object creation factory we will use to instantiate objects as required based on the attributes specified in + * the matched XML element. */ - protected ObjectCreationFactory creationFactory = null; + protected ObjectCreationFactory creationFactory; // --------------------------------------------------------- Public Methods @@ -90,8 +85,8 @@ Object instance = creationFactory.createObject(attributes); if (digester.log.isTraceEnabled()) { - digester.log.trace("[FactoryCreateRule]{" + digester.match + - "} New " + instance.getClass().getName()); + digester.log + .trace("[FactoryCreateRule]{" + digester.match + "} New " + instance.getClass().getName()); } digester.push(instance); exceptionIgnoredStack.push(Boolean.FALSE); @@ -103,7 +98,7 @@ ((e.getMessage() == null) ? e.getClass().getName() : e.getMessage())), e); } else if (digester.log.isInfoEnabled()) { digester.log.info(sm.getString("rule.createError", - ((e.getMessage() == null) ? e.getClass().getName() : e.getMessage()))); + ((e.getMessage() == null) ? e.getClass().getName() : e.getMessage()))); } exceptionIgnoredStack.push(Boolean.TRUE); } @@ -112,8 +107,7 @@ Object instance = creationFactory.createObject(attributes); if (digester.log.isTraceEnabled()) { - digester.log.trace("[FactoryCreateRule]{" + digester.match + - "} New " + instance.getClass().getName()); + digester.log.trace("[FactoryCreateRule]{" + digester.match + "} New " + instance.getClass().getName()); } digester.push(instance); } @@ -127,11 +121,8 @@ public void end(String namespace, String name) throws Exception { // check if object was created - // this only happens if an exception was thrown and we're ignoring them - if ( - ignoreCreateExceptions && - exceptionIgnoredStack != null && - !(exceptionIgnoredStack.empty())) { + // this only happens if an exception was thrown, and we're ignoring them + if (ignoreCreateExceptions && exceptionIgnoredStack != null && !(exceptionIgnoredStack.empty())) { if ((exceptionIgnoredStack.pop()).booleanValue()) { // creation exception was ignored @@ -145,8 +136,7 @@ Object top = digester.pop(); if (digester.log.isTraceEnabled()) { - digester.log.trace("[FactoryCreateRule]{" + digester.match + - "} Pop " + top.getClass().getName()); + digester.log.trace("[FactoryCreateRule]{" + digester.match + "} Pop " + top.getClass().getName()); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/ObjectCreateRule.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ObjectCreateRule.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/ObjectCreateRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ObjectCreateRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,7 @@ /** - * Rule implementation that creates a new object and pushes it - * onto the object stack. When the element is complete, the + * Rule implementation that creates a new object and pushes it onto the object stack. When the element is complete, the * object will be popped */ @@ -45,15 +44,13 @@ /** - * Construct an object create rule with the specified class name and an - * optional attribute name containing an override. + * Construct an object create rule with the specified class name and an optional attribute name containing an + * override. * - * @param className Java class name of the object to be created - * @param attributeName Attribute name which, if present, contains an - * override of the class name to create + * @param className Java class name of the object to be created + * @param attributeName Attribute name which, if present, contains an override of the class name to create */ - public ObjectCreateRule(String className, - String attributeName) { + public ObjectCreateRule(String className, String attributeName) { this.className = className; this.attributeName = attributeName; @@ -66,13 +63,13 @@ /** * The attribute containing an override class name if it is present. */ - protected String attributeName = null; + protected String attributeName; /** * The Java class name of the object to be created. */ - protected String className = null; + protected String className; // --------------------------------------------------------- Public Methods @@ -81,16 +78,13 @@ /** * Process the beginning of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise * @param attributes The attribute list for this element */ @Override - public void begin(String namespace, String name, Attributes attributes) - throws Exception { + public void begin(String namespace, String name, Attributes attributes) throws Exception { String realClassName = getRealClassName(attributes); @@ -115,7 +109,9 @@ /** * Return the actual class name of the class to be instantiated. + * * @param attributes The attribute list for this element + * * @return the class name */ protected String getRealClassName(Attributes attributes) { @@ -134,19 +130,16 @@ /** * Process the end of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise */ @Override public void end(String namespace, String name) throws Exception { Object top = digester.pop(); if (digester.log.isTraceEnabled()) { - digester.log.trace("[ObjectCreateRule]{" + digester.match + - "} Pop " + top.getClass().getName()); + digester.log.trace("[ObjectCreateRule]{" + digester.match + "} Pop " + top.getClass().getName()); } } @@ -157,13 +150,7 @@ */ @Override public String toString() { - StringBuilder sb = new StringBuilder("ObjectCreateRule["); - sb.append("className="); - sb.append(className); - sb.append(", attributeName="); - sb.append(attributeName); - sb.append(']'); - return sb.toString(); + return "ObjectCreateRule[" + "className=" + className + ", attributeName=" + attributeName + ']'; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ObjectCreationFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,36 +20,35 @@ import org.xml.sax.Attributes; /** - *

          Interface for use with {@link FactoryCreateRule}. - * The rule calls {@link #createObject} to create an object - * to be pushed onto the Digester stack - * whenever it is matched.

          - * - *

          {@link AbstractObjectCreationFactory} is an abstract - * implementation suitable for creating anonymous + *

          + * Interface for use with {@link FactoryCreateRule}. The rule calls {@link #createObject} to create an object to be + * pushed onto the Digester stack whenever it is matched. + *

          + *

          + * {@link AbstractObjectCreationFactory} is an abstract implementation suitable for creating anonymous * ObjectCreationFactory implementations. */ public interface ObjectCreationFactory { /** - * Factory method called by {@link FactoryCreateRule} to supply an - * object based on the element's attributes. + * Factory method called by {@link FactoryCreateRule} to supply an object based on the element's attributes. * * @param attributes the element's attributes + * * @return the created object + * * @throws Exception any exception thrown will be propagated upwards */ Object createObject(Attributes attributes) throws Exception; /** - * @return the {@link Digester} that was set by the - * {@link FactoryCreateRule} upon initialization. + * @return the {@link Digester} that was set by the {@link FactoryCreateRule} upon initialization. */ Digester getDigester(); /** - * Set the {@link Digester} to allow the implementation to do logging, - * classloading based on the digester's classloader, etc. + * Set the {@link Digester} to allow the implementation to do logging, classloading based on the digester's + * classloader, etc. * * @param digester parent Digester object */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/Rule.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/Rule.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/Rule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/Rule.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,8 @@ import org.xml.sax.Attributes; /** - * Concrete implementations of this class implement actions to be taken when - * a corresponding nested pattern of XML elements has been matched. + * Concrete implementations of this class implement actions to be taken when a corresponding nested pattern of XML + * elements has been matched. */ public abstract class Rule { @@ -30,10 +30,12 @@ // ----------------------------------------------------------- Constructors /** - *

          Base constructor. - * Now the digester will be set when the rule is added.

          + *

          + * Base constructor. Now the digester will be set when the rule is added. + *

          */ - public Rule() {} + public Rule() { + } // ----------------------------------------------------- Instance Variables @@ -64,8 +66,7 @@ /** - * Set the Digester with which this Rule is - * associated. + * Set the Digester with which this Rule is associated. * * @param digester The digester with which to associate this rule */ @@ -77,8 +78,7 @@ /** * Return the namespace URI for which this Rule is relevant, if any. * - * @return The namespace URI for which this rule is relevant or - * null if none. + * @return The namespace URI for which this rule is relevant or null if none. */ public String getNamespaceURI() { return namespaceURI; @@ -88,8 +88,8 @@ /** * Set the namespace URI for which this Rule is relevant, if any. * - * @param namespaceURI Namespace URI for which this Rule is relevant, - * or null to match independent of namespace. + * @param namespaceURI Namespace URI for which this Rule is relevant, or null to match independent of + * namespace. */ public void setNamespaceURI(String namespaceURI) { this.namespaceURI = namespaceURI; @@ -99,14 +99,12 @@ // --------------------------------------------------------- Public Methods /** - * This method is called when the beginning of a matching XML element - * is encountered. The default implementation is a NO-OP. + * This method is called when the beginning of a matching XML element is encountered. The default implementation is + * a NO-OP. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the - * element has no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise * @param attributes The attribute list of this element * * @throws Exception if an error occurs while processing the event @@ -117,16 +115,13 @@ /** - * This method is called when the body of a matching XML element is - * encountered. If the element has no body, this method is not called at - * all. The default implementation is a NO-OP. - * - * @param namespace the namespace URI of the matching element, or an empty - * string if the parser is not namespace aware or the - * element has no namespace - * @param name the local name if the parser is namespace aware, or just the - * element name otherwise - * @param text The text of the body of this element + * This method is called when the body of a matching XML element is encountered. If the element has no body, this + * method is not called at all. The default implementation is a NO-OP. + * + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise + * @param text The text of the body of this element * * @throws Exception if an error occurs while processing the event */ @@ -136,14 +131,12 @@ /** - * This method is called when the end of a matching XML element - * is encountered. The default implementation is a NO-OP. + * This method is called when the end of a matching XML element is encountered. The default implementation is a + * NO-OP. * - * @param namespace the namespace URI of the matching element, or an empty - * string if the parser is not namespace aware or the - * element has no namespace - * @param name the local name if the parser is namespace aware, or just the - * element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise * * @throws Exception if an error occurs while processing the event */ @@ -153,8 +146,7 @@ /** - * This method is called after all parsing methods have been - * called, to allow Rules to remove temporary data. + * This method is called after all parsing methods have been called, to allow Rules to remove temporary data. * * @throws Exception if an error occurs while processing the event */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/RuleSet.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/RuleSet.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/RuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/RuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,31 +17,28 @@ package org.apache.tomcat.util.digester; /** - *

          Public interface defining a shorthand means of configuring a complete - * set of related Rule definitions, possibly associated with - * a particular namespace URI, in one operation. To use an instance of a - * class that implements this interface:

          + *

          + * Public interface defining a shorthand means of configuring a complete set of related Rule definitions, + * possibly associated with a particular namespace URI, in one operation. To use an instance of a class that implements + * this interface: + *

          *
            *
          • Create a concrete implementation of this interface.
          • - *
          • Optionally, you can configure a RuleSet to be relevant - * only for a particular namespace URI by configuring the value to be - * returned by getNamespaceURI().
          • - *
          • As you are configuring your Digester instance, call - * digester.addRuleSet() and pass the RuleSet instance.
          • - *
          • Digester will call the addRuleInstances() method of - * your RuleSet to configure the necessary rules.
          • + *
          • Optionally, you can configure a RuleSet to be relevant only for a particular namespace URI by + * configuring the value to be returned by getNamespaceURI().
          • + *
          • As you are configuring your Digester instance, call digester.addRuleSet() and pass the RuleSet + * instance.
          • + *
          • Digester will call the addRuleInstances() method of your RuleSet to configure the necessary + * rules.
          • *
          */ public interface RuleSet { /** - * Add the set of Rule instances defined in this RuleSet to the - * specified Digester instance, associating them with - * our namespace URI (if any). This method should only be called - * by a Digester instance. + * Add the set of Rule instances defined in this RuleSet to the specified Digester instance, + * associating them with our namespace URI (if any). This method should only be called by a Digester instance. * - * @param digester Digester instance to which the new Rule instances - * should be added. + * @param digester Digester instance to which the new Rule instances should be added. */ void addRuleInstances(Digester digester); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/Rules.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/Rules.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/Rules.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/Rules.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,18 +19,16 @@ import java.util.List; /** - * Public interface defining a collection of Rule instances (and corresponding - * matching patterns) plus an implementation of a matching policy that selects - * the rules that match a particular pattern of nested elements discovered - * during parsing. + * Public interface defining a collection of Rule instances (and corresponding matching patterns) plus an implementation + * of a matching policy that selects the rules that match a particular pattern of nested elements discovered during + * parsing. */ public interface Rules { // ------------------------------------------------------------- Properties /** - * @return the Digester instance with which this Rules instance is - * associated. + * @return the Digester instance with which this Rules instance is associated. */ Digester getDigester(); @@ -49,7 +47,7 @@ * Register a new Rule instance matching the specified pattern. * * @param pattern Nesting pattern to be matched for this Rule - * @param rule Rule instance to be registered + * @param rule Rule instance to be registered */ void add(String pattern, Rule rule); @@ -61,26 +59,24 @@ /** - * Return a List of all registered Rule instances that match the specified - * nesting pattern, or a zero-length List if there are no matches. If more - * than one Rule instance matches, they must be returned - * in the order originally registered through the add() - * method. + * Return a List of all registered Rule instances that match the specified nesting pattern, or a zero-length List if + * there are no matches. If more than one Rule instance matches, they must be returned in the order + * originally registered through the add() method. + * + * @param namespaceURI Namespace URI for which to select matching rules, or null to match regardless of + * namespace URI + * @param pattern Nesting pattern to be matched * - * @param namespaceURI Namespace URI for which to select matching rules, - * or null to match regardless of namespace URI - * @param pattern Nesting pattern to be matched * @return a rules list */ List match(String namespaceURI, String pattern); /** - * Return a List of all registered Rule instances, or a zero-length List - * if there are no registered Rule instances. If more than one Rule - * instance has been registered, they must be returned - * in the order originally registered through the add() - * method. + * Return a List of all registered Rule instances, or a zero-length List if there are no registered Rule instances. + * If more than one Rule instance has been registered, they must be returned in the order + * originally registered through the add() method. + * * @return a rules list */ List rules(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/RulesBase.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/RulesBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/RulesBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/RulesBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,19 +21,18 @@ import java.util.List; /** - *

          Default implementation of the Rules interface that supports - * the standard rule matching behavior. This class can also be used as a - * base class for specialized Rules implementations.

          - * - *

          The matching policies implemented by this class support two different - * types of pattern matching rules:

          + *

          + * Default implementation of the Rules interface that supports the standard rule matching behavior. This + * class can also be used as a base class for specialized Rules implementations. + *

          + *

          + * The matching policies implemented by this class support two different types of pattern matching rules: + *

          *
            - *
          • Exact Match - A pattern "a/b/c" exactly matches a - * <c> element, nested inside a <b> - * element, which is nested inside an <a> element.
          • - *
          • Tail Match - A pattern "*/a/b" matches a - * <b> element, nested inside an <a> - * element, no matter how deeply the pair is nested.
          • + *
          • Exact Match - A pattern "a/b/c" exactly matches a <c> element, nested inside a + * <b> element, which is nested inside an <a> element.
          • + *
          • Tail Match - A pattern "*/a/b" matches a <b> element, nested inside an + * <a> element, no matter how deeply the pair is nested.
          • *
          */ public class RulesBase implements Rules { @@ -41,9 +40,8 @@ // ----------------------------------------------------- Instance Variables /** - * The set of registered Rule instances, keyed by the matching pattern. - * Each value is a List containing the Rules for that pattern, in the - * order that they were originally registered. + * The set of registered Rule instances, keyed by the matching pattern. Each value is a List containing the Rules + * for that pattern, in the order that they were originally registered. */ protected HashMap> cache = new HashMap<>(); @@ -55,8 +53,7 @@ /** - * The set of registered Rule instances, in the order that they were - * originally registered. + * The set of registered Rule instances, in the order that they were originally registered. */ protected ArrayList rules = new ArrayList<>(); @@ -64,8 +61,7 @@ // ------------------------------------------------------------- Properties /** - * Return the Digester instance with which this Rules instance is - * associated. + * Return the Digester instance with which this Rules instance is associated. */ @Override public Digester getDigester() { @@ -93,14 +89,14 @@ * Register a new Rule instance matching the specified pattern. * * @param pattern Nesting pattern to be matched for this Rule - * @param rule Rule instance to be registered + * @param rule Rule instance to be registered */ @Override public void add(String pattern, Rule rule) { // to help users who accidentally add '/' to the end of their patterns int patternLength = pattern.length(); - if (patternLength>1 && pattern.endsWith("/")) { - pattern = pattern.substring(0, patternLength-1); + if (patternLength > 1 && pattern.endsWith("/")) { + pattern = pattern.substring(0, patternLength - 1); } cache.computeIfAbsent(pattern, k -> new ArrayList<>()).add(rule); @@ -122,28 +118,25 @@ /** - * Return a List of all registered Rule instances that match the specified - * nesting pattern, or a zero-length List if there are no matches. If more - * than one Rule instance matches, they must be returned - * in the order originally registered through the add() - * method. + * Return a List of all registered Rule instances that match the specified nesting pattern, or a zero-length List if + * there are no matches. If more than one Rule instance matches, they must be returned in the order + * originally registered through the add() method. * - * @param namespaceURI Namespace URI for which to select matching rules, - * or null to match regardless of namespace URI - * @param pattern Nesting pattern to be matched + * @param namespaceURI Namespace URI for which to select matching rules, or null to match regardless of + * namespace URI + * @param pattern Nesting pattern to be matched */ @Override public List match(String namespaceURI, String pattern) { // List rulesList = (List) this.cache.get(pattern); List rulesList = lookup(namespaceURI, pattern); - if ((rulesList == null) || (rulesList.size() < 1)) { + if ((rulesList == null) || (rulesList.isEmpty())) { // Find the longest key, ie more discriminant String longKey = ""; for (String key : this.cache.keySet()) { if (key.startsWith("*/")) { - if (pattern.equals(key.substring(2)) || - pattern.endsWith(key.substring(1))) { + if (pattern.equals(key.substring(2)) || pattern.endsWith(key.substring(1))) { if (key.length() > longKey.length()) { // rulesList = (List) this.cache.get(key); rulesList = lookup(namespaceURI, key); @@ -161,11 +154,9 @@ /** - * Return a List of all registered Rule instances, or a zero-length List - * if there are no registered Rule instances. If more than one Rule - * instance has been registered, they must be returned - * in the order originally registered through the add() - * method. + * Return a List of all registered Rule instances, or a zero-length List if there are no registered Rule instances. + * If more than one Rule instance has been registered, they must be returned in the order + * originally registered through the add() method. */ @Override public List rules() { @@ -176,13 +167,13 @@ // ------------------------------------------------------ Protected Methods /** - * Return a List of Rule instances for the specified pattern that also - * match the specified namespace URI (if any). If there are no such - * rules, return null. + * Return a List of Rule instances for the specified pattern that also match the specified namespace URI (if any). + * If there are no such rules, return null. + * + * @param namespaceURI Namespace URI to match, or null to select matching rules regardless of namespace + * URI + * @param pattern Pattern to be matched * - * @param namespaceURI Namespace URI to match, or null to - * select matching rules regardless of namespace URI - * @param pattern Pattern to be matched * @return a rules list */ protected List lookup(String namespaceURI, String pattern) { @@ -191,15 +182,14 @@ if (list == null) { return null; } - if (namespaceURI == null || namespaceURI.length() == 0) { + if (namespaceURI == null || namespaceURI.isEmpty()) { return list; } // Select only Rules that match on the specified namespace URI List results = new ArrayList<>(); for (Rule item : list) { - if ((namespaceURI.equals(item.getNamespaceURI())) || - (item.getNamespaceURI() == null)) { + if ((namespaceURI.equals(item.getNamespaceURI())) || (item.getNamespaceURI() == null)) { results.add(item); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/ServiceBindingPropertySource.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ServiceBindingPropertySource.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/ServiceBindingPropertySource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/ServiceBindingPropertySource.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,18 +27,17 @@ import org.apache.tomcat.util.security.PermissionCheck; /** - * A {@link org.apache.tomcat.util.IntrospectionUtils.SecurePropertySource} - * that uses Kubernetes service bindings to resolve expressions. - * + * A {@link org.apache.tomcat.util.IntrospectionUtils.SecurePropertySource} that uses Kubernetes service bindings to + * resolve expressions. *

          - * The Kubernetes service binding specification can be found at - * https://servicebinding.io/. + * The Kubernetes service binding specification can be found at + * https://servicebinding.io/. + *

          + *

          + * Usage example: *

          - * - *

          Usage example:

          - * * Configure the certificate with a service binding. - * + *

          * When the service binding is constructed as follows: * *

          @@ -49,6 +48,7 @@
            *                                            /chainFile
            *                                            /keyPassword
            * 
          + * *
            *   {@code
            *     
          @@ -59,20 +59,19 @@
            *                        type="RSA" />
            *      }
            * 
          - * *

          - * The optional chomp: prefix will cause the ServiceBindingPropertySource - * to trim a single newline (\r\n, \r, or \n) - * from the end of the file, if it exists. This is a convenience for hand-edited - * files/values where removing a trailing newline is difficult, and trailing - * whitespace changes the meaning of the value. + * The optional chomp: prefix will cause the ServiceBindingPropertySource to trim a single newline + * (\r\n, \r, or \n) from the end of the file, if it exists. This is a + * convenience for hand-edited files/values where removing a trailing newline is difficult, and trailing whitespace + * changes the meaning of the value. *

          - * * How to configure: + * *
            * {@code
            *   echo "org.apache.tomcat.util.digester.PROPERTY_SOURCE=org.apache.tomcat.util.digester.ServiceBindingPropertySource" >> conf/catalina.properties}
            * 
          + * * or add this to {@code CATALINA_OPTS} * *
          @@ -80,13 +79,11 @@
            *   -Dorg.apache.tomcat.util.digester.PROPERTY_SOURCE=org.apache.tomcat.util.digester.ServiceBindingPropertySource}
            * 
          * - * NOTE: When configured the PropertySource for resolving expressions - * from system properties is still active. + * NOTE: When configured the PropertySource for resolving expressions from system properties is still active. * * @see Digester - * * @see Tomcat - * Configuration Reference System Properties + * Configuration Reference System Properties */ public class ServiceBindingPropertySource implements IntrospectionUtils.SecurePropertySource { @@ -144,7 +141,7 @@ int length = bytes.length; if (chomp) { - if(length > 1 && bytes[length - 2] == '\r' && bytes[length - 1] == '\n') { + if (length > 1 && bytes[length - 2] == '\r' && bytes[length - 1] == '\n') { length -= 2; } else if (length > 0) { byte c = bytes[length - 1]; @@ -155,7 +152,8 @@ } return new String(bytes, 0, length); - } catch (IOException e) { + } catch (IOException ioe) { + // Treat as not found return null; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/SetNextRule.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/SetNextRule.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/SetNextRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/SetNextRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,14 +20,14 @@ /** - *

          Rule implementation that calls a method on the (top-1) (parent) - * object, passing the top object (child) as an argument. It is - * commonly used to establish parent-child relationships.

          - * - *

          This rule now supports more flexible method matching by default. - * It is possible that this may break (some) code - * written against release 1.1.1 or earlier. - * See {@link #isExactMatch()} for more details.

          + *

          + * Rule implementation that calls a method on the (top-1) (parent) object, passing the top object (child) as an + * argument. It is commonly used to establish parent-child relationships. + *

          + *

          + * This rule now supports more flexible method matching by default. It is possible that this may break (some) code + * written against release 1.1.1 or earlier. See {@link #isExactMatch()} for more details. + *

          */ public class SetNextRule extends Rule { @@ -38,13 +38,11 @@ * Construct a "set next" rule with the specified method name. * * @param methodName Method name of the parent method to call - * @param paramType Java class of the parent method's argument - * (if you wish to use a primitive type, specify the corresponding - * Java wrapper class instead, such as java.lang.Boolean - * for a boolean parameter) + * @param paramType Java class of the parent method's argument (if you wish to use a primitive type, specify the + * corresponding Java wrapper class instead, such as java.lang.Boolean for a + * boolean parameter) */ - public SetNextRule(String methodName, - String paramType) { + public SetNextRule(String methodName, String paramType) { this.methodName = methodName; this.paramType = paramType; @@ -58,13 +56,13 @@ /** * The method name to call on the parent object. */ - protected String methodName = null; + protected String methodName; /** * The Java class name of the parameter type expected by the method. */ - protected String paramType = null; + protected String paramType; /** * Should we use exact matching. Default is no. @@ -75,23 +73,26 @@ /** - *

          Is exact matching being used.

          - * - *

          This rule uses org.apache.commons.beanutils.MethodUtils - * to introspect the relevant objects so that the right method can be called. - * Originally, MethodUtils.invokeExactMethod was used. - * This matches methods very strictly - * and so may not find a matching method when one exists. - * This is still the behaviour when exact matching is enabled.

          - * - *

          When exact matching is disabled, MethodUtils.invokeMethod is used. - * This method finds more methods but is less precise when there are several methods - * with correct signatures. - * So, if you want to choose an exact signature you might need to enable this property.

          - * - *

          The default setting is to disable exact matches.

          + *

          + * Is exact matching being used. + *

          + *

          + * This rule uses org.apache.commons.beanutils.MethodUtils to introspect the relevant objects so that + * the right method can be called. Originally, MethodUtils.invokeExactMethod was used. This matches + * methods very strictly and so may not find a matching method when one exists. This is still the behaviour when + * exact matching is enabled. + *

          + *

          + * When exact matching is disabled, MethodUtils.invokeMethod is used. This method finds more methods + * but is less precise when there are several methods with correct signatures. So, if you want to choose an exact + * signature you might need to enable this property. + *

          + *

          + * The default setting is to disable exact matches. + *

          * * @return true iff exact matching is enabled + * * @since Digester Release 1.1.1 */ public boolean isExactMatch() { @@ -100,11 +101,15 @@ } /** - *

          Set whether exact matching is enabled.

          - * - *

          See {@link #isExactMatch()}.

          + *

          + * Set whether exact matching is enabled. + *

          + *

          + * See {@link #isExactMatch()}. + *

          * * @param useExactMatch should this rule use exact method matching + * * @since Digester Release 1.1.1 */ public void setExactMatch(boolean useExactMatch) { @@ -115,11 +120,9 @@ /** * Process the end of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param name the local name if the parser is namespace aware, or just - * the element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param name the local name if the parser is namespace aware, or just the element name otherwise */ @Override public void end(String namespace, String name) throws Exception { @@ -129,19 +132,16 @@ Object parent = digester.peek(1); if (digester.log.isTraceEnabled()) { if (parent == null) { - digester.log.trace("[SetNextRule]{" + digester.match + - "} Call [NULL PARENT]." + - methodName + "(" + child + ")"); + digester.log.trace( + "[SetNextRule]{" + digester.match + "} Call [NULL PARENT]." + methodName + "(" + child + ")"); } else { - digester.log.trace("[SetNextRule]{" + digester.match + - "} Call " + parent.getClass().getName() + "." + + digester.log.trace("[SetNextRule]{" + digester.match + "} Call " + parent.getClass().getName() + "." + methodName + "(" + child + ")"); } } // Call the specified method - IntrospectionUtils.callMethod1(parent, methodName, - child, paramType, digester.getClassLoader()); + IntrospectionUtils.callMethod1(parent, methodName, child, paramType, digester.getClassLoader()); StringBuilder code = digester.getGeneratedCode(); if (code != null) { @@ -157,13 +157,7 @@ */ @Override public String toString() { - StringBuilder sb = new StringBuilder("SetNextRule["); - sb.append("methodName="); - sb.append(methodName); - sb.append(", paramType="); - sb.append(paramType); - sb.append(']'); - return sb.toString(); + return "SetNextRule[" + "methodName=" + methodName + ", paramType=" + paramType + ']'; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/SetPropertiesRule.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/SetPropertiesRule.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/SetPropertiesRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/SetPropertiesRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,10 @@ /** - *

          Rule implementation that sets properties on the object at the top of the - * stack, based on attributes with corresponding names.

          + *

          + * Rule implementation that sets properties on the object at the top of the stack, based on attributes with + * corresponding names. + *

          */ public class SetPropertiesRule extends Rule { @@ -52,23 +54,19 @@ /** * Process the beginning of this element. * - * @param namespace the namespace URI of the matching element, or an - * empty string if the parser is not namespace aware or the element has - * no namespace - * @param theName the local name if the parser is namespace aware, or just - * the element name otherwise + * @param namespace the namespace URI of the matching element, or an empty string if the parser is not namespace + * aware or the element has no namespace + * @param theName the local name if the parser is namespace aware, or just the element name otherwise * @param attributes The attribute list for this element */ @Override - public void begin(String namespace, String theName, Attributes attributes) - throws Exception { + public void begin(String namespace, String theName, Attributes attributes) throws Exception { // Populate the corresponding properties of the top object Object top = digester.peek(); if (digester.log.isTraceEnabled()) { - digester.log.trace("[SetPropertiesRule]{" + digester.match + - "} Set " + top.getClass().getName() + - " properties"); + digester.log.trace( + "[SetPropertiesRule]{" + digester.match + "} Set " + top.getClass().getName() + " properties"); } StringBuilder code = digester.getGeneratedCode(); String variableName = null; @@ -84,8 +82,7 @@ String value = attributes.getValue(i); if (digester.log.isTraceEnabled()) { - digester.log.trace("[SetPropertiesRule]{" + digester.match + - "} Setting property '" + name + "' to '" + + digester.log.trace("[SetPropertiesRule]{" + digester.match + "} Setting property '" + name + "' to '" + value + "'"); } if (!digester.isFakeAttribute(top, name) && (excludes == null || !excludes.containsKey(name))) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/SystemPropertySource.java tomcat10-10.1.52/java/org/apache/tomcat/util/digester/SystemPropertySource.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/SystemPropertySource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/SystemPropertySource.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,8 @@ import org.apache.tomcat.util.security.PermissionCheck; /** - * A {@link org.apache.tomcat.util.IntrospectionUtils.SecurePropertySource} - * that uses system properties to resolve expressions. - * This property source is always active by default. + * A {@link org.apache.tomcat.util.IntrospectionUtils.SecurePropertySource} that uses system properties to resolve + * expressions. This property source is always active by default. * * @see Digester */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/digester/package.html tomcat10-10.1.52/java/org/apache/tomcat/util/digester/package.html --- tomcat10-10.1.34/java/org/apache/tomcat/util/digester/package.html 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/digester/package.html 2026-01-23 19:33:36.000000000 +0000 @@ -157,7 +157,7 @@ the XML document against a Document Type Definition (DTD) that is specified in its DOCTYPE declaration. The default value of false requests a parse that only detects - "well formed" XML documents, rather than "valid" ones. + "well-formed" XML documents, rather than "valid" ones.

      Request Values
      "); - if (sortListings) { + if (order != null) { sb.append(""); @@ -1798,7 +1878,7 @@ } sb.append(""); - if (sortListings) { + if (order != null) { sb.append(""); @@ -1809,7 +1889,7 @@ } sb.append(""); - if (sortListings) { + if (order != null) { sb.append(""); @@ -1915,7 +1995,7 @@ rightSide = 1; } - return ("" + leftSide + "." + rightSide + " KiB"); + return (String.valueOf(leftSide) + "." + String.valueOf(rightSide) + " KiB"); } @@ -1955,14 +2035,18 @@ } else { reader = new InputStreamReader(is); } - copyRange(reader, new PrintWriter(buffer)); - } catch (IOException e) { - log(sm.getString("defaultServlet.readerCloseFailed"), e); + IOException e = copyRange(reader, new PrintWriter(buffer)); + if (debug > 10) { + log("readme '" + readmeFile + "' output error: " + ((e != null) ? e.getMessage() : "")); + } + } catch (IOException ioe) { + log(sm.getString("defaultServlet.readerCloseFailed"), ioe); } finally { if (reader != null) { try { reader.close(); - } catch (IOException e) { + } catch (IOException ignore) { + // Ignore } } } @@ -2024,7 +2108,7 @@ } /* - * Open and read in file in one fell swoop to reduce chance chance of leaving handle open. + * Open and read in file in one fell swoop to reduce the chance of leaving handle open. */ if (globalXsltFile != null) { File f = validateGlobalXsltFile(); @@ -2034,7 +2118,7 @@ log("globalXsltFile [" + f.getAbsolutePath() + "] is too big to buffer"); } else { try (FileInputStream fis = new FileInputStream(f)) { - byte b[] = new byte[(int) f.length()]; + byte[] b = new byte[(int) f.length()]; IOTools.readFully(fis, b); return new StreamSource(new ByteArrayInputStream(b)); } @@ -2166,35 +2250,48 @@ protected boolean checkIfMatch(HttpServletRequest request, HttpServletResponse response, WebResource resource) throws IOException { - String headerValue = request.getHeader("If-Match"); - if (headerValue != null) { - - boolean conditionSatisfied; - - if (!headerValue.equals("*")) { - String resourceETag = generateETag(resource); - if (resourceETag == null) { - conditionSatisfied = false; - } else { - // RFC 7232 requires strong comparison for If-Match headers - Boolean matched = EntityTag.compareEntityTag(new StringReader(headerValue), false, resourceETag); - if (matched == null) { - if (debug > 10) { - log("DefaultServlet.checkIfMatch: Invalid header value [" + headerValue + "]"); - } - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return false; + boolean conditionSatisfied = false; + Enumeration headerValues = request.getHeaders("If-Match"); + String resourceETag = generateETag(resource); + + boolean hasAsteriskValue = false;// check existence of special header value '*' + int headerCount = 0; + while (headerValues.hasMoreElements() && !conditionSatisfied) { + headerCount++; + String headerValue = headerValues.nextElement(); + if ("*".equals(headerValue)) { + hasAsteriskValue = true; + if (resourceETag != null) { + conditionSatisfied = true; + } + } else { + // RFC 7232 requires strong comparison for If-Match headers + Boolean matched = EntityTag.compareEntityTag(new StringReader(headerValue), false, resourceETag); + if (matched == null) { + if (debug > 10) { + log("DefaultServlet.checkIfMatch: Invalid header value [" + headerValue + "]"); } + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return false; + } else { conditionSatisfied = matched.booleanValue(); } - } else { - conditionSatisfied = true; } + } + if (headerValues.hasMoreElements()) { + headerCount++; + } - if (!conditionSatisfied) { - response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); - return false; - } + if (hasAsteriskValue && headerCount > 1) { + // Note that an If-Match header field with a list value containing "*" and other values (including other + // instances of "*") is syntactically invalid (therefore not allowed to be generated) and furthermore is + // unlikely to be interoperable. + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return false; + } + if (!conditionSatisfied) { + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + return false; } return true; } @@ -2212,21 +2309,35 @@ */ protected boolean checkIfModifiedSince(HttpServletRequest request, HttpServletResponse response, WebResource resource) { - try { - long headerValue = request.getDateHeader("If-Modified-Since"); - long lastModified = resource.getLastModified(); - if (headerValue != -1) { - // If an If-None-Match header has been specified, if modified since - // is ignored. - if ((request.getHeader("If-None-Match") == null) && (lastModified < headerValue + 1000)) { - // The entity has not been modified since the date - // specified by the client. This is not an error case. - response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); - response.setHeader("ETag", generateETag(resource)); + String method = request.getMethod(); + if (!Method.GET.equals(method) && !Method.HEAD.equals(method)) { + return true; + } - return false; - } + long resourceLastModified = resource.getLastModified(); + if (resourceLastModified <= 0) { + // MUST ignore if the resource does not have a modification date available. + return true; + } + + // Must be at least one header for this method to be called + Enumeration headerEnum = request.getHeaders("If-Modified-Since"); + headerEnum.nextElement(); + if (headerEnum.hasMoreElements()) { + // If-Modified-Since is a list of dates + return true; + } + + try { + // Header is present so -1 will be not returned. Only a valid date or an IAE are possible. + long headerValue = request.getDateHeader("If-Modified-Since"); + if (resourceLastModified < (headerValue + 1000)) { + // The entity has not been modified since the date + // specified by the client. This is not an error case. + response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + response.setHeader("ETag", generateETag(resource)); + return false; } } catch (IllegalArgumentException illegalArgument) { return true; @@ -2250,44 +2361,71 @@ protected boolean checkIfNoneMatch(HttpServletRequest request, HttpServletResponse response, WebResource resource) throws IOException { - String headerValue = request.getHeader("If-None-Match"); - if (headerValue != null) { + String resourceETag = generateETag(resource); - boolean conditionSatisfied; - - String resourceETag = generateETag(resource); - if (!headerValue.equals("*")) { - if (resourceETag == null) { + Enumeration headerValues = request.getHeaders("If-None-Match"); + boolean hasAsteriskValue = false;// check existence of special header value '*' + boolean conditionSatisfied = true; + int headerCount = 0; + while (headerValues.hasMoreElements()) { + headerCount++; + String headerValue = headerValues.nextElement(); + + if (headerValue.equals("*")) { + hasAsteriskValue = true; + if (headerCount > 1 || headerValues.hasMoreElements()) { conditionSatisfied = false; } else { - // RFC 7232 requires weak comparison for If-None-Match headers - Boolean matched = EntityTag.compareEntityTag(new StringReader(headerValue), true, resourceETag); - if (matched == null) { - if (debug > 10) { - log("DefaultServlet.checkIfNoneMatch: Invalid header value [" + headerValue + "]"); - } - response.sendError(HttpServletResponse.SC_BAD_REQUEST); - return false; + // asterisk '*' is the only field value. + // RFC9110: If the field value is "*", the condition is false if the origin server has a current + // representation for the target resource. + if (resourceETag != null) { + conditionSatisfied = false; } - conditionSatisfied = matched.booleanValue(); } + break; } else { - conditionSatisfied = true; + // RFC 7232 requires weak comparison for If-None-Match headers + Boolean matched = EntityTag.compareEntityTag(new StringReader(headerValue), true, resourceETag); + if (matched == null) { + if (debug > 10) { + log("DefaultServlet.checkIfNoneMatch: Invalid header value [" + headerValue + "]"); + } + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return false; + } + if (matched.booleanValue()) { + // RFC9110: If the field value is a list of entity tags, the condition is false if one of the + // listed tags + // matches the entity tag of the selected representation. + conditionSatisfied = false; + break; + } } + } + if (headerValues.hasMoreElements()) { + headerCount++; + } - if (conditionSatisfied) { - // For GET and HEAD, we should respond with - // 304 Not Modified. - // For every other method, 412 Precondition Failed is sent - // back. - if ("GET".equals(request.getMethod()) || "HEAD".equals(request.getMethod())) { - response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); - response.setHeader("ETag", resourceETag); - } else { - response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); - } - return false; + if (hasAsteriskValue && headerCount > 1) { + // Note that an If-None-Match header field with a list value containing "*" and other values (including + // other instances of "*") is syntactically invalid (therefore not allowed to be generated) and furthermore + // is unlikely to be interoperable. + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return false; + } + if (!conditionSatisfied) { + // For GET and HEAD, we should respond with + // 304 Not Modified. + // For every other method, 412 Precondition Failed is sent + // back. + if (Method.GET.equals(request.getMethod()) || Method.HEAD.equals(request.getMethod())) { + response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); + response.setHeader("ETag", resourceETag); + } else { + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); } + return false; } return true; } @@ -2307,16 +2445,28 @@ */ protected boolean checkIfUnmodifiedSince(HttpServletRequest request, HttpServletResponse response, WebResource resource) throws IOException { + + long resourceLastModified = resource.getLastModified(); + if (resourceLastModified <= 0) { + // MUST ignore if the resource does not have a modification date available. + return true; + } + // Must be at least one header for this method to be called + Enumeration headerEnum = request.getHeaders("If-Unmodified-Since"); + headerEnum.nextElement(); + if (headerEnum.hasMoreElements()) { + // If-Unmodified-Since is a list of dates + return true; + } + try { - long lastModified = resource.getLastModified(); + // Header is present so -1 will be not returned. Only a valid date or an IAE are possible. long headerValue = request.getDateHeader("If-Unmodified-Since"); - if (headerValue != -1) { - if (lastModified >= (headerValue + 1000)) { - // The entity has not been modified since the date - // specified by the client. This is not an error case. - response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); - return false; - } + if (resourceLastModified >= (headerValue + 1000)) { + // The entity has not been modified since the date + // specified by the client. This is not an error case. + response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); + return false; } } catch (IllegalArgumentException illegalArgument) { return true; @@ -2326,6 +2476,78 @@ /** + * Check if the if-range condition is satisfied. The calling method is required to ensure a Range header is present + * and that Range requests are supported for the current resource. + * + * @param request The servlet request we are processing + * @param response The servlet response we are creating + * @param resource The resource + * + * @return {@code true} if the resource meets the specified condition, and {@code false} if the condition is not + * satisfied, resulting in transfer of the new selected representation instead of a 412 (Precondition + * Failed) response. If the if-range condition is not valid then an appropriate status code will be set, + * the response will be committed and this method will return {@code false} + * + * @throws IOException an IO error occurred + */ + protected boolean checkIfRange(HttpServletRequest request, HttpServletResponse response, WebResource resource) + throws IOException { + String resourceETag = generateETag(resource); + long resourceLastModified = resource.getLastModified(); + + Enumeration headerEnum = request.getHeaders("If-Range"); + if (!headerEnum.hasMoreElements()) { + // If-Range is not present + return true; + } + String headerValue = headerEnum.nextElement().trim(); + if (headerEnum.hasMoreElements()) { + // Multiple If-Range headers + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return false; + } + + if (headerValue.length() > 2 && (headerValue.charAt(0) == '"' || headerValue.charAt(2) == '"')) { + boolean weakETag = headerValue.startsWith("W/\""); + if ((!weakETag && headerValue.charAt(0) != '"') || headerValue.charAt(headerValue.length() - 1) != '"' || + headerValue.indexOf('"', weakETag ? 3 : 1) != headerValue.length() - 1) { + // Not a single entity tag + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return false; + } + // If the ETag the client gave does not match the entity + // etag, then the entire entity is returned. + return !weakETag && resourceETag != null && resourceETag.equals(headerValue); + } else { + long headerValueTime = -1L; + try { + headerValueTime = request.getDateHeader("If-Range"); + } catch (IllegalArgumentException ignore) { + // Ignore + } + if (headerValueTime >= 0) { + // unit of HTTP date is second, ignore millisecond part. + return resourceLastModified >= headerValueTime && resourceLastModified < headerValueTime + 1000; + } else { + // Not a single entity tag and not a valid date either + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return false; + } + } + + } + + /** + * Checks if range request is supported by server + * + * @return true server supports range requests feature. + */ + protected boolean isRangeRequestsSupported() { + // Range-Requests optional feature is enabled implicitly. + return true; + } + + /** * Provides the entity tag (the ETag header) for the given resource. Intended to be over-ridden by custom * DefaultServlet implementations that wish to use an alternative format for the entity tag. * @@ -2353,11 +2575,10 @@ */ protected void copy(InputStream is, ServletOutputStream ostream) throws IOException { - IOException exception = null; InputStream istream = new BufferedInputStream(is, input); // Copy the input stream to the output stream - exception = copyRange(istream, ostream); + IOException exception = copyRange(istream, ostream); // Clean up the input stream istream.close(); @@ -2380,7 +2601,6 @@ * @exception IOException if an input/output error occurs */ protected void copy(InputStream is, PrintWriter writer, String encoding) throws IOException { - IOException exception = null; Reader reader; if (encoding == null) { @@ -2390,7 +2610,7 @@ } // Copy the input stream to the output stream - exception = copyRange(reader, writer); + IOException exception = copyRange(reader, writer); // Clean up the reader reader.close(); @@ -2416,11 +2636,9 @@ protected void copy(WebResource resource, long length, ServletOutputStream ostream, Ranges.Entry range) throws IOException { - IOException exception = null; - InputStream resourceInputStream = resource.getInputStream(); InputStream istream = new BufferedInputStream(resourceInputStream, input); - exception = copyRange(istream, ostream, getStart(range, length), getEnd(range, length)); + IOException exception = copyRange(istream, ostream, getStart(range, length), getEnd(range, length)); // Clean up the input stream istream.close(); @@ -2497,18 +2715,16 @@ // Copy the input stream to the output stream IOException exception = null; - byte buffer[] = new byte[input]; - int len = buffer.length; + byte[] buffer = new byte[input]; while (true) { try { - len = istream.read(buffer); + int len = istream.read(buffer); if (len == -1) { break; } ostream.write(buffer, 0, len); - } catch (IOException e) { - exception = e; - len = -1; + } catch (IOException ioe) { + exception = ioe; break; } } @@ -2530,18 +2746,16 @@ // Copy the input stream to the output stream IOException exception = null; - char buffer[] = new char[input]; - int len = buffer.length; + char[] buffer = new char[input]; while (true) { try { - len = reader.read(buffer); + int len = reader.read(buffer); if (len == -1) { break; } writer.write(buffer, 0, len); - } catch (IOException e) { - exception = e; - len = -1; + } catch (IOException ioe) { + exception = ioe; break; } } @@ -2567,11 +2781,11 @@ log("Serving bytes: " + start + "-" + end); } - long skipped = 0; + long skipped; try { skipped = istream.skip(start); - } catch (IOException e) { - return e; + } catch (IOException ioe) { + return ioe; } if (skipped < start) { return new IOException(sm.getString("defaultServlet.skipfail", Long.valueOf(skipped), Long.valueOf(start))); @@ -2580,7 +2794,7 @@ IOException exception = null; long bytesToRead = end - start + 1; - byte buffer[] = new byte[input]; + byte[] buffer = new byte[input]; int len = buffer.length; while ((bytesToRead > 0) && (len >= buffer.length)) { try { @@ -2592,13 +2806,10 @@ ostream.write(buffer, 0, (int) bytesToRead); bytesToRead = 0; } - } catch (IOException e) { - exception = e; + } catch (IOException ioe) { + exception = ioe; len = -1; } - if (len < buffer.length) { - break; - } } return exception; @@ -2677,7 +2888,7 @@ /** * A class encapsulating the sorting of resources. */ - private static class SortManager { + protected static class SortManager { /** * The default sort. */ @@ -2798,7 +3009,7 @@ * @return An Order specifying the column and ascending/descending to be applied to resources. */ public Order getOrder(String order) { - if (null == order || 0 == order.trim().length()) { + if (null == order || order.trim().isEmpty()) { return Order.DEFAULT; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,6 @@ cgiServlet.expandCreateDirFail=Failed to create destination directory [{0}] for script expansion cgiServlet.expandDeleteFail=Failed to delete file at [{0}] after IOException during expansion cgiServlet.expandFail=Failed to expand script at path [{0}] to [{1}] -cgiServlet.expandNotFound=Unable to expand [{0}] as it could not be found cgiServlet.expandOk=Expanded script at path [{0}] to [{1}] cgiServlet.find.found=Found CGI: name [{0}], path [{1}], script name [{2}] and CGI name [{3}] cgiServlet.find.location=Looking for a file at [{0}] @@ -29,6 +28,7 @@ cgiServlet.invalidArgumentDecoded=The decoded command line argument [{0}] did not match the configured cmdLineArgumentsDecoded pattern [{1}] cgiServlet.invalidArgumentEncoded=The encoded command line argument [{0}] did not match the configured cmdLineArgumentsEncoded pattern [{1}] cgiServlet.invalidCommand=Illegal Character in CGI command path ('.' or '..') detected, not running CGI [{0}] +cgiServlet.noResources=No static resources were found cgiServlet.notReady=CGI Servlet is not ready to run cgiServlet.runBadHeader=Bad header line [{0}] cgiServlet.runFail=I/O problems processing CGI @@ -43,6 +43,7 @@ defaultServlet.blockExternalEntity=Blocked access to external entity with publicId [{0}] and systemId [{0}] defaultServlet.blockExternalEntity2=Blocked access to external entity with name [{0}], publicId [{1}], baseURI [{2}] and systemId [{3}] defaultServlet.blockExternalSubset=Blocked access to external subset with name [{0}] and baseURI [{1}] +defaultServlet.deleteTempFileFailed=Failed to delete temporary file [{0}] used for partial PUT defaultServlet.directory.parent=Up To [{0}] defaultServlet.directory.title=Directory Listing For [{0}] defaultServlet.missingResource=The requested resource [{0}] is not available @@ -53,8 +54,11 @@ defaultServlet.resource.size=Size defaultServlet.skipfail=Read failed because only [{0}] bytes were available but needed to skip [{1}] bytes to reach the start of the requested range defaultServlet.unknownBomConfig=Unrecognised value of [{0}] provided for useBomIfPresent initialization parameter +defaultServlet.wrongByteCountForRange=An invalid amount [{0}] of bytes were received for range with length [{1}] defaultServlet.xslError=XSL transformer error +webdavservlet.dataSourceStore.error=Error processing [{0}] on dead properties for path [{1}] +webdavservlet.dataSourceStore.noDataSource=DataSource [{0}] was not found in the webapp environment webdavservlet.externalEntityIgnored=The request included a reference to an external entity with PublicID [{0}] and SystemID [{1}] which was ignored webdavservlet.inputstreamclosefail=Failed to close the inputStream of [{0}] webdavservlet.jaxpfailed=JAXP initialization failed @@ -62,6 +66,3 @@ webdavservlet.noStoreParameter=Init parameter [{0}] with value [{1}] was not found on the configured store webdavservlet.nonWildcardMapping=The mapping [{0}] is not a wildcard mapping and should not be used for the WebDAV Servlet webdavservlet.storeError=Error creating store of class [{0}], the default memory store will be used instead - -webdavservlet.dataSourceStore.error=Error processing [{0}] on dead properties for path [{1}] -webdavservlet.dataSourceStore.noDataSource=DataSource [{0}] was not found in the webapp environment diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,6 @@ cgiServlet.expandCreateDirFail=Echec de la création du répertoire de destination [{0}] pour la décompression du script cgiServlet.expandDeleteFail=Impossible d''effacer le fichier [{0}] suite à une IOException pendant la décompression cgiServlet.expandFail=Impossible de faire l''expansion du script au chemin [{0}] vers [{1}] -cgiServlet.expandNotFound=Impossible de décompresser [{0}] car il n''a pas été trouvé cgiServlet.expandOk=Extrait le script du chemin [{0}] vers [{1}] cgiServlet.find.found=Trouvé le CGI : nom [{0}], chemin [{1}], nom de script [{2}] et nom du CGI [{3}] cgiServlet.find.location=Recherche d''un fichier en [{0}] @@ -29,6 +28,7 @@ cgiServlet.invalidArgumentDecoded=Les paramètres de ligne de commande décodés [{0}] ne correspondent pas au modèle cmdLineArgumentsDecoded configuré [{1}] cgiServlet.invalidArgumentEncoded=Les paramètres de ligne de commande encodés [{0}] ne correspondent pas au modèle cmdLineArgumentsEncoded configuré [{1}] cgiServlet.invalidCommand=Un caractère illégal (''.'' or ''..'') a été trouvé dans le chemin de commande CGI, le CGI [{0}] n''est pas exécuté +cgiServlet.noResources=Pas de ressources statiques trouvées cgiServlet.notReady=Le Servlet CGI n'est pas prêt à fonctionner cgiServlet.runBadHeader=Mauvaise ligne d''en-tête [{0}] cgiServlet.runFail=Problèmes d'IO lors de l'exécution du CGI @@ -43,6 +43,7 @@ defaultServlet.blockExternalEntity=L''accès aux entités externes avec le publicId [{0}] et le systemId [{1}] est bloqué defaultServlet.blockExternalEntity2=L''accès à l''entité externe nommée [{0}], publicId [{1}], baseURI [{2}], systemId [{3}] a été bloqué defaultServlet.blockExternalSubset=L''accès au sous-ensemble externe de nom [{0}] et de baseURI [{1}] a été bloqué +defaultServlet.deleteTempFileFailed=Echec lors de la suppression du fichier [{0}] utilisé pour le PUT partiel defaultServlet.directory.parent=Jusqu''à [{0}] defaultServlet.directory.title=Liste du répertoire pour [{0}] defaultServlet.missingResource=La ressource demandée [{0}] n''est pas disponible @@ -53,8 +54,11 @@ defaultServlet.resource.size=Taille defaultServlet.skipfail=La lecture a échouée parce que seuls [{0}] octets étaient disponibles alors qu''il était nécessaire d''en sauter [{1}] pour atteindre le début de la plage demandée defaultServlet.unknownBomConfig=La valeur [{0}] inconnue a été donnée pour le paramètre d’initialisation useBomIfPresent +defaultServlet.wrongByteCountForRange=Une quantité invalide d''octets [{0}] a été reçue pour la range dont la longueur est [{1}] defaultServlet.xslError=Erreur de transformation XSL +webdavservlet.dataSourceStore.error=Erreur [{0}] lors du traitement des prorpiétés pour le chemin [{1}] +webdavservlet.dataSourceStore.noDataSource=La DataSource [{0}] n''a pas été trouvé dans l''environnement de la webapp webdavservlet.externalEntityIgnored=La requête a inclus une référence à une entité externe avec publicId [{0}] et systemId [{1}] qui a été ignorée webdavservlet.inputstreamclosefail=Impossible de fermer le flux d''entrée pour [{0}] webdavservlet.jaxpfailed=Erreur d'initialisation de JAXP diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,6 @@ cgiServlet.expandCreateDirFail=スクリプトの展開先ディレクトリ[{0}]の作成に失敗しました。 cgiServlet.expandDeleteFail=拡張中にIOExceptionの後に [{0}] でファイルを削除できませんでした cgiServlet.expandFail=パス [{0}] のスクリプトを [{1}] に展開できませんでした -cgiServlet.expandNotFound=見つけることができなかったため [{0}] を展開できません cgiServlet.expandOk=パス [{0}] の [{1}] に展開されたスクリプト cgiServlet.find.found=見つかったCGI: 名前 [{0}]、パス [{1}]、スクリプト名 [{2}]、CGI名 [{3}] cgiServlet.find.location=ファイル [{0}] を探しています。 @@ -29,6 +28,7 @@ cgiServlet.invalidArgumentDecoded=デコードされたコマンドライン引数 [{0}] は、構成されたcmdLineArgumentsDecoded パターン [{1}] にマッチしません cgiServlet.invalidArgumentEncoded=エンコードされたコマンドライン引数 [{0}] は、構成されたcmdLineArgumentsEncoded パターン [{1}] にマッチしません cgiServlet.invalidCommand=CGI コマンド パスに不正な文字 (''.'' または ''..'') が検出されました。CGI [{0}] は実行されていません +cgiServlet.noResources=静的リソースが見つかりませんでした cgiServlet.notReady=CGI サーブレットは実行の準備ができていません cgiServlet.runBadHeader=悪いヘッダライン [{0}] cgiServlet.runFail=CGI処理中のIO問題 @@ -43,6 +43,7 @@ defaultServlet.blockExternalEntity=publicId [{0}]およびsystemId [{0}]を持つ外部エンティティへのアクセスがブロックされました defaultServlet.blockExternalEntity2=エンティティ名 [{0}]、publicId [{1}]、baseURI [{2}] および systemId [{3}] を持つ外部エンティティへのアクセスがブロックされました defaultServlet.blockExternalSubset=名前[{0}]およびベースURI [{1}]を持つ外部サブセットへのアクセスがブロックされました +defaultServlet.deleteTempFileFailed=部分的なPUTに使用された一時ファイル [{0}] が削除できませんでした defaultServlet.directory.parent=[{0}] に移動 defaultServlet.directory.title=[{0}] のディレクトリの一覧 defaultServlet.missingResource=要求されたリソース [{0}] は利用できません。 @@ -53,8 +54,11 @@ defaultServlet.resource.size=サイズ defaultServlet.skipfail=[{1}]バイトをスキップして要求された範囲の先頭に到達する必要がありましたが、[{0}]バイトしか利用できなかったため読み取りに失敗しました。 defaultServlet.unknownBomConfig=useBomIfPresentの初期化パラメーターに提供された認識されない値 [{0}] +defaultServlet.wrongByteCountForRange=長さ [{1}] の範囲に対して無効な [{0}] バイトを受信しました defaultServlet.xslError=XSL変換エラー +webdavservlet.dataSourceStore.error=パス [{1}] の無効なプロパティで [{0}] を処理中にエラーが発生しました +webdavservlet.dataSourceStore.noDataSource=データソース [{0}] が webapp 環境に見つかりませんでした webdavservlet.externalEntityIgnored=PublicID [{0}]およびSystemID [{1}]を持つ外部エンティティへの参照を含むリクエストが無視されました webdavservlet.inputstreamclosefail=入力ストリーム [{0}] を切断できません。 webdavservlet.jaxpfailed=JAXPの初期化に失敗しました diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_ko.properties tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_ko.properties --- tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_ko.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_ko.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,6 @@ cgiServlet.expandCreateDirFail=스크립트를 압축해제 하기 위한 대상 디렉토리 [{0}]을(를) 생성하지 못했습니다. cgiServlet.expandDeleteFail=압축해제 중 IOException이 발생한 후, [{0}]에 위치한 해당 파일을 삭제하지 못했습니다. cgiServlet.expandFail=경로 [{0}]의 스크립트를 [{1}](으)로 압축해제 하지 못했습니다. -cgiServlet.expandNotFound=[{0}]을(를) 찾을 수 없어서 압축해제 할 수 없습니다. cgiServlet.expandOk=[{0}] 경로에 있는 스트립트가 [{1}](으)로 압축 해제되었습니다. cgiServlet.find.found=CGI 발견: 이름 [{0}], 경로 [{1}], 스크립트 이름 [{2}], CGI 이름 [{3}] cgiServlet.find.location=[{0}]에 위치한 파일을 찾는 중 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations cgiServlet.expandFail=Невозможно развернуть скрипт [{0}] в [{1}] +cgiServlet.find.location=Поиск файла в [{0}] cgiServlet.runInvalidStatus=Неверный статус [{0}] defaultServlet.skipfail=Чтение завершилось ошибкой, потому что только [{0}] байт было доступно, а требовалось пропустить [{1}] байт, чтобы достигнуть начала требуемоего диапазона diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,6 @@ cgiServlet.expandCreateDirFail=无法为脚本扩展创建目标目录[{0}] cgiServlet.expandDeleteFail=扩展期间,发生IOException异常后删除文件[{0}]失败 cgiServlet.expandFail=在路径[{0}] 到[{1}] 展开脚本失败. -cgiServlet.expandNotFound=无法展开[{0}],因为找不到它。 cgiServlet.expandOk=从路径[{0}]到[{1}]扩展脚本 cgiServlet.find.found=找到CGI:name[{0}]、path[{1}]、script name[{2}]和CGI name[{3}] cgiServlet.find.location=在 [{0}] 查找文件 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/servlets/WebdavServlet.java tomcat10-10.1.52/java/org/apache/catalina/servlets/WebdavServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/servlets/WebdavServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/servlets/WebdavServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ package org.apache.catalina.servlets; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; @@ -56,11 +57,13 @@ import org.apache.catalina.WebResource; import org.apache.catalina.connector.RequestFacade; import org.apache.catalina.util.DOMWriter; +import org.apache.catalina.util.IOTools; import org.apache.catalina.util.XMLWriter; import org.apache.tomcat.PeriodicEventListener; import org.apache.tomcat.util.IntrospectionUtils; import org.apache.tomcat.util.http.ConcurrentDateFormat; import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.RequestUtil; import org.apache.tomcat.util.http.WebdavIfHeader; import org.w3c.dom.Document; @@ -72,15 +75,32 @@ import org.xml.sax.SAXException; /** - * Servlet which adds support for WebDAV + * This servlet adds support for WebDAV * level 3. All the basic HTTP requests are handled by the - * DefaultServlet. The WebdavServlet must not be used as the default servlet (ie mapped to '/') as it will not work in - * this configuration. + * DefaultServlet. *

      - * Mapping a subpath (e.g. /webdav/* to this servlet has the effect of re-mounting the entire web - * application under that sub-path, with WebDAV access to all the resources. To restore the DefaultServlet behavior set - * serveSubpathOnly to true. The WEB-INF and META-INF directories - * are protected in this re-mounted resource tree. + * The WebDAV servlet is only designed for use with path mapping. The WebdavServlet must not be used as the default + * servlet (i.e. mapped to '/') or with any other mapping types as it will not work in those configurations. + *

      + * By default, the entire web application is exposed via the WebDAV servlet. Mapping the WebDAV servlet to + * /* provides WebDAV access to all the resources within the web application. To aid separation of normal + * users and WebDAV users, the WebDAV servlet may be mounted at a sub-path (e.g. /webdav/*) which creates + * an additional mapping for the entire web application under that sub-path, with WebDAV access to all the resources. + *

      + * By default, the WEB-INF and META-INF directories are not accessible via WebDAV. This may be + * changed by setting the allowSpecialPaths initialisation parameter to true. + *

      + * It is also possible to enable WebDAV access to a sub-set of the standard web application URL space rather than + * creating an additional, WebDAV specific mapping. To do this, map the WebDAV servlet to the desired sub-path and set + * the serveSubpathOnly initialisation parameter to true. + *

      + * Security constraints using the same URL pattern as the mapping (e.g. /webdav/*) can be used to limit the + * users with access to WebDAV functionality. Care is required if using security constraints to further limit WebDAV + * functionality. In particular, administrators should be aware that security constraints apply only to the request URL. + * Security constraints do not apply to any destination URL associated with the WebDAV operation (such as COPY or MOVE). + *

      + * If WebDAV functionality is included in a web application where legitimate users may access it via a browser, it is + * recommended that the application include CORS protection. *

      * To enable WebDAV for a context add the following to web.xml: * @@ -94,7 +114,7 @@ * </init-param> * <init-param> * <param-name>listings</param-name> - * <param-value>false</param-value> + * <param-value>true</param-value> * </init-param> * </servlet> * <servlet-mapping> @@ -103,7 +123,7 @@ * </servlet-mapping> * * - * This will enable read only access. To enable read-write access add: + * This will enable read only access with folder listings enabled. To enable read-write access add: * *

        *  <init-param>
      @@ -121,7 +141,7 @@
        *  </servlet-mapping>
        * 
      * - * By default access to /WEB-INF and META-INF are not available via WebDAV. To enable access to these URLs, use add: + * By default, access to /WEB-INF and META-INF are not available via WebDAV. To enable access to these URLs, add: * *
        *  <init-param>
      @@ -136,12 +156,12 @@
        * http://host:port/context/webdavedit/content
        * 

      * The Servlet provides support for arbitrary dead properties on all resources (dead properties are properties whose - * values are not protected by the server, such as the content length of a resource). By default the Servlet will use + * values are not protected by the server, such as the content length of a resource). By default, the Servlet will use * non persistent memory storage for them. Persistence can be achieved by implementing the PropertyStore * interface and configuring the Servlet to use that store. The propertyStore init-param allows configuring - * the classname of the store to use, while the parameters in the form of store.xxx will be set on the + * the class name of the store to use, while the parameters in the form of store.xxx will be set on the * store object as bean properties. For example, this would configure a store with class - * com.MyPropertyStore, and set its field myName to value myValue: + * com.MyPropertyStore, and set its property myName to value myValue: * *

        *  <init-param>
      @@ -162,17 +182,6 @@
           private static final long serialVersionUID = 1L;
       
       
      -    // -------------------------------------------------------------- Constants
      -
      -    private static final String METHOD_PROPFIND = "PROPFIND";
      -    private static final String METHOD_PROPPATCH = "PROPPATCH";
      -    private static final String METHOD_MKCOL = "MKCOL";
      -    private static final String METHOD_COPY = "COPY";
      -    private static final String METHOD_MOVE = "MOVE";
      -    private static final String METHOD_LOCK = "LOCK";
      -    private static final String METHOD_UNLOCK = "UNLOCK";
      -
      -
           /**
            * Default lock timeout value.
            */
      @@ -344,11 +353,7 @@
                           resourceLocks.remove(currentLock.path);
                       }
                   } else {
      -                for (String token : currentLock.sharedTokens) {
      -                    if (sharedLocks.get(token) == null) {
      -                        currentLock.sharedTokens.remove(token);
      -                    }
      -                }
      +                currentLock.sharedTokens.removeIf(token -> sharedLocks.get(token) == null);
                       if (currentLock.sharedTokens.isEmpty()) {
                           resourceLocks.remove(currentLock.path);
                       }
      @@ -464,7 +469,7 @@
               }
       
               /**
      -         * @return the statusCode the statusCode to set as a result of the operation
      +         * @return the statusCode to set as a result of the operation
                */
               public int getStatusCode() {
                   return this.statusCode;
      @@ -488,7 +493,7 @@
           /**
            * Type of PROPFIND request.
            */
      -    enum PropfindType {
      +    public enum PropfindType {
               FIND_BY_PROPERTY,
               FIND_ALL_PROP,
               FIND_PROPERTY_NAMES
      @@ -498,7 +503,7 @@
           /**
            * Type of property update in a PROPPATCH.
            */
      -    enum PropertyUpdateType {
      +    public enum PropertyUpdateType {
               SET,
               REMOVE
           }
      @@ -516,10 +521,9 @@
            *                              exception)
            */
           protected DocumentBuilder getDocumentBuilder() throws ServletException {
      -        DocumentBuilder documentBuilder = null;
      -        DocumentBuilderFactory documentBuilderFactory = null;
      +        DocumentBuilder documentBuilder;
               try {
      -            documentBuilderFactory = DocumentBuilderFactory.newInstance();
      +            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                   documentBuilderFactory.setNamespaceAware(true);
                   documentBuilderFactory.setExpandEntityReferences(false);
                   documentBuilder = documentBuilderFactory.newDocumentBuilder();
      @@ -560,19 +564,19 @@
                   log("[" + method + "] " + path);
               }
       
      -        if (method.equals(METHOD_PROPFIND)) {
      +        if (Method.PROPFIND.equals(method)) {
                   doPropfind(req, resp);
      -        } else if (method.equals(METHOD_PROPPATCH)) {
      +        } else if (Method.PROPPATCH.equals(method)) {
                   doProppatch(req, resp);
      -        } else if (method.equals(METHOD_MKCOL)) {
      +        } else if (Method.MKCOL.equals(method)) {
                   doMkcol(req, resp);
      -        } else if (method.equals(METHOD_COPY)) {
      +        } else if (Method.COPY.equals(method)) {
                   doCopy(req, resp);
      -        } else if (method.equals(METHOD_MOVE)) {
      +        } else if (Method.MOVE.equals(method)) {
                   doMove(req, resp);
      -        } else if (method.equals(METHOD_LOCK)) {
      +        } else if (Method.LOCK.equals(method)) {
                   doLock(req, resp);
      -        } else if (method.equals(METHOD_UNLOCK)) {
      +        } else if (Method.UNLOCK.equals(method)) {
                   doUnlock(req, resp);
               } else {
                   // DefaultServlet processing
      @@ -602,12 +606,16 @@
                   // Get all hrefs from the if header
                   Iterator hrefs = ifHeader.getResources();
       
      -            String currentPath = null;
      -            String currentHref = null;
      -            WebResource currentWebResource = null;
      +            String currentPath;
      +            String currentHref;
      +            WebResource currentWebResource;
                   if (hrefs.hasNext()) {
                       currentHref = hrefs.next();
                       currentPath = getPathFromHref(currentHref, request);
      +                if (currentPath == null) {
      +                    // The path was invalid
      +                    return false;
      +                }
                       currentWebResource = resources.getResource(currentPath);
                   } else {
                       currentPath = path;
      @@ -633,23 +641,19 @@
                                   if (parentLock.hasExpired()) {
                                       resourceLocks.remove(parentPath);
                                   } else {
      -                                if ((parentPath != currentPath && parentLock.depth > 0) || parentPath == currentPath) {
      +                                // parentPath == currentPath is a check for the first loop
      +                                if (parentPath == currentPath || parentLock.depth > 0) {
                                           if (parentLock.isExclusive()) {
                                               lockTokens.add(LOCK_SCHEME + parentLock.token);
                                           } else {
      -                                        for (String token : parentLock.sharedTokens) {
      -                                            if (sharedLocks.get(token) == null) {
      -                                                parentLock.sharedTokens.remove(token);
      -                                            }
      -                                        }
      +                                        parentLock.sharedTokens.removeIf(token -> sharedLocks.get(token) == null);
                                               if (parentLock.sharedTokens.isEmpty()) {
                                                   resourceLocks.remove(parentLock.path);
                                               }
                                               for (String token : parentLock.sharedTokens) {
                                                   LockInfo sharedLock = sharedLocks.get(token);
                                                   if (sharedLock != null) {
      -                                                if ((parentPath != currentPath && sharedLock.depth > 0) ||
      -                                                        parentPath == currentPath) {
      +                                                if (parentPath == currentPath || sharedLock.depth > 0) {
                                                           lockTokens.add(LOCK_SCHEME + token);
                                                       }
                                                   }
      @@ -674,6 +678,10 @@
                       if (hrefs.hasNext()) {
                           currentHref = hrefs.next();
                           currentPath = getPathFromHref(currentHref, request);
      +                    if (currentPath == null) {
      +                        // The path was invalid
      +                        return false;
      +                    }
                           currentWebResource = resources.getResource(currentPath);
                       } else {
                           break;
      @@ -751,7 +759,7 @@
               // if the resource does not exist.
               StringBuilder methodsAllowed = new StringBuilder("OPTIONS, GET, POST, HEAD");
       
      -        if (!readOnly) {
      +        if (!isReadOnly()) {
                   methodsAllowed.append(", DELETE");
                   if (!resource.isDirectory()) {
                       methodsAllowed.append(", PUT");
      @@ -765,7 +773,7 @@
       
               methodsAllowed.append(", LOCK, UNLOCK, PROPPATCH, COPY, MOVE");
       
      -        if (listings) {
      +        if (isListings()) {
                   methodsAllowed.append(", PROPFIND");
               }
       
      @@ -796,23 +804,17 @@
            */
           protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       
      -        if (!listings) {
      +        if (!isListings()) {
                   sendNotAllowed(req, resp);
                   return;
               }
       
               String path = getRelativePath(req);
       
      -        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
      -        if (isSpecialPath(path)) {
      -            resp.sendError(WebdavStatus.SC_FORBIDDEN);
      -            return;
      -        }
      -
               // Properties which are to be displayed.
               List properties = new ArrayList<>();
               // Propfind depth
      -        int depth = maxDepth;
      +        int depth;
               // Propfind type
               PropfindType type = null;
       
      @@ -833,11 +835,19 @@
                   }
               }
       
      -        if (req.getContentLengthLong() > 0 || "chunked".equalsIgnoreCase(req.getHeader("Transfer-Encoding"))) {
      +        byte[] body;
      +        try (InputStream is = req.getInputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream()) {
      +            IOTools.flow(is, os);
      +            body = os.toByteArray();
      +        } catch (IOException ioe) {
      +            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
      +            return;
      +        }
      +        if (body.length > 0) {
                   DocumentBuilder documentBuilder = getDocumentBuilder();
       
                   try {
      -                Document document = documentBuilder.parse(new InputSource(req.getInputStream()));
      +                Document document = documentBuilder.parse(new InputSource(new ByteArrayInputStream(body)));
       
                       // Get the root element of the document
                       Element rootElement = document.getDocumentElement();
      @@ -1013,7 +1023,7 @@
                   return;
               }
       
      -        if (readOnly) {
      +        if (isReadOnly()) {
                   resp.sendError(WebdavStatus.SC_FORBIDDEN);
                   return;
               }
      @@ -1026,8 +1036,20 @@
               DocumentBuilder documentBuilder = getDocumentBuilder();
               ArrayList operations = new ArrayList<>();
       
      +        byte[] body;
      +        try (InputStream is = req.getInputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream()) {
      +            IOTools.flow(is, os);
      +            body = os.toByteArray();
      +        } catch (IOException ioe) {
      +            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
      +            return;
      +        }
      +        if (body.length == 0) {
      +            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
      +            return;
      +        }
               try {
      -            Document document = documentBuilder.parse(new InputSource(req.getInputStream()));
      +            Document document = documentBuilder.parse(new InputSource(new ByteArrayInputStream(body)));
       
                   // Get the root element of the document
                   Element rootElement = document.getDocumentElement();
      @@ -1053,18 +1075,7 @@
                                               break;
                                           case Node.ELEMENT_NODE:
                                               if ("prop".equals(getDAVNode(currentNode2))) {
      -                                            NodeList propChildList = currentNode2.getChildNodes();
      -                                            Node property = null;
      -                                            for (int k = 0; k < propChildList.getLength(); k++) {
      -                                                Node currentNode3 = propChildList.item(k);
      -                                                switch (currentNode3.getNodeType()) {
      -                                                    case Node.TEXT_NODE:
      -                                                        break;
      -                                                    case Node.ELEMENT_NODE:
      -                                                        property = currentNode3;
      -                                                        break;
      -                                                }
      -                                            }
      +                                            Node property = getNode(currentNode2);
                                                   if (property != null) {
                                                       operations
                                                               .add(new ProppatchOperation(PropertyUpdateType.SET, property));
      @@ -1086,18 +1097,7 @@
                                               break;
                                           case Node.ELEMENT_NODE:
                                               if ("prop".equals(getDAVNode(currentNode2))) {
      -                                            NodeList propChildList = currentNode2.getChildNodes();
      -                                            Node property = null;
      -                                            for (int k = 0; k < propChildList.getLength(); k++) {
      -                                                Node currentNode3 = propChildList.item(k);
      -                                                switch (currentNode3.getNodeType()) {
      -                                                    case Node.TEXT_NODE:
      -                                                        break;
      -                                                    case Node.ELEMENT_NODE:
      -                                                        property = currentNode3;
      -                                                        break;
      -                                                }
      -                                            }
      +                                            Node property = getNode(currentNode2);
                                                   if (property != null) {
                                                       operations.add(
                                                               new ProppatchOperation(PropertyUpdateType.REMOVE, property));
      @@ -1161,6 +1161,22 @@
       
           }
       
      +    private static Node getNode(Node currentNode2) {
      +        NodeList propChildList = currentNode2.getChildNodes();
      +        Node property = null;
      +        for (int k = 0; k < propChildList.getLength(); k++) {
      +            Node currentNode3 = propChildList.item(k);
      +            switch (currentNode3.getNodeType()) {
      +                case Node.TEXT_NODE:
      +                    break;
      +                case Node.ELEMENT_NODE:
      +                    property = currentNode3;
      +                    break;
      +            }
      +        }
      +        return property;
      +    }
      +
       
           /**
            * MKCOL Method.
      @@ -1175,12 +1191,6 @@
       
               String path = getRelativePath(req);
       
      -        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
      -        if (isSpecialPath(path)) {
      -            resp.sendError(WebdavStatus.SC_FORBIDDEN);
      -            return;
      -        }
      -
               WebResource resource = resources.getResource(path);
               if (!checkIfHeaders(req, resp, resource)) {
                   resp.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
      @@ -1194,7 +1204,7 @@
                   return;
               }
       
      -        if (readOnly) {
      +        if (isReadOnly()) {
                   resp.sendError(WebdavStatus.SC_FORBIDDEN);
                   return;
               }
      @@ -1204,7 +1214,7 @@
                   return;
               }
       
      -        if (req.getContentLengthLong() > 0) {
      +        if (req.getContentLengthLong() > 0 || "chunked".equalsIgnoreCase(req.getHeader("Transfer-Encoding"))) {
                   // No support for MKCOL bodies, which are non standard
                   resp.sendError(WebdavStatus.SC_UNSUPPORTED_MEDIA_TYPE);
                   return;
      @@ -1221,14 +1231,18 @@
           @Override
           protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       
      -        if (readOnly) {
      +        if (isReadOnly()) {
                   sendNotAllowed(req, resp);
                   return;
               }
       
               String path = getRelativePath(req);
       
      -        deleteResource(path, req, resp);
      +        WebResource resource = resources.getResource(path);
      +        if (!checkIfHeaders(req, resp, resource)) {
      +            resp.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
      +        }
      +        deleteResource(path, req, resp, true);
           }
       
       
      @@ -1268,7 +1282,7 @@
            */
           protected void doCopy(HttpServletRequest req, HttpServletResponse resp) throws IOException {
       
      -        if (readOnly) {
      +        if (isReadOnly()) {
                   resp.sendError(WebdavStatus.SC_FORBIDDEN);
                   return;
               }
      @@ -1289,7 +1303,7 @@
            */
           protected void doMove(HttpServletRequest req, HttpServletResponse resp) throws IOException {
       
      -        if (readOnly) {
      +        if (isReadOnly()) {
                   resp.sendError(WebdavStatus.SC_FORBIDDEN);
                   return;
               }
      @@ -1318,7 +1332,7 @@
            */
           protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       
      -        if (readOnly) {
      +        if (isReadOnly()) {
                   resp.sendError(WebdavStatus.SC_FORBIDDEN);
                   return;
               }
      @@ -1374,17 +1388,25 @@
                       lockDuration = MAX_TIMEOUT;
                   }
               }
      -        lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000);
      +        lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000L);
       
               boolean lockCreation = false;
       
               Node lockInfoNode = null;
       
      -        if (req.getContentLengthLong() > 0 || "chunked".equalsIgnoreCase(req.getHeader("Transfer-Encoding"))) {
      +        byte[] body;
      +        try (InputStream is = req.getInputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream()) {
      +            IOTools.flow(is, os);
      +            body = os.toByteArray();
      +        } catch (IOException ioe) {
      +            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
      +            return;
      +        }
      +        if (body.length > 0) {
                   DocumentBuilder documentBuilder = getDocumentBuilder();
       
                   try {
      -                Document document = documentBuilder.parse(new InputSource(req.getInputStream()));
      +                Document document = documentBuilder.parse(new InputSource(new ByteArrayInputStream(body)));
       
                       // Get the root element of the document
                       Element rootElement = document.getDocumentElement();
      @@ -1405,8 +1427,8 @@
                   // Reading lock information
       
                   NodeList childList = lockInfoNode.getChildNodes();
      -            StringWriter strWriter = null;
      -            DOMWriter domWriter = null;
      +            StringWriter strWriter;
      +            DOMWriter domWriter;
       
                   Node lockScopeNode = null;
                   Node lockTypeNode = null;
      @@ -1657,7 +1679,8 @@
                           if (parentLock.hasExpired()) {
                               resourceLocks.remove(parentPath);
                           } else {
      -                        if ((parentPath != path && parentLock.depth > 0) || parentPath == path) {
      +                        // parentPath == currentPath is a check for the first loop
      +                        if (parentPath == path || parentLock.depth > 0) {
                                   if (parentLock.isExclusive()) {
                                       if (ifHeader.contains(":" + parentLock.token + ">") && (parentLock.principal == null ||
                                               parentLock.principal.equals(req.getRemoteUser()))) {
      @@ -1670,7 +1693,7 @@
                                               LockInfo sharedLock = sharedLocks.get(token);
                                               if (sharedLock != null && (sharedLock.principal == null ||
                                                       sharedLock.principal.equals(req.getRemoteUser()))) {
      -                                            if ((parentPath != path && sharedLock.depth > 0) || parentPath == path) {
      +                                            if (parentPath == path || sharedLock.depth > 0) {
                                                       toRenew = sharedLock;
                                                       break;
                                                   }
      @@ -1731,7 +1754,7 @@
            */
           protected void doUnlock(HttpServletRequest req, HttpServletResponse resp) throws IOException {
       
      -        if (readOnly) {
      +        if (isReadOnly()) {
                   resp.sendError(WebdavStatus.SC_FORBIDDEN);
                   return;
               }
      @@ -1758,19 +1781,19 @@
                       if (parentLock.hasExpired()) {
                           resourceLocks.remove(parentPath);
                       } else {
      -                    if ((parentPath != path && parentLock.depth > 0) || parentPath == path) {
      +                    // parentPath == currentPath is a check for the first loop
      +                    if (parentPath == path || parentLock.depth > 0) {
                               if (parentLock.isExclusive()) {
                                   if (lockTokenHeader.contains(":" + parentLock.token + ">") &&
                                           (parentLock.principal == null ||
                                                   parentLock.principal.equals(req.getRemoteUser()))) {
                                       resourceLocks.remove(parentPath);
                                       unlocked = true;
      -                                break;
                                   } else {
                                       // No parent exclusive lock will be found
                                       unlocked = false;
      -                                break;
                                   }
      +                            break;
                               } else {
                                   for (String token : parentLock.sharedTokens) {
                                       if (lockTokenHeader.contains(":" + token + ">")) {
      @@ -1779,7 +1802,7 @@
                                               parentLock.sharedTokens.remove(token);
                                           } else if (lock.principal == null || lock.principal.equals(req.getRemoteUser())) {
                                               // The shared lock might not have the same depth
      -                                        if ((parentPath != path && lock.depth > 0) || parentPath == path) {
      +                                        if (parentPath == path || lock.depth > 0) {
                                                   parentLock.sharedTokens.remove(token);
                                                   sharedLocks.remove(token);
                                                   unlocked = true;
      @@ -1822,8 +1845,12 @@
            * @return true if the resource specified is under a special path
            */
           private boolean isSpecialPath(final String path) {
      -        return !allowSpecialPaths && (path.toUpperCase(Locale.ENGLISH).startsWith("/WEB-INF") ||
      -                path.toUpperCase(Locale.ENGLISH).startsWith("/META-INF"));
      +        if (!allowSpecialPaths) {
      +            String upperCasePath = path.toUpperCase(Locale.ENGLISH);
      +            return upperCasePath.startsWith("/WEB-INF/") || upperCasePath.startsWith("/META-INF/") ||
      +                    upperCasePath.equals("/WEB-INF") || upperCasePath.equals("/META-INF");
      +        }
      +        return false;
           }
       
       
      @@ -1945,19 +1972,17 @@
                       if (parentLock.hasExpired()) {
                           resourceLocks.remove(parentPath);
                       } else {
      -                    if ((parentPath != path && parentLock.depth > 0) || parentPath == path) {
      +                    // parentPath == currentPath is a check for the first loop
      +                    if (parentPath == path || parentLock.depth > 0) {
                               if (parentLock.isExclusive()) {
      -                            if (ifHeader.contains(":" + parentLock.token + ">") &&
      -                                    (parentLock.principal == null || parentLock.principal.equals(principal))) {
      -                                return false;
      -                            }
      -                            return true;
      +                            return !ifHeader.contains(":" + parentLock.token + ">") ||
      +                                    (parentLock.principal != null && !parentLock.principal.equals(principal));
                               } else {
                                   for (String token : parentLock.sharedTokens) {
                                       LockInfo lock = sharedLocks.get(token);
                                       if (lock != null) {
                                           // The shared lock might not have the same depth
      -                                    if ((parentPath != path && lock.depth > 0) || parentPath == path) {
      +                                    if (parentPath == path || lock.depth > 0) {
                                               if (ifHeader.contains(":" + token + ">") &&
                                                       (lock.principal == null || lock.principal.equals(principal))) {
                                                   return false;
      @@ -2058,7 +2083,12 @@
       
               // Cross-context operations aren't supported
               String reqContextPath = getPathPrefix(req);
      -        if (!destinationPath.startsWith(reqContextPath + "/")) {
      +        String expectedTargetPath = reqContextPath;
      +        // Also ensure copy (and move) operations do not escape the configured sub-path when limited to the sub-path
      +        if (serveSubpathOnly && req.getServletPath() != null) {
      +            expectedTargetPath = expectedTargetPath + req.getServletPath();
      +        }
      +        if (!destinationPath.startsWith(expectedTargetPath + "/")) {
                   resp.sendError(WebdavStatus.SC_FORBIDDEN);
                   return false;
               }
      @@ -2097,11 +2127,7 @@
               boolean overwrite = true;
               String overwriteHeader = req.getHeader("Overwrite");
               if (overwriteHeader != null) {
      -            if (overwriteHeader.equalsIgnoreCase("T")) {
      -                overwrite = true;
      -            } else {
      -                overwrite = false;
      -            }
      +            overwrite = overwriteHeader.equalsIgnoreCase("T");
               }
       
               // Overwriting the destination
      @@ -2235,8 +2261,8 @@
                       } else {
                           store.copy(source, dest);
                       }
      -            } catch (IOException e) {
      -                log(sm.getString("webdavservlet.inputstreamclosefail", source), e);
      +            } catch (IOException ioe) {
      +                log(sm.getString("webdavservlet.inputstreamclosefail", source), ioe);
                   }
               } else {
                   errorList.put(source, Integer.valueOf(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
      @@ -2249,27 +2275,6 @@
           /**
            * Delete a resource.
            *
      -     * @param path Path of the resource which is to be deleted
      -     * @param req  Servlet request
      -     * @param resp Servlet response
      -     *
      -     * @return true if the delete is successful
      -     *
      -     * @throws IOException If an IO error occurs
      -     */
      -    private boolean deleteResource(String path, HttpServletRequest req, HttpServletResponse resp) throws IOException {
      -        WebResource resource = resources.getResource(path);
      -        if (!checkIfHeaders(req, resp, resource)) {
      -            resp.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
      -            return false;
      -        }
      -        return deleteResource(path, req, resp, true);
      -    }
      -
      -
      -    /**
      -     * Delete a resource.
      -     *
            * @param path      Path of the resource which is to be deleted
            * @param req       Servlet request
            * @param resp      Servlet response
      @@ -2468,7 +2473,7 @@
       
               generatedXML.writeElement("D", DEFAULT_NAMESPACE, "error", XMLWriter.OPENING);
       
      -        if (errorPath != null && errorPath.length() > 0) {
      +        if (errorPath != null && !errorPath.isEmpty()) {
                   generatedXML.writeElement("D", error, XMLWriter.OPENING);
                   generatedXML.writeElement("D", "href", XMLWriter.OPENING);
                   generatedXML.writeText(getEncodedPath(errorPath, null, req));
      @@ -2498,12 +2503,6 @@
               generatedXML.writeText(rewrittenUrl);
               generatedXML.writeElement("D", "href", XMLWriter.CLOSING);
       
      -        String resourceName = path;
      -        int lastSlash = path.lastIndexOf('/');
      -        if (lastSlash != -1) {
      -            resourceName = resourceName.substring(lastSlash + 1);
      -        }
      -
               switch (propFindType) {
       
                   case FIND_ALL_PROP:
      @@ -2681,7 +2680,8 @@
                       if (parentLock.hasExpired()) {
                           resourceLocks.remove(parentPath);
                       } else {
      -                    if ((parentPath != path && parentLock.depth > 0) || parentPath == path) {
      +                    // parentPath == currentPath is a check for the first loop
      +                    if (parentPath == path || parentLock.depth > 0) {
                               if (parentLock.isExclusive()) {
                                   parentLock.toXML(generatedXML);
                               } else {
      @@ -2691,7 +2691,7 @@
                                           if (sharedLock.hasExpired()) {
                                               sharedLocks.remove(lockToken);
                                           } else {
      -                                        if ((parentPath != path && sharedLock.depth > 0) || parentPath == path) {
      +                                        if (parentPath == path || sharedLock.depth > 0) {
                                                   sharedLock.toXML(generatedXML);
                                               }
                                           }
      @@ -2731,12 +2731,9 @@
       
       
           private static boolean propertyEquals(Node node1, Node node2) {
      -        if (node1.getLocalName().equals(node2.getLocalName()) &&
      +        return node1.getLocalName().equals(node2.getLocalName()) &&
                       ((node1.getNamespaceURI() == null && node2.getNamespaceURI() == null) ||
      -                        (node1.getNamespaceURI() != null && node1.getNamespaceURI().equals(node2.getNamespaceURI())))) {
      -            return true;
      -        }
      -        return false;
      +                        (node1.getNamespaceURI() != null && node1.getNamespaceURI().equals(node2.getNamespaceURI())));
           }
       
       
      @@ -2774,21 +2771,8 @@
       
               @Override
               public String toString() {
      -
      -            StringBuilder result = new StringBuilder("Type:");
      -            result.append(type);
      -            result.append("\nScope:");
      -            result.append(scope);
      -            result.append("\nDepth:");
      -            result.append(depth);
      -            result.append("\nOwner:");
      -            result.append(owner);
      -            result.append("\nExpiration:");
      -            result.append(FastHttpDateFormat.formatDate(expiresAt));
      -            result.append("\nToken:");
      -            result.append(token);
      -            result.append("\n");
      -            return result.toString();
      +            return "Type:" + type + "\nScope:" + scope + "\nDepth:" + String.valueOf(depth) + "\nOwner:" + owner +
      +                    "\nExpiration:" + FastHttpDateFormat.formatDate(expiresAt) + "\nToken:" + token + "\n";
               }
       
       
      @@ -2796,7 +2780,7 @@
                * @return true if the lock has expired.
                */
               public boolean hasExpired() {
      -            return sharedTokens.size() == 0 && System.currentTimeMillis() > expiresAt;
      +            return sharedTokens.isEmpty() && System.currentTimeMillis() > expiresAt;
               }
       
       
      @@ -2868,7 +2852,7 @@
            * references are filtered out for security reasons. See CVE-2007-5461.
            */
           private static class WebdavResolver implements EntityResolver {
      -        private ServletContext context;
      +        private final ServletContext context;
       
               WebdavResolver(ServletContext theContext) {
                   context = theContext;
      @@ -2997,11 +2981,7 @@
                           }
                       }
                   } else {
      -                ArrayList properties = deadProperties.get(resource);
      -                if (properties == null) {
      -                    properties = new ArrayList<>();
      -                    deadProperties.put(resource, properties);
      -                }
      +                ArrayList properties = deadProperties.computeIfAbsent(resource, k -> new ArrayList<>());
                       synchronized (properties) {
                           for (ProppatchOperation operation : operations) {
                               if (operation.getUpdateType() == PropertyUpdateType.SET) {
      @@ -3045,8 +3025,6 @@
       /**
        * Wraps the HttpServletResponse class to abstract the specific protocol used. To support other protocols we would only
        * need to modify this class and the WebDavRetCode classes.
      - *
      - * @author Marc Eaddy
        */
       class WebdavStatus {
       
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/Constants.java tomcat10-10.1.52/java/org/apache/catalina/session/Constants.java
      --- tomcat10-10.1.34/java/org/apache/catalina/session/Constants.java	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/Constants.java	2026-01-23 19:33:36.000000000 +0000
      @@ -16,8 +16,6 @@
        */
       package org.apache.catalina.session;
       
      -import java.util.Collections;
      -import java.util.HashSet;
       import java.util.Set;
       
       import org.apache.catalina.Globals;
      @@ -25,8 +23,6 @@
       
       /**
        * Manifest constants for the org.apache.catalina.session package.
      - *
      - * @author Craig R. McClanahan
        */
       
       public class Constants {
      @@ -35,12 +31,8 @@
            * Set of session attribute names used internally by Tomcat that should always be removed from the session before it
            * is persisted, replicated or equivalent.
            */
      -    public static final Set excludedAttributeNames;
      +    @SuppressWarnings("deprecation")
      +    public static final Set excludedAttributeNames =
      +            Set.of(Globals.SUBJECT_ATTR, CrawlerSessionManagerValve.class.getName());
       
      -    static {
      -        Set names = new HashSet<>();
      -        names.add(Globals.SUBJECT_ATTR);
      -        names.add(CrawlerSessionManagerValve.class.getName());
      -        excludedAttributeNames = Collections.unmodifiableSet(names);
      -    }
       }
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/DataSourceStore.java tomcat10-10.1.52/java/org/apache/catalina/session/DataSourceStore.java
      --- tomcat10-10.1.34/java/org/apache/catalina/session/DataSourceStore.java	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/DataSourceStore.java	2026-01-23 19:33:36.000000000 +0000
      @@ -46,8 +46,6 @@
       /**
        * Implementation of the {@link org.apache.catalina.Store Store} interface that stores serialized session objects in a
        * database. Sessions that are saved are still subject to being expired based on inactivity.
      - *
      - * @author Bip Thelin
        */
       public class DataSourceStore extends StoreBase {
       
      @@ -328,11 +326,9 @@
            * @param expiredOnly flag, whether only keys of expired sessions should be returned
            *
            * @return array containing the list of session IDs
      -     *
      -     * @exception IOException if an input/output error occurred
            */
      -    private String[] keys(boolean expiredOnly) throws IOException {
      -        String keys[] = null;
      +    private String[] keys(boolean expiredOnly) {
      +        String[] keys = null;
               int numberOfTries = 2;
               while (numberOfTries > 0) {
       
      @@ -365,7 +361,7 @@
                           }
                       }
                   } catch (SQLException e) {
      -                manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException", e));
      +                manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException"), e);
                       keys = new String[0];
                       // Close the connection so that it gets reopened next time
                   } finally {
      @@ -399,7 +395,7 @@
                           numberOfTries = 0;
                       }
                   } catch (SQLException e) {
      -                manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException", e));
      +                manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException"), e);
                   } finally {
                       release(_conn);
                   }
      @@ -446,7 +442,7 @@
                           numberOfTries = 0;
                       }
                   } catch (SQLException e) {
      -                contextLog.error(sm.getString("dataSourceStore.SQLException", e));
      +                contextLog.error(sm.getString("dataSourceStore.SQLException"), e);
                   } finally {
                       context.unbind(Globals.IS_SECURITY_ENABLED, oldThreadContextCL);
                       release(_conn);
      @@ -472,7 +468,7 @@
                       // Break out after the finally block
                       numberOfTries = 0;
                   } catch (SQLException e) {
      -                manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException", e));
      +                manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException"), e);
                   } finally {
                       release(_conn);
                   }
      @@ -520,7 +516,7 @@
                       // Break out after the finally block
                       numberOfTries = 0;
                   } catch (SQLException e) {
      -                manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException", e));
      +                manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException"), e);
                   } finally {
                       release(_conn);
                   }
      @@ -530,7 +526,6 @@
       
           @Override
           public void save(Session session) throws IOException {
      -        ByteArrayOutputStream bos = null;
               String saveSql = "INSERT INTO " + sessionTable + " (" + sessionIdCol + ", " + sessionAppCol + ", " +
                       sessionDataCol + ", " + sessionValidCol + ", " + sessionMaxInactiveCol + ", " + sessionLastAccessedCol +
                       ") VALUES (?, ?, ?, ?, ?, ?)";
      @@ -547,7 +542,7 @@
                           // Remove session if it exists and insert again.
                           remove(session.getIdInternal(), _conn);
       
      -                    bos = new ByteArrayOutputStream();
      +                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                           try (ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(bos))) {
                               ((StandardSession) session).writeObjectData(oos);
                           }
      @@ -567,8 +562,8 @@
                               numberOfTries = 0;
                           }
                       } catch (SQLException e) {
      -                    manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException", e));
      -                } catch (IOException e) {
      +                    manager.getContext().getLogger().error(sm.getString("dataSourceStore.SQLException"), e);
      +                } catch (IOException ioe) {
                           // Ignore
                       } finally {
                           release(_conn);
      @@ -604,7 +599,7 @@
                       }
                   }
               } catch (SQLException ex) {
      -            manager.getContext().getLogger().error(sm.getString("dataSourceStore.checkConnectionSQLException", ex));
      +            manager.getContext().getLogger().error(sm.getString("dataSourceStore.checkConnectionSQLException"), ex);
               }
       
               return conn;
      @@ -649,7 +644,8 @@
                               Context envCtx = (Context) (new InitialContext()).lookup("java:comp/env");
                               this.dataSource = (DataSource) envCtx.lookup(this.dataSourceName);
                           } catch (NamingException e) {
      -                        context.getLogger().error(sm.getString("dataSourceStore.wrongDataSource", this.dataSourceName), e);
      +                        context.getLogger().error(sm.getString("dataSourceStore.wrongDataSource", this.dataSourceName),
      +                                e);
                           }
                       }
                   }
      @@ -688,7 +684,7 @@
               try {
                   dbConnection.close();
               } catch (SQLException e) {
      -            manager.getContext().getLogger().error(sm.getString("dataSourceStore.close", e));
      +            manager.getContext().getLogger().error(sm.getString("dataSourceStore.close"), e);
               }
           }
       
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/FileStore.java tomcat10-10.1.52/java/org/apache/catalina/session/FileStore.java
      --- tomcat10-10.1.34/java/org/apache/catalina/session/FileStore.java	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/FileStore.java	2026-01-23 19:33:36.000000000 +0000
      @@ -26,6 +26,7 @@
       import java.io.ObjectOutputStream;
       import java.util.ArrayList;
       import java.util.List;
      +import java.util.concurrent.locks.Lock;
       
       import jakarta.servlet.ServletContext;
       
      @@ -34,13 +35,12 @@
       import org.apache.catalina.Session;
       import org.apache.juli.logging.Log;
       import org.apache.juli.logging.LogFactory;
      +import org.apache.tomcat.util.concurrent.KeyedReentrantReadWriteLock;
       import org.apache.tomcat.util.res.StringManager;
       
       /**
        * Concrete implementation of the Store interface that utilizes a file per saved Session in a configured
        * directory. Sessions that are saved are still subject to being expired based on inactivity.
      - *
      - * @author Craig R. McClanahan
        */
       public final class FileStore extends StoreBase {
       
      @@ -62,7 +62,7 @@
            * The pathname of the directory in which Sessions are stored. This may be an absolute pathname, or a relative path
            * that is resolved against the temporary work directory for this application.
            */
      -    private String directory = ".";
      +    private volatile String directory = ".";
       
       
           /**
      @@ -70,6 +70,7 @@
            */
           private File directoryFile = null;
       
      +    private KeyedReentrantReadWriteLock sessionLocksById = new KeyedReentrantReadWriteLock();
       
           /**
            * Name to register for this Store, used for logging.
      @@ -98,7 +99,7 @@
            *
            * @param path The new directory path
            */
      -    public void setDirectory(String path) {
      +    public synchronized void setDirectory(String path) {
               String oldDirectory = this.directory;
               this.directory = path;
               this.directoryFile = null;
      @@ -127,7 +128,7 @@
               if (dir == null) {
                   return 0;
               }
      -        String files[] = dir.list();
      +        String[] files = dir.list();
       
               // Figure out which files are sessions
               int keycount = 0;
      @@ -160,7 +161,7 @@
               if (dir == null) {
                   return new String[0];
               }
      -        String files[] = dir.list();
      +        String[] files = dir.list();
       
               // Bugzilla 32130
               if (files == null || files.length < 1) {
      @@ -183,7 +184,7 @@
           public Session load(String id) throws ClassNotFoundException, IOException {
               // Open an input stream to the specified pathname, if any
               File file = file(id);
      -        if (file == null || !file.exists()) {
      +        if (file == null) {
                   return null;
               }
       
      @@ -195,19 +196,28 @@
               }
       
               ClassLoader oldThreadContextCL = context.bind(Globals.IS_SECURITY_ENABLED, null);
      -
      -        try (FileInputStream fis = new FileInputStream(file.getAbsolutePath());
      -                ObjectInputStream ois = getObjectInputStream(fis)) {
      -
      -            StandardSession session = (StandardSession) manager.createEmptySession();
      -            session.readObjectData(ois);
      -            session.setManager(manager);
      -            return session;
      -        } catch (FileNotFoundException e) {
      -            if (contextLog.isDebugEnabled()) {
      -                contextLog.debug(sm.getString("fileStore.noFile", id, file.getAbsolutePath()));
      +        try {
      +            Lock readLock = sessionLocksById.getLock(id).readLock();
      +            readLock.lock();
      +            try {
      +                if (!file.exists()) {
      +                    return null;
      +                }
      +                try (FileInputStream fis = new FileInputStream(file.getAbsolutePath());
      +                        ObjectInputStream ois = getObjectInputStream(fis)) {
      +                    StandardSession session = (StandardSession) manager.createEmptySession();
      +                    session.readObjectData(ois);
      +                    session.setManager(manager);
      +                    return session;
      +                } catch (FileNotFoundException e) {
      +                    if (contextLog.isDebugEnabled()) {
      +                        contextLog.debug(sm.getString("fileStore.noFile", id, file.getAbsolutePath()), e);
      +                    }
      +                    return null;
      +                }
      +            } finally {
      +                readLock.unlock();
                   }
      -            return null;
               } finally {
                   context.unbind(Globals.IS_SECURITY_ENABLED, oldThreadContextCL);
               }
      @@ -225,8 +235,14 @@
                           .trace(sm.getString(getStoreName() + ".removing", id, file.getAbsolutePath()));
               }
       
      -        if (file.exists() && !file.delete()) {
      -            throw new IOException(sm.getString("fileStore.deleteSessionFailed", file));
      +        Lock writeLock = sessionLocksById.getLock(id).writeLock();
      +        writeLock.lock();
      +        try {
      +            if (file.exists() && !file.delete()) {
      +                throw new IOException(sm.getString("fileStore.deleteSessionFailed", file));
      +            }
      +        } finally {
      +            writeLock.unlock();
               }
           }
       
      @@ -243,9 +259,15 @@
                           .trace(sm.getString(getStoreName() + ".saving", session.getIdInternal(), file.getAbsolutePath()));
               }
       
      -        try (FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());
      -                ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(fos))) {
      -            ((StandardSession) session).writeObjectData(oos);
      +        Lock writeLock = sessionLocksById.getLock(session.getIdInternal()).writeLock();
      +        writeLock.lock();
      +        try {
      +            try (FileOutputStream fos = new FileOutputStream(file.getAbsolutePath());
      +                    ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(fos))) {
      +                ((StandardSession) session).writeObjectData(oos);
      +            }
      +        } finally {
      +            writeLock.unlock();
               }
           }
       
      @@ -256,12 +278,12 @@
            * Return a File object representing the pathname to our session persistence directory, if any. The directory will
            * be created if it does not already exist.
            */
      -    private File directory() throws IOException {
      +    private synchronized File directory() throws IOException {
      +        // Synchronised to avoid concurrent attempts to create the directory.
               if (this.directory == null) {
                   return null;
               }
               if (this.directoryFile != null) {
      -            // NOTE: Race condition is harmless, so do not synchronize
                   return this.directoryFile;
               }
               File file = new File(this.directory);
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings.properties
      --- tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings.properties	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings.properties	2026-01-23 19:33:36.000000000 +0000
      @@ -16,11 +16,11 @@
       # Do not edit this file directly.
       # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations
       
      -dataSourceStore.SQLException=SQL Error [{0}]
      +dataSourceStore.SQLException=SQL Error
       dataSourceStore.checkConnectionDBClosed=The database connection is null or was found to be closed. Trying to re-open it.
       dataSourceStore.checkConnectionDBReOpenFail=The re-open on the database failed. The database could be down.
      -dataSourceStore.checkConnectionSQLException=A SQL exception occurred [{0}]
      -dataSourceStore.close=Exception closing database connection [{0}]
      +dataSourceStore.checkConnectionSQLException=A SQL exception occurred checking the connection
      +dataSourceStore.close=Exception closing database connection
       dataSourceStore.commitSQLException=SQLException committing connection before closing
       dataSourceStore.loading=Loading Session [{0}] from database [{1}]
       dataSourceStore.missingDataSource=No data source available
      @@ -54,7 +54,7 @@
       persistentManager.loading=Loading [{0}] persisted sessions
       persistentManager.noStore=No Store configured, persistence disabled
       persistentManager.removeError=Error removing session [{0}] from the store
      -persistentManager.serializeError=Error serializing Session [{0}]: [{1}]
      +persistentManager.serializeError=Error serializing Session [{0}]
       persistentManager.storeClearError=Error clearning all sessions from the store
       persistentManager.storeKeysException=Unable to determine the list of session IDs for sessions in the session store, assuming that the store is empty
       persistentManager.storeLoadError=Error swapping in sessions from the store
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_es.properties tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_es.properties
      --- tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_es.properties	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_es.properties	2026-01-23 19:33:36.000000000 +0000
      @@ -19,8 +19,8 @@
       dataSourceStore.SQLException=Error SQL [{0}]
       dataSourceStore.checkConnectionDBClosed=La conexióna a base de datos es nula o está cerrada. Intentando reabrirla.
       dataSourceStore.checkConnectionDBReOpenFail=Falló la reapertura de la base de datos. Puede que la base de datos esté caída.
      -dataSourceStore.checkConnectionSQLException=Ha tenido lugar una excepción SQL [{0}]
      -dataSourceStore.close=Excepción cerrando conexión a base de datos [{0}]
      +dataSourceStore.checkConnectionSQLException=Ha tenido lugar una excepción SQL
      +dataSourceStore.close=Excepción cerrando conexión a base de datos
       dataSourceStore.loading=Cargando Sesión [{0}] desde base de datos [{1}]
       dataSourceStore.missingDataSourceName=No se proporcionó un nombre JNDI válido
       dataSourceStore.removing=Quitando Sesión [{0}] en base de datos [{1}]
      @@ -38,7 +38,7 @@
       persistentManager.backupMaxIdle=Respaldando sesión [{0}] a Almacén, ociosa durante [{1}] segundos
       persistentManager.deserializeError=Error des-serializando Sesión [{0}]: [{1}]
       persistentManager.loading=Cargando [{0}] sesiones persistidas
      -persistentManager.serializeError=Error serializando Sesión [{0}]: [{1}]
      +persistentManager.serializeError=Error serializando Sesión [{0}]
       persistentManager.storeKeysException=Imposible determinar la lista de IDs de sesiones en la tienda de sesiones, asumiendo que la tienda esta vacia
       persistentManager.storeSizeException=No se puede determinar el numero de sesiones en el almacenamiento de sesiones, asumiendo que el almacenamiento esta vacío
       persistentManager.swapIn=Intercambiando sesión [{0}] a dentro desde Almacén
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_fr.properties
      --- tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_fr.properties	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_fr.properties	2026-01-23 19:33:36.000000000 +0000
      @@ -19,8 +19,8 @@
       dataSourceStore.SQLException=Erreur SQL [{0}]
       dataSourceStore.checkConnectionDBClosed=La connexion à la base de données est nulle ou a été trouvée fermée. Tentative de réouverture.
       dataSourceStore.checkConnectionDBReOpenFail=La tentative de réouverture de la base de données a échoué. La base de données est peut-être arrêtée.
      -dataSourceStore.checkConnectionSQLException=Une exception SQL s''est produite [{0}]
      -dataSourceStore.close=Exception lors de la fermeture de la connection vers la base de donnée [{0}]
      +dataSourceStore.checkConnectionSQLException=Une exception SQL s'est produite
      +dataSourceStore.close=Exception lors de la fermeture de la connection vers la base de donnée
       dataSourceStore.commitSQLException=Une SQLException a été retournée lors du commit de la connection avant sa fermeture
       dataSourceStore.loading=Chargement de la Session [{0}] depuis la base de données [{1}]
       dataSourceStore.missingDataSource=Aucune source de données n'est disponible
      @@ -54,7 +54,7 @@
       persistentManager.loading=Chargement de [{0}] sessions persistantes
       persistentManager.noStore=Aucun stockage (Store) n'a été configuré, la persistence est désactivée
       persistentManager.removeError=Erreur en enlevant la session [{0}] du stockage
      -persistentManager.serializeError=Erreur lors de la sérialisation de la session [{0}] : [{1}]
      +persistentManager.serializeError=Erreur lors de la sérialisation de la session [{0}]
       persistentManager.storeClearError=Erreur en supprimant toutes les sessions du stockage
       persistentManager.storeKeysException=Incapacité de déterminer la liste des ID de session, pour les sessions dans le magasin de sessions.  Supposant le magasin vide.
       persistentManager.storeLoadError=Erreur en déplaçant les sessions à partir du stockage
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_ja.properties
      --- tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_ja.properties	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_ja.properties	2026-01-23 19:33:36.000000000 +0000
      @@ -19,8 +19,8 @@
       dataSourceStore.SQLException=SQLエラー [{0}]
       dataSourceStore.checkConnectionDBClosed=データベース接続がnullであるか、クローズされているのが見つかりました。再オープンしてください。
       dataSourceStore.checkConnectionDBReOpenFail=データベースの再オープンが失敗しました。データベースがダウンしているかもしれません。
      -dataSourceStore.checkConnectionSQLException=SQL例外が発生しました [{0}]
      -dataSourceStore.close=データベース接続 [{0}] をクローズ中の例外です
      +dataSourceStore.checkConnectionSQLException=SQL例外が発生しました
      +dataSourceStore.close=データベース接続 をクローズ中の例外です
       dataSourceStore.commitSQLException=クローズ前のデータベース接続のコミット中にSQL例外が発生しました
       dataSourceStore.loading=セッション [{0}] をデータベース [{1}] からロードします
       dataSourceStore.missingDataSource=利用可能なデータソースがありません
      @@ -54,7 +54,7 @@
       persistentManager.loading=[{0}] の永続化セッションをロードします
       persistentManager.noStore=ストアが構成されておらず、永続性が無効になっています
       persistentManager.removeError=ストアからセッション[{0}]削除中のエラー
      -persistentManager.serializeError=セッション [{0}] をシリアライズ中のエラーです: [{1}]
      +persistentManager.serializeError=セッション [{0}] をシリアライズ中のエラーです
       persistentManager.storeClearError=ストア上の全セッション消去中のエラー
       persistentManager.storeKeysException=セッションストアからセッションIDのリストを取得できませんでした。セッションストアが空の可能性があります
       persistentManager.storeLoadError=ストアからのセッションスワップイン中のエラー
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_ko.properties tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_ko.properties
      --- tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_ko.properties	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_ko.properties	2026-01-23 19:33:36.000000000 +0000
      @@ -19,8 +19,8 @@
       dataSourceStore.SQLException=SQL 오류 [{0}]
       dataSourceStore.checkConnectionDBClosed=데이터베이스 연결이 널이거나 닫힌 상태입니다. 다시 열려고 시도합니다.
       dataSourceStore.checkConnectionDBReOpenFail=데이터베이스에 대해 다시 연결을 맺지 못했습니다. 데이터베이스가 다운되었을 수 있습니다.
      -dataSourceStore.checkConnectionSQLException=SQL 예외 발생 [{0}]
      -dataSourceStore.close=데이터베이스 연결 [{0}]을(를) 닫는 동안 예외 발생
      +dataSourceStore.checkConnectionSQLException=SQL 예외 발생
      +dataSourceStore.close=데이터베이스 연결을(를) 닫는 동안 예외 발생
       dataSourceStore.commitSQLException=데이터베이스 연결을 닫기 전, 커밋을 시도하는 중 SQLException 발생
       dataSourceStore.loading=데이터베이스 [{1}](으)로부터 세션 [{0}]을(를) 로드합니다.
       dataSourceStore.missingDataSource=DataSource를 사용할 수 없습니다.
      @@ -51,7 +51,7 @@
       persistentManager.isLoadedError=세션 [{0}]이(가) 메모리에 로드되었는지 점검 중 오류 발생
       persistentManager.loading=[{0}]개의 저장된 세션들을 로드합니다.
       persistentManager.removeError=세션 [{0}]을(를) 저장소로부터 제거하는 중 오류 발생
      -persistentManager.serializeError=세션을 직렬화하는 중 오류 발생 [{0}]: [{1}]
      +persistentManager.serializeError=세션을 직렬화하는 중 오류 발생 [{0}]
       persistentManager.storeClearError=저장소로부터 모든 세션들을 해제하는 중 오류 발생
       persistentManager.storeKeysException=세션 저장소에 있는 세션들의 ID 목록을 결정할 수 없습니다. 아마도 세션 저장소가 비어 있는 것 같습니다.
       persistentManager.storeLoadError=저장소로부터 세션들을 메모리로 로드하는 중 오류 발생
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_zh_CN.properties
      --- tomcat10-10.1.34/java/org/apache/catalina/session/LocalStrings_zh_CN.properties	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/LocalStrings_zh_CN.properties	2026-01-23 19:33:36.000000000 +0000
      @@ -19,8 +19,8 @@
       dataSourceStore.SQLException=SQL错误[{0}]
       dataSourceStore.checkConnectionDBClosed=数据库连接为空或已关闭。正在尝试重新连接。
       dataSourceStore.checkConnectionDBReOpenFail=重新打开数据库失败,数据库可能已经宕机。
      -dataSourceStore.checkConnectionSQLException=发生 SQL 异常 [{0}]
      -dataSourceStore.close=关闭数据库连接[{0}]时发生异常
      +dataSourceStore.checkConnectionSQLException=发生 SQL 异常
      +dataSourceStore.close=关闭数据库连接时发生异常
       dataSourceStore.commitSQLException=关闭前提交连接的SQLException
       dataSourceStore.loading=正在从数据库[{1}]加载会话[{0}]
       dataSourceStore.missingDataSource=没有可用的数据源
      @@ -51,7 +51,7 @@
       persistentManager.isLoadedError=检查内存中是否加载了会话[{0}]时出错
       persistentManager.loading=正在加载[{0}]持久化会话
       persistentManager.removeError=从存储中删除会话[{0}]时出错
      -persistentManager.serializeError=错误的序列化会话 [{0}]:[{1}]
      +persistentManager.serializeError=错误的序列化会话 [{0}]
       persistentManager.storeClearError=清除存储区中的所有会话时出错
       persistentManager.storeKeysException=不能从 session存储中获取session ID 的列表,假设存储为空
       persistentManager.storeLoadError=从存储区交换会话时出错
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/ManagerBase.java tomcat10-10.1.52/java/org/apache/catalina/session/ManagerBase.java
      --- tomcat10-10.1.34/java/org/apache/catalina/session/ManagerBase.java	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/ManagerBase.java	2026-01-23 19:33:36.000000000 +0000
      @@ -55,8 +55,6 @@
       /**
        * Minimal implementation of the Manager interface that supports no session persistence or distributable
        * capabilities. This class may be subclassed to create more sophisticated Manager implementations.
      - *
      - * @author Craig R. McClanahan
        */
       public abstract class ManagerBase extends LifecycleMBeanBase implements Manager {
       
      @@ -296,7 +294,7 @@
            * @throws PatternSyntaxException If the expression is not valid
            */
           public void setSessionAttributeNameFilter(String sessionAttributeNameFilter) throws PatternSyntaxException {
      -        if (sessionAttributeNameFilter == null || sessionAttributeNameFilter.length() == 0) {
      +        if (sessionAttributeNameFilter == null || sessionAttributeNameFilter.isEmpty()) {
                   sessionAttributeNamePattern = null;
               } else {
                   sessionAttributeNamePattern = Pattern.compile(sessionAttributeNameFilter);
      @@ -354,7 +352,7 @@
            */
           public void setSessionAttributeValueClassNameFilter(String sessionAttributeValueClassNameFilter)
                   throws PatternSyntaxException {
      -        if (sessionAttributeValueClassNameFilter == null || sessionAttributeValueClassNameFilter.length() == 0) {
      +        if (sessionAttributeValueClassNameFilter == null || sessionAttributeValueClassNameFilter.isEmpty()) {
                   sessionAttributeValueClassNamePattern = null;
               } else {
                   sessionAttributeValueClassNamePattern = Pattern.compile(sessionAttributeValueClassNameFilter);
      @@ -594,7 +592,7 @@
           public void processExpires() {
       
               long timeNow = System.currentTimeMillis();
      -        Session sessions[] = findSessions();
      +        Session[] sessions = findSessions();
               int expireHere = 0;
       
               if (log.isTraceEnabled()) {
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/PersistentManager.java tomcat10-10.1.52/java/org/apache/catalina/session/PersistentManager.java
      --- tomcat10-10.1.34/java/org/apache/catalina/session/PersistentManager.java	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/PersistentManager.java	2026-01-23 19:33:36.000000000 +0000
      @@ -26,8 +26,6 @@
        * 
        * If used with a load-balancer, the load-balancer must be configured to use sticky sessions for this manager to operate
        * correctly.
      - *
      - * @author Kief Morris (kief@kief.com)
        */
       public final class PersistentManager extends PersistentManagerBase {
       
      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/PersistentManagerBase.java tomcat10-10.1.52/java/org/apache/catalina/session/PersistentManagerBase.java
      --- tomcat10-10.1.34/java/org/apache/catalina/session/PersistentManagerBase.java	2024-12-05 16:07:56.000000000 +0000
      +++ tomcat10-10.1.52/java/org/apache/catalina/session/PersistentManagerBase.java	2026-01-23 19:33:36.000000000 +0000
      @@ -42,8 +42,6 @@
        * 

      * IMPLEMENTATION NOTE: Correct behavior of session storing and reloading depends upon external calls to the * {@link Lifecycle#start()} and {@link Lifecycle#stop()} methods of this class at the correct times. - * - * @author Craig R. McClanahan */ public abstract class PersistentManagerBase extends ManagerBase implements StoreManager { @@ -291,8 +289,8 @@ if (super.findSession(id) != null) { return true; } - } catch (IOException e) { - log.error(sm.getString("persistentManager.isLoadedError", id), e); + } catch (IOException ioe) { + log.error(sm.getString("persistentManager.isLoadedError", id), ioe); } return false; } @@ -377,8 +375,8 @@ } else { store.clear(); } - } catch (IOException e) { - log.error(sm.getString("persistentManager.storeClearError"), e); + } catch (IOException ioe) { + log.error(sm.getString("persistentManager.storeClearError"), ioe); } } @@ -393,7 +391,7 @@ public void processExpires() { long timeNow = System.currentTimeMillis(); - Session sessions[] = findSessions(); + Session[] sessions = findSessions(); int expireHere = 0; if (log.isTraceEnabled()) { log.trace("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length); @@ -490,7 +488,7 @@ return; } - String[] ids = null; + String[] ids; try { if (SecurityUtil.isPackageProtectionEnabled()) { try { @@ -502,8 +500,8 @@ } else { ids = store.keys(); } - } catch (IOException e) { - log.error(sm.getString("persistentManager.storeLoadKeysError"), e); + } catch (IOException ioe) { + log.error(sm.getString("persistentManager.storeLoadKeysError"), ioe); return; } @@ -519,8 +517,8 @@ for (String id : ids) { try { swapIn(id); - } catch (IOException e) { - log.error(sm.getString("persistentManager.storeLoadError"), e); + } catch (IOException ioe) { + log.error(sm.getString("persistentManager.storeLoadError"), ioe); } } @@ -559,8 +557,8 @@ } else { store.remove(id); } - } catch (IOException e) { - log.error(sm.getString("persistentManager.removeError"), e); + } catch (IOException ioe) { + log.error(sm.getString("persistentManager.removeError"), ioe); } } @@ -578,7 +576,7 @@ return; } - Session sessions[] = findSessions(); + Session[] sessions = findSessions(); int n = sessions.length; if (n == 0) { return; @@ -591,7 +589,7 @@ for (Session session : sessions) { try { swapOut(session); - } catch (IOException e) { + } catch (IOException ignore) { // This is logged in writeSession() } } @@ -607,7 +605,7 @@ // Store session count result += getStore().getSize(); } catch (IOException ioe) { - log.warn(sm.getString("persistentManager.storeSizeException")); + log.warn(sm.getString("persistentManager.storeSizeException"), ioe); } return result; } @@ -620,8 +618,8 @@ try { // Store session ID list sessionIds.addAll(Arrays.asList(getStore().keys())); - } catch (IOException e) { - log.warn(sm.getString("persistentManager.storeKeysException")); + } catch (IOException ioe) { + log.warn(sm.getString("persistentManager.storeKeysException"), ioe); } return sessionIds; } @@ -646,7 +644,7 @@ return null; } - Object swapInLock = null; + Object swapInLock; /* * The purpose of this sync and these locks is to make sure that a session is only loaded once. It doesn't @@ -658,7 +656,7 @@ swapInLock = sessionSwapInLocks.computeIfAbsent(id, k -> new Object()); } - Session session = null; + Session session; synchronized (swapInLock) { // First check to see if another thread has loaded the session into @@ -796,9 +794,9 @@ } else { store.save(session); } - } catch (IOException e) { - log.error(sm.getString("persistentManager.serializeError", session.getIdInternal(), e)); - throw e; + } catch (IOException ioe) { + log.error(sm.getString("persistentManager.serializeError", session.getIdInternal()), ioe); + throw ioe; } } @@ -846,7 +844,7 @@ unload(); } else { // Expire all active sessions - Session sessions[] = findSessions(); + Session[] sessions = findSessions(); for (Session value : sessions) { StandardSession session = (StandardSession) value; if (!session.isValid()) { @@ -877,7 +875,7 @@ return; } - Session sessions[] = findSessions(); + Session[] sessions = findSessions(); // Swap out all sessions idle longer than maxIdleSwap if (maxIdleSwap >= 0) { @@ -899,7 +897,7 @@ } try { swapOut(session); - } catch (IOException e) { + } catch (IOException ignore) { // This is logged in writeSession() } } @@ -919,7 +917,7 @@ return; } - Session sessions[] = findSessions(); + Session[] sessions = findSessions(); // FIXME: Smarter algorithm (LRU) int limit = (int) (getMaxActiveSessions() * 0.9); @@ -949,7 +947,7 @@ } try { swapOut(session); - } catch (IOException e) { + } catch (IOException ignore) { // This is logged in writeSession() } toswap--; @@ -969,7 +967,7 @@ return; } - Session sessions[] = findSessions(); + Session[] sessions = findSessions(); // Back up all sessions idle longer than maxIdleBackup if (maxIdleBackup >= 0) { @@ -994,7 +992,7 @@ try { writeSession(session); - } catch (IOException e) { + } catch (IOException ignore) { // This is logged in writeSession() } session.setNote(PERSISTED_LAST_ACCESSED_TIME, Long.valueOf(lastAccessedTime)); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/StandardManager.java tomcat10-10.1.52/java/org/apache/catalina/session/StandardManager.java --- tomcat10-10.1.34/java/org/apache/catalina/session/StandardManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/session/StandardManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,12 +47,10 @@ /** * Standard implementation of the Manager interface that provides simple session persistence across restarts of * this component (such as when the entire server is shut down and restarted, or when a particular web application is - * reloaded. + * reloaded). *

      * IMPLEMENTATION NOTE: Correct behavior of session storing and reloading depends upon external calls to the * start() and stop() methods of this class at the correct times. - * - * @author Craig R. McClanahan */ public class StandardManager extends ManagerBase { @@ -181,9 +179,9 @@ if (log.isTraceEnabled()) { log.trace(sm.getString("standardManager.loading", pathname)); } - Loader loader = null; + Loader loader; ClassLoader classLoader = null; - Log logger = null; + Log logger; try (FileInputStream fis = new FileInputStream(file.getAbsolutePath()); BufferedInputStream bis = new BufferedInputStream(fis)) { Context c = getContext(); @@ -229,7 +227,7 @@ } } catch (FileNotFoundException e) { if (log.isDebugEnabled()) { - log.debug(sm.getString("standardManager.noFile", file.getAbsolutePath())); + log.debug(sm.getString("standardManager.noFile", file.getAbsolutePath()), e); } return; } @@ -377,7 +375,7 @@ } // Expire all active sessions - Session sessions[] = findSessions(); + Session[] sessions = findSessions(); for (Session session : sessions) { try { if (session.isValid()) { @@ -405,7 +403,7 @@ * @return the file */ protected File file() { - if (pathname == null || pathname.length() == 0) { + if (pathname == null || pathname.isEmpty()) { return null; } File file = new File(pathname); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/StandardSession.java tomcat10-10.1.52/java/org/apache/catalina/session/StandardSession.java --- tomcat10-10.1.34/java/org/apache/catalina/session/StandardSession.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/session/StandardSession.java 2026-01-23 19:33:36.000000000 +0000 @@ -65,17 +65,8 @@ * Standard implementation of the Session interface. This object is serializable, so that it can be stored in * persistent storage or transferred to a different JVM for distributable session support. *

      - * IMPLEMENTATION NOTE: An instance of this class represents both the internal (Session) and application level - * (HttpSession) view of the session. However, because the class itself is not declared public, Java logic outside of - * the org.apache.catalina.session package cannot cast an HttpSession view of this instance back to a - * Session view. - *

      * IMPLEMENTATION NOTE: If you add fields to this class, you must make sure that you carry them over in the * read/writeObject methods so that this class is properly serialized. - * - * @author Craig R. McClanahan - * @author Sean Legassick - * @author Jon S. Stevens */ public class StandardSession implements HttpSession, Session, Serializable { @@ -114,7 +105,7 @@ /** * Type array. */ - protected static final String EMPTY_ARRAY[] = new String[0]; + protected static final String[] EMPTY_ARRAY = new String[0]; /** @@ -171,7 +162,7 @@ /** * The Manager with which this Session is associated. */ - protected transient Manager manager = null; + protected transient Manager manager; /** @@ -318,7 +309,7 @@ // Notify interested application event listeners Context context = manager.getContext(); - Object listeners[] = context.getApplicationLifecycleListeners(); + Object[] listeners = context.getApplicationLifecycleListeners(); if (listeners != null && listeners.length > 0) { HttpSessionEvent event = new HttpSessionEvent(getSession()); for (Object o : listeners) { @@ -347,6 +338,9 @@ @Override public void tellChangedSessionId(String newId, String oldId, boolean notifySessionListeners, boolean notifyContainerListeners) { + // Notify interested session event listeners + fireSessionEvent(SESSION_CHANGED_ID_EVENT, oldId); + Context context = manager.getContext(); // notify ContainerListeners if (notifyContainerListeners) { @@ -355,7 +349,7 @@ // notify HttpSessionIdListener if (notifySessionListeners) { - Object listeners[] = context.getApplicationEventListeners(); + Object[] listeners = context.getApplicationEventListeners(); if (listeners != null && listeners.length > 0) { HttpSessionEvent event = new HttpSessionEvent(getSession()); @@ -671,7 +665,7 @@ expiring = false; // Unbind any objects associated with this session - String keys[] = keys(); + String[] keys = keys(); ClassLoader oldContextClassLoader = null; try { oldContextClassLoader = context.bind(Globals.IS_SECURITY_ENABLED, null); @@ -696,7 +690,7 @@ // Notify ActivationListeners HttpSessionEvent event = null; - String keys[] = keys(); + String[] keys = keys(); for (String key : keys) { Object attribute = attributes.get(key); if (attribute instanceof HttpSessionActivationListener) { @@ -730,7 +724,7 @@ // Notify ActivationListeners HttpSessionEvent event = null; - String keys[] = keys(); + String[] keys = keys(); for (String key : keys) { Object attribute = attributes.get(key); if (attribute instanceof HttpSessionActivationListener) { @@ -806,11 +800,7 @@ @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("StandardSession["); - sb.append(id); - sb.append(']'); - return sb.toString(); + return "StandardSession[" + id + "]"; } @@ -1045,7 +1035,7 @@ } // Notify interested application event listeners - Object listeners[] = context.getApplicationEventListeners(); + Object[] listeners = context.getApplicationEventListeners(); if (listeners == null) { return; } @@ -1103,7 +1093,7 @@ /** * {@inheritDoc} *

      - * This implementation simply checks the value for serializability. Sub-classes might use other distribution + * This implementation simply checks the value for serializability. Subclasses might use other distribution * technology not based on serialization and can override this check. */ @Override @@ -1285,13 +1275,13 @@ stream.writeObject(savedRequest); // Accumulate the names of serializable and non-serializable attributes - String keys[] = keys(); + String[] keys = keys(); List saveNames = new ArrayList<>(); List saveValues = new ArrayList<>(); for (String key : keys) { Object value = attributes.get(key); if (value == null) { - continue; + // Continue } else if (isAttributeDistributable(key, value) && !exclude(key, value)) { saveNames.add(key); saveValues.add(value); @@ -1373,11 +1363,11 @@ * @param data Event data */ public void fireSessionEvent(String type, Object data) { - if (listeners.size() < 1) { + if (listeners.isEmpty()) { return; } SessionEvent event = new SessionEvent(this, type, data); - SessionListener list[] = new SessionListener[0]; + SessionListener[] list = new SessionListener[0]; synchronized (listeners) { list = listeners.toArray(list); } @@ -1434,7 +1424,7 @@ // Notify interested application event listeners Context context = manager.getContext(); - Object listeners[] = context.getApplicationEventListeners(); + Object[] listeners = context.getApplicationEventListeners(); if (listeners == null) { return; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/StandardSessionFacade.java tomcat10-10.1.52/java/org/apache/catalina/session/StandardSessionFacade.java --- tomcat10-10.1.34/java/org/apache/catalina/session/StandardSessionFacade.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/session/StandardSessionFacade.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ /** * Facade for the StandardSession object. - * - * @author Remy Maucherat */ public class StandardSessionFacade implements HttpSession { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/session/StoreBase.java tomcat10-10.1.52/java/org/apache/catalina/session/StoreBase.java --- tomcat10-10.1.34/java/org/apache/catalina/session/StoreBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/session/StoreBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,8 +35,6 @@ /** * Abstract implementation of the {@link Store} interface to support most of the functionality required by a * {@link Store}. - * - * @author Bip Thelin */ public abstract class StoreBase extends LifecycleBase implements Store { @@ -101,7 +99,7 @@ /** * Get only those keys of sessions, that are saved in the Store and are to be expired. * - * @return list of session keys, that are to be expired + * @return array of session keys, that are to be expired * * @throws IOException if an input-/output error occurred */ @@ -114,7 +112,7 @@ * so expire the Session and remove it from the Store. */ public void processExpires() { - String[] keys = null; + String[] keys; if (!getState().isAvailable()) { return; @@ -122,8 +120,8 @@ try { keys = expiredKeys(); - } catch (IOException e) { - manager.getContext().getLogger().error(sm.getString("store.keysFail"), e); + } catch (IOException ioe) { + manager.getContext().getLogger().error(sm.getString("store.keysFail"), ioe); return; } if (manager.getContext().getLogger().isTraceEnabled()) { @@ -171,8 +169,8 @@ manager.getContext().getLogger().error(sm.getString("store.expireFail", key), e); try { remove(key); - } catch (IOException e2) { - manager.getContext().getLogger().error(sm.getString("store.removeFail", key), e2); + } catch (IOException ioe) { + manager.getContext().getLogger().error(sm.getString("store.removeFail", key), ioe); } } } @@ -182,10 +180,10 @@ // --------------------------------------------------------- Protected Methods /** - * Create the object input stream to use to read a session from the store. Sub-classes must have set the + * Create the object input stream to use to read a session from the store. Subclasses must have set the * thread context class loader before calling this method. * - * @param is The input stream provided by the sub-class that will provide the data for a session + * @param is The input stream provided by the subclass that will provide the data for a session * * @return An appropriately configured ObjectInputStream from which the session can be read. * diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/ByteArrayServletOutputStream.java tomcat10-10.1.52/java/org/apache/catalina/ssi/ByteArrayServletOutputStream.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/ByteArrayServletOutputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/ByteArrayServletOutputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Class that extends ServletOutputStream, used as a wrapper from within SsiInclude * - * @author Bip Thelin - * * @see ServletOutputStream and ByteArrayOutputStream */ public class ByteArrayServletOutputStream extends ServletOutputStream { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/ExpressionParseTree.java tomcat10-10.1.52/java/org/apache/catalina/ssi/ExpressionParseTree.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/ExpressionParseTree.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/ExpressionParseTree.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,6 @@ /** * Represents a parsed expression. - * - * @author Paul Speed */ public class ExpressionParseTree { private static final StringManager sm = StringManager.getManager(ExpressionParseTree.class); @@ -58,7 +56,7 @@ * Creates a new parse tree for the specified expression. * * @param expr The expression string - * @param ssiMediator Used to evaluated the expressions + * @param ssiMediator Used to evaluate the expressions * * @throws ParseException a parsing error occurred */ @@ -93,14 +91,14 @@ private void pushOpp(OppNode node) { // If node is null then it's just a group marker if (node == null) { - oppStack.add(0, node); + oppStack.addFirst(null); return; } while (true) { - if (oppStack.size() == 0) { + if (oppStack.isEmpty()) { break; } - OppNode top = oppStack.get(0); + OppNode top = oppStack.getFirst(); // If the top is a spacer then don't pop // anything if (top == null) { @@ -112,14 +110,14 @@ break; } // Remove the top node - oppStack.remove(0); + oppStack.removeFirst(); // Let it fill its branches top.popValues(nodeStack); // Stick it on the resolved node stack - nodeStack.add(0, top); + nodeStack.addFirst(top); } // Add the new node to the opp stack - oppStack.add(0, node); + oppStack.addFirst(node); } @@ -127,12 +125,12 @@ * Resolves all pending opp nodes on the stack until the next group marker is reached. */ private void resolveGroup() { - OppNode top = null; - while ((top = oppStack.remove(0)) != null) { + OppNode top; + while ((top = oppStack.removeFirst()) != null) { // Let it fill its branches top.popValues(nodeStack); // Stick it on the resolved node stack - nodeStack.add(0, top); + nodeStack.addFirst(top); } } @@ -159,7 +157,7 @@ case ExpressionTokenizer.TOKEN_STRING: if (currStringNode == null) { currStringNode = new StringNode(et.getTokenValue()); - nodeStack.add(0, currStringNode); + nodeStack.addFirst(currStringNode); } else { // Add to the existing currStringNode.value.append(' '); @@ -180,9 +178,9 @@ break; case ExpressionTokenizer.TOKEN_NOT_EQ: pushOpp(new NotNode()); - // Sneak the regular node in. The NOT will + // Sneak the regular node in. They will NOT // be resolved when the next opp comes along. - oppStack.add(0, new EqualNode()); + oppStack.addFirst(new EqualNode()); break; case ExpressionTokenizer.TOKEN_RBRACE: // Closeout the current group @@ -196,13 +194,13 @@ pushOpp(new NotNode()); // Similar strategy to NOT_EQ above, except this // is NOT less than - oppStack.add(0, new LessThanNode()); + oppStack.addFirst(new LessThanNode()); break; case ExpressionTokenizer.TOKEN_LE: pushOpp(new NotNode()); // Similar strategy to NOT_EQ above, except this // is NOT greater than - oppStack.add(0, new GreaterThanNode()); + oppStack.addFirst(new GreaterThanNode()); break; case ExpressionTokenizer.TOKEN_GT: pushOpp(new GreaterThanNode()); @@ -216,16 +214,16 @@ } // Finish off the rest of the opps resolveGroup(); - if (nodeStack.size() == 0) { + if (nodeStack.isEmpty()) { throw new ParseException(sm.getString("expressionParseTree.noNodes"), et.getIndex()); } if (nodeStack.size() > 1) { throw new ParseException(sm.getString("expressionParseTree.extraNodes"), et.getIndex()); } - if (oppStack.size() != 0) { + if (!oppStack.isEmpty()) { throw new ParseException(sm.getString("expressionParseTree.unusedOpCodes"), et.getIndex()); } - root = nodeStack.get(0); + root = nodeStack.getFirst(); } /** @@ -269,7 +267,7 @@ */ @Override public boolean evaluate() { - return !(getValue().length() == 0); + return !(getValue().isEmpty()); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/ExpressionTokenizer.java tomcat10-10.1.52/java/org/apache/catalina/ssi/ExpressionTokenizer.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/ExpressionTokenizer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/ExpressionTokenizer.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Parses an expression string to return the individual tokens. This is patterned similar to the StreamTokenizer in the * JDK but customized for SSI conditional expression parsing. - * - * @author Paul Speed */ public class ExpressionTokenizer { public static final int TOKEN_STRING = 0; @@ -138,10 +136,9 @@ // Otherwise it's a string break; } - int end = index; + int end; if (currentChar == '"' || currentChar == '\'') { // It's a quoted string and the end is the next unescaped quote - char endChar = currentChar; boolean escaped = false; start++; for (; index < length; index++) { @@ -149,7 +146,7 @@ escaped = true; continue; } - if (expr[index] == endChar && !escaped) { + if (expr[index] == currentChar && !escaped) { break; } escaped = false; @@ -158,14 +155,13 @@ index++; // Skip the end quote } else if (currentChar == '/') { // It's a regular expression and the end is the next unescaped / - char endChar = currentChar; boolean escaped = false; for (; index < length; index++) { if (expr[index] == '\\' && !escaped) { escaped = true; continue; } - if (expr[index] == endChar && !escaped) { + if (expr[index] == currentChar && !escaped) { break; } escaped = false; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java tomcat10-10.1.52/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/ResponseIncludeWrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,10 +28,7 @@ import org.apache.tomcat.util.http.FastHttpDateFormat; /** - * An HttpServletResponseWrapper, used from SSIServletExternalResolver - * - * @author Bip Thelin - * @author David Becker + * An HttpServletResponseWrapper, used from SSIServletExternalResolver. */ public class ResponseIncludeWrapper extends HttpServletResponseWrapper { /** diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSICommand.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSICommand.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSICommand.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSICommand.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,15 +16,10 @@ */ package org.apache.catalina.ssi; - import java.io.PrintWriter; /** * The interface that all SSI commands ( SSIEcho, SSIInclude, ...) must implement. - * - * @author Bip Thelin - * @author Dan Sandberg - * @author David Becker */ public interface SSICommand { /** diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIConditional.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIConditional.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIConditional.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIConditional.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,6 @@ /** * SSI command that handles all conditional directives. - * - * @author Paul Speed - * @author David Becker */ public class SSIConditional implements SSICommand { @Override @@ -100,8 +97,6 @@ state.branchTaken = true; } else { throw new SSIStopProcessingException(); - // throw new SsiCommandException( "Not a conditional command:" + - // cmdName ); } return lastModified; } @@ -115,13 +110,11 @@ String expr = getExpression(names, values); if (expr == null) { throw new SSIStopProcessingException(); - // throw new SsiCommandException( "No expression specified." ); } try { ExpressionParseTree tree = new ExpressionParseTree(expr, ssiMediator); return tree.evaluateTree(); } catch (ParseException e) { - // throw new SsiCommandException( "Error parsing expression." ); throw new SSIStopProcessingException(); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIConditionalState.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIConditionalState.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIConditionalState.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIConditionalState.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,15 +16,11 @@ */ package org.apache.catalina.ssi; - /** * This class is used by SSIMediator and SSIConditional to keep track of state information necessary to process the * nested conditional commands ( if, elif, else, endif ). - * - * @author Dan Sandberg - * @author Paul Speed */ -class SSIConditionalState { +public class SSIConditionalState { /** * Set to true if the current conditional has already been completed, i.e.: a branch was taken. */ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIConfig.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,18 +16,12 @@ */ package org.apache.catalina.ssi; - import java.io.PrintWriter; import org.apache.tomcat.util.res.StringManager; /** - * Implements the Server-side #exec command - * - * @author Bip Thelin - * @author Paul Speed - * @author Dan Sandberg - * @author David Becker + * Implements the Server-side #exec command. */ public final class SSIConfig implements SSICommand { private static final StringManager sm = StringManager.getManager(SSIConfig.class); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIEcho.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIEcho.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIEcho.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIEcho.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,11 +23,6 @@ /** * Return the result associated with the supplied Server Variable. - * - * @author Bip Thelin - * @author Paul Speed - * @author Dan Sandberg - * @author David Becker */ public class SSIEcho implements SSICommand { private static final StringManager sm = StringManager.getManager(SSIEcho.class); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIExec.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIExec.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIExec.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIExec.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,13 +26,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * Implements the Server-side #exec command - * - * @author Bip Thelin - * @author Amy Roh - * @author Paul Speed - * @author Dan Sandberg - * @author David Becker + * Implements the Server-side #exec command. */ public class SSIExec implements SSICommand { private static final StringManager sm = StringManager.getManager(SSIExec.class); @@ -65,7 +59,7 @@ char[] buf = new char[BUFFER_SIZE]; try (BufferedReader stdOutReader = new BufferedReader(new InputStreamReader(proc.getInputStream())); BufferedReader stdErrReader = - new BufferedReader(new InputStreamReader(proc.getErrorStream()));) { + new BufferedReader(new InputStreamReader(proc.getErrorStream()))) { IOTools.flow(stdErrReader, writer, buf); IOTools.flow(stdOutReader, writer, buf); } @@ -74,12 +68,12 @@ } catch (InterruptedException e) { ssiMediator.log(sm.getString("ssiExec.executeFailed", substitutedValue), e); writer.write(configErrMsg); - } catch (IOException e) { + } catch (IOException ioe) { if (!foundProgram) { // Apache doesn't output an error message if it can't find // a program } - ssiMediator.log(sm.getString("ssiExec.executeFailed", substitutedValue), e); + ssiMediator.log(sm.getString("ssiExec.executeFailed", substitutedValue), ioe); } } return lastModified; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIExternalResolver.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIExternalResolver.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIExternalResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIExternalResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,9 +22,7 @@ import java.util.Date; /** - * Interface used by SSIMediator to talk to the 'outside world' ( usually a servlet ) - * - * @author Dan Sandberg + * Interface used by SSIMediator to talk to the 'outside world' ( usually a servlet ). */ public interface SSIExternalResolver { /** diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIFilter.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIFilter.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,8 +39,6 @@ /** * Filter to process SSI requests within a webpage. Mapped to a content types from within web.xml. * - * @author David Becker - * * @see org.apache.catalina.ssi.SSIServlet */ public class SSIFilter extends GenericFilter { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIFlastmod.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIFlastmod.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIFlastmod.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIFlastmod.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,12 +25,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * Implements the Server-side #flastmod command - * - * @author Bip Thelin - * @author Paul Speed - * @author Dan Sandberg - * @author David Becker + * Implements the Server-side #flastmod command. */ public final class SSIFlastmod implements SSICommand { private static final StringManager sm = StringManager.getManager(SSIFlastmod.class); @@ -55,8 +50,8 @@ ssiMediator.log(sm.getString("ssiCommand.invalidAttribute", paramName)); writer.write(configErrMsg); } - } catch (IOException e) { - ssiMediator.log(sm.getString("ssiFlastmod.noLastModified", substitutedValue), e); + } catch (IOException ioe) { + ssiMediator.log(sm.getString("ssiFlastmod.noLastModified", substitutedValue), ioe); writer.write(configErrMsg); } } @@ -64,7 +59,7 @@ } - protected String formatDate(Date date, String configTimeFmt) { + private String formatDate(Date date, String configTimeFmt) { Strftime strftime = new Strftime(configTimeFmt, Locale.US); return strftime.format(date); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIFsize.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIFsize.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIFsize.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIFsize.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,12 +24,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * Implements the Server-side #fsize command - * - * @author Bip Thelin - * @author Paul Speed - * @author Dan Sandberg - * @author David Becker + * Implements the Server-side #fsize command. */ public final class SSIFsize implements SSICommand { private static final StringManager sm = StringManager.getManager(SSIFsize.class); @@ -57,8 +52,8 @@ ssiMediator.log(sm.getString("ssiCommand.invalidAttribute", paramName)); writer.write(configErrMsg); } - } catch (IOException e) { - ssiMediator.log(sm.getString("ssiFsize.noSize", substitutedValue), e); + } catch (IOException ioe) { + ssiMediator.log(sm.getString("ssiFsize.noSize", substitutedValue), ioe); writer.write(configErrMsg); } } @@ -66,23 +61,11 @@ } - public String repeat(char aChar, int numChars) { - if (numChars < 0) { - throw new IllegalArgumentException(sm.getString("ssiFsize.invalidNumChars")); - } - StringBuilder buf = new StringBuilder(); - for (int i = 0; i < numChars; i++) { - buf.append(aChar); - } - return buf.toString(); - } - - public String padLeft(String str, int maxChars) { String result = str; int charsToAdd = maxChars - str.length(); if (charsToAdd > 0) { - result = repeat(' ', charsToAdd) + str; + result = " ".repeat(charsToAdd) + str; } return result; } @@ -91,8 +74,8 @@ // We try to mimic httpd here, as we do everywhere. // All the 'magic' numbers are from the util_script.c httpd source file. // Should use KiB and MiB in output but use k and M for consistency with httpd. - protected String formatSize(long size, String format) { - String retString = ""; + private String formatSize(long size, String format) { + String retString; if (format.equalsIgnoreCase("bytes")) { DecimalFormat decimalFormat = new DecimalFormat("#,##0"); retString = decimalFormat.format(size); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIInclude.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIInclude.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIInclude.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIInclude.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,12 +23,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * Implements the Server-side #include command - * - * @author Bip Thelin - * @author Paul Speed - * @author Dan Sandberg - * @author David Becker + * Implements the Server-side #include command. */ public final class SSIInclude implements SSICommand { private static final StringManager sm = StringManager.getManager(SSIInclude.class); @@ -52,8 +47,8 @@ ssiMediator.log(sm.getString("ssiCommand.invalidAttribute", paramName)); writer.write(configErrMsg); } - } catch (IOException e) { - ssiMediator.log(sm.getString("ssiInclude.includeFailed", substitutedValue), e); + } catch (IOException ioe) { + ssiMediator.log(sm.getString("ssiInclude.includeFailed", substitutedValue), ioe); writer.write(configErrMsg); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIMediator.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIMediator.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIMediator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIMediator.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,13 +32,7 @@ import org.apache.tomcat.util.security.Escape; /** - * Allows the different SSICommand implementations to share data/talk to each other - * - * @author Bip Thelin - * @author Amy Roh - * @author Paul Speed - * @author Dan Sandberg - * @author David Becker + * Allows the different SSICommand implementations to share data/talk to each other. */ public class SSIMediator { private static final StringManager sm = StringManager.getManager(SSIMediator.class); @@ -230,8 +224,8 @@ } int nameStart = i; int start = i - 1; - int end = -1; - int nameEnd = -1; + int end; + int nameEnd; char endChar = ' '; // Check for {} wrapped var if (sb.charAt(i) == '{') { @@ -280,7 +274,7 @@ protected String encode(String value, String encoding) { - String retVal = null; + String retVal; if (encoding.equalsIgnoreCase(ENCODING_URL)) { retVal = URLEncoder.DEFAULT.encode(value, StandardCharsets.UTF_8); } else if (encoding.equalsIgnoreCase(ENCODING_NONE)) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIPrintenv.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIPrintenv.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIPrintenv.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIPrintenv.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,10 +21,7 @@ import java.util.Collection; /** - * Implements the Server-side #printenv command - * - * @author Dan Sandberg - * @author David Becker + * Implements the Server-side #printenv command. */ public class SSIPrintenv implements SSICommand { @Override diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIProcessor.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIProcessor.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIProcessor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,9 +30,6 @@ /** * The entry point to SSI processing. This class does the actual parsing, delegating to the SSIMediator, SSICommand, and * SSIExternalResolver as necessary. - * - * @author Dan Sandberg - * @author David Becker */ public class SSIProcessor { /** The start pattern */ @@ -95,7 +92,6 @@ StringWriter stringWriter = new StringWriter(); IOTools.flow(reader, stringWriter); String fileContents = stringWriter.toString(); - stringWriter = null; int index = 0; boolean inside = false; StringBuilder command = new StringBuilder(); @@ -175,7 +171,7 @@ protected String[] parseParamNames(StringBuilder cmd, int start) { int bIdx = start; int i = 0; - int quotes = 0; + int quotes; boolean inside = false; StringBuilder retBuf = new StringBuilder(); while (bIdx < cmd.length()) { @@ -186,14 +182,14 @@ if (bIdx >= cmd.length()) { break; } - inside = !inside; + inside = true; } else { while (bIdx < cmd.length() && cmd.charAt(bIdx) != '=') { retBuf.append(cmd.charAt(bIdx)); bIdx++; } retBuf.append('='); - inside = !inside; + inside = false; quotes = 0; boolean escaped = false; for (; bIdx < cmd.length() && quotes != 2; bIdx++) { @@ -242,7 +238,7 @@ if (bIdx >= cmd.length()) { break; } - inside = !inside; + inside = true; endQuote = cmd.charAt(bIdx); } else { boolean escaped = false; @@ -273,7 +269,7 @@ } vals[valIndex++] = sb.toString(); sb.delete(0, sb.length()); // clear the buffer - inside = !inside; + inside = false; } } return vals; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIServlet.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIServlet.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,11 +35,6 @@ /** * Servlet to process SSI requests within a webpage. Mapped to a path from within web.xml. - * - * @author Bip Thelin - * @author Amy Roh - * @author Dan Sandberg - * @author David Becker */ public class SSIServlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -171,7 +166,7 @@ SSIExternalResolver ssiExternalResolver = new SSIServletExternalResolver(getServletContext(), req, res, isVirtualWebappRelative, debug, inputEncoding); SSIProcessor ssiProcessor = new SSIProcessor(ssiExternalResolver, debug, allowExec); - PrintWriter printWriter = null; + PrintWriter printWriter; StringWriter stringWriter = null; if (buffered) { stringWriter = new StringWriter(); @@ -198,9 +193,8 @@ if (lastModified > 0) { res.setDateHeader("last-modified", lastModified); } - if (buffered) { + if (stringWriter != null) { printWriter.flush(); - @SuppressWarnings("null") String text = stringWriter.toString(); res.getWriter().write(text); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIServletExternalResolver.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIServletExternalResolver.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIServletExternalResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIServletExternalResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,6 +27,7 @@ import java.util.Date; import java.util.Enumeration; import java.util.Locale; +import java.util.Objects; import jakarta.servlet.RequestDispatcher; import jakarta.servlet.ServletContext; @@ -38,18 +39,16 @@ import org.apache.catalina.connector.Request; import org.apache.tomcat.util.buf.B2CConverter; import org.apache.tomcat.util.buf.UDecoder; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.RequestUtil; import org.apache.tomcat.util.res.StringManager; /** * An implementation of SSIExternalResolver that is used with servlets. - * - * @author Dan Sandberg - * @author David Becker */ public class SSIServletExternalResolver implements SSIExternalResolver { private static final StringManager sm = StringManager.getManager(SSIServletExternalResolver.class); - protected final String VARIABLE_NAMES[] = { "AUTH_TYPE", "CONTENT_LENGTH", "CONTENT_TYPE", "DOCUMENT_NAME", + protected final String[] VARIABLE_NAMES = { "AUTH_TYPE", "CONTENT_LENGTH", "CONTENT_TYPE", "DOCUMENT_NAME", "DOCUMENT_URI", "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", "HTTP_HOST", "HTTP_REFERER", "HTTP_USER_AGENT", "PATH_INFO", "PATH_TRANSLATED", "QUERY_STRING", "QUERY_STRING_UNESCAPED", "REMOTE_ADDR", "REMOTE_HOST", "REMOTE_PORT", "REMOTE_USER", @@ -141,7 +140,7 @@ @Override public String getVariableValue(String name) { - String retVal = null; + String retVal; Object object = getReqAttributeIgnoreCase(name); if (object != null) { retVal = object.toString(); @@ -256,13 +255,11 @@ Charset queryStringCharset; // If valid, apply settings from request / connector + // Use default as a last resort if (useBodyEncodingForURI && requestCharset != null) { queryStringCharset = requestCharset; - } else if (uriCharset != null) { - queryStringCharset = uriCharset; } else { - // Use default as a last resort - queryStringCharset = StandardCharsets.UTF_8; + queryStringCharset = Objects.requireNonNullElse(uriCharset, StandardCharsets.UTF_8); } retVal = UDecoder.URLDecode(queryString, queryStringCharset); @@ -309,14 +306,8 @@ } else if (nameParts[1].equals("PROTOCOL")) { retVal = req.getProtocol(); } else if (nameParts[1].equals("SOFTWARE")) { - StringBuilder rv = new StringBuilder(context.getServerInfo()); - rv.append(' '); - rv.append(System.getProperty("java.vm.name")); - rv.append('/'); - rv.append(System.getProperty("java.vm.version")); - rv.append(' '); - rv.append(System.getProperty("os.name")); - retVal = rv.toString(); + retVal = context.getServerInfo() + ' ' + System.getProperty("java.vm.name") + '/' + + System.getProperty("java.vm.version") + ' ' + System.getProperty("os.name"); } } else if (name.equalsIgnoreCase("UNIQUE_ID")) { retVal = req.getRequestedSessionId(); @@ -385,9 +376,7 @@ throw new IOException( sm.getString("ssiServletExternalResolver.pathTraversalNonVirtualPath", nonVirtualPath)); } - String path = getAbsolutePath(nonVirtualPath); - ServletContextAndPath csAndP = new ServletContextAndPath(context, path); - return csAndP; + return new ServletContextAndPath(context, getAbsolutePath(nonVirtualPath)); } @@ -427,16 +416,14 @@ protected ServletContextAndPath getServletContextAndPath(String originalPath, boolean virtual) throws IOException { - ServletContextAndPath csAndP = null; if (debug > 0) { log("SSIServletExternalResolver.getServletContextAndPath( " + originalPath + ", " + virtual + ")", null); } if (virtual) { - csAndP = getServletContextAndPathFromVirtualPath(originalPath); + return getServletContextAndPathFromVirtualPath(originalPath); } else { - csAndP = getServletContextAndPathFromNonVirtualPath(originalPath); + return getServletContextAndPathFromNonVirtualPath(originalPath); } - return csAndP; } @@ -448,8 +435,7 @@ if (url == null) { throw new IOException(sm.getString("ssiServletExternalResolver.noResource", path)); } - URLConnection urlConnection = url.openConnection(); - return urlConnection; + return url.openConnection(); } @@ -459,7 +445,7 @@ try { URLConnection urlConnection = getURLConnection(path, virtual); lastModified = urlConnection.getLastModified(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore this. It will always fail for non-file based includes } return lastModified; @@ -472,7 +458,7 @@ try { URLConnection urlConnection = getURLConnection(path, virtual); fileSize = urlConnection.getContentLengthLong(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore this. It will always fail for non-file based includes } return fileSize; @@ -512,7 +498,7 @@ * Make an assumption that an empty response is a failure. This is a problem if a truly empty file were * included, but not sure how else to tell. */ - if (retVal.equals("") && !req.getMethod().equalsIgnoreCase("HEAD")) { + if (retVal.isEmpty() && !Method.HEAD.equals(req.getMethod())) { throw new IOException(sm.getString("ssiServletExternalResolver.noFile", path)); } return retVal; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIServletRequestUtil.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIServletRequestUtil.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIServletRequestUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIServletRequestUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,7 +37,7 @@ if (result == null) { result = (String) request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); } - if ((result == null) || (result.equals(""))) { + if ((result == null) || (result.isEmpty())) { result = "/"; } return result; @@ -47,7 +47,7 @@ if (result == null) { result = request.getServletPath(); } - if ((result == null) || (result.equals(""))) { + if ((result == null) || (result.isEmpty())) { result = "/"; } return RequestUtil.normalize(result); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSISet.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSISet.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSISet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSISet.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,11 +22,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * Implements the Server-side #set command - * - * @author Paul Speed - * @author Dan Sandberg - * @author David Becker + * Implements the Server-side #set command. */ public class SSISet implements SSICommand { private static final StringManager sm = StringManager.getManager(SSISet.class); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIStopProcessingException.java tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIStopProcessingException.java --- tomcat10-10.1.34/java/org/apache/catalina/ssi/SSIStopProcessingException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/ssi/SSIStopProcessingException.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,13 +16,9 @@ */ package org.apache.catalina.ssi; - /** * Exception used to tell SSIProcessor that it should stop processing SSI commands. This is used to mimic the Apache * behavior in #set with invalid attributes. - * - * @author Paul Speed - * @author Dan Sandberg */ public class SSIStopProcessingException extends Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/Bootstrap.java tomcat10-10.1.52/java/org/apache/catalina/startup/Bootstrap.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/Bootstrap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/Bootstrap.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,9 +41,6 @@ * regular execution of the container. The purpose of this roundabout approach is to keep the Catalina internal classes * (and any other classes they depend on, such as an XML parser) out of the system class path and therefore not visible * to application level classes. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public final class Bootstrap { @@ -157,7 +154,7 @@ private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception { String value = CatalinaProperties.getProperty(name + ".loader"); - if ((value == null) || (value.equals(""))) { + if ((value == null) || (value.isEmpty())) { return parent; } @@ -201,7 +198,7 @@ * * @return the modified string */ - protected String replace(String str) { + private String replace(String str) { // Implementation is copied from ClassLoaderLogManager.replace(), // but added special processing for catalina.home and catalina.base. String result = str; @@ -218,7 +215,7 @@ } String propName = str.substring(pos_start + 2, pos_end); String replacement; - if (propName.length() == 0) { + if (propName.isEmpty()) { replacement = null; } else if (Constants.CATALINA_HOME_PROP.equals(propName)) { replacement = getCatalinaHome(); @@ -266,9 +263,9 @@ log.trace("Setting startup class properties"); } String methodName = "setParentClassLoader"; - Class paramTypes[] = new Class[1]; + Class[] paramTypes = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); - Object paramValues[] = new Object[1]; + Object[] paramValues = new Object[1]; paramValues[0] = sharedLoader; Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); @@ -284,8 +281,8 @@ // Call the load() method String methodName = "load"; - Object param[]; - Class paramTypes[]; + Object[] param; + Class[] paramTypes; if (arguments == null || arguments.length == 0) { paramTypes = null; param = null; @@ -341,7 +338,7 @@ init(); } - Method method = catalinaDaemon.getClass().getMethod("start", (Class[]) null); + Method method = catalinaDaemon.getClass().getMethod("start", (Class[]) null); method.invoke(catalinaDaemon, (Object[]) null); } @@ -352,7 +349,7 @@ * @throws Exception Fatal stop error */ public void stop() throws Exception { - Method method = catalinaDaemon.getClass().getMethod("stop", (Class[]) null); + Method method = catalinaDaemon.getClass().getMethod("stop", (Class[]) null); method.invoke(catalinaDaemon, (Object[]) null); } @@ -364,7 +361,7 @@ */ public void stopServer() throws Exception { - Method method = catalinaDaemon.getClass().getMethod("stopServer", (Class[]) null); + Method method = catalinaDaemon.getClass().getMethod("stopServer", (Class[]) null); method.invoke(catalinaDaemon, (Object[]) null); } @@ -378,8 +375,8 @@ */ public void stopServer(String[] arguments) throws Exception { - Object param[]; - Class paramTypes[]; + Object[] param; + Class[] paramTypes; if (arguments == null || arguments.length == 0) { paramTypes = null; param = null; @@ -403,17 +400,17 @@ */ public void setAwait(boolean await) throws Exception { - Class paramTypes[] = new Class[1]; + Class[] paramTypes = new Class[1]; paramTypes[0] = Boolean.TYPE; - Object paramValues[] = new Object[1]; + Object[] paramValues = new Object[1]; paramValues[0] = Boolean.valueOf(await); Method method = catalinaDaemon.getClass().getMethod("setAwait", paramTypes); method.invoke(catalinaDaemon, paramValues); } public boolean getAwait() throws Exception { - Class paramTypes[] = new Class[0]; - Object paramValues[] = new Object[0]; + Class[] paramTypes = new Class[0]; + Object[] paramValues = new Object[0]; Method method = catalinaDaemon.getClass().getMethod("getAwait", paramTypes); Boolean b = (Boolean) method.invoke(catalinaDaemon, paramValues); return b.booleanValue(); @@ -432,7 +429,7 @@ * * @param args Command line arguments to be processed */ - public static void main(String args[]) { + public static void main(String[] args) { synchronized (daemonLock) { if (daemon == null) { @@ -460,38 +457,46 @@ command = args[args.length - 1]; } - if (command.equals("startd")) { - args[args.length - 1] = "start"; - daemon.load(args); - daemon.start(); - } else if (command.equals("stopd")) { - args[args.length - 1] = "stop"; - daemon.stop(); - } else if (command.equals("start")) { - daemon.setAwait(true); - daemon.load(args); - daemon.start(); - if (null == daemon.getServer()) { - System.exit(1); - } - } else if (command.equals("stop")) { - daemon.stopServer(args); - } else if (command.equals("configtest")) { - daemon.load(args); - if (null == daemon.getServer()) { - System.exit(1); - } - System.exit(0); - } else { - log.warn("Bootstrap: command \"" + command + "\" does not exist."); + switch (command) { + case "startd": + args[args.length - 1] = "start"; + daemon.load(args); + daemon.start(); + break; + case "stopd": + args[args.length - 1] = "stop"; + daemon.stop(); + break; + case "start": + daemon.setAwait(true); + daemon.load(args); + daemon.start(); + if (null == daemon.getServer()) { + System.exit(1); + } + break; + case "stop": + daemon.stopServer(args); + break; + case "configtest": + daemon.load(args); + if (null == daemon.getServer()) { + System.exit(1); + } + System.exit(0); + break; + default: + log.warn("Bootstrap: command \"" + command + "\" does not exist."); + break; } } catch (Throwable t) { // Unwrap the Exception for clearer error reporting - if (t instanceof InvocationTargetException && t.getCause() != null) { - t = t.getCause(); + Throwable throwable = t; + if (throwable instanceof InvocationTargetException && throwable.getCause() != null) { + throwable = throwable.getCause(); } - handleThrowable(t); - log.error("Error running command", t); + handleThrowable(throwable); + log.error("Error running command", throwable); System.exit(1); } } @@ -564,7 +569,7 @@ } // Protected for unit testing - protected static String[] getPaths(String value) { + static String[] getPaths(String value) { List result = new ArrayList<>(); Matcher matcher = PATH_PATTERN.matcher(value); @@ -573,7 +578,7 @@ String path = value.substring(matcher.start(), matcher.end()); path = path.trim(); - if (path.length() == 0) { + if (path.isEmpty()) { continue; } @@ -583,7 +588,7 @@ if (first == '"' && last == '"' && path.length() > 1) { path = path.substring(1, path.length() - 1); path = path.trim(); - if (path.length() == 0) { + if (path.isEmpty()) { continue; } } else if (path.contains("\"")) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/Catalina.java tomcat10-10.1.52/java/org/apache/catalina/startup/Catalina.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/Catalina.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/Catalina.java 2026-01-23 19:33:36.000000000 +0000 @@ -66,9 +66,6 @@ *
    • start - Start an instance of Catalina.
    • *
    • stop - Stop the currently running instance of Catalina.
    • * - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class Catalina { @@ -314,7 +311,7 @@ * * @return true if we should continue processing */ - protected boolean arguments(String args[]) { + protected boolean arguments(String[] args) { boolean isConfig = false; boolean isGenerateCode = false; @@ -497,8 +494,8 @@ * Cluster support is optional. The JARs may have been removed. */ private void addClusterRuleSet(Digester digester, String prefix) { - Class clazz = null; - Constructor constructor = null; + Class clazz; + Constructor constructor; try { clazz = Class.forName("org.apache.catalina.ha.ClusterRuleSet"); constructor = clazz.getConstructor(String.class); @@ -549,7 +546,7 @@ Digester.setGeneratedCodeLoader(loader); } catch (Exception e) { if (log.isDebugEnabled()) { - log.info(sm.getString("catalina.noLoader", loaderClassName), e); + log.debug(sm.getString("catalina.noLoader", loaderClassName), e); } else { log.info(sm.getString("catalina.noLoader", loaderClassName)); } @@ -667,8 +664,8 @@ String.valueOf(s.getPortOffset()))); log.error(sm.getString("catalina.stopError"), ce); System.exit(1); - } catch (IOException e) { - log.error(sm.getString("catalina.stopError"), e); + } catch (IOException ioe) { + log.error(sm.getString("catalina.stopError"), ioe); System.exit(1); } } else { @@ -728,7 +725,7 @@ /* * Load using arguments */ - public void load(String args[]) { + public void load(String[] args) { try { if (arguments(args)) { @@ -925,9 +922,9 @@ File loaderLocation = new File(generatedCodeLocation, generatedCodePackage); try (FileWriter writer = new FileWriter(new File(loaderLocation, loaderClassName + ".java"))) { writer.write(code.toString()); - } catch (IOException e) { + } catch (IOException ioe) { // Should not happen - log.debug(sm.getString("catalina.loaderWriteFail"), e); + log.debug(sm.getString("catalina.loaderWriteFail"), ioe); } } @@ -961,7 +958,7 @@ // --------------------------------------- CatalinaShutdownHook Inner Class - // XXX Should be moved to embedded ! + /** * Shutdown hook which will perform a clean shutdown of Catalina if needed. */ @@ -1003,7 +1000,7 @@ } - ClassLoader parentClassLoader = null; + ClassLoader parentClassLoader; @Override public void begin(String namespace, String name, Attributes attributes) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/CatalinaBaseConfigurationSource.java tomcat10-10.1.52/java/org/apache/catalina/startup/CatalinaBaseConfigurationSource.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/CatalinaBaseConfigurationSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/CatalinaBaseConfigurationSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,7 +47,7 @@ @Override public Resource getServerXml() throws IOException { - IOException ioe = null; + IOException ioException = null; Resource result = null; try { if (serverXmlPath == null || serverXmlPath.equals(Catalina.SERVER_XML)) { @@ -55,8 +55,8 @@ } else { result = getResource(serverXmlPath); } - } catch (IOException e) { - ioe = e; + } catch (IOException ioe) { + ioException = ioe; } if (result == null) { // Compatibility with legacy server-embed.xml location @@ -71,8 +71,8 @@ } } - if (result == null && ioe != null) { - throw ioe; + if (result == null && ioException != null) { + throw ioException; } else { return result; } @@ -108,7 +108,7 @@ } // Then try URI. - URI uri = null; + URI uri; try { uri = getURIInternal(name); } catch (IllegalArgumentException e) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/CatalinaProperties.java tomcat10-10.1.52/java/org/apache/catalina/startup/CatalinaProperties.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/CatalinaProperties.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/CatalinaProperties.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,8 +30,6 @@ /** * Utility class to read the bootstrap Catalina configuration. - * - * @author Remy Maucherat */ public class CatalinaProperties { @@ -102,7 +100,7 @@ properties.load(is); } catch (Throwable t) { handleThrowable(t); - log.warn(t); + log.warn(t.getMessage(), t); } finally { try { is.close(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/CertificateCreateRule.java tomcat10-10.1.52/java/org/apache/catalina/startup/CertificateCreateRule.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/CertificateCreateRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/CertificateCreateRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,7 +33,7 @@ Type type; String typeValue = attributes.getValue("type"); - if (typeValue == null || typeValue.length() == 0) { + if (typeValue == null || typeValue.isEmpty()) { type = Type.UNDEFINED; } else { type = Type.valueOf(typeValue); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/ClassLoaderFactory.java tomcat10-10.1.52/java/org/apache/catalina/startup/ClassLoaderFactory.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/ClassLoaderFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/ClassLoaderFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,8 +45,6 @@ * directories will be added to the class loader's repositories. *
    • ClassLoader instance that should become the parent of the new class loader.
    • * - * - * @author Craig R. McClanahan */ public final class ClassLoaderFactory { @@ -69,7 +67,7 @@ * * @exception Exception if an error occurs constructing the class loader */ - public static ClassLoader createClassLoader(File unpacked[], File packed[], final ClassLoader parent) + public static ClassLoader createClassLoader(File[] unpacked, File[] packed, final ClassLoader parent) throws Exception { if (log.isDebugEnabled()) { @@ -100,7 +98,7 @@ if (!directory.isDirectory() || !directory.canRead()) { continue; } - String filenames[] = directory.list(); + String[] filenames = directory.list(); if (filenames == null) { continue; } @@ -192,7 +190,7 @@ if (log.isDebugEnabled()) { log.debug(" Including directory glob " + directory.getAbsolutePath()); } - String filenames[] = directory.list(); + String[] filenames = directory.list(); if (filenames == null) { continue; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/ConnectorCreateRule.java tomcat10-10.1.52/java/org/apache/catalina/startup/ConnectorCreateRule.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/ConnectorCreateRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/ConnectorCreateRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -86,7 +86,7 @@ Method m = IntrospectionUtils.findMethod(con.getProtocolHandler().getClass(), "setExecutor", new Class[] { java.util.concurrent.Executor.class }); if (m != null) { - m.invoke(con.getProtocolHandler(), new Object[] { ex }); + m.invoke(con.getProtocolHandler(), ex); } else { log.warn(sm.getString("connector.noSetExecutor", con)); } @@ -96,7 +96,7 @@ Method m = IntrospectionUtils.findMethod(con.getProtocolHandler().getClass(), "setSslImplementationName", new Class[] { String.class }); if (m != null) { - m.invoke(con.getProtocolHandler(), new Object[] { sslImplementationName }); + m.invoke(con.getProtocolHandler(), sslImplementationName); } else { log.warn(sm.getString("connector.noSetSSLImplementationName", con)); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/Constants.java tomcat10-10.1.52/java/org/apache/catalina/startup/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ * String constants for the startup package.
      * Note that some values include a leading '/' and that some do not. This is intentional based on how the values are * used. - * - * @author Craig R. McClanahan */ public final class Constants { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/ContextConfig.java tomcat10-10.1.52/java/org/apache/catalina/startup/ContextConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/ContextConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/ContextConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -118,8 +118,6 @@ /** * Startup event listener for a Context that configures the properties of that Context, and the associated * defined servlets. - * - * @author Craig R. McClanahan */ public class ContextConfig implements LifecycleListener { @@ -604,8 +602,8 @@ } } catch (MalformedURLException e) { log.error(sm.getString("contextConfig.badUrl", defaultContextXml), e); - } catch (IOException e) { - // Not found + } catch (IOException ignore) { + // Ignore - Not found } } @@ -646,8 +644,8 @@ } } catch (MalformedURLException e) { log.error(sm.getString("contextConfig.badUrl", hostContextFile), e); - } catch (IOException e) { - // Not found + } catch (IOException ignore) { + // Ignore - Not found } } } @@ -667,7 +665,6 @@ } catch (Exception e) { log.warn(sm.getString("contextConfig.loadError"), e); } - contextXml = null; } else if (!useGeneratedCode) { if (generateCode) { contextXmlJavaSource = getContextXmlJavaSource(contextXmlPackageName, contextXmlSimpleClassName); @@ -679,7 +676,7 @@ generateClassFooter(digester); try (FileWriter writer = new FileWriter(contextXmlJavaSource)) { writer.write(digester.getGeneratedCode().toString()); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } digester.endGeneratingCode(); @@ -730,7 +727,7 @@ XmlErrorHandler errorHandler = new XmlErrorHandler(); digester.setErrorHandler(errorHandler); digester.parse(source); - if (errorHandler.getWarnings().size() > 0 || errorHandler.getErrors().size() > 0) { + if (!errorHandler.getWarnings().isEmpty() || !errorHandler.getErrors().isEmpty()) { errorHandler.logFindings(log, contextXml.toString()); ok = false; } @@ -740,7 +737,8 @@ } } catch (SAXParseException e) { log.error(sm.getString("contextConfig.contextParse", context.getName()), e); - log.error(sm.getString("contextConfig.defaultPosition", "" + e.getLineNumber(), "" + e.getColumnNumber())); + log.error(sm.getString("contextConfig.defaultPosition", Integer.toString(e.getLineNumber()), + Integer.toString(e.getColumnNumber()))); ok = false; } catch (Exception e) { log.error(sm.getString("contextConfig.contextParse", context.getName()), e); @@ -750,8 +748,8 @@ if (stream != null) { stream.close(); } - } catch (IOException e) { - log.error(sm.getString("contextConfig.contextClose"), e); + } catch (IOException ioe) { + log.error(sm.getString("contextConfig.contextClose"), ioe); } } } @@ -803,7 +801,7 @@ } // At this point we need to determine if we have a WAR file in the - // appBase that needs to be expanded. Therefore we consider the absolute + // appBase that needs to be expanded. Therefore, we consider the absolute // docBase NOT the canonical docBase. This is because some users symlink // WAR files into the appBase and we want this to work correctly. boolean docBaseAbsoluteInAppBase = docBaseAbsolute.startsWith(appBase.getPath() + File.separatorChar); @@ -919,7 +917,7 @@ /** - * Process a "init" event for this Context. + * Process an "init" event for this Context. */ protected synchronized void init() { // Called from StandardContext.init() @@ -947,8 +945,8 @@ try { fixDocBase(); - } catch (IOException e) { - log.error(sm.getString("contextConfig.fixDocBase", context.getName()), e); + } catch (IOException ioe) { + log.error(sm.getString("contextConfig.fixDocBase", context.getName()), ioe); } antiLocking(); @@ -988,7 +986,7 @@ if (log.isTraceEnabled()) { log.trace("Pipeline Configuration:"); Pipeline pipeline = context.getPipeline(); - Valve valves[] = null; + Valve[] valves = null; if (pipeline != null) { valves = pipeline.getValves(); } @@ -1161,9 +1159,9 @@ protected void validateSecurityRoles() { // Check role names used in elements - SecurityConstraint constraints[] = context.findConstraints(); + SecurityConstraint[] constraints = context.findConstraints(); for (SecurityConstraint constraint : constraints) { - String roles[] = constraint.findAuthRoles(); + String[] roles = constraint.findAuthRoles(); for (String role : roles) { if (!"*".equals(role) && !context.findSecurityRole(role)) { log.warn(sm.getString("contextConfig.role.auth", role)); @@ -1173,7 +1171,7 @@ } // Check role names used in elements - Container wrappers[] = context.findChildren(); + Container[] wrappers = context.findChildren(); for (Container container : wrappers) { Wrapper wrapper = (Wrapper) container; String runAs = wrapper.getRunAs(); @@ -1181,7 +1179,7 @@ log.warn(sm.getString("contextConfig.role.runas", runAs)); context.addSecurityRole(runAs); } - String names[] = wrapper.findSecurityReferences(); + String[] names = wrapper.findSecurityReferences(); for (String name : names) { String link = wrapper.findSecurityReference(name); if ((link != null) && !context.findSecurityRole(link)) { @@ -1260,15 +1258,14 @@ Map fragments = processJarsForWebFragments(webXml, webXmlParser); // Step 2. Order the fragments. - Set orderedFragments = null; - orderedFragments = WebXml.orderWebFragments(webXml, fragments, sContext); + Set orderedFragments = WebXml.orderWebFragments(webXml, fragments, sContext); // Step 3. Look for ServletContainerInitializer implementations if (ok) { processServletContainerInitializers(); } - if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) { + if (!webXml.isMetadataComplete() || !typeInitializerMap.isEmpty()) { // Steps 4 & 5. processClasses(webXml, orderedFragments); } @@ -1522,7 +1519,7 @@ for (Map.Entry attribute : attributes.entrySet()) { scc.setAttribute(attribute.getKey(), attribute.getValue()); } - if (sessionConfig.getSessionTrackingModes().size() > 0) { + if (!sessionConfig.getSessionTrackingModes().isEmpty()) { context.getServletContext().setSessionTrackingModes(sessionConfig.getSessionTrackingModes()); } } @@ -1534,7 +1531,7 @@ * The following will result in a welcome file of "" so don't add that to the context * */ - if (welcomeFile != null && welcomeFile.length() > 0) { + if (welcomeFile != null && !welcomeFile.isEmpty()) { context.addWelcomeFile(welcomeFile); } } @@ -1623,8 +1620,8 @@ if (uc != null) { try { uc.getInputStream().close(); - } catch (IOException e) { - ExceptionUtils.handleThrowable(e); + } catch (IOException ioe) { + ExceptionUtils.handleThrowable(ioe); globalTimeStamp = -1; } } @@ -1644,8 +1641,8 @@ if (uc != null) { try { uc.getInputStream().close(); - } catch (IOException e) { - ExceptionUtils.handleThrowable(e); + } catch (IOException ioe) { + ExceptionUtils.handleThrowable(ioe); hostTimeStamp = -1; } } @@ -1766,8 +1763,8 @@ try { WebappServiceLoader loader = new WebappServiceLoader<>(context); detectedScis = loader.load(ServletContainerInitializer.class); - } catch (IOException e) { - log.error(sm.getString("contextConfig.servletContainerInitializerFail", context.getName()), e); + } catch (IOException ioe) { + log.error(sm.getString("contextConfig.servletContainerInitializerFail", context.getName()), ioe); ok = false; return; } @@ -1780,7 +1777,7 @@ ht = sci.getClass().getAnnotation(HandlesTypes.class); } catch (Exception e) { if (log.isDebugEnabled()) { - log.info(sm.getString("contextConfig.sci.debug", sci.getClass().getName()), e); + log.debug(sm.getString("contextConfig.sci.debug", sci.getClass().getName()), e); } else { log.info(sm.getString("contextConfig.sci.info", sci.getClass().getName())); } @@ -1893,7 +1890,7 @@ InputSource source = null; URL url = null; - String altDDName = null; + String altDDName; // Open the application web.xml file, if it exists ServletContext servletContext = context.getServletContext(); @@ -1930,7 +1927,7 @@ if (source == null && stream != null) { try { stream.close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } @@ -1969,7 +1966,7 @@ * @return the input source */ protected InputSource getWebXmlSource(String filename, boolean global) { - ConfigurationSource.Resource webXmlResource = null; + ConfigurationSource.Resource webXmlResource; try { if (global) { if (Constants.DefaultWebXml.equals(filename)) { @@ -1981,7 +1978,7 @@ String hostWebXml = Container.getConfigPath(context, Constants.HostWebXml); webXmlResource = ConfigFileLoader.getSource().getResource(hostWebXml); } - } catch (IOException e) { + } catch (IOException ignore) { // Ignore if not found return null; } @@ -2001,7 +1998,7 @@ if (source == null && stream != null) { try { stream.close(); - } catch (IOException e) { + } catch (IOException ioe) { // Ignore } } @@ -2088,7 +2085,7 @@ private class AnnotationScanTask implements Runnable { private final WebXml fragment; private final boolean handlesTypesOnly; - private Map javaClassCache; + private final Map javaClassCache; private AnnotationScanTask(WebXml fragment, boolean handlesTypesOnly, Map javaClassCache) { @@ -2164,7 +2161,6 @@ Map javaClassCache) { if (url == null) { // Nothing to do. - return; } else if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) { processAnnotationsJar(url, fragment, handlesTypesOnly, javaClassCache); } else if ("file".equals(url.getProtocol())) { @@ -2201,8 +2197,8 @@ jar.nextEntry(); entryName = jar.getEntryName(); } - } catch (IOException e) { - log.error(sm.getString("contextConfig.jarFile", url), e); + } catch (IOException ioe) { + log.error(sm.getString("contextConfig.jarFile", url), ioe); } } @@ -2276,7 +2272,7 @@ protected void checkHandlesTypes(JavaClass javaClass, Map javaClassCache) { // Skip this if we can - if (typeInitializerMap.size() == 0) { + if (typeInitializerMap.isEmpty()) { return; } @@ -2346,7 +2342,6 @@ private String classHierarchyToString(String className, JavaClassCacheEntry entry, Map javaClassCache) { - JavaClassCacheEntry start = entry; StringBuilder msg = new StringBuilder(className); msg.append("->"); @@ -2354,7 +2349,7 @@ JavaClassCacheEntry parent = javaClassCache.get(parentName); int count = 0; - while (count < 100 && parent != null && parent != start) { + while (count < 100 && parent != null && parent != entry) { msg.append(parentName); msg.append("->"); @@ -2474,7 +2469,7 @@ } } if (servletName == null) { - // classname is default servletName as annotation has no name! + // class name is default servletName as annotation has no name! servletName = className; } ServletDef servletDef = fragment.getServlets().get(servletName); @@ -2574,7 +2569,7 @@ } } if (filterName == null) { - // classname is default filterName as annotation has no name! + // class name is default filterName as annotation has no name! filterName = className; } FilterDef filterDef = fragment.getFilters().get(filterName); @@ -2593,7 +2588,7 @@ boolean urlPatternsSet = false; boolean servletNamesSet = false; boolean dispatchTypesSet = false; - String[] urlPatterns = null; + String[] urlPatterns; for (ElementValuePair evp : evps) { String name = evp.getNameString(); @@ -2731,7 +2726,7 @@ return result; } - private static class DefaultWebXmlCacheEntry { + protected static class DefaultWebXmlCacheEntry { private final WebXml webXml; private final long globalTimeStamp; private final long hostTimeStamp; @@ -2767,7 +2762,7 @@ } } - static class JavaClassCacheEntry { + protected static class JavaClassCacheEntry { public final String superclassName; public final String[] interfaceNames; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/ContextRuleSet.java tomcat10-10.1.52/java/org/apache/catalina/startup/ContextRuleSet.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/ContextRuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/ContextRuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,6 @@ /** * RuleSet for processing the contents of a Context definition element. - * - * @author Craig R. McClanahan */ public class ContextRuleSet implements RuleSet { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java tomcat10-10.1.52/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/CopyParentClassLoaderRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,6 @@ /** * Rule that copies the parentClassLoader property from the next-to-top item on the stack (which must be a * Container) to the top item on the stack (which must also be a Container). - * - * @author Craig R. McClanahan */ public class CopyParentClassLoaderRule extends Rule { @@ -54,7 +52,7 @@ } Container child = (Container) digester.peek(0); Object parent = digester.peek(1); - Method method = parent.getClass().getMethod("getParentClassLoader", new Class[0]); + Method method = parent.getClass().getMethod("getParentClassLoader"); ClassLoader classLoader = (ClassLoader) method.invoke(parent, new Object[0]); child.setParentClassLoader(classLoader); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/EngineConfig.java tomcat10-10.1.52/java/org/apache/catalina/startup/EngineConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/EngineConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/EngineConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,6 @@ /** * Startup event listener for an Engine that configures the properties of that Engine, and the associated defined * contexts. - * - * @author Craig R. McClanahan */ public class EngineConfig implements LifecycleListener { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/EngineRuleSet.java tomcat10-10.1.52/java/org/apache/catalina/startup/EngineRuleSet.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/EngineRuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/EngineRuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,10 +20,8 @@ import org.apache.tomcat.util.digester.RuleSet; /** - * RuleSet for processing the contents of a Engine definition element. This RuleSet does + * RuleSet for processing the contents of an Engine definition element. This RuleSet does * NOT include any rules for nested Host elements, which should be added via instances of HostRuleSet. - * - * @author Craig R. McClanahan */ public class EngineRuleSet implements RuleSet { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/ExpandWar.java tomcat10-10.1.52/java/org/apache/catalina/startup/ExpandWar.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/ExpandWar.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/ExpandWar.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,10 +40,6 @@ /** * Expand out a WAR in a Host's appBase. - * - * @author Craig R. McClanahan - * @author Remy Maucherat - * @author Glenn L. Nielsen */ public class ExpandWar { @@ -81,7 +77,7 @@ boolean success = false; File docBase = new File(host.getAppBaseFile(), pathname); File warTracker = new File(host.getAppBaseFile(), pathname + Constants.WarTracker); - long warLastModified = -1; + long warLastModified; try (InputStream is = jfuc.getInputStream()) { // Get the last modified time for the WAR @@ -95,7 +91,7 @@ // changes to the WAR while Tomcat is stopped can be detected if (!warTracker.exists() || warTracker.lastModified() == warLastModified) { // No (detectable) changes to the WAR - success = true; + // success = true; return docBase.getAbsolutePath(); } @@ -170,8 +166,6 @@ } success = true; - } catch (IOException e) { - throw e; } finally { if (!success) { // If something went wrong, delete expanded dir to keep things @@ -216,8 +210,6 @@ expandedFile.getCanonicalPath(), canonicalDocBasePath)); } } - } catch (IOException e) { - throw e; } } @@ -234,7 +226,7 @@ boolean result = true; - String files[] = null; + String[] files; if (src.isDirectory()) { files = src.list(); result = dest.mkdir(); @@ -266,8 +258,8 @@ throw new EOFException(); } } - } catch (IOException e) { - log.error(sm.getString("expandWar.copy", fileSrc, fileDest), e); + } catch (IOException ioe) { + log.error(sm.getString("expandWar.copy", fileSrc, fileDest), ioe); result = false; } } @@ -277,8 +269,8 @@ /** - * Delete the specified directory, including all of its contents and sub-directories recursively. Any failure will - * be logged. + * Delete the specified directory, including all of its contents and subdirectories recursively. Any failure will be + * logged. * * @param dir File object representing the directory to be deleted * @@ -291,7 +283,7 @@ /** - * Delete the specified directory, including all of its contents and sub-directories recursively. + * Delete the specified directory, including all of its contents and subdirectories recursively. * * @param dir File object representing the directory to be deleted * @param logFailure true if failure to delete the resource should be logged @@ -317,8 +309,8 @@ /** - * Delete the specified directory, including all of its contents and sub-directories recursively. Any failure will - * be logged. + * Delete the specified directory, including all of its contents and subdirectories recursively. Any failure will be + * logged. * * @param dir File object representing the directory to be deleted * @@ -330,7 +322,7 @@ /** - * Delete the specified directory, including all of its contents and sub-directories recursively. + * Delete the specified directory, including all of its contents and subdirectories recursively. * * @param dir File object representing the directory to be deleted * @param logFailure true if failure to delete the resource should be logged @@ -339,7 +331,7 @@ */ public static boolean deleteDir(File dir, boolean logFailure) { - String files[] = dir.list(); + String[] files = dir.list(); if (files == null) { files = new String[0]; } @@ -377,7 +369,7 @@ */ private static void expand(InputStream input, File file) throws IOException { try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(file))) { - byte buffer[] = new byte[2048]; + byte[] buffer = new byte[2048]; while (true) { int n = input.read(buffer); if (n <= 0) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/FailedContext.java tomcat10-10.1.52/java/org/apache/catalina/startup/FailedContext.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/FailedContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/FailedContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,6 +21,7 @@ import java.net.URL; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import jakarta.servlet.ServletContainerInitializer; @@ -184,7 +185,7 @@ } else if (c == null) { // May happen in unit testing and/or some embedding scenarios keyProperties.append(",container"); - keyProperties.append(containerCount++); + keyProperties.append(containerCount); keyProperties.append("=null"); break; } else { @@ -206,11 +207,7 @@ StringBuilder keyProperties = new StringBuilder("j2eeType=WebModule,name=//"); String hostname = getParent().getName(); - if (hostname == null) { - keyProperties.append("DEFAULT"); - } else { - keyProperties.append(hostname); - } + keyProperties.append(Objects.requireNonNullElse(hostname, "DEFAULT")); String contextName = getName(); if (!contextName.startsWith("/")) { @@ -1434,4 +1431,15 @@ public void setSuspendWrappedResponseAfterForward(boolean suspendWrappedResponseAfterForward) { } + public long getStartTime() { + return -1; + } + + public long getStartupTime() { + return -1; + } + + public long getTldScanTime() { + return -1; + } } \ No newline at end of file diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/HomesUserDatabase.java tomcat10-10.1.52/java/org/apache/catalina/startup/HomesUserDatabase.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/HomesUserDatabase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/HomesUserDatabase.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Concrete implementation of the UserDatabase interface considers all directories in a directory whose * pathname is specified to our constructor to be "home" directories for those users. - * - * @author Craig R. McClanahan */ public final class HomesUserDatabase implements UserDatabase { @@ -76,7 +74,7 @@ if (!homeBaseDir.exists() || !homeBaseDir.isDirectory()) { return; } - String homeBaseFiles[] = homeBaseDir.list(); + String[] homeBaseFiles = homeBaseDir.list(); if (homeBaseFiles == null) { return; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/HostConfig.java tomcat10-10.1.52/java/org/apache/catalina/startup/HostConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/HostConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/HostConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -77,9 +77,6 @@ /** * Startup event listener for a Host that configures the properties of that Host, and the associated defined * contexts. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class HostConfig implements LifecycleListener { @@ -144,7 +141,7 @@ /** * Set of applications which are being serviced, and shouldn't be deployed/undeployed/redeployed at the moment. */ - private Set servicedSet = ConcurrentHashMap.newKeySet(); + private final Set servicedSet = ConcurrentHashMap.newKeySet(); /** * The Digester instance used to parse context descriptors. @@ -315,10 +312,7 @@ * @return {@code true} if the application was not already in the list */ public boolean tryAddServiced(String name) { - if (servicedSet.add(name)) { - return true; - } - return false; + return servicedSet.add(name); } @@ -392,7 +386,7 @@ } try { return file.getCanonicalFile(); - } catch (IOException e) { + } catch (IOException ioe) { return file; } } @@ -570,7 +564,7 @@ Context context = null; boolean isExternalWar = false; boolean isExternal = false; - File expandedDocBase = null; + File expandedDocBase; try { synchronized (digesterLock) { @@ -641,7 +635,8 @@ // default to appBase dir + name expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName()); - if (context.getDocBase() != null && !context.getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) { + if (context != null && context.getDocBase() != null && + !context.getDocBase().toLowerCase(Locale.ENGLISH).endsWith(".war")) { // first assume docBase is absolute expandedDocBase = new File(context.getDocBase()); if (!expandedDocBase.isAbsolute()) { @@ -793,7 +788,7 @@ // not end with File.separator for a directory StringBuilder docBase; - String canonicalDocBase = null; + String canonicalDocBase; try { String canonicalAppBase = appBase.getCanonicalPath(); @@ -819,7 +814,7 @@ // Compare the two. If they are not the same, the contextPath must // have /../ like sequences in it - return canonicalDocBase.equals(docBase.toString()); + return canonicalDocBase.contentEquals(docBase); } /** @@ -842,19 +837,17 @@ if (entry != null) { xmlInWar = true; } - } catch (IOException e) { - /* Ignore */ + } catch (IOException ignore) { + // Ignore } // If there is an expanded directory then any xml in that directory // should only be used if the directory is not out of date and // unpackWARs is true. Note the code below may apply further limits - boolean useXml = false; - // If the xml file exists then expandedDir must exists so no need to + boolean useXml = + xml.exists() && unpackWARs && (!warTracker.exists() || warTracker.lastModified() == war.lastModified()); + // If the xml file exists then expandedDir must exist so no need to // test that here - if (xml.exists() && unpackWARs && (!warTracker.exists() || warTracker.lastModified() == war.lastModified())) { - useXml = true; - } Context context = null; boolean deployThisXML = isDeployThisXML(war, cn); @@ -929,8 +922,8 @@ OutputStream ostream = new FileOutputStream(xml)) { IOTools.flow(istream, ostream); } - } catch (IOException e) { - /* Ignore */ + } catch (IOException ignore) { + // Ignore } } } @@ -1099,7 +1092,7 @@ } } - if (copyThisXml == false && context instanceof StandardContext) { + if (!copyThisXml && context instanceof StandardContext) { // Host is using default value. Context may override it. copyThisXml = ((StandardContext) context).getCopyXML(); } @@ -1224,7 +1217,7 @@ protected void migrateLegacyApp(File source, File destination) { File tempNew = null; - File tempOld = null; + File tempOld; try { tempNew = File.createTempFile("new", null, host.getLegacyAppBaseFile()); tempOld = File.createTempFile("old", null, host.getLegacyAppBaseFile()); @@ -1395,8 +1388,8 @@ } } } else { - // There is a chance the the resource was only missing - // temporarily eg renamed during a text editor save + // There is a chance the resource was only missing + // temporarily e.g. renamed during a text editor save if (resource.exists() || !resource.getName().toLowerCase(Locale.ENGLISH).endsWith(".war")) { try { Thread.sleep(500); @@ -1554,16 +1547,16 @@ String canonicalLocation; try { canonicalLocation = resource.getParentFile().getCanonicalPath(); - } catch (IOException e) { - log.warn(sm.getString("hostConfig.canonicalizing", resource.getParentFile(), app.name), e); + } catch (IOException ioe) { + log.warn(sm.getString("hostConfig.canonicalizing", resource.getParentFile(), app.name), ioe); return false; } String canonicalAppBase; try { canonicalAppBase = host.getAppBaseFile().getCanonicalPath(); - } catch (IOException e) { - log.warn(sm.getString("hostConfig.canonicalizing", host.getAppBaseFile(), app.name), e); + } catch (IOException ioe) { + log.warn(sm.getString("hostConfig.canonicalizing", host.getAppBaseFile(), app.name), ioe); return false; } @@ -1575,18 +1568,14 @@ String canonicalConfigBase; try { canonicalConfigBase = host.getConfigBaseFile().getCanonicalPath(); - } catch (IOException e) { - log.warn(sm.getString("hostConfig.canonicalizing", host.getConfigBaseFile(), app.name), e); + } catch (IOException ioe) { + log.warn(sm.getString("hostConfig.canonicalizing", host.getConfigBaseFile(), app.name), ioe); return false; } - if (canonicalLocation.equals(canonicalConfigBase) && resource.getName().endsWith(".xml")) { - // Resource is an xml file in the configBase so it may be deleted - return true; - } - + // Resource is a xml file in the configBase so it may be deleted + return canonicalLocation.equals(canonicalConfigBase) && resource.getName().endsWith(".xml"); // All other resources should not be deleted - return false; } @@ -1614,7 +1603,7 @@ try { ObjectName hostON = host.getObjectName(); oname = new ObjectName(hostON.getDomain() + ":type=Deployer,host=" + host.getName()); - Registry.getRegistry(null, null).registerComponent(this, oname, this.getClass().getName()); + Registry.getRegistry(null).registerComponent(this, oname, this.getClass().getName()); } catch (Exception e) { log.warn(sm.getString("hostConfig.jmx.register", oname), e); } @@ -1642,7 +1631,7 @@ if (oname != null) { try { - Registry.getRegistry(null, null).unregisterComponent(oname); + Registry.getRegistry(null).unregisterComponent(oname); } catch (Exception e) { log.warn(sm.getString("hostConfig.jmx.unregister", oname), e); } @@ -1691,7 +1680,7 @@ */ public void check(String name) { synchronized (host) { - if (!((Lifecycle) host).getState().isAvailable()) { + if (!host.getState().isAvailable()) { return; } if (tryAddServiced(name)) { @@ -1873,9 +1862,9 @@ private static class DeployDescriptor implements Runnable { - private HostConfig config; - private ContextName cn; - private File descriptor; + private final HostConfig config; + private final ContextName cn; + private final File descriptor; DeployDescriptor(HostConfig config, ContextName cn, File descriptor) { this.config = config; @@ -1895,9 +1884,9 @@ private static class DeployWar implements Runnable { - private HostConfig config; - private ContextName cn; - private File war; + private final HostConfig config; + private final ContextName cn; + private final File war; DeployWar(HostConfig config, ContextName cn, File war) { this.config = config; @@ -1917,9 +1906,9 @@ private static class DeployDirectory implements Runnable { - private HostConfig config; - private ContextName cn; - private File dir; + private final HostConfig config; + private final ContextName cn; + private final File dir; DeployDirectory(HostConfig config, ContextName cn, File dir) { this.config = config; @@ -1940,10 +1929,10 @@ private static class MigrateApp implements Runnable { - private HostConfig config; - private ContextName cn; - private File source; - private File destination; + private final HostConfig config; + private final ContextName cn; + private final File source; + private final File destination; MigrateApp(HostConfig config, ContextName cn, File source, File destination) { this.config = config; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/HostRuleSet.java tomcat10-10.1.52/java/org/apache/catalina/startup/HostRuleSet.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/HostRuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/HostRuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,6 @@ /** * RuleSet for processing the contents of a Host definition element. This RuleSet does NOT * include any rules for nested Context which should be added via instances of ContextRuleSet. - * - * @author Craig R. McClanahan */ public class HostRuleSet implements RuleSet { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/LifecycleListenerRule.java tomcat10-10.1.52/java/org/apache/catalina/startup/LifecycleListenerRule.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/LifecycleListenerRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/LifecycleListenerRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -97,7 +97,7 @@ // Check the container's parent for the specified attribute if (p != null && className == null) { String configClass = (String) IntrospectionUtils.getProperty(p, attributeName); - if (configClass != null && configClass.length() > 0) { + if (configClass != null && !configClass.isEmpty()) { className = configClass; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/ListenerCreateRule.java tomcat10-10.1.52/java/org/apache/catalina/startup/ListenerCreateRule.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/ListenerCreateRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/ListenerCreateRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,7 +49,7 @@ } catch (Exception e) { String className = getRealClassName(attributes); if (log.isDebugEnabled()) { - log.info(sm.getString("listener.createFailed", className), e); + log.debug(sm.getString("listener.createFailed", className), e); } else { log.info(sm.getString("listener.createFailed", className)); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/startup/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -75,6 +75,7 @@ contextConfig.jarFile=Impossible de traiter les annotations du JAR [{0}] contextConfig.jspFile.error=Le fichier JSP [{0}] doit commencer par un ''/'' contextConfig.jspFile.warning=WARNING : Le fichier JSP [{0}] doit commencer par un ''/'' dans l''API Servlet 2.4 +contextConfig.loadError=Erreur lors du chargement du code généré contextConfig.missingRealm=Aucun royaume (realm) n'a été configuré pour réaliser l'authentification contextConfig.noAntiLocking=La valeur [{0}] configurée pour java.io.tmpdir ne correspond pas à un répertoire valide, le paramètre antiResourceLocking configuré pour l''application [{1}] sera ignoré contextConfig.noJsp=Le groupe de propriétés JSP pour l''URL [{0}] sera sauté, le Servlet JSP avec le nom [{1}] n''a pas été trouvé diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/startup/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -75,6 +75,7 @@ contextConfig.jarFile=Jar [{0}] のアノテーションを処理できません contextConfig.jspFile.error=JSPファイル [{0}] は''/''で始まらなければいけません contextConfig.jspFile.warning=警告: Servlet 2.4ではJSPファイル [{0}] は''/''で始まらなければいけません +contextConfig.loadError=生成されたコードのロード中にエラーが発生しました contextConfig.missingRealm=認証するためにレルムが設定されていません contextConfig.noAntiLocking=java.io.tmpdirに設定された値[{0}]が有効なディレクトリを指していません。 Webアプリケーション[{1}]のantiResourceLocking設定は無視されます。 contextConfig.noJsp=URL [{0}] の JSP プロパティ グループをスキップしています。名前 [{1}] の JSP サーブレットが見つかりません @@ -152,7 +153,7 @@ hostConfig.migrateError=マイグレーション失敗 hostConfig.reload=リロード中のコンテキスト [{0}] hostConfig.resourceNotAbsolute=[{1}] は完全パスではないためコンテキスト [{0}] からリソースを削除できません -hostConfig.start=HostConfig: 処理を停止します +hostConfig.start=HostConfig: 処理を開始します hostConfig.stop=HostConfig: 処理を停止します hostConfig.undeploy=コンテキストパス [{0}] のWebアプリケーションの配備を解除します hostConfig.undeployVersion=コンテキスト [{0}] について有効なセッションの存在しない古いバージョンの配備を解除します。 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/startup/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,12 @@ contextConfig.defaultMissing=Не обнаружен глобальный web.xml contextConfig.defaultPosition=Произошло в строке [{0}] столбце [{1}] contextConfig.inputStreamWebResource=Не возможно обработать веб ресурс [{0}] для аннотаций\n +contextConfig.jspFile.error=JSP файл [{0}] должен начинаться с ''/'' +contextConfig.processAnnotationsDir.debug=Сканируется директория в поисках файлов классов с аннотациями [{0}] contextConfig.tomcatWebXmlError=Ошибка обработки /WEB-INF/tomcat-web.xml +expandWar.createFailed=Невозможно создать директорию [{0}] + hostConfig.deployDir=Установка веб приложения в папку [{0}] hostConfig.deployWar.error=Ошибка при развертывании архива с веб-приложением [{0}] hostConfig.docBaseUrlInvalid=Предоставленый docBase не может быть представлен в виде URL diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/startup/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -69,6 +69,7 @@ contextConfig.jarFile=无法处理Jar[{0}]的注解 contextConfig.jspFile.error=JSP文件[{0}]必须以''/''开头。 contextConfig.jspFile.warning=警告:在Servlet 2.4 中,JSP文件[{0}]必须以‘/’开头 +contextConfig.loadError=加载生成的代码错误 contextConfig.missingRealm=对应的认证领域未配置 contextConfig.noAntiLocking=配置 java.io.tmpdir的值[{0}]未指向有效路径。Web应用[{1}]antiResourceLocking配置将被忽略 contextConfig.processAnnotationsDir.debug=使用注解 [{0}]扫描目录中的类文件 @@ -126,7 +127,7 @@ hostConfig.deployDir.finished=Web应用程序目录[{0}]的部署已在[{1}]毫秒内完成 hostConfig.deployDir.threaded.error=等待目录的多线程部署完成时出错 hostConfig.deployWar=正在部署web应用程序存档文件[{0}] -hostConfig.deployWar.error=部署 Web 应用程序 archive [{0}] 时出错 +hostConfig.deployWar.error=部署 Web 应用程序存档 [{0}] 时出错 hostConfig.deployWar.finished=web应用程序存档文件[{0}]的部署已在[{1}]ms内完成 hostConfig.deployWar.hiddenDir=将忽略目录[{0}],因为WAR [{1}]优先,unpackWAR为false hostConfig.deployWar.threaded.error=等待WAR文件的多线程部署完成时出错 @@ -162,7 +163,7 @@ tomcat.noContextClass=无法为主机[{1}]和url[{2}]实例化上下文类[{0}] tomcat.noContextXml=不能找到web 应用的context.xml [{0}] -userConfig.database=加载用户数据库异常 +userConfig.database=用户数据库加载异常 userConfig.deploy=正在为用户[{0}]部署web应用程序 userConfig.deploy.threaded.error=等待用户目录的多线程部署完成时出错 userConfig.deploying=正在部署用户 web 应用程序 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/MimeTypeMappings.properties tomcat10-10.1.52/java/org/apache/catalina/startup/MimeTypeMappings.properties --- tomcat10-10.1.34/java/org/apache/catalina/startup/MimeTypeMappings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/MimeTypeMappings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -62,6 +62,7 @@ atx=application/vnd.antix.game-component au=audio/basic avi=video/x-msvideo +avif=image/avif avx=video/x-rad-screenplay aw=application/applixware axa=audio/annodex @@ -388,6 +389,7 @@ json=application/json jsonml=application/jsonml+json jspf=text/plain +jxl=image/jxl kar=audio/midi karbon=application/vnd.kde.karbon kfo=application/vnd.kde.kformula @@ -430,6 +432,8 @@ m1v=video/mpeg m21=application/mp21 m2a=audio/mpeg +m2t=video/mp2t +m2ts=video/mp2t m2v=video/mpeg m3a=audio/mpeg m3u=audio/x-mpegurl @@ -522,7 +526,7 @@ msi=application/x-msdownload msl=application/vnd.mobius.msl msty=application/vnd.muvee.style -mts=model/vnd.mts +mts=video/mp2t mus=application/vnd.musician musicxml=application/vnd.recordare.musicxml+xml mvb=application/x-msmediaview @@ -777,6 +781,8 @@ spq=application/scvp-vp-request spx=audio/ogg sql=application/x-sql +sqlite=application/vnd.sqlite3 +sqlite3=application/vnd.sqlite3 src=application/x-wais-source srt=application/x-subrip sru=application/sru+xml @@ -839,6 +845,7 @@ tr=text/troff tra=application/vnd.trueapp trm=application/x-msterminal +ts=video/mp2t tsd=application/timestamped-data tsv=text/tab-separated-values ttc=font/collection diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/NamingRuleSet.java tomcat10-10.1.52/java/org/apache/catalina/startup/NamingRuleSet.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/NamingRuleSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/NamingRuleSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,9 +21,6 @@ /** * RuleSet for processing the JNDI Enterprise Naming Context resource declaration elements. - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public class NamingRuleSet implements RuleSet { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/PasswdUserDatabase.java tomcat10-10.1.52/java/org/apache/catalina/startup/PasswdUserDatabase.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/PasswdUserDatabase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/PasswdUserDatabase.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,8 +30,6 @@ /** * Concrete implementation of the UserDatabase interface that processes the /etc/passwd file * on a Unix system. - * - * @author Craig R. McClanahan */ public final class PasswdUserDatabase implements UserDatabase { @@ -45,7 +43,7 @@ /** - * The set of home directories for all defined users, keyed by user name. + * The set of home directories for all defined users, keyed by username. */ private final Map homes = new HashMap<>(); @@ -88,9 +86,9 @@ try (BufferedReader reader = new BufferedReader(new FileReader(PASSWORD_FILE))) { String line = reader.readLine(); while (line != null) { - String tokens[] = line.split(":"); + String[] tokens = line.split(":"); // Need non-zero 1st and 6th tokens - if (tokens.length > 5 && tokens[0].length() > 0 && tokens[5].length() > 0) { + if (tokens.length > 5 && !tokens[0].isEmpty() && !tokens[5].isEmpty()) { // Add this user and corresponding directory homes.put(tokens[0], tokens[5]); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/SafeForkJoinWorkerThreadFactory.java tomcat10-10.1.52/java/org/apache/catalina/startup/SafeForkJoinWorkerThreadFactory.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/SafeForkJoinWorkerThreadFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/SafeForkJoinWorkerThreadFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,7 @@ * Provides a {@link ForkJoinWorkerThreadFactory} that provides {@link ForkJoinWorkerThread}s that won't trigger memory * leaks due to retained references to web application class loaders. *

      - * Note: This class must be available on the boot strap class path for it to be visible to {@link ForkJoinPool}. + * Note: This class must be available on the bootstrap class path for it to be visible to {@link ForkJoinPool}. */ public class SafeForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/SetNextNamingRule.java tomcat10-10.1.52/java/org/apache/catalina/startup/SetNextNamingRule.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/SetNextNamingRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/SetNextNamingRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -78,7 +78,7 @@ Object parent = digester.peek(1); boolean context = false; - NamingResourcesImpl namingResources = null; + NamingResourcesImpl namingResources; if (parent instanceof Context) { namingResources = ((Context) parent).getNamingResources(); context = true; @@ -105,13 +105,7 @@ @Override public String toString() { - StringBuilder sb = new StringBuilder("SetNextRule["); - sb.append("methodName="); - sb.append(methodName); - sb.append(", paramType="); - sb.append(paramType); - sb.append(']'); - return sb.toString(); + return "SetNextRule[" + "methodName=" + methodName + ", paramType=" + paramType + ']'; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/Tomcat.java tomcat10-10.1.52/java/org/apache/catalina/startup/Tomcat.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/Tomcat.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/Tomcat.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,6 +27,7 @@ import java.net.URLConnection; import java.security.Principal; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -131,8 +132,6 @@ * * @see TestTomcat - * - * @author Costin Manolache */ public class Tomcat { @@ -192,7 +191,7 @@ } /** - * The the hostname of the default host, default is 'localhost'. + * The hostname of the default host, default is 'localhost'. * * @param s The default host name */ @@ -462,7 +461,7 @@ /** * Add a user for the in-memory realm. All created apps use this by default, can be replaced using setRealm(). * - * @param user The user name + * @param user The username * @param pass The password */ public void addUser(String user, String pass) { @@ -474,7 +473,7 @@ * * @see #addUser(String, String) * - * @param user The user name + * @param user The username * @param role The role name */ public void addRole(String user, String role) { @@ -665,12 +664,12 @@ * @return the deployed context */ public Context addWebapp(Host host, String contextPath, String docBase) { - LifecycleListener listener = null; + LifecycleListener listener; try { Class clazz = Class.forName(getHost().getConfigClass()); listener = (LifecycleListener) clazz.getConstructor().newInstance(); } catch (ReflectiveOperationException e) { - // Wrap in IAE since we can't easily change the method signature to + // Wrap in IAE since we can't easily change the method signature // to throw the specific checked exceptions throw new IllegalArgumentException(e); } @@ -811,7 +810,7 @@ } try { baseFile = baseFile.getCanonicalFile(); - } catch (IOException e) { + } catch (IOException ioe) { baseFile = baseFile.getAbsoluteFile(); } server.setCatalinaBase(baseFile); @@ -828,7 +827,7 @@ } try { homeFile = homeFile.getCanonicalFile(); - } catch (IOException e) { + } catch (IOException ioe) { homeFile = homeFile.getAbsoluteFile(); } server.setCatalinaHome(homeFile); @@ -876,10 +875,10 @@ /** - * By default, when calling addWebapp() to create a Context, the settings from from the default web.xml are added to - * the context. Calling this method with a false value prior to calling addWebapp() allows to opt out - * of the default settings. In that event you will need to add the configurations yourself, either programmatically - * or by using web.xml deployment descriptors. + * By default, when calling addWebapp() to create a Context, the settings from the default web.xml are added to the + * context. Calling this method with a false value prior to calling addWebapp() allows to opt out of + * the default settings. In that event you will need to add the configurations yourself, either programmatically or + * by using web.xml deployment descriptors. * * @param addDefaultWebXmlToWebapp false will prevent the class from automatically adding the default * settings when calling addWebapp(). true will add the default @@ -909,7 +908,7 @@ loggerName.append(host.getName()); loggerName.append("].["); // Context name - if (contextName == null || contextName.equals("")) { + if (contextName == null || contextName.isEmpty()) { loggerName.append('/'); } else if (contextName.startsWith("##")) { loggerName.append('/'); @@ -992,7 +991,6 @@ *

    • MIME mappings (subset of those in conf/web.xml)
    • *
    • Welcome files
    • * - * TODO: Align the MIME mappings with conf/web.xml - possibly via a common file. * * @param contextPath The path of the context to set the defaults for */ @@ -1034,11 +1032,15 @@ ctx.addWelcomeFile("index.html"); ctx.addWelcomeFile("index.htm"); ctx.addWelcomeFile("index.jsp"); + // Any application configured welcome files should override the defaults. + if (ctx instanceof StandardContext) { + ((StandardContext) ctx).setReplaceWelcomeFiles(true); + } } /** - * Add the default MIME type mappings to the provide Context. + * Add the default MIME type mappings to the provided Context. * * @param context The web application to which the default MIME type mappings should be added. */ @@ -1049,8 +1051,8 @@ for (Map.Entry entry : defaultMimeMappings.entrySet()) { context.addMimeMapping((String) entry.getKey(), (String) entry.getValue()); } - } catch (IOException e) { - throw new IllegalStateException(sm.getString("tomcat.defaultMimeTypeMappingsFail"), e); + } catch (IOException ioe) { + throw new IllegalStateException(sm.getString("tomcat.defaultMimeTypeMappingsFail"), ioe); } } @@ -1080,6 +1082,7 @@ } } } catch (ClassCastException e) { + // Ignore } } } @@ -1181,9 +1184,9 @@ if (entry != null) { result = UriUtil.buildJarUrl(docBase, Constants.ApplicationContextXml); } - } catch (IOException e) { + } catch (IOException ioe) { Logger.getLogger(getLoggerName(getHost(), contextName)).log(Level.WARNING, - sm.getString("tomcat.noContextXml", docBase), e); + sm.getString("tomcat.noContextXml", docBase), ioe); } return result; } @@ -1192,7 +1195,7 @@ // Graal native images don't load any configuration except the VM default if (JreCompat.isGraalAvailable()) { try (InputStream is = new FileInputStream( - new File(System.getProperty("java.util.logging.config.file", "conf/logging.properties")))) { + System.getProperty("java.util.logging.config.file", "conf/logging.properties"))) { LogManager.getLogManager().readConfiguration(is); } catch (SecurityException | IOException e) { // Ignore, the VM default will be used @@ -1216,10 +1219,7 @@ } else if (args[i].equals("--catalina")) { // This was already processed before // Skip the rest of the arguments as they are for Catalina - ArrayList result = new ArrayList<>(); - for (int j = i + 1; j < args.length; j++) { - result.add(args[j]); - } + ArrayList result = new ArrayList<>(Arrays.asList(args).subList(i + 1, args.length)); catalinaArguments = result.toArray(new String[0]); break; } @@ -1233,28 +1233,34 @@ boolean await = false; String path = ""; // Process command line parameters + label: for (int i = 0; i < args.length; i++) { - if (args[i].equals("--war")) { - if (++i >= args.length) { - throw new IllegalArgumentException(sm.getString("tomcat.invalidCommandLine", args[i - 1])); - } - File war = new File(args[i]); - tomcat.addWebapp(path, war.getAbsolutePath()); - } else if (args[i].equals("--path")) { - if (++i >= args.length) { - throw new IllegalArgumentException(sm.getString("tomcat.invalidCommandLine", args[i - 1])); - } - path = args[i]; - } else if (args[i].equals("--await")) { - await = true; - } else if (args[i].equals("--no-jmx")) { - // This was already processed before - } else if (args[i].equals("--catalina")) { - // This was already processed before - // Skip the rest of the arguments as they are for Catalina - break; - } else { - throw new IllegalArgumentException(sm.getString("tomcat.invalidCommandLine", args[i])); + switch (args[i]) { + case "--war": + if (++i >= args.length) { + throw new IllegalArgumentException(sm.getString("tomcat.invalidCommandLine", args[i - 1])); + } + File war = new File(args[i]); + tomcat.addWebapp(path, war.getAbsolutePath()); + break; + case "--path": + if (++i >= args.length) { + throw new IllegalArgumentException(sm.getString("tomcat.invalidCommandLine", args[i - 1])); + } + path = args[i]; + break; + case "--await": + await = true; + break; + case "--no-jmx": + // This was already processed before + break; + case "--catalina": + // This was already processed before + // Skip the rest of the arguments as they are for Catalina + break label; + default: + throw new IllegalArgumentException(sm.getString("tomcat.invalidCommandLine", args[i])); } } tomcat.start(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/Tool.java tomcat10-10.1.52/java/org/apache/catalina/startup/Tool.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/Tool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/Tool.java 2026-01-23 19:33:36.000000000 +0000 @@ -60,8 +60,6 @@ *
    • ${arguments} - Command line arguments to be passed to the application's main() * method.
    • * - * - * @author Craig R. McClanahan */ public final class Tool { @@ -110,7 +108,7 @@ * @param args Command line arguments to be processed */ @SuppressWarnings("null") - public static void main(String args[]) { + public static void main(String[] args) { // Verify that "catalina.home" was passed. if (catalinaHome == null) { @@ -120,28 +118,30 @@ // Process command line options int index = 0; + label: while (true) { if (index == args.length) { usage(); System.exit(1); } - if ("-ant".equals(args[index])) { - ant = true; - } else if ("-common".equals(args[index])) { - common = true; - } else if ("-server".equals(args[index])) { - server = true; - } else if ("-shared".equals(args[index])) { - shared = true; - } else { - break; + switch (args[index]) { + case "-ant": + ant = true; + break; + case "-common": + common = true; + break; + case "-server": + server = true; + break; + case "-shared": + shared = true; + break; + default: + break label; } index++; } - if (index > args.length) { - usage(); - System.exit(1); - } // Set "ant.home" if requested if (ant) { @@ -191,14 +191,14 @@ } Method method = null; - String params[] = new String[args.length - index]; + String[] params = new String[args.length - index]; System.arraycopy(args, index, params, 0, params.length); try { if (log.isTraceEnabled()) { log.trace("Identifying main() method"); } String methodName = "main"; - Class paramTypes[] = new Class[1]; + Class[] paramTypes = new Class[1]; paramTypes[0] = params.getClass(); method = clazz.getMethod(methodName, paramTypes); } catch (Throwable t) { @@ -212,13 +212,13 @@ if (log.isTraceEnabled()) { log.trace("Calling main() method"); } - Object paramValues[] = new Object[1]; + Object[] paramValues = new Object[1]; paramValues[0] = params; method.invoke(null, paramValues); } catch (Throwable t) { - t = Bootstrap.unwrapInvocationTargetException(t); - Bootstrap.handleThrowable(t); - log.error("Exception calling main() method", t); + Throwable throwable = Bootstrap.unwrapInvocationTargetException(t); + Bootstrap.handleThrowable(throwable); + log.error("Exception calling main() method", throwable); System.exit(1); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/UserConfig.java tomcat10-10.1.52/java/org/apache/catalina/startup/UserConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/UserConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/UserConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,8 +40,6 @@ * a web application in a directory with the specified name in their home directories. The context path of each deployed * application will be set to ~xxxxx, where xxxxx is the username of the owning user for that web * application - * - * @author Craig R. McClanahan */ public final class UserConfig implements LifecycleListener { @@ -212,7 +210,7 @@ * @param allow The new allow expression */ public void setAllow(String allow) { - if (allow == null || allow.length() == 0) { + if (allow == null || allow.isEmpty()) { this.allow = null; } else { this.allow = Pattern.compile(allow); @@ -237,7 +235,7 @@ * @param deny The new deny expression */ public void setDeny(String deny) { - if (deny == null || deny.length() == 0) { + if (deny == null || deny.isEmpty()) { this.deny = null; } else { this.deny = Pattern.compile(deny); @@ -287,7 +285,7 @@ } // Load the user database object for this host - UserDatabase database = null; + UserDatabase database; try { Class clazz = Class.forName(userClass); database = (UserDatabase) clazz.getConstructor().newInstance(); @@ -394,20 +392,16 @@ return false; } if (allow != null) { - if (allow.matcher(user).matches()) { - return true; - } else { - return false; - } + return allow.matcher(user).matches(); } return true; } private static class DeployUserDirectory implements Runnable { - private UserConfig config; - private String user; - private String home; + private final UserConfig config; + private final String user; + private final String home; DeployUserDirectory(UserConfig config, String user, String home) { this.config = config; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/UserDatabase.java tomcat10-10.1.52/java/org/apache/catalina/startup/UserDatabase.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/UserDatabase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/UserDatabase.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,6 @@ /** * Abstraction of the set of users defined by the operating system on the current server platform. - * - * @author Craig R. McClanahan */ public interface UserDatabase { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/WebAnnotationSet.java tomcat10-10.1.52/java/org/apache/catalina/startup/WebAnnotationSet.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/WebAnnotationSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/WebAnnotationSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.Objects; import jakarta.annotation.Resource; import jakarta.annotation.Resources; @@ -333,11 +334,7 @@ private static String getType(Resource annotation, Class defaultType) { Class type = annotation.type(); if (type == null || type.equals(Object.class)) { - if (defaultType != null) { - type = defaultType; - } else { - type = Object.class; - } + type = Objects.requireNonNullElse(defaultType, Object.class); } return Introspection.convertPrimitiveType(type).getCanonicalName(); } @@ -345,7 +342,7 @@ private static String getName(Resource annotation, String defaultName) { String name = annotation.name(); - if (name == null || name.equals("")) { + if (name == null || name.isEmpty()) { if (defaultName != null) { name = defaultName; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/startup/WebappServiceLoader.java tomcat10-10.1.52/java/org/apache/catalina/startup/WebappServiceLoader.java --- tomcat10-10.1.34/java/org/apache/catalina/startup/WebappServiceLoader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/startup/WebappServiceLoader.java 2026-01-23 19:33:36.000000000 +0000 @@ -75,7 +75,7 @@ this.context = context; this.servletContext = context.getServletContext(); String containerSciFilter = context.getContainerSciFilter(); - if (containerSciFilter != null && containerSciFilter.length() > 0) { + if (containerSciFilter != null && !containerSciFilter.isEmpty()) { containerSciFilterPattern = Pattern.compile(containerSciFilter); } else { containerSciFilterPattern = null; @@ -214,7 +214,7 @@ line = line.substring(0, i); } line = line.trim(); - if (line.length() == 0) { + if (line.isEmpty()) { continue; } servicesFound.add(line); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/CatalinaClusterSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/CatalinaClusterSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/CatalinaClusterSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/CatalinaClusterSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -64,15 +64,15 @@ } // Store nested element // ClusterValve are not store at Hosts element, see - Valve valves[] = cluster.getValves(); + Valve[] valves = cluster.getValves(); storeElementArray(aWriter, indent, valves); if (aCluster instanceof SimpleTcpCluster) { // Store nested elements - LifecycleListener listeners[] = ((SimpleTcpCluster) cluster).findLifecycleListeners(); + LifecycleListener[] listeners = ((SimpleTcpCluster) cluster).findLifecycleListeners(); storeElementArray(aWriter, indent, listeners); // Store nested elements - ClusterListener mlisteners[] = ((SimpleTcpCluster) cluster).findClusterListeners(); + ClusterListener[] mlisteners = ((SimpleTcpCluster) cluster).findClusterListeners(); List clusterListeners = new ArrayList<>(); for (ClusterListener clusterListener : mlisteners) { if (clusterListener != deployer) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/CertificateStoreAppender.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/CertificateStoreAppender.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/CertificateStoreAppender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/CertificateStoreAppender.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ -/** +/* * 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. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/ConnectorSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/ConnectorSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/ConnectorSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/ConnectorSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ -/** +/* * 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. @@ -35,7 +35,7 @@ if (aConnector instanceof Connector) { Connector connector = (Connector) aConnector; // Store nested elements - LifecycleListener listeners[] = connector.findLifecycleListeners(); + LifecycleListener[] listeners = connector.findLifecycleListeners(); storeElementArray(aWriter, indent, listeners); // Store nested elements UpgradeProtocol[] upgradeProtocols = connector.findUpgradeProtocols(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/ConnectorStoreAppender.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ -/** +/* * 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. @@ -113,7 +113,7 @@ // Acquire the list of properties for this bean ProtocolHandler protocolHandler = bean.getProtocolHandler(); // Acquire the list of properties for this bean - PropertyDescriptor descriptors[] = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors(); + PropertyDescriptor[] descriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors(); if (descriptors == null) { descriptors = new PropertyDescriptor[0]; } @@ -254,7 +254,8 @@ File file = new File(System.getProperty(Globals.CATALINA_BASE_PROP)); try { file = file.getCanonicalFile(); - } catch (IOException e) { + } catch (IOException ioe) { + // Ignore } return file; } @@ -268,7 +269,7 @@ } try { jkHomeBase = file.getCanonicalFile(); - } catch (IOException e) { + } catch (IOException ioe) { jkHomeBase = file; } return jkHomeBase; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/CredentialHandlerSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/CredentialHandlerSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/CredentialHandlerSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/CredentialHandlerSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,7 +28,7 @@ */ public class CredentialHandlerSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(CredentialHandlerSF.class); + private static final Log log = LogFactory.getLog(CredentialHandlerSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/GlobalNamingResourcesSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/GlobalNamingResourcesSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/GlobalNamingResourcesSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/GlobalNamingResourcesSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,7 +26,7 @@ * store server.xml GlobalNamingResource. */ public class GlobalNamingResourcesSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(GlobalNamingResourcesSF.class); + private static final Log log = LogFactory.getLog(GlobalNamingResourcesSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/InterceptorSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/InterceptorSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/InterceptorSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/InterceptorSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,7 +28,7 @@ */ public class InterceptorSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(InterceptorSF.class); + private static final Log log = LogFactory.getLog(InterceptorSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/LoaderSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/LoaderSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/LoaderSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/LoaderSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,7 +28,7 @@ */ public class LoaderSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(LoaderSF.class); + private static final Log log = LogFactory.getLog(LoaderSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { @@ -44,9 +44,7 @@ } } else { if (log.isWarnEnabled()) { - if (log.isWarnEnabled()) { - log.warn(sm.getString("factory.storeNoDescriptor", aElement.getClass())); - } + log.warn(sm.getString("factory.storeNoDescriptor", aElement.getClass())); } } } @@ -64,10 +62,7 @@ return false; } WebappLoader wloader = (WebappLoader) loader; - if ((wloader.getDelegate() != false) || - !wloader.getLoaderClass().equals("org.apache.catalina.loader.WebappClassLoader")) { - return false; - } - return true; + return (!wloader.getDelegate()) && + wloader.getLoaderClass().equals("org.apache.catalina.loader.WebappClassLoader"); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/ManagerSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/ManagerSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/ManagerSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/ManagerSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,7 +29,7 @@ */ public class ManagerSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(ManagerSF.class); + private static final Log log = LogFactory.getLog(ManagerSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { @@ -62,10 +62,7 @@ */ protected boolean isDefaultManager(StandardManager smanager) { - if (!"SESSIONS.ser".equals(smanager.getPathname()) || (smanager.getMaxActiveSessions() != -1)) { - return false; - } - return true; + return "SESSIONS.ser".equals(smanager.getPathname()) && (smanager.getMaxActiveSessions() == -1); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/NamingResourcesSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/NamingResourcesSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/NamingResourcesSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/NamingResourcesSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,7 +32,7 @@ * Store server.xml elements Resources at context and GlobalNamingResources */ public class NamingResourcesSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(NamingResourcesSF.class); + private static final Log log = LogFactory.getLog(NamingResourcesSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/OpenSSLConfSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/OpenSSLConfSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/OpenSSLConfSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/OpenSSLConfSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ -/** +/* * 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. @@ -17,6 +17,7 @@ package org.apache.catalina.storeconfig; import java.io.PrintWriter; +import java.util.Set; import org.apache.tomcat.util.net.openssl.OpenSSLConf; import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd; @@ -26,6 +27,8 @@ */ public class OpenSSLConfSF extends StoreFactoryBase { + private static final Set INTERNAL_COMMANDS = Set.of(OpenSSLConfCmd.NO_OCSP_CHECK); + /** * Store nested OpenSSLConfCmd elements. *

      @@ -37,9 +40,13 @@ if (aOpenSSLConf instanceof OpenSSLConf) { OpenSSLConf openSslConf = (OpenSSLConf) aOpenSSLConf; // Store nested elements - OpenSSLConfCmd[] openSSLConfCmds = openSslConf.getCommands().toArray(new OpenSSLConfCmd[0]); - storeElementArray(aWriter, indent + 2, openSSLConfCmds); + for (OpenSSLConfCmd openSSLConfCmd : openSslConf.getCommands()) { + // Don't store the internal commands that are created from other SslHostConfig attributes. + if (INTERNAL_COMMANDS.contains(openSSLConfCmd.getName())) { + continue; + } + storeElement(aWriter, indent + 2, openSSLConfCmd); + } } } - } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/RealmSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/RealmSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/RealmSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/RealmSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,7 +29,7 @@ */ public class RealmSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(RealmSF.class); + private static final Log log = LogFactory.getLog(RealmSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/SSLHostConfigSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ -/** +/* * 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. @@ -31,7 +31,7 @@ */ public class SSLHostConfigSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(SSLHostConfigSF.class); + private static final Log log = LogFactory.getLog(SSLHostConfigSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { @@ -48,7 +48,7 @@ SSLHostConfig bean2 = (SSLHostConfig) getStoreAppender().defaultInstance(aElement); SSLHostConfig sslHostConfig = (SSLHostConfig) aElement; if (!bean2.getProtocols().equals(sslHostConfig.getProtocols())) { - StringBuffer protocolsValue = new StringBuffer(); + StringBuilder protocolsValue = new StringBuilder(); for (String protocol : sslHostConfig.getProtocols()) { protocolsValue.append('+').append(protocol); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardContextSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardContextSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardContextSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardContextSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -56,7 +56,7 @@ */ public class StandardContextSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(StandardContextSF.class); + private static final Log log = LogFactory.getLog(StandardContextSF.class); /** * Store a Context as Separate file as configFile value from context exists. filename can be relative to @@ -199,7 +199,7 @@ if (aContext instanceof StandardContext) { StandardContext context = (StandardContext) aContext; // Store nested elements - LifecycleListener listeners[] = context.findLifecycleListeners(); + LifecycleListener[] listeners = context.findLifecycleListeners(); List listenersArray = new ArrayList<>(); for (LifecycleListener listener : listeners) { if (!(listener instanceof ThreadLocalLeakPreventionListener)) { @@ -209,7 +209,7 @@ storeElementArray(aWriter, indent, listenersArray.toArray()); // Store nested elements - Valve valves[] = context.getPipeline().getValves(); + Valve[] valves = context.getPipeline().getValves(); storeElementArray(aWriter, indent, valves); // Store nested elements @@ -239,10 +239,10 @@ storeElement(aWriter, indent, resources); // Store nested elements - String wLifecycles[] = context.findWrapperLifecycles(); + String[] wLifecycles = context.findWrapperLifecycles(); getStoreAppender().printTagArray(aWriter, "WrapperListener", indent + 2, wLifecycles); // Store nested elements - String wListeners[] = context.findWrapperListeners(); + String[] wListeners = context.findWrapperListeners(); getStoreAppender().printTagArray(aWriter, "WrapperLifecycle", indent + 2, wListeners); // Store nested elements @@ -288,8 +288,8 @@ file = new File(file, host.getName()); try { file = file.getCanonicalFile(); - } catch (IOException e) { - log.error(sm.getString("standardContextSF.canonicalPathError"), e); + } catch (IOException ioe) { + log.error(sm.getString("standardContextSF.canonicalPathError"), ioe); } } return file; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardEngineSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardEngineSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardEngineSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardEngineSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,7 +44,7 @@ if (aEngine instanceof StandardEngine) { StandardEngine engine = (StandardEngine) aEngine; // Store nested elements - LifecycleListener listeners[] = engine.findLifecycleListeners(); + LifecycleListener[] listeners = engine.findLifecycleListeners(); storeElementArray(aWriter, indent, listeners); // Store nested element @@ -60,7 +60,7 @@ } // Store nested elements - Valve valves[] = engine.getPipeline().getValves(); + Valve[] valves = engine.getPipeline().getValves(); if (valves != null && valves.length > 0) { List engineValves = new ArrayList<>(); for (Valve valve : valves) { @@ -77,7 +77,7 @@ storeElement(aWriter, indent, cluster); } // store all elements - Container children[] = engine.findChildren(); + Container[] children = engine.findChildren(); storeElementArray(aWriter, indent, children); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardHostSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardHostSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardHostSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardHostSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,11 +44,11 @@ if (aHost instanceof StandardHost) { StandardHost host = (StandardHost) aHost; // Store nested elements - LifecycleListener listeners[] = host.findLifecycleListeners(); + LifecycleListener[] listeners = host.findLifecycleListeners(); storeElementArray(aWriter, indent, listeners); // Store nested elements - String aliases[] = host.findAliases(); + String[] aliases = host.findAliases(); getStoreAppender().printTagArray(aWriter, "Alias", indent + 2, aliases); // Store nested element @@ -64,7 +64,7 @@ } // Store nested elements - Valve valves[] = host.getPipeline().getValves(); + Valve[] valves = host.getPipeline().getValves(); if (valves != null && valves.length > 0) { List hostValves = new ArrayList<>(); for (Valve valve : valves) { @@ -88,7 +88,7 @@ } // store all elements - Container children[] = host.findChildren(); + Container[] children = host.findChildren(); storeElementArray(aWriter, indent, children); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardServerSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardServerSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardServerSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardServerSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,7 +45,7 @@ if (aObject instanceof StandardServer) { StandardServer server = (StandardServer) aObject; // Store nested elements - LifecycleListener listeners[] = server.findLifecycleListeners(); + LifecycleListener[] listeners = server.findLifecycleListeners(); storeElementArray(aWriter, indent, listeners); // Store nested element NamingResourcesImpl globalNamingResources = server.getGlobalNamingResources(); @@ -55,7 +55,7 @@ elementDesc.getStoreFactory().store(aWriter, indent, globalNamingResources); } // Store nested elements - Service services[] = server.findServices(); + Service[] services = server.findServices(); storeElementArray(aWriter, indent, services); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardServiceSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardServiceSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StandardServiceSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StandardServiceSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,14 +40,14 @@ if (aService instanceof StandardService) { StandardService service = (StandardService) aService; // Store nested elements - LifecycleListener listeners[] = service.findLifecycleListeners(); + LifecycleListener[] listeners = service.findLifecycleListeners(); storeElementArray(aWriter, indent, listeners); // Store nested elements Executor[] executors = service.findExecutors(); storeElementArray(aWriter, indent, executors); - Connector connectors[] = service.findConnectors(); + Connector[] connectors = service.findConnectors(); storeElementArray(aWriter, indent, connectors); // Store nested element diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreAppender.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreAppender.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreAppender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreAppender.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,9 +35,9 @@ /** * The set of classes that represent persistable properties. */ - private static Class persistables[] = { String.class, Integer.class, Integer.TYPE, Boolean.class, Boolean.TYPE, - Byte.class, Byte.TYPE, Character.class, Character.TYPE, Double.class, Double.TYPE, Float.class, Float.TYPE, - Long.class, Long.TYPE, Short.class, Short.TYPE, InetAddress.class }; + private static final Class[] persistables = { String.class, Integer.class, Integer.TYPE, Boolean.class, + Boolean.TYPE, Byte.class, Byte.TYPE, Character.class, Character.TYPE, Double.class, Double.TYPE, + Float.class, Float.TYPE, Long.class, Long.TYPE, Short.class, Short.TYPE, InetAddress.class }; private int pos = 0; @@ -211,7 +211,7 @@ } // Acquire the list of properties for this bean - PropertyDescriptor descriptors[] = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors(); + PropertyDescriptor[] descriptors = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors(); if (descriptors == null) { descriptors = new PropertyDescriptor[0]; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreConfig.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,7 +38,7 @@ * Store Server/Service/Host/Context at file or PrintWriter. Default server.xml is at $catalina.base/conf/server.xml */ public class StoreConfig implements IStoreConfig { - private static Log log = LogFactory.getLog(StoreConfig.class); + private static final Log log = LogFactory.getLog(StoreConfig.class); protected static final StringManager sm = StringManager.getManager(Constants.Package); private String serverFilename = "conf/server.xml"; @@ -102,7 +102,7 @@ */ public synchronized void storeServer(String aServerName, boolean backup, boolean externalAllowed) throws MalformedObjectNameException { - if (aServerName == null || aServerName.length() == 0) { + if (aServerName == null || aServerName.isEmpty()) { log.error(sm.getString("config.emptyObjectName")); return; } @@ -111,8 +111,7 @@ if (mserver.isRegistered(objectName)) { try { Server aServer = (Server) mserver.getAttribute(objectName, "managedResource"); - StoreDescription desc = null; - desc = getRegistry().findDescription(StandardContext.class); + StoreDescription desc = getRegistry().findDescription(StandardContext.class); if (desc != null) { boolean oldSeparate = desc.isStoreSeparate(); boolean oldBackup = desc.isBackup(); @@ -150,7 +149,7 @@ */ public synchronized void storeContext(String aContextName, boolean backup, boolean externalAllowed) throws MalformedObjectNameException { - if (aContextName == null || aContextName.length() == 0) { + if (aContextName == null || aContextName.isEmpty()) { log.error(sm.getString("config.emptyObjectName")); return; } @@ -161,8 +160,7 @@ Context aContext = (Context) mserver.getAttribute(objectName, "managedResource"); URL configFile = aContext.getConfigFile(); if (configFile != null) { - StoreDescription desc = null; - desc = getRegistry().findDescription(aContext.getClass()); + StoreDescription desc = getRegistry().findDescription(aContext.getClass()); if (desc != null) { boolean oldSeparate = desc.isStoreSeparate(); boolean oldBackup = desc.isBackup(); @@ -209,8 +207,7 @@ @Override public synchronized boolean store(Context aContext) { try { - StoreDescription desc = null; - desc = getRegistry().findDescription(aContext.getClass()); + StoreDescription desc = getRegistry().findDescription(aContext.getClass()); if (desc != null) { boolean old = desc.isStoreSeparate(); try { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreConfigLifecycleListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,8 +37,8 @@ */ public class StoreConfigLifecycleListener implements LifecycleListener { - private static Log log = LogFactory.getLog(StoreConfigLifecycleListener.class); - private static StringManager sm = StringManager.getManager(StoreConfigLifecycleListener.class); + private static final Log log = LogFactory.getLog(StoreConfigLifecycleListener.class); + private static final StringManager sm = StringManager.getManager(StoreConfigLifecycleListener.class); /** * The configuration information registry for our managed beans. @@ -97,8 +97,8 @@ // Note: Hard-coded domain used since this object is per Server/JVM oname = new ObjectName("Catalina:type=StoreConfig"); registry.registerComponent(storeConfig, oname, "StoreConfig"); - } catch (Exception ex) { - log.error(sm.getString("storeConfigListener.registerError"), ex); + } catch (Exception e) { + log.error(sm.getString("storeConfigListener.registerError"), e); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreContextAppender.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreContextAppender.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreContextAppender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreContextAppender.java 2026-01-23 19:33:36.000000000 +0000 @@ -92,7 +92,7 @@ } try { appBase = file.getCanonicalFile(); - } catch (IOException e) { + } catch (IOException ioe) { appBase = file; } return appBase; @@ -111,7 +111,7 @@ } try { docBase = file.getCanonicalFile(); - } catch (IOException e) { + } catch (IOException ioe) { docBase = file; } return docBase; @@ -127,7 +127,7 @@ protected String getDefaultWorkDir(StandardContext context) { String defaultWorkDir = null; String contextWorkDir = context.getName(); - if (contextWorkDir.length() == 0) { + if (contextWorkDir.isEmpty()) { contextWorkDir = "_"; } if (contextWorkDir.startsWith("/")) { @@ -163,7 +163,6 @@ @Override public Object defaultInstance(Object bean) throws ReflectiveOperationException { if (bean instanceof StandardContext) { - StandardContext defaultContext = new StandardContext(); // @formatter:off /* * if (!((StandardContext) bean).getOverride()) { @@ -176,7 +175,7 @@ * } */ // @formatter:on - return defaultContext; + return new StandardContext(); } else { return super.defaultInstance(bean); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreFactoryBase.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreFactoryBase.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreFactoryBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreFactoryBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,7 +27,7 @@ * StoreFactory saves special elements. Output was generate with StoreAppenders. */ public class StoreFactoryBase implements IStoreFactory { - private static Log log = LogFactory.getLog(StoreFactoryBase.class); + private static final Log log = LogFactory.getLog(StoreFactoryBase.class); private StoreRegistry registry; @@ -160,9 +160,9 @@ for (Object element : elements) { try { storeElement(aWriter, indent, element); - } catch (IOException ioe) { - // ignore children report error them self! - // see StandardContext.storeWithBackup() + } catch (IOException ignore) { + // Ignore. Children report error themselves. + // See StandardContext.storeWithBackup() } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreFactoryRule.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreFactoryRule.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreFactoryRule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreFactoryRule.java 2026-01-23 19:33:36.000000000 +0000 @@ -54,16 +54,16 @@ /** * The attribute name of an attribute that can override the implementation class name. */ - private String attributeName; + private final String attributeName; - private String appenderAttributeName; + private final String appenderAttributeName; /** * The name of the IStoreFactory implementation class. */ - private String storeFactoryClass; + private final String storeFactoryClass; - private String storeAppenderClass; + private final String storeAppenderClass; // --------------------------------------------------------- Public Methods diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreLoader.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreLoader.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreLoader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreLoader.java 2026-01-23 19:33:36.000000000 +0000 @@ -132,7 +132,7 @@ synchronized (digester) { registry = (StoreRegistry) digester.parse(is); } - } catch (IOException e) { + } catch (IOException ioe) { // Try default classloader location try (InputStream is = StoreLoader.class.getResourceAsStream("/org/apache/catalina/storeconfig/server-registry.xml")) { @@ -143,7 +143,7 @@ registry = (StoreRegistry) digester.parse(is); } } else { - throw e; + throw ioe; } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreRegistry.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreRegistry.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/StoreRegistry.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/StoreRegistry.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,10 +49,10 @@ * Central StoreRegistry for all server.xml elements */ public class StoreRegistry { - private static Log log = LogFactory.getLog(StoreRegistry.class); - private static StringManager sm = StringManager.getManager(StoreRegistry.class); + private static final Log log = LogFactory.getLog(StoreRegistry.class); + private static final StringManager sm = StringManager.getManager(StoreRegistry.class); - private Map descriptors = new HashMap<>(); + private final Map descriptors = new HashMap<>(); private String encoding = "UTF-8"; @@ -61,7 +61,7 @@ private String version; // Access Information - private static Class interfaces[] = { CatalinaCluster.class, ChannelSender.class, ChannelReceiver.class, + private static final Class[] interfaces = { CatalinaCluster.class, ChannelSender.class, ChannelReceiver.class, Channel.class, MembershipService.class, ClusterDeployer.class, Realm.class, Manager.class, DirContext.class, LifecycleListener.class, Valve.class, ClusterListener.class, MessageListener.class, DataSender.class, ChannelInterceptor.class, Member.class, WebResourceRoot.class, WebResourceSet.class, @@ -197,7 +197,7 @@ */ public StoreDescription unregisterDescription(StoreDescription desc) { String key = desc.getId(); - if (key == null || "".equals(key)) { + if (key == null || key.isEmpty()) { key = desc.getTagClass(); } return descriptors.remove(key); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/WatchedResourceSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/WatchedResourceSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/WatchedResourceSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/WatchedResourceSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,7 @@ import org.apache.juli.logging.LogFactory; public class WatchedResourceSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(WatchedResourceSF.class); + private static final Log log = LogFactory.getLog(WatchedResourceSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/WrapperLifecycleSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/WrapperLifecycleSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/WrapperLifecycleSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/WrapperLifecycleSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,7 @@ import org.apache.juli.logging.LogFactory; public class WrapperLifecycleSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(WrapperLifecycleSF.class); + private static final Log log = LogFactory.getLog(WrapperLifecycleSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/storeconfig/WrapperListenerSF.java tomcat10-10.1.52/java/org/apache/catalina/storeconfig/WrapperListenerSF.java --- tomcat10-10.1.34/java/org/apache/catalina/storeconfig/WrapperListenerSF.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/storeconfig/WrapperListenerSF.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,7 @@ import org.apache.juli.logging.LogFactory; public class WrapperListenerSF extends StoreFactoryBase { - private static Log log = LogFactory.getLog(WrapperListenerSF.class); + private static final Log log = LogFactory.getLog(WrapperListenerSF.class); @Override public void store(PrintWriter aWriter, int indent, Object aElement) throws Exception { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/ByteMessage.java tomcat10-10.1.52/java/org/apache/catalina/tribes/ByteMessage.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/ByteMessage.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/ByteMessage.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,7 @@ /** * A byte message is not serialized and deserialized by the channel instead it is sent as a byte array. *

      - * By default Tribes uses java serialization when it receives an object to be sent over the wire. Java serialization is + * By default, Tribes uses java serialization when it receives an object to be sent over the wire. Java serialization is * not the most efficient of serializing data, and Tribes might not even have access to the correct class loaders to * deserialize the object properly. *

      diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/Channel.java tomcat10-10.1.52/java/org/apache/catalina/tribes/Channel.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/Channel.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/Channel.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,7 +38,7 @@ * * The channel has 5 major components: *

        - *
      • Data receiver, with a built in thread pool to receive messages from other peers
      • + *
      • Data receiver, with a built-in thread pool to receive messages from other peers
      • *
      • Data sender, an implementation for sending data using NIO or java.io
      • *
      • Membership listener,listens for membership broadcasts
      • *
      • Membership broadcaster, broadcasts membership pings.
      • @@ -144,7 +144,7 @@ *

        * However, there are five default flags that every channel implementation must implement. *

        - * SEND_OPTIONS_BYTE_MESSAGE - The message is a pure byte message and no marshaling or unmarshaling will be + * SEND_OPTIONS_BYTE_MESSAGE - The message is a pure byte message and no marshaling or unmarshalling will be * performed. * * @see #send(Member[], Serializable , int) @@ -279,12 +279,12 @@ void start(int svc) throws ChannelException; /** - * Shuts down the channel. This can be called multiple times for individual services to shutdown The svc parameter + * Shuts down the channel. This can be called multiple times for individual services to shut down. The svc parameter * can be the logical or value of any constants * * @param svc one of: *

          - *
        • DEFAULT - will shutdown all services
        • + *
        • DEFAULT - will shut down all services
        • *
        • MBR_RX_SEQ - stops the membership receiver
        • *
        • MBR_TX_SEQ - stops the membership broadcaster
        • *
        • SND_TX_SEQ - stops the replication transmitter
        • @@ -335,7 +335,7 @@ * Sends a heart beat through the interceptor stacks. Use this method to alert interceptors and other components to * clean up garbage, timed out messages etc. *

          - * If you application has a background thread, then you can save one thread, by configuring your channel to not use + * If your application has a background thread, then you can save one thread, by configuring your channel to not use * an internal heartbeat thread and invoking this method. * * @see #setHeartbeat(boolean) @@ -412,7 +412,7 @@ Member[] getMembers(); /** - * Return the member that represents this node. This is also the data that gets broadcasted through the membership + * Return the member that represents this node. This is also the data that gets broadcast through the membership * broadcaster component * * @param incAlive - optimization, true if you want it to calculate alive time since the membership service started. @@ -515,8 +515,10 @@ return Integer.parseInt(input); } catch (NumberFormatException nfe) { final Log log = LogFactory.getLog(Channel.class); - log.trace(String.format("Failed to parse [%s] as integer, channelSendOptions possibly set by name(s)", - input)); + if (log.isTraceEnabled()) { + log.trace(String.format("Failed to parse [%s] as integer, channelSendOptions possibly set by name(s)", + input), nfe); + } } String[] options = input.split("\\s*,\\s*"); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/ChannelException.java tomcat10-10.1.52/java/org/apache/catalina/tribes/ChannelException.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/ChannelException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/ChannelException.java 2026-01-23 19:33:36.000000000 +0000 @@ -91,7 +91,7 @@ @Override public String getMessage() { StringBuilder buf = new StringBuilder(super.getMessage()); - if (faultyMembers == null || faultyMembers.size() == 0) { + if (faultyMembers == null || faultyMembers.isEmpty()) { buf.append("; No faulty members identified."); } else { buf.append("; Faulty members:"); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/ChannelInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/ChannelInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/ChannelInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/ChannelInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -182,12 +182,12 @@ void start(int svc) throws ChannelException; /** - * Shuts down the channel. This can be called multiple times for individual services to shutdown The svc parameter + * Shuts down the channel. This can be called multiple times for individual services to shut down. The svc parameter * can be the logical or value of any constants * * @param svc one of: *

            - *
          • Channel.DEFAULT - will shutdown all services
          • + *
          • Channel.DEFAULT - will shut down all services
          • *
          • Channel.MBR_RX_SEQ - stops the membership receiver
          • *
          • Channel.MBR_TX_SEQ - stops the membership broadcaster
          • *
          • Channel.SND_TX_SEQ - stops the replication transmitter
          • diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/Member.java tomcat10-10.1.52/java/org/apache/catalina/tribes/Member.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/Member.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/Member.java 2026-01-23 19:33:36.000000000 +0000 @@ -56,17 +56,17 @@ /** * Returns the listen port for the ChannelReceiver implementation * - * @return the listen port for this member, -1 if its not listening on an insecure port + * @return the listen port for this member, -1 if it's not listening on an insecure port * * @see ChannelReceiver */ int getPort(); /** - * Returns the secure listen port for the ChannelReceiver implementation. Returns -1 if its not listening to a + * Returns the secure listen port for the ChannelReceiver implementation. Returns -1 if it's not listening to a * secure port. * - * @return the listen port for this member, -1 if its not listening on a secure port + * @return the listen port for this member, -1 if it's not listening on a secure port * * @see ChannelReceiver */ @@ -75,13 +75,13 @@ /** * Returns the UDP port that this member is listening to for UDP messages. * - * @return the listen UDP port for this member, -1 if its not listening on a UDP port + * @return the listen UDP port for this member, -1 if it's not listening on a UDP port */ int getUdpPort(); /** - * Contains information on how long this member has been online. The result is the number of milli seconds this + * Contains information on how long this member has been online. The result is the number of milliseconds this * member has been broadcasting its membership to the group. * * @return nr of milliseconds since this member started. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/MembershipService.java tomcat10-10.1.52/java/org/apache/catalina/tribes/MembershipService.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/MembershipService.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/MembershipService.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,7 +51,7 @@ * Starts the membership service. If a membership listeners is added the listener will start to receive membership * events. * - * @param level - level MBR_RX starts listening for members, level MBR_TX starts broad casting the server + * @param level - level MBR_RX starts listening for members, level MBR_TX starts broadcasting the server * * @throws Exception if the service fails to start. * @throws java.lang.IllegalArgumentException if the level is incorrect. @@ -63,14 +63,14 @@ * Stops the membership service. If a membership listeners is added the listener will start to receive membership * events. * - * @param level - level MBR_RX stops listening for members, level MBR_TX stops broad casting the server + * @param level - level MBR_RX stops listening for members, level MBR_TX stops broadcasting the server * * @throws java.lang.IllegalArgumentException if the level is incorrect. */ void stop(int level); /** - * @return true if the the group contains members + * @return true if the group contains members */ boolean hasMembers(); @@ -84,7 +84,7 @@ Member getMember(Member mbr); /** - * @return a list of all the members in the cluster. + * @return an array of all the members in the cluster. */ Member[] getMembers(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/UniqueId.java tomcat10-10.1.52/java/org/apache/catalina/tribes/UniqueId.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/UniqueId.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/UniqueId.java 2026-01-23 19:33:36.000000000 +0000 @@ -55,15 +55,15 @@ if (result) { UniqueId uid = (UniqueId) other; if (this.id == null && uid.id == null) { - result = true; - } else if (this.id == null && uid.id != null) { + // Nothing to do + } else if (this.id == null) { result = false; - } else if (this.id != null && uid.id == null) { + } else if (uid.id == null) { result = false; } else { result = Arrays.equals(this.id, uid.id); } - } // end if + } return result; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/AbsoluteOrder.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/AbsoluteOrder.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/AbsoluteOrder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/AbsoluteOrder.java 2026-01-23 19:33:36.000000000 +0000 @@ -113,15 +113,7 @@ } protected int compareInts(int b1, int b2) { - int result = 0; - if (b1 == b2) { - - } else if (b1 < b2) { - result = -1; - } else { - result = 1; - } - return result; + return Integer.compare(b1, b2); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/ChannelCoordinator.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/ChannelCoordinator.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/ChannelCoordinator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/ChannelCoordinator.java 2026-01-23 19:33:36.000000000 +0000 @@ -175,8 +175,8 @@ startLevel = (startLevel | svc); } catch (ChannelException cx) { throw cx; - } catch (Exception x) { - throw new ChannelException(x); + } catch (Exception e) { + throw new ChannelException(e); } } @@ -231,8 +231,8 @@ startLevel = (startLevel & (~svc)); setChannel(null); - } catch (Exception x) { - throw new ChannelException(x); + } catch (Exception e) { + throw new ChannelException(e); } } @@ -274,7 +274,10 @@ return membershipService; } - public void setClusterReceiver(ChannelReceiver clusterReceiver) { + public synchronized void setClusterReceiver(ChannelReceiver clusterReceiver) { + if (startLevel != 0) { + throw new IllegalStateException(sm.getString("channelCoordinator.invalidState.notStopped")); + } if (clusterReceiver != null) { this.clusterReceiver = clusterReceiver; this.clusterReceiver.setMessageListener(this); @@ -286,11 +289,17 @@ } } - public void setClusterSender(ChannelSender clusterSender) { + public synchronized void setClusterSender(ChannelSender clusterSender) { + if (startLevel != 0) { + throw new IllegalStateException(sm.getString("channelCoordinator.invalidState.notStopped")); + } this.clusterSender = clusterSender; } - public void setMembershipService(MembershipService membershipService) { + public synchronized void setMembershipService(MembershipService membershipService) { + if (startLevel != 0) { + throw new IllegalStateException(sm.getString("channelCoordinator.invalidState.notStopped")); + } this.membershipService = membershipService; this.membershipService.setMembershipListener(this); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/GroupChannel.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/GroupChannel.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/GroupChannel.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/GroupChannel.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,6 +21,7 @@ import java.io.Serializable; import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; @@ -61,7 +62,7 @@ /** * The default implementation of a Channel.
            * The GroupChannel manages the replication channel. It coordinates message being sent and received with membership - * announcements. The channel has an chain of interceptors that can modify the message or perform other logic.
            + * announcements. The channel has a chain of interceptors that can modify the message or perform other logic.
            * It manages a complete group, both membership and replication. */ public class GroupChannel extends ChannelInterceptorBase implements ManagedChannel, JmxChannel, GroupChannelMBean { @@ -216,7 +217,7 @@ ChannelData data = new ChannelData(true);// generates a unique Id data.setAddress(getLocalMember(false)); data.setTimestamp(System.currentTimeMillis()); - byte[] b = null; + byte[] b; if (msg instanceof ByteMessage) { b = ((ByteMessage) msg).getMessage(); options = options | SEND_OPTIONS_BYTE_MESSAGE; @@ -255,7 +256,7 @@ /** * Callback from the interceptor stack.
            * When a message is received from a remote node, this method will be invoked by the previous interceptor.
            - * This method can also be used to send a message to other components within the same application, but its an + * This method can also be used to send a message to other components within the same application, but it's an * extreme case, and you're probably better off doing that logic between the applications itself. * * @param msg ChannelMessage @@ -271,14 +272,14 @@ new java.sql.Timestamp(System.currentTimeMillis()) + " from " + msg.getAddress().getName()); } - Serializable fwd = null; + Serializable fwd; if ((msg.getOptions() & SEND_OPTIONS_BYTE_MESSAGE) == SEND_OPTIONS_BYTE_MESSAGE) { fwd = new ByteMessage(msg.getMessage().getBytes()); } else { try { fwd = XByteBuffer.deserialize(msg.getMessage().getBytesDirect(), 0, msg.getMessage().getLength()); - } catch (Exception sx) { - log.error(sm.getString("groupChannel.unable.deserialize", msg), sx); + } catch (Exception e) { + log.error(sm.getString("groupChannel.unable.deserialize", msg), e); return; } } @@ -310,13 +311,13 @@ Logs.MESSAGES.trace("GroupChannel delivered[" + delivered + "] id:" + new UniqueId(msg.getUniqueId())); } - } catch (Exception x) { + } catch (Exception e) { // this could be the channel listener throwing an exception, we should log it // as a warning. if (log.isWarnEnabled()) { - log.warn(sm.getString("groupChannel.receiving.error"), x); + log.warn(sm.getString("groupChannel.receiving.error"), e); } - throw new RemoteProcessException(sm.getString("groupChannel.receiving.error"), x); + throw new RemoteProcessException(sm.getString("groupChannel.receiving.error"), e); } } @@ -336,8 +337,8 @@ } RpcMessage.NoRpcChannelReply reply = new RpcMessage.NoRpcChannelReply(msg.rpcId, msg.uuid); send(new Member[] { destination }, reply, SEND_OPTIONS_ASYNCHRONOUS); - } catch (Exception x) { - log.error(sm.getString("groupChannel.sendFail.noRpcChannelReply"), x); + } catch (Exception e) { + log.error(sm.getString("groupChannel.sendFail.noRpcChannelReply"), e); } } @@ -450,7 +451,7 @@ } protected void startHeartbeat() { - if (heartbeat && (heartbeatFuture == null || (heartbeatFuture != null && heartbeatFuture.isDone()))) { + if (heartbeat && (heartbeatFuture == null || heartbeatFuture.isDone())) { if (heartbeatFuture != null && heartbeatFuture.isDone()) { // There was an error executing the scheduled task, get it and log it try { @@ -492,11 +493,7 @@ * @return ChannelInterceptor */ public ChannelInterceptor getFirstInterceptor() { - if (interceptors != null) { - return interceptors; - } else { - return coordinator; - } + return Objects.requireNonNullElse(interceptors, coordinator); } @Override diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/group/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,7 @@ channelCoordinator.alreadyStarted=Channel already started for level:[{0}] channelCoordinator.invalid.startLevel=Invalid start level, valid levels are:SND_RX_SEQ,SND_TX_SEQ,MBR_TX_SEQ,MBR_RX_SEQ +channelCoordinator.invalidState.notStopped=Configuration may not be changed until the channel has been fully stopped groupChannel.listener.alreadyExist=Listener already exists:[{0}][{1}] groupChannel.noDestination=No destination given diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/group/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,7 @@ channelCoordinator.alreadyStarted=Canal déjà démarré pour le niveau : [{0}] channelCoordinator.invalid.startLevel=Niveau de départ invalide, les niveaux valides sont : SND_RX_SEQ,SND_TX_SEQ,MBR_TX_SEQ,MBR_RX_SEQ +channelCoordinator.invalidState.notStopped=La configuration ne peut pas être changée avant que ce canal (Channel) ne soit complètement arrêté groupChannel.listener.alreadyExist=L''écouteur existe déjà : [{0}][{1}] groupChannel.noDestination=Aucune destination donnée diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/group/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,7 @@ channelCoordinator.alreadyStarted=チャンネルは既にレベル:[{0}]で開始されました。 channelCoordinator.invalid.startLevel=無効な開始レベルです。有効なレベルは SND_RX_SEQ、SND_TX_SEQ、MBR_TX_SEQ、MBR_RX_SEQ です +channelCoordinator.invalidState.notStopped=channel が完全に停止するまで設定を変更することはできません groupChannel.listener.alreadyExist=チャンネルリスナー [{0}][{1}] はすでに存在します。 groupChannel.noDestination=宛先が指定されていません。 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/RpcChannel.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/RpcChannel.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/RpcChannel.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/RpcChannel.java 2026-01-23 19:33:36.000000000 +0000 @@ -87,7 +87,7 @@ return new Response[0]; } - // avoid dead lock + // avoid deadlock int sendOptions = channelOptions & ~Channel.SEND_OPTIONS_SYNCHRONIZED_ACK; RpcCollectorKey key = new RpcCollectorKey(UUIDGenerator.randomUUID(false)); @@ -100,7 +100,11 @@ RpcMessage rmsg = new RpcMessage(rpcId, key.id, message); channel.send(destination, rmsg, sendOptions); if (rpcOptions != NO_REPLY) { - collector.wait(timeout); + long timeoutExpiry = System.nanoTime() + timeout * 1_000_000; + while (!collector.isComplete() && timeout > 0) { + collector.wait(timeout); + timeout = (timeoutExpiry - System.nanoTime()) / 1_000_000; + } } } } catch (InterruptedException ix) { @@ -175,11 +179,11 @@ replyMessageOptions & ~Channel.SEND_OPTIONS_SYNCHRONIZED_ACK); } finished = true; - } catch (Exception x) { + } catch (Exception e) { if (excallback != null && !asyncReply) { - excallback.replyFailed(rmsg.message, reply, sender, x); + excallback.replyFailed(rmsg.message, reply, sender, e); } else { - log.error(sm.getString("rpcChannel.replyFailed"), x); + log.error(sm.getString("rpcChannel.replyFailed"), e); } } if (finished && excallback != null && !asyncReply) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/RpcMessage.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/RpcMessage.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/RpcMessage.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/RpcMessage.java 2026-01-23 19:33:36.000000000 +0000 @@ -65,15 +65,8 @@ @Override public String toString() { - StringBuilder buf = new StringBuilder("RpcMessage["); - buf.append(super.toString()); - buf.append("] rpcId="); - buf.append(Arrays.toString(rpcId)); - buf.append("; uuid="); - buf.append(Arrays.toString(uuid)); - buf.append("; msg="); - buf.append(message); - return buf.toString(); + return "RpcMessage[" + super.toString() + "] rpcId=" + Arrays.toString(rpcId) + "; uuid=" + + Arrays.toString(uuid) + "; msg=" + message; } public static class NoRpcChannelReply extends RpcMessage { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/DomainFilterInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -60,7 +60,7 @@ if (membership == null) { setupMembership(); } - boolean notify = false; + boolean notify; synchronized (membership) { notify = Arrays.equals(domain, member.getDomain()); if (notify) { @@ -81,7 +81,7 @@ if (membership == null) { setupMembership(); } - boolean notify = false; + boolean notify; synchronized (membership) { notify = Arrays.equals(domain, member.getDomain()); if (notify) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/EncryptInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -263,7 +263,7 @@ // Copied from org.apache.tomcat.util.buf.HexUtils // @formatter:off private static final int[] DEC = { - 00, 01, 02, 03, 04, 05, 06, 07, 8, 9, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, @@ -390,7 +390,7 @@ } public void shutdown() { - // Individual Cipher and SecureRandom objects need no explicit teardown + // Individual Cipher and SecureRandom objects need no explicit tear down cipherPool.clear(); randomPool.clear(); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -148,9 +148,9 @@ removeFragCollection(key); } } - } catch (Exception x) { + } catch (Exception e) { if (log.isErrorEnabled()) { - log.error(sm.getString("fragmentationInterceptor.heartbeat.failed"), x); + log.error(sm.getString("fragmentationInterceptor.heartbeat.failed"), e); } } super.heartbeat(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/GzipInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -94,9 +94,9 @@ if (statsEnabled && interval > 0 && currentCount % interval == 0) { report(); } - } catch (IOException x) { + } catch (IOException ioe) { log.error(sm.getString("gzipInterceptor.compress.failed")); - throw new ChannelException(x); + throw new ChannelException(ioe); } } @@ -129,8 +129,8 @@ if (statsEnabled && interval > 0 && currentCount % interval == 0) { report(); } - } catch (IOException x) { - log.error(sm.getString("gzipInterceptor.decompress.failed"), x); + } catch (IOException ioe) { + log.error(sm.getString("gzipInterceptor.decompress.failed"), ioe); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -83,7 +83,7 @@ tcpFailureDetector.member.disappeared=Verification complete. Member disappeared[{0}] tcpFailureDetector.memberDisappeared.verify=Received memberDisappeared[{0}] message. Will verify. tcpFailureDetector.performBasicCheck.memberAdded=Member added, even though we weren''t notified:[{0}] -tcpFailureDetector.recievedPacket=Received a failure detector packet [{0}] +tcpFailureDetector.receivedPacket=Received a failure detector packet [{0}] tcpFailureDetector.still.alive=Verification complete. Member still alive[{0}] tcpFailureDetector.suspectMember.alive=Suspect member, confirmed alive.[{0}] tcpFailureDetector.suspectMember.dead=Suspect member, confirmed dead.[{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -83,7 +83,7 @@ tcpFailureDetector.member.disappeared=La vérfication est complète, le membre a disparu [{0}] tcpFailureDetector.memberDisappeared.verify=Reçu un message memberDisappeared[{0}], qui sera vérifié tcpFailureDetector.performBasicCheck.memberAdded=Le membre a été ajouté bien qu''aucune notification n''ait été reçue : [{0}] -tcpFailureDetector.recievedPacket=Réception d''un paquet de détection d''échec [{0}] +tcpFailureDetector.receivedPacket=Réception d''un paquet de détection d''échec [{0}] tcpFailureDetector.still.alive=Vérification terminée. Le membre [{0}] vit toujours tcpFailureDetector.suspectMember.alive=Membre suspect, confirmé vivant.[{0}] tcpFailureDetector.suspectMember.dead=Un membre suspect a été confirmé mort [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -83,7 +83,7 @@ tcpFailureDetector.member.disappeared=メンバ検証が完了しました。メンバーが消えました [{0}] tcpFailureDetector.memberDisappeared.verify=memberDisappeared[{0}]メッセージを受信しました。メンバ検証します。 tcpFailureDetector.performBasicCheck.memberAdded=私たちに通知されなかったにもかかわらず、メンバーが追加されました:[{0}] -tcpFailureDetector.recievedPacket=障害検出パケット [{0}] を受信しました +tcpFailureDetector.receivedPacket=障害検出パケット [{0}] を受信しました tcpFailureDetector.still.alive=故障検出チェックが完了しました。メンバー [{0}] は正常です。 tcpFailureDetector.suspectMember.alive=疑わしいクラスタメンバーの生存を確認しました。[{0}] tcpFailureDetector.suspectMember.dead=疑義メンバが死亡したことが確認されました。[{0}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations domainFilterInterceptor.member.refused=Участнику [{0}] было отказано в присоединении к кластеру +domainFilterInterceptor.message.refused=Сообщение полученое от кластера [{0}] было отклонено. encryptInterceptor.decrypt.error.short-message=Невозможно расшифровать сообщение: слишком мало символов diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/MessageDispatchInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -244,18 +244,18 @@ if (handler != null) { handler.handleCompletion(new UniqueId(msg.getUniqueId())); } - } catch (Exception ex) { - log.error(sm.getString("messageDispatchInterceptor.completeMessage.failed"), ex); + } catch (Exception e) { + log.error(sm.getString("messageDispatchInterceptor.completeMessage.failed"), e); } - } catch (Exception x) { - ChannelException cx = null; - if (x instanceof ChannelException) { - cx = (ChannelException) x; + } catch (Exception e) { + ChannelException cx; + if (e instanceof ChannelException) { + cx = (ChannelException) e; } else { - cx = new ChannelException(x); + cx = new ChannelException(e); } if (log.isDebugEnabled()) { - log.debug(sm.getString("messageDispatchInterceptor.AsyncMessage.failed"), x); + log.debug(sm.getString("messageDispatchInterceptor.AsyncMessage.failed"), e); } try { if (handler != null) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/NonBlockingCoordinator.java 2026-01-23 19:33:36.000000000 +0000 @@ -100,8 +100,8 @@ * the view. *

            *

            - * Lets assume that C1 arrives, C1 has lower priority than C, but higher priority than D.
            - * Lets also assume that C1 sees the following view {B,D,E}
            + * Let's assume that C1 arrives, C1 has lower priority than C, but higher priority than D.
            + * Let's also assume that C1 sees the following view {B,D,E}
            * C1 waits for a token to arrive. When the token arrives, the same scenario as above will happen.
            * In the scenario where C1 sees {D,E} and A,B,C cannot see C1, no token will ever arrive.
            * In this case, C1 sends a Z{C1-ldr, C1-src, mbrs-C1,D,E} to D
            @@ -238,14 +238,24 @@ new CoordinationEvent(CoordinationEvent.EVT_PROCESS_ELECT, this, "Election, sending request")); sendElectionMsg(local, others[0], msg); } else { - try { - coordMsgReceived.set(false); - fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_WAIT_FOR_MSG, this, - "Election, waiting for request")); - electionMutex.wait(waitForCoordMsgTimeout); - } catch (InterruptedException x) { - Thread.currentThread().interrupt(); - } + coordMsgReceived.set(false); + fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_WAIT_FOR_MSG, this, + "Election, waiting for request")); + long timeout = waitForCoordMsgTimeout; + long timeoutEndNanos = System.nanoTime() + timeout * 1_000_000; + do { + try { + electionMutex.wait(timeout); + } catch (InterruptedException x) { + Thread.currentThread().interrupt(); + } + timeout = (timeoutEndNanos - System.nanoTime()) / 1_000_000; + /* + * Spurious wake-ups are possible. Keep waiting if a) the condition we were waiting for hasn't + * happened (i.e. notify() was not called) AND b) the timeout has not expired AND c) the thread was + * not interrupted. + */ + } while (suggestedviewId == null && !coordMsgReceived.get() && timeout > 0 && !Thread.interrupted()); String msg; if (suggestedviewId == null && !coordMsgReceived.get()) { if (Thread.interrupted()) { @@ -266,9 +276,8 @@ Arrays.fill(m, others); Member[] mbrs = m.getMembers(); m.reset(); - CoordinationMessage msg = new CoordinationMessage(leader, local, mbrs, - new UniqueId(UUIDGenerator.randomUUID(true)), COORD_REQUEST); - return msg; + return new CoordinationMessage(leader, local, mbrs, new UniqueId(UUIDGenerator.randomUUID(true)), + COORD_REQUEST); } protected void sendElectionMsg(Member local, Member next, CoordinationMessage msg) throws ChannelException { @@ -287,6 +296,7 @@ sendElectionMsg(local, msg.getMembers()[current], msg); sent = true; } catch (ChannelException x) { + // Exception is logged further up stack log.warn(sm.getString("nonBlockingCoordinator.electionMessage.sendfailed", msg.getMembers()[current])); current = Arrays.nextIndex(msg.getMembers()[current], msg.getMembers()); if (current == next) { @@ -323,8 +333,8 @@ return true; } catch (SocketTimeoutException | ConnectException x) { // do nothing, we couldn't connect - } catch (Exception x) { - log.error(sm.getString("nonBlockingCoordinator.memberAlive.failed"), x); + } catch (Exception e) { + log.error(sm.getString("nonBlockingCoordinator.memberAlive.failed"), e); } return false; } @@ -468,7 +478,7 @@ } /** - * Block in/out messages while a election is going on + * Block in/out messages while an election is going on */ protected void halt() { @@ -544,7 +554,7 @@ @Override public void messageReceived(ChannelMessage msg) { if (Arrays.contains(msg.getMessage().getBytesDirect(), 0, COORD_ALIVE, 0, COORD_ALIVE.length)) { - // ignore message, its an alive message + // ignore message, it's an alive message fireInterceptorEvent(new CoordinationEvent(CoordinationEvent.EVT_MSG_ARRIVE, this, "Alive Message")); } else if (Arrays.contains(msg.getMessage().getBytesDirect(), 0, COORD_HEADER, 0, COORD_HEADER.length)) { @@ -626,8 +636,8 @@ startElection(true); } } - } catch (Exception x) { - log.error(sm.getString("nonBlockingCoordinator.heartbeat.failed"), x); + } catch (Exception e) { + log.error(sm.getString("nonBlockingCoordinator.heartbeat.failed"), e); } finally { super.heartbeat(); } @@ -786,12 +796,10 @@ byte[] ldr = leader.getData(false, false); buf.append(ldr.length); buf.append(ldr, 0, ldr.length); - ldr = null; // source byte[] src = source.getData(false, false); buf.append(src.length); buf.append(src, 0, src.length); - src = null; // view buf.append(view.length); for (Member member : view) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/OrderInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -70,7 +70,7 @@ ChannelException cx = null; for (Member member : destination) { try { - int nr = 0; + int nr; outLock.writeLock().lock(); try { nr = incCounter(member); @@ -157,7 +157,7 @@ MessageOrder prev = null; tmp = order; // flag to empty out the queue when it larger than maxQueue - boolean empty = order != null ? order.getCount() >= maxQueue : false; + boolean empty = order != null && order.getCount() >= maxQueue; while (tmp != null) { // process expired messages or empty out the queue if (tmp.isExpired(expire) || empty) { @@ -249,7 +249,7 @@ private final long received = System.currentTimeMillis(); private MessageOrder next; private final int msgNr; - private ChannelMessage msg = null; + private ChannelMessage msg; public MessageOrder(int msgNr, ChannelMessage msg) { this.msgNr = msgNr; @@ -317,7 +317,6 @@ // add before prev.next = add; // prev cannot be null here, warning suppressed add.next = iter; - } else { throw new ArithmeticException(sm.getString("orderInterceptor.messageAdded.sameCounter")); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/SimpleCoordinator.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/SimpleCoordinator.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/SimpleCoordinator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/SimpleCoordinator.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,6 @@ /** * A dinky coordinator, just uses a sorted version of the member array. - * - * @author rnewson */ public class SimpleCoordinator extends ChannelInterceptorBase { @@ -110,7 +108,7 @@ } public boolean isCoordinator() { - return view == null ? false : getLocalMember(false).equals(getCoordinator()); + return view != null && getLocalMember(false).equals(getCoordinator()); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/StaticMembershipInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -61,9 +61,7 @@ public void removeStaticMember(Member member) { synchronized (members) { - if (members.contains(member)) { - members.remove(member); - } + members.remove(member); } } @@ -100,20 +98,18 @@ @Override public boolean hasMembers() { - return super.hasMembers() || (members.size() > 0); + return super.hasMembers() || (!members.isEmpty()); } @Override public Member[] getMembers() { - if (members.size() == 0) { + if (members.isEmpty()) { return super.getMembers(); } else { synchronized (members) { Member[] others = super.getMembers(); Member[] result = new Member[members.size() + others.length]; - for (int i = 0; i < others.length; i++) { - result[i] = others[i]; - } + System.arraycopy(others, 0, result, 0, others.length); for (int i = 0; i < members.size(); i++) { result[i + others.length] = members.get(i); } @@ -218,7 +214,7 @@ } protected ChannelInterceptor getfirstInterceptor() { - ChannelInterceptor result = null; + ChannelInterceptor result; ChannelInterceptor now = this; do { result = now; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/TcpFailureDetector.java 2026-01-23 19:33:36.000000000 +0000 @@ -111,7 +111,7 @@ if (process) { super.messageReceived(msg); } else if (log.isDebugEnabled()) { - log.debug(sm.getString("tcpFailureDetector.recievedPacket", msg)); + log.debug(sm.getString("tcpFailureDetector.receivedPacket", msg)); } }// messageReceived @@ -250,8 +250,8 @@ performForcedCheck(); } } - } catch (Exception x) { - log.warn(sm.getString("tcpFailureDetector.heartbeat.failed"), x); + } catch (Exception e) { + log.warn(sm.getString("tcpFailureDetector.heartbeat.failed"), e); } } @@ -383,8 +383,8 @@ return true; } catch (SocketTimeoutException | ConnectException | NoRouteToHostException noop) { // do nothing, we couldn't connect - } catch (Exception x) { - log.error(sm.getString("tcpFailureDetector.failureDetection.failed", mbr), x); + } catch (Exception e) { + log.error(sm.getString("tcpFailureDetector.failureDetection.failed", mbr), e); } return false; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/TcpPingInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -191,8 +191,8 @@ // Ignore. Probably triggered by a call to stop(). // In the highly unlikely event it was a different trigger, // simply ignore it and continue. - } catch (Exception x) { - log.warn(sm.getString("tcpPingInterceptor.pingFailed.pingThread"), x); + } catch (Exception e) { + log.warn(sm.getString("tcpPingInterceptor.pingFailed.pingThread"), e); } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/group/interceptors/TwoPhaseCommitInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -52,7 +52,7 @@ // and just send one message if (okToProcess(msg.getOptions())) { super.sendMessage(destination, msg, null); - ChannelMessage confirmation = null; + ChannelMessage confirmation; if (deepclone) { confirmation = (ChannelMessage) msg.deepclone(); } else { @@ -129,8 +129,8 @@ messages.remove(entry.id); } } - } catch (Exception x) { - log.warn(sm.getString("twoPhaseCommitInterceptor.heartbeat.failed"), x); + } catch (Exception e) { + log.warn(sm.getString("twoPhaseCommitInterceptor.heartbeat.failed"), e); } finally { super.heartbeat(); } @@ -150,7 +150,6 @@ public boolean expired(long now, long expiration) { return (now - timestamp) > expiration; } - } } \ No newline at end of file diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/io/ChannelData.java tomcat10-10.1.52/java/org/apache/catalina/tribes/io/ChannelData.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/io/ChannelData.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/io/ChannelData.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,6 @@ * The ChannelData object is used to transfer a message through the channel interceptor stack and * eventually out on a transport to be sent to another node. While the message is being processed by the different * interceptors, the message data can be manipulated as each interceptor seems appropriate. - * - * @author Peter Rossbach */ public class ChannelData implements ChannelMessage { private static final long serialVersionUID = 1L; @@ -153,7 +151,7 @@ } public int getDataPackageLength() { - int length = 4 + // options + return 4 + // options 8 + // timestamp off=4 4 + // unique id length off=12 uniqueId.length + // id data off=12+uniqueId.length @@ -161,8 +159,6 @@ address.getDataLength() + // member data off=12+uniqueId.length+4+add.length 4 + // message length off=12+uniqueId.length+4+add.length+4 message.getLength(); - return length; - } /** @@ -339,12 +335,8 @@ @Override public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append("ClusterData[src="); - buf.append(getAddress()).append("; id="); - buf.append(bToS(getUniqueId())).append("; sent="); - buf.append(new Timestamp(this.getTimestamp()).toString()).append(']'); - return buf.toString(); + return "ClusterData[src=" + getAddress() + "; id=" + bToS(getUniqueId()) + "; sent=" + + new Timestamp(this.getTimestamp()).toString() + ']'; } public static String bToS(byte[] data) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/io/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/io/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/io/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/io/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -17,3 +17,5 @@ # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations replicationStream.conflict=конфликтующие непубличные загрузчики классов + +xByteBuffer.size.larger.buffer=Размер больше чем существующий буфер diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/io/ObjectReader.java tomcat10-10.1.52/java/org/apache/catalina/tribes/io/ObjectReader.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/io/ObjectReader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/io/ObjectReader.java 2026-01-23 19:33:36.000000000 +0000 @@ -66,10 +66,10 @@ public ObjectReader(Socket socket) { try { this.buffer = new XByteBuffer(socket.getReceiveBufferSize(), true); - } catch (IOException x) { + } catch (IOException ioe) { // unable to get buffer size log.warn(sm.getString("objectReader.retrieveFailed.socketReceiverBufferSize", - Integer.toString(Constants.DEFAULT_CLUSTER_MSG_BUFFER_SIZE))); + Integer.toString(Constants.DEFAULT_CLUSTER_MSG_BUFFER_SIZE)), ioe); this.buffer = new XByteBuffer(Constants.DEFAULT_CLUSTER_MSG_BUFFER_SIZE, true); } } @@ -124,7 +124,7 @@ * @see XByteBuffer#doesPackageExist() * @see XByteBuffer#extractPackage(boolean) * - * @return number of received packages/messages + * @return array of received packages/messages */ public ChannelMessage[] execute() { int pkgCnt = buffer.countPackages(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/io/ReplicationStream.java tomcat10-10.1.52/java/org/apache/catalina/tribes/io/ReplicationStream.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/io/ReplicationStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/io/ReplicationStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,9 +28,6 @@ /** * Custom subclass of ObjectInputStream that loads from the class loader for this web application. This * allows classes defined only with the web application to be found correctly. - * - * @author Craig R. McClanahan - * @author Bip Thelin */ public final class ReplicationStream extends ObjectInputStream { @@ -39,7 +36,7 @@ /** * The class loader we will use to resolve classes. */ - private ClassLoader[] classLoaders = null; + private ClassLoader[] classLoaders; /** * Construct a new instance of CustomObjectInputStream @@ -83,7 +80,7 @@ } else { return findExternalClass(name); } - } catch (Exception x) { + } catch (Exception e) { if (tryRepFirst) { return findExternalClass(name); } else { @@ -140,16 +137,14 @@ public Class findReplicationClass(String name) throws ClassNotFoundException { - Class clazz = Class.forName(name, false, getClass().getClassLoader()); - return clazz; + return Class.forName(name, false, getClass().getClassLoader()); } public Class findExternalClass(String name) throws ClassNotFoundException { ClassNotFoundException cnfe = null; for (ClassLoader classLoader : classLoaders) { try { - Class clazz = Class.forName(name, false, classLoader); - return clazz; + return Class.forName(name, false, classLoader); } catch (ClassNotFoundException x) { cnfe = x; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/io/XByteBuffer.java tomcat10-10.1.52/java/org/apache/catalina/tribes/io/XByteBuffer.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/io/XByteBuffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/io/XByteBuffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -64,7 +64,7 @@ /** * Variable to hold the data */ - protected byte[] buf = null; + protected byte[] buf; /** * Current length of data in the buffer @@ -75,7 +75,7 @@ * Flag for discarding invalid packages If this flag is set to true, and append(byte[],...) is called, the data * added will be inspected, and if it doesn't start with START_DATA it will be thrown away. */ - protected boolean discard = true; + protected boolean discard; /** * Constructs a new XByteBuffer.
            @@ -241,7 +241,7 @@ public void expand(int newcount) { // don't change the allocation strategy - byte newbuf[] = new byte[Math.max(buf.length << 1, newcount)]; + byte[] newbuf = new byte[Math.max(buf.length << 1, newcount)]; System.arraycopy(buf, 0, newbuf, 0, bufSize); buf = newbuf; } @@ -337,8 +337,7 @@ public ChannelData extractPackage(boolean clearFromBuffer) { XByteBuffer xbuf = extractDataPackage(clearFromBuffer); - ChannelData cdata = ChannelData.getDataFromPackage(xbuf); - return cdata; + return ChannelData.getDataFromPackage(xbuf); } /** @@ -379,12 +378,10 @@ public static int getDataPackageLength(int datalength) { - int length = START_DATA.length + // header length + return START_DATA.length + // header length 4 + // data length indicator datalength + // actual data length END_DATA.length; // footer length - return length; - } public static byte[] createDataPackage(byte[] data) { @@ -404,7 +401,7 @@ */ public static int toInt(byte[] b, int off) { return ((b[off + 3]) & 0xFF) + (((b[off + 2]) & 0xFF) << 8) + (((b[off + 1]) & 0xFF) << 16) + - (((b[off + 0]) & 0xFF) << 24); + (((b[off]) & 0xFF) << 24); } /** @@ -419,7 +416,7 @@ return (((long) b[off + 7]) & 0xFF) + ((((long) b[off + 6]) & 0xFF) << 8) + ((((long) b[off + 5]) & 0xFF) << 16) + ((((long) b[off + 4]) & 0xFF) << 24) + ((((long) b[off + 3]) & 0xFF) << 32) + ((((long) b[off + 2]) & 0xFF) << 40) + - ((((long) b[off + 1]) & 0xFF) << 48) + ((((long) b[off + 0]) & 0xFF) << 56); + ((((long) b[off + 1]) & 0xFF) << 48) + ((((long) b[off]) & 0xFF) << 56); } @@ -466,7 +463,7 @@ n >>>= 8; b[offset + 1] = (byte) (n); n >>>= 8; - b[offset + 0] = (byte) (n); + b[offset] = (byte) (n); return b; } @@ -494,7 +491,7 @@ n >>>= 8; b[offset + 1] = (byte) (n); n >>>= 8; - b[offset + 0] = (byte) (n); + b[offset] = (byte) (n); return b; } @@ -512,7 +509,7 @@ if (find.length > src.length) { return result; } - if (find.length == 0 || src.length == 0) { + if (find.length == 0) { return result; } if (srcOff >= src.length) { @@ -577,7 +574,7 @@ } if (data != null && length > 0) { InputStream instream = new ByteArrayInputStream(data, offset, length); - ObjectInputStream stream = null; + ObjectInputStream stream; stream = (cls.length > 0) ? new ReplicationStream(instream, cls) : new ObjectInputStream(instream); message = stream.readObject(); instream.close(); @@ -606,8 +603,7 @@ ObjectOutputStream out = new ObjectOutputStream(outs); out.writeObject(msg); out.flush(); - byte[] data = outs.toByteArray(); - return data; + return outs.toByteArray(); } public void setDiscard(boolean discard) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/jmx/JmxRegistry.java tomcat10-10.1.52/java/org/apache/catalina/tribes/jmx/JmxRegistry.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/jmx/JmxRegistry.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/jmx/JmxRegistry.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,9 +36,9 @@ private static final Log log = LogFactory.getLog(JmxRegistry.class); protected static final StringManager sm = StringManager.getManager(JmxRegistry.class); - private static ConcurrentHashMap registryCache = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap registryCache = new ConcurrentHashMap<>(); - private MBeanServer mbserver = ManagementFactory.getPlatformMBeanServer(); + private final MBeanServer mbserver = ManagementFactory.getPlatformMBeanServer(); private ObjectName baseOname = null; private JmxRegistry() { @@ -110,7 +110,7 @@ return null; } String oNameStr = baseOname.toString() + keyprop; - ObjectName oName = null; + ObjectName oName; try { oName = new ObjectName(oNameStr); if (mbserver.isRegistered(oName)) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/jmx/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/jmx/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/jmx/LocalStrings_ru.properties 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/jmx/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,19 @@ +# 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. + +# Do not edit this file directly. +# To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations + +jmxRegistry.registerJmx.failed=Невозможно зарегистрировать объект [{0}] с именем [{1}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/Constants.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Manifest constants for the org.apache.catalina.tribes.membership package. - * - * @author Peter Rossbach */ public class Constants { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -30,6 +30,7 @@ mcastServiceImpl.bind.failed=Binding to multicast address, failed. Binding to port only. mcastServiceImpl.error.receiving=Error receiving mcast package. Sleeping 500ms mcastServiceImpl.error.receivingNoSleep=Error receiving multicast package +mcastServiceImpl.error.stop=Error during stop of membership service mcastServiceImpl.invalid.startLevel=Invalid start level. Only acceptable levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ mcastServiceImpl.invalid.stopLevel=Invalid stop level. Only acceptable levels are Channel.MBR_RX_SEQ and Channel.MBR_TX_SEQ mcastServiceImpl.invalidMemberPackage=Invalid member multicast package diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -30,6 +30,7 @@ mcastServiceImpl.bind.failed=Echec de l'association à l’adresse multicast, association uniquement sur le port mcastServiceImpl.error.receiving=Erreur en recevant un paquet multicast, attente de 500ms mcastServiceImpl.error.receivingNoSleep=Erreur lors de la réception d'un paquet multicast +mcastServiceImpl.error.stop=Erreur lors de l'arrêt du service de gestion des membres mcastServiceImpl.invalid.startLevel=Niveau de départ invalide. Les seuls niveaux acceptables sont Channel.MBR_RX_SEQ et Channel.MBR_TX_SEQ mcastServiceImpl.invalid.stopLevel=Niveau de stop invalide, les seuls niveaux valides sont Channel.MBR_RX_SEQ et Channel.MBR_TX_SEQ mcastServiceImpl.invalidMemberPackage=Membre invalide dans le paquet multicast diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -30,6 +30,7 @@ mcastServiceImpl.bind.failed=マルチキャストアドレスへのバインドに失敗しました。ポートのみにバインドします。 mcastServiceImpl.error.receiving=マルチキャストパッケージを受信中にエラーが発生しました。500msスリープします。 mcastServiceImpl.error.receivingNoSleep=マルチキャスト パッケージの受信エラー +mcastServiceImpl.error.stop=membership サービス停止中にエラーが発生しました mcastServiceImpl.invalid.startLevel=不正な開始レベルです。受け付けられるのは Channel.MBR_RX_SEQ と Channel.MBR_TX_SEQ です。 mcastServiceImpl.invalid.stopLevel=無効な停止レベルです。受け入れ可能なレベルはChannel.MBR_RX_SEQとChannel.MBR_TX_SEQのみです。 mcastServiceImpl.invalidMemberPackage=無効なメンバマルチキャストパッケージ diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/McastService.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/McastService.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/McastService.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/McastService.java 2026-01-23 19:33:36.000000000 +0000 @@ -147,8 +147,8 @@ localMember.setSecurePort(securePort); localMember.setUdpPort(udpPort); localMember.getData(true, true); - } catch (IOException x) { - throw new IllegalArgumentException(x); + } catch (IOException ioe) { + throw new IllegalArgumentException(ioe); } } @@ -320,15 +320,15 @@ if (properties.getProperty("mcastTTL") != null) { try { ttl = Integer.parseInt(properties.getProperty("mcastTTL")); - } catch (Exception x) { - log.error(sm.getString("McastService.parseTTL", properties.getProperty("mcastTTL")), x); + } catch (Exception e) { + log.error(sm.getString("McastService.parseTTL", properties.getProperty("mcastTTL")), e); } } if (properties.getProperty("mcastSoTimeout") != null) { try { soTimeout = Integer.parseInt(properties.getProperty("mcastSoTimeout")); - } catch (Exception x) { - log.error(sm.getString("McastService.parseSoTimeout", properties.getProperty("mcastSoTimeout")), x); + } catch (Exception e) { + log.error(sm.getString("McastService.parseSoTimeout", properties.getProperty("mcastSoTimeout")), e); } } @@ -372,8 +372,8 @@ impl = null; channel = null; } - } catch (Exception x) { - log.error(sm.getString("McastService.stopFail", Integer.valueOf(svc)), x); + } catch (Exception e) { + log.error(sm.getString("McastService.stopFail", Integer.valueOf(svc)), e); } } @@ -411,8 +411,8 @@ DatagramPacket packet = new DatagramPacket(data, 0, data.length); try { impl.send(false, packet); - } catch (Exception x) { - throw new ChannelException(x); + } catch (Exception e) { + throw new ChannelException(e); } } @@ -454,8 +454,8 @@ if (impl != null) { impl.send(false); } - } catch (Exception x) { - log.error(sm.getString("McastService.payload"), x); + } catch (Exception e) { + log.error(sm.getString("McastService.payload"), e); } } } @@ -469,8 +469,8 @@ if (impl != null) { impl.send(false); } - } catch (Exception x) { - log.error(sm.getString("McastService.domain"), x); + } catch (Exception e) { + log.error(sm.getString("McastService.domain"), e); } } } @@ -526,7 +526,7 @@ * * @throws Exception If an error occurs */ - public static void main(String args[]) throws Exception { + public static void main(String[] args) throws Exception { McastService service = new McastService(); Properties p = new Properties(); p.setProperty("mcastPort", "5555"); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/McastServiceImpl.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/McastServiceImpl.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/McastServiceImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/McastServiceImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -64,7 +64,7 @@ */ protected MulticastSocket socket; /** - * The local member that we intend to broad cast over and over again + * The local member that we intend to broadcast over and over again */ protected final MemberImpl member; /** @@ -116,7 +116,7 @@ /** * Read timeout on the mcast socket */ - protected int mcastSoTimeout = -1; + protected int mcastSoTimeout; /** * bind address */ @@ -202,7 +202,11 @@ * On some platforms (e.g. Linux) it is not possible to bind to the multicast address. In this case only * bind to the port. */ - log.info(sm.getString("mcastServiceImpl.bind.failed")); + if (log.isDebugEnabled()) { + log.debug(sm.getString("mcastServiceImpl.bind.failed"), e); + } else { + log.info(sm.getString("mcastServiceImpl.bind.failed")); + } socket = new MulticastSocket(port); } } else { @@ -289,6 +293,7 @@ try { Thread.sleep(memberwait); } catch (InterruptedException ignore) { + // Ignore } if (log.isInfoEnabled()) { log.info(sm.getString("mcastServiceImpl.waitForMembers.done", Integer.toString(level))); @@ -328,13 +333,19 @@ // leave mcast group try { socket.leaveGroup(new InetSocketAddress(address, 0), null); - } catch (Exception ignore) { - // NO-OP + } catch (Exception e) { + // Shutting down. Only log at debug. + if (log.isDebugEnabled()) { + log.debug(sm.getString("mcastServiceImpl.error.stop"), e); + } } try { socket.close(); - } catch (Exception ignore) { - // NO-OP + } catch (Exception e) { + // Shutting down. Only log at debug. + if (log.isDebugEnabled()) { + log.debug(sm.getString("mcastServiceImpl.error.stop"), e); + } } member.setServiceStartTime(-1); } @@ -347,9 +358,7 @@ * @throws IOException Received failed */ public void receive() throws IOException { - boolean checkexpired = true; try { - socket.receive(receivePacket); if (receivePacket.getLength() > MAX_PACKET_SIZE) { log.error(sm.getString("mcastServiceImpl.packet.tooLong", Integer.toString(receivePacket.getLength()))); @@ -361,16 +370,14 @@ } else { memberBroadcastsReceived(data); } - } - } catch (SocketTimeoutException x) { - // do nothing, this is normal, we don't want to block forever - // since the receive thread is the same thread - // that does membership expiration - } - if (checkexpired) { - checkExpired(); + } catch (SocketTimeoutException ignore) { + /* + * Do nothing. This is normal. We don't want to block forever since the receive thread is the same thread + * that does membership expiration. + */ } + checkExpired(); } private void memberDataReceived(byte[] data) { @@ -477,8 +484,8 @@ } }; executor.execute(t); - } catch (Exception x) { - log.error(sm.getString("mcastServiceImpl.memberDisappeared.failed"), x); + } catch (Exception e) { + log.error(sm.getString("mcastServiceImpl.memberDisappeared.failed"), e); } } } @@ -523,7 +530,7 @@ } public long getServiceStartTime() { - return (member != null) ? member.getServiceStartTime() : -1l; + return (member != null) ? member.getServiceStartTime() : -1L; } public int getRecoveryCounter() { @@ -570,14 +577,14 @@ if (log.isDebugEnabled()) { log.debug(sm.getString("mcastServiceImpl.invalidMemberPackage"), ax); } - } catch (Exception x) { + } catch (Exception e) { if (errorCounter == 0 && doRunReceiver) { - log.warn(sm.getString("mcastServiceImpl.error.receiving"), x); + log.warn(sm.getString("mcastServiceImpl.error.receiving"), e); } else if (log.isDebugEnabled()) { if (doRunReceiver) { - log.debug(sm.getString("mcastServiceImpl.error.receiving"), x); + log.debug(sm.getString("mcastServiceImpl.error.receiving"), e); } else { - log.warn(sm.getString("mcastServiceImpl.error.receivingNoSleep"), x); + log.debug(sm.getString("mcastServiceImpl.error.receivingNoSleep"), e); } } if (doRunReceiver) { @@ -616,11 +623,11 @@ try { send(true); errorCounter = 0; - } catch (Exception x) { + } catch (Exception e) { if (errorCounter == 0) { - log.warn(sm.getString("mcastServiceImpl.send.failed"), x); + log.warn(sm.getString("mcastServiceImpl.send.failed"), e); } else { - log.debug(sm.getString("mcastServiceImpl.send.failed"), x); + log.debug(sm.getString("mcastServiceImpl.send.failed"), e); } if ((++errorCounter) >= recoveryCounter) { errorCounter = 0; @@ -671,8 +678,8 @@ try { parent.stop(Channel.MBR_RX_SEQ | Channel.MBR_TX_SEQ); return true; - } catch (Exception x) { - log.warn(sm.getString("mcastServiceImpl.recovery.stopFailed"), x); + } catch (Exception e) { + log.warn(sm.getString("mcastServiceImpl.recovery.stopFailed"), e); return false; } } @@ -682,8 +689,8 @@ parent.init(); parent.start(Channel.MBR_RX_SEQ | Channel.MBR_TX_SEQ); return true; - } catch (Exception x) { - log.warn(sm.getString("mcastServiceImpl.recovery.startFailed"), x); + } catch (Exception e) { + log.warn(sm.getString("mcastServiceImpl.recovery.startFailed"), e); return false; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/MemberImpl.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/MemberImpl.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/MemberImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/MemberImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,8 +33,8 @@ */ public class MemberImpl implements Member, java.io.Externalizable { - public static final transient byte[] TRIBES_MBR_BEGIN = new byte[] { 84, 82, 73, 66, 69, 83, 45, 66, 1, 0 }; - public static final transient byte[] TRIBES_MBR_END = new byte[] { 84, 82, 73, 66, 69, 83, 45, 69, 1, 0 }; + public static final byte[] TRIBES_MBR_BEGIN = new byte[] { 84, 82, 73, 66, 69, 83, 45, 66, 1, 0 }; + public static final byte[] TRIBES_MBR_END = new byte[] { 84, 82, 73, 66, 69, 83, 45, 69, 1, 0 }; protected static final StringManager sm = StringManager.getManager(Constants.Package); /** @@ -517,7 +517,7 @@ for (int i = 0; data != null && i < data.length; i++) { buf.append(String.valueOf(data[i])).append(' '); if (i == max) { - buf.append("...(" + data.length + ")"); + buf.append("...(").append(data.length).append(")"); break; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/Membership.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/Membership.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/Membership.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/Membership.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,6 @@ * A membership implementation using simple multicast. This is the representation of a multicast membership. This * class is responsible for maintaining a list of active cluster nodes in the cluster. If a node fails to send out a * heartbeat, the node will be dismissed. - * - * @author Peter Rossbach */ public class Membership implements Cloneable { @@ -175,7 +173,7 @@ synchronized (membersLock) { if (!map.containsKey(member)) { map.put(member, entry); - Member results[] = new Member[members.length + 1]; + Member[] results = new Member[members.length + 1]; System.arraycopy(members, 0, results, 0, members.length); results[members.length] = member; Arrays.sort(results, memberComparator); @@ -203,7 +201,7 @@ if (n < 0) { return; } - Member results[] = new Member[members.length - 1]; + Member[] results = new Member[members.length - 1]; int j = 0; for (int i = 0; i < members.length; i++) { if (i != n) { @@ -220,7 +218,7 @@ * * @param maxtime - the max time a member can remain unannounced before it is considered dead. * - * @return the list of expired members + * @return the array of expired members */ public Member[] expire(long maxtime) { synchronized (membersLock) { @@ -263,11 +261,9 @@ public Member getMember(Member mbr) { Member[] members = this.members; - if (members.length > 0) { - for (Member member : members) { - if (member.equals(mbr)) { - return member; - } + for (Member member : members) { + if (member.equals(mbr)) { + return member; } } return null; @@ -292,7 +288,7 @@ /** * Inner class that represents a member entry */ - protected static class MbrEntry { + public static class MbrEntry { protected final Member mbr; protected long lastHeardFrom; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/StaticMember.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/StaticMember.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/StaticMember.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/StaticMember.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,8 +46,8 @@ } else { try { setHostname(host); - } catch (IOException x) { - throw new RuntimeException(x); + } catch (IOException ioe) { + throw new RuntimeException(ioe); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/StaticMembershipProvider.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/StaticMembershipProvider.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/StaticMembershipProvider.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/StaticMembershipProvider.java 2026-01-23 19:33:36.000000000 +0000 @@ -374,15 +374,8 @@ @Override public String toString() { - StringBuilder buf = new StringBuilder("MemberMessage["); - buf.append("name="); - buf.append(new String(membershipId)); - buf.append("; type="); - buf.append(getTypeDesc()); - buf.append("; member="); - buf.append(member); - buf.append(']'); - return buf.toString(); + return "MemberMessage[" + "name=" + new String(membershipId) + "; type=" + getTypeDesc() + "; member=" + + member + ']'; } protected String getTypeDesc() { @@ -407,8 +400,9 @@ sleep(pingInterval); ping(); } catch (InterruptedException ix) { - } catch (Exception x) { - log.warn(sm.getString("staticMembershipProvider.pingThread.failed"), x); + // Ignore + } catch (Exception e) { + log.warn(sm.getString("staticMembershipProvider.pingThread.failed"), e); } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/StaticMembershipService.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/StaticMembershipService.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/StaticMembershipService.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/StaticMembershipService.java 2026-01-23 19:33:36.000000000 +0000 @@ -115,8 +115,8 @@ localMember.setSecurePort(securePort); localMember.setUdpPort(udpPort); localMember.getData(true, true); - } catch (IOException x) { - throw new IllegalArgumentException(x); + } catch (IOException ioe) { + throw new IllegalArgumentException(ioe); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java 2026-01-23 19:33:36.000000000 +0000 @@ -76,9 +76,9 @@ keyManagerFactory.init(keyStore, clientKeyPassword); return keyManagerFactory.getKeyManagers(); - } catch (IOException e) { + } catch (IOException ioe) { log.error(sm.getString("certificateStream.clientCertError", clientCertFile, clientKeyFile)); - throw e; + throw ioe; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java 2026-01-23 19:33:36.000000000 +0000 @@ -92,7 +92,7 @@ */ protected String getNamespace() { String namespace = getEnv(CUSTOM_ENV_PREFIX + "NAMESPACE", "KUBERNETES_NAMESPACE"); - if (namespace == null || namespace.length() == 0) { + if (namespace == null || namespace.isEmpty()) { log.warn(sm.getString("kubernetesMembershipProvider.noNamespace")); namespace = "tomcat"; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java 2026-01-23 19:33:36.000000000 +0000 @@ -200,8 +200,8 @@ localMember.setPayload(payload); localMember.setDomain(domain); localMember.getData(true, true); - } catch (IOException e) { - throw new IllegalArgumentException(e); + } catch (IOException ioe) { + throw new IllegalArgumentException(ioe); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,6 +21,7 @@ import java.net.InetAddress; import java.net.URLEncoder; import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -124,7 +125,7 @@ if (log.isDebugEnabled()) { log.debug(sm.getString("cloudMembershipProvider.start", dnsServiceName)); } - dnsServiceName = URLEncoder.encode(dnsServiceName, "UTF-8"); + dnsServiceName = URLEncoder.encode(dnsServiceName, StandardCharsets.UTF_8); // Fetch initial members heartbeat(); @@ -161,11 +162,11 @@ continue; } long aliveTime = -1; - MemberImpl member = null; + MemberImpl member; try { member = new MemberImpl(ip, port, aliveTime); - } catch (IOException e) { - log.error(sm.getString("kubernetesMembershipProvider.memberError"), e); + } catch (IOException ioe) { + log.error(sm.getString("kubernetesMembershipProvider.memberError"), ioe); continue; } member.setUniqueId(id); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,6 +24,8 @@ import java.nio.charset.StandardCharsets; import java.nio.file.FileSystems; import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileTime; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; @@ -43,8 +45,13 @@ */ public class KubernetesMembershipProvider extends CloudMembershipProvider { + private static final Log log = LogFactory.getLog(KubernetesMembershipProvider.class); + private Path saTokenPath; + private FileTime saTokenLastModifiedTime; + + @Override public void start(int level) throws Exception { if ((level & MembershipService.MBR_RX) == 0) { @@ -80,10 +87,12 @@ saTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"; } try { - byte[] bytes = Files.readAllBytes(FileSystems.getDefault().getPath(saTokenFile)); + saTokenPath = FileSystems.getDefault().getPath(saTokenFile); + byte[] bytes = Files.readAllBytes(saTokenPath); streamProvider = new TokenStreamProvider(new String(bytes, StandardCharsets.US_ASCII), caCertFile); - } catch (IOException e) { - log.error(sm.getString("kubernetesMembershipProvider.streamError"), e); + saTokenLastModifiedTime = Files.getLastModifiedTime(saTokenPath); + } catch (IOException ioe) { + log.error(sm.getString("kubernetesMembershipProvider.streamError"), ioe); } } else { if (protocol == null) { @@ -110,11 +119,11 @@ String labels = getEnv(CUSTOM_ENV_PREFIX + "LABELS", "KUBERNETES_LABELS"); - namespace = URLEncoder.encode(namespace, "UTF-8"); - labels = labels == null ? null : URLEncoder.encode(labels, "UTF-8"); + namespace = URLEncoder.encode(namespace, StandardCharsets.UTF_8); + labels = labels == null ? null : URLEncoder.encode(labels, StandardCharsets.UTF_8); url = String.format("%s://%s:%s/api/%s/namespaces/%s/pods", protocol, masterHost, masterPort, ver, namespace); - if (labels != null && labels.length() > 0) { + if (labels != null && !labels.isEmpty()) { url = url + "?labelSelector=" + labels; } @@ -122,6 +131,7 @@ heartbeat(); } + @Override public boolean stop(int level) throws Exception { try { @@ -131,24 +141,52 @@ } } + @Override protected Member[] fetchMembers() { if (streamProvider == null) { return new Member[0]; } + reloadSaTokenIfChanged(); + List members = new ArrayList<>(); try (InputStream stream = streamProvider.openStream(url, headers, connectionTimeout, readTimeout); - InputStreamReader reader = new InputStreamReader(stream, "UTF-8")) { + InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) { parsePods(reader, members); - } catch (IOException e) { - log.error(sm.getString("kubernetesMembershipProvider.streamError"), e); + } catch (IOException ioe) { + log.error(sm.getString("kubernetesMembershipProvider.streamError"), ioe); } return members.toArray(new Member[0]); } + + private void reloadSaTokenIfChanged() { + if (saTokenPath == null) { + // Service account token not being used. + return; + } + if (!Files.exists(saTokenPath)) { + // If the service account token is being used, this path should exist + log.warn(sm.getString("kubernetesMembershipProvider.serviceAccountTokenMissing", saTokenPath)); + return; + } + try { + FileTime oldSaTokenLastModifiedTime = saTokenLastModifiedTime; + saTokenLastModifiedTime = Files.getLastModifiedTime(saTokenPath); + // Use != to protect against clock issues + if (!saTokenLastModifiedTime.equals(oldSaTokenLastModifiedTime)) { + byte[] bytes = Files.readAllBytes(saTokenPath); + ((TokenStreamProvider) streamProvider).setToken(new String(bytes, StandardCharsets.US_ASCII)); + } + } catch (IOException ioe) { + log.error(sm.getString("kubernetesMembershipProvider.streamError"), ioe); + } + } + + @SuppressWarnings("unchecked") protected void parsePods(Reader reader, List members) { JSONParser parser = new JSONParser(reader); @@ -222,13 +260,13 @@ long aliveTime = Duration.between(Instant.parse(creationTimestampObject.toString()), startTime).toMillis(); - MemberImpl member = null; + MemberImpl member; try { member = new MemberImpl(podIP, port, aliveTime); - } catch (IOException e) { + } catch (IOException ioe) { // Shouldn't happen: // an exception is thrown if hostname can't be resolved to IP, but we already provide an IP - log.error(sm.getString("kubernetesMembershipProvider.memberError"), e); + log.error(sm.getString("kubernetesMembershipProvider.memberError"), ioe); continue; } byte[] id = md5.digest(uid.getBytes(StandardCharsets.US_ASCII)); @@ -239,5 +277,4 @@ log.error(sm.getString("kubernetesMembershipProvider.jsonError"), e); } } - } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -38,6 +38,7 @@ kubernetesMembershipProvider.memberError=Error creating member kubernetesMembershipProvider.noKey=Client certificate key was not specified in the environment kubernetesMembershipProvider.noNamespace=Namespace not set +kubernetesMembershipProvider.serviceAccountTokenMissing=Service account token not found at [{0}] kubernetesMembershipProvider.streamError=Failed to open stream tokenStream.failedConnection=Failed connection to [{0}] with token [{1}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -38,6 +38,7 @@ kubernetesMembershipProvider.memberError=Erreur de création d'un membre kubernetesMembershipProvider.noKey=La clé du certificat client n'a pas été spécifiée dans l'environnement kubernetesMembershipProvider.noNamespace=L'espace de noms n'est pas paramétré +kubernetesMembershipProvider.serviceAccountTokenMissing=Le jeton du compte de service n''a pas été trouvé à [{0}] kubernetesMembershipProvider.streamError=Échec d'ouverture du flux tokenStream.failedConnection=Impossible de se connecter à [{0}] avec le token [{1}] diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -38,6 +38,7 @@ kubernetesMembershipProvider.memberError=メンバー作成中のエラー kubernetesMembershipProvider.noKey=クライアント証明書キーが環境変数に指定されていませんでした kubernetesMembershipProvider.noNamespace=Namespace が未指定です。 +kubernetesMembershipProvider.serviceAccountTokenMissing=サービスアカウントのトークンが [{0}] に見つかりません kubernetesMembershipProvider.streamError=ストリームのオープンに失敗しました tokenStream.failedConnection=トークン[{1}]で[{0}]への接続に失敗しました。 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,7 +27,8 @@ public class TokenStreamProvider extends AbstractStreamProvider { private String token; - private SSLSocketFactory factory; + private final SSLSocketFactory factory; + TokenStreamProvider(String token, String caCertFile) throws Exception { this.token = token; @@ -37,11 +38,18 @@ this.factory = context.getSocketFactory(); } + @Override protected SSLSocketFactory getSocketFactory() { return factory; } + + protected void setToken(String token) { + this.token = token; + } + + @Override public InputStream openStream(String url, Map headers, int connectTimeout, int readTimeout) throws IOException { @@ -51,10 +59,9 @@ } try { return super.openStream(url, headers, connectTimeout, readTimeout); - } catch (IOException e) { + } catch (IOException ioe) { // Add debug information - throw new IOException(sm.getString("tokenStream.failedConnection", url, token), e); + throw new IOException(sm.getString("tokenStream.failedConnection", url, token), ioe); } } - } \ No newline at end of file diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java tomcat10-10.1.52/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java 2026-01-23 19:33:36.000000000 +0000 @@ -223,7 +223,7 @@ "Created Lazy Map with name:" + mapContextName + ", bytes:" + Arrays.toString(this.mapContextName)); } - // create an rpc channel and add the map as a listener + // create a rpc channel and add the map as a listener this.rpcChannel = new RpcChannel(this.mapContextName, channel, this); // add this map as a message listener this.channel.addChannelListener(this); @@ -238,10 +238,13 @@ // state is transferred, we are ready for messaging broadcast(MapMessage.MSG_START, true); } catch (ChannelException x) { - log.warn(sm.getString("abstractReplicatedMap.unableSend.startMessage")); if (terminate) { + // Exception is logged further up stack + log.warn(sm.getString("abstractReplicatedMap.unableSend.startMessage")); breakdown(); throw new RuntimeException(sm.getString("abstractReplicatedMap.unableStart"), x); + } else { + log.warn(sm.getString("abstractReplicatedMap.unableSend.startMessage"), x); } } this.state = State.INITIALIZED; @@ -369,6 +372,7 @@ try { broadcast(MapMessage.MSG_STOP, false); } catch (Exception ignore) { + // Ignore } // cleanup this.channel.removeChannelListener(this); @@ -438,7 +442,7 @@ * @param complete - if set to true, the object is replicated to its backup if set to false, only objects that * implement ReplicatedMapEntry and the isDirty() returns true will be replicated */ - public void replicate(Object key, boolean complete) { + public void replicate(K key, boolean complete) { if (log.isTraceEnabled()) { log.trace("Replicate invoked on key:" + key); } @@ -475,8 +479,8 @@ msg = new MapMessage(mapContextName, getReplicateMessageType(), true, (Serializable) entry.getKey(), null, rentry.getDiff(), entry.getPrimary(), entry.getBackupNodes()); rentry.resetDiff(); - } catch (IOException x) { - log.error(sm.getString("abstractReplicatedMap.unable.diffObject"), x); + } catch (IOException ioe) { + log.error(sm.getString("abstractReplicatedMap.unable.diffObject"), ioe); } finally { rentry.unlock(); } @@ -487,7 +491,7 @@ (Serializable) entry.getValue(), null, entry.getPrimary(), entry.getBackupNodes()); } if (msg == null) { - // construct a access message + // construct an access message msg = new MapMessage(mapContextName, MapMessage.MSG_ACCESS, false, (Serializable) entry.getKey(), null, null, entry.getPrimary(), entry.getBackupNodes()); } @@ -715,8 +719,8 @@ diff.lock(); try { diff.applyDiff(mapmsg.getDiffValue(), 0, mapmsg.getDiffValue().length); - } catch (Exception x) { - log.error(sm.getString("abstractReplicatedMap.unableApply.diff", entry.getKey()), x); + } catch (Exception e) { + log.error(sm.getString("abstractReplicatedMap.unableApply.diff", entry.getKey()), e); } finally { diff.unlock(); } @@ -863,9 +867,8 @@ @Override public void memberDisappeared(Member member) { - boolean removed = false; synchronized (mapMembers) { - removed = (mapMembers.remove(member) != null); + boolean removed = (mapMembers.remove(member) != null); if (!removed) { if (log.isDebugEnabled()) { log.debug(sm.getString("replicatedMap.member.disappeared.unknown", member)); @@ -941,7 +944,7 @@ public int getNextBackupIndex() { synchronized (mapMembers) { int size = mapMembers.size(); - if (mapMembers.size() == 0) { + if (mapMembers.isEmpty()) { return -1; } int node = currentNode++; @@ -983,8 +986,8 @@ if (this.state.isAvailable()) { ping(accessTimeout); } - } catch (Exception x) { - log.error(sm.getString("abstractReplicatedMap.heartbeat.failed"), x); + } catch (Exception e) { + log.error(sm.getString("abstractReplicatedMap.heartbeat.failed"), e); } } @@ -1030,7 +1033,7 @@ // if the message is not primary, we need to retrieve the latest value try { Member[] backup = null; - MapMessage msg = null; + MapMessage msg; if (entry.isBackup()) { // select a new backup node backup = publishEntryInfo(key, entry.getValue()); @@ -1112,13 +1115,13 @@ int cnt = 0; while (i.hasNext()) { - Map.Entry e = i.next(); + Map.Entry e = i.next(); System.out.println((++cnt) + ". " + innerMap.get(e.getKey())); } System.out.println("EndMap]\n\n"); - } catch (Exception ignore) { + } catch (Exception e) { if (log.isTraceEnabled()) { - log.trace("Error printing map", ignore); + log.trace("Error printing map", e); } } } @@ -1226,8 +1229,7 @@ public Set> entrySet() { LinkedHashSet> set = new LinkedHashSet<>(innerMap.size()); for (Entry> e : innerMap.entrySet()) { - Object key = e.getKey(); - MapEntry entry = innerMap.get(key); + MapEntry entry = innerMap.get(e.getKey()); if (entry != null && entry.isActive()) { set.add(entry); } @@ -1257,9 +1259,7 @@ // todo, implement a counter variable instead // only count active members in this node int counter = 0; - Iterator>> it = innerMap.entrySet().iterator(); - while (it != null && it.hasNext()) { - Map.Entry e = it.next(); + for (Entry e : innerMap.entrySet()) { if (e != null) { MapEntry entry = innerMap.get(e.getKey()); if (entry != null && entry.isActive() && entry.getValue() != null) { @@ -1435,13 +1435,8 @@ @Override public String toString() { - StringBuilder buf = new StringBuilder("MapEntry[key:"); - buf.append(getKey()).append("; "); - buf.append("value:").append(getValue()).append("; "); - buf.append("primary:").append(isPrimary()).append("; "); - buf.append("backup:").append(isBackup()).append("; "); - buf.append("proxy:").append(isProxy()).append(";]"); - return buf.toString(); + return "MapEntry[key:" + getKey() + "; " + "value:" + getValue() + "; " + "primary:" + isPrimary() + "; " + + "backup:" + isBackup() + "; " + "proxy:" + isProxy() + ";]"; } } @@ -1479,16 +1474,8 @@ @Override public String toString() { - StringBuilder buf = new StringBuilder("MapMessage[context="); - buf.append(new String(mapId)); - buf.append("; type="); - buf.append(getTypeDesc()); - buf.append("; key="); - buf.append(key); - buf.append("; value="); - buf.append(value); - buf.append(']'); - return buf.toString(); + return "MapMessage[context=" + new String(mapId) + "; type=" + getTypeDesc() + "; key=" + key + "; value=" + + value + ']'; } public String getTypeDesc() { @@ -1554,8 +1541,8 @@ public Serializable getKey() { try { return key(null); - } catch (Exception x) { - throw new RuntimeException(sm.getString("mapMessage.deserialize.error.key"), x); + } catch (Exception e) { + throw new RuntimeException(sm.getString("mapMessage.deserialize.error.key"), e); } } @@ -1578,8 +1565,8 @@ public Serializable getValue() { try { return value(null); - } catch (Exception x) { - throw new RuntimeException(sm.getString("mapMessage.deserialize.error.value"), x); + } catch (Exception e) { + throw new RuntimeException(sm.getString("mapMessage.deserialize.error.value"), e); } } @@ -1625,8 +1612,8 @@ valuedata = XByteBuffer.serialize(value); } this.value = value; - } catch (IOException x) { - throw new RuntimeException(x); + } catch (IOException ioe) { + throw new RuntimeException(ioe); } } @@ -1636,8 +1623,8 @@ keydata = XByteBuffer.serialize(key); } this.key = key; - } catch (IOException x) { - throw new RuntimeException(x); + } catch (IOException ioe) { + throw new RuntimeException(ioe); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java tomcat10-10.1.52/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java 2026-01-23 19:33:36.000000000 +0000 @@ -180,7 +180,7 @@ if (next == null) { continue; } - MapMessage msg = null; + MapMessage msg; try { Member[] tmpBackup = wrap(next); // publish the backup data to one node @@ -203,7 +203,7 @@ try { // publish the data out to all nodes Member[] proxies = excludeFromSet(backup, getMapMembers()); - if (success && proxies.length > 0) { + if (proxies.length > 0) { msg = new MapMessage(getMapContextName(), MapMessage.MSG_PROXY, false, (Serializable) key, null, null, channel.getLocalMember(false), backup); if (log.isTraceEnabled()) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/tipis/ReplicatedMap.java tomcat10-10.1.52/java/org/apache/catalina/tribes/tipis/ReplicatedMap.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/tipis/ReplicatedMap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/tipis/ReplicatedMap.java 2026-01-23 19:33:36.000000000 +0000 @@ -186,10 +186,9 @@ @Override public void memberDisappeared(Member member) { - boolean removed = false; Log log = getLog(); synchronized (mapMembers) { - removed = (mapMembers.remove(member) != null); + boolean removed = (mapMembers.remove(member) != null); if (!removed) { if (log.isDebugEnabled()) { log.debug(sm.getString("replicatedMap.member.disappeared.unknown", member)); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/Constants.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Manifest constants for the org.apache.catalina.tribes.transport package. - * - * @author Peter Rossbach */ public class Constants { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/PooledSender.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/PooledSender.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/PooledSender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/PooledSender.java 2026-01-23 19:33:36.000000000 +0000 @@ -92,7 +92,7 @@ @Override public boolean keepalive() { // do nothing, the pool checks on every return - return (queue == null) ? false : queue.checkIdleKeepAlive(); + return queue != null && queue.checkIdleKeepAlive(); } @Override @@ -110,13 +110,13 @@ // ----------------------------------------------------- Inner Class private static class SenderQueue { - private int limit = 25; + private int limit; - PooledSender parent = null; + PooledSender parent; - private List notinuse = null; + private final List notinuse; - private List inuse = null; + private final List inuse; private boolean isOpen = true; @@ -165,15 +165,15 @@ throw new IllegalStateException(sm.getString("pooledSender.closed.queue")); } DataSender sender = null; - if (notinuse.size() == 0 && inuse.size() < limit) { + if (notinuse.isEmpty() && inuse.size() < limit) { sender = parent.getNewDataSender(); - } else if (notinuse.size() > 0) { + } else if (!notinuse.isEmpty()) { sender = notinuse.remove(0); } if (sender != null) { inuse.add(sender); return sender; - } // end if + } long delta = System.currentTimeMillis() - start; if (delta > timeout && timeout > 0) { return null; @@ -181,8 +181,9 @@ try { wait(Math.max(timeout - delta, 1)); } catch (InterruptedException x) { + // Ignore } - } // end if + } } } @@ -215,11 +216,11 @@ for (Object value : unused) { DataSender sender = (DataSender) value; sender.disconnect(); - } // for + } for (Object o : used) { DataSender sender = (DataSender) o; sender.disconnect(); - } // for + } notinuse.clear(); inuse.clear(); notifyAll(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/ReceiverBase.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/ReceiverBase.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/ReceiverBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/ReceiverBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -97,8 +97,6 @@ @Override public void start() throws IOException { if (executor == null) { - // executor = new ThreadPoolExecutor(minThreads,maxThreads,60,TimeUnit.SECONDS,new - // LinkedBlockingQueue()); String channelName = ""; if (channel.getName() != null) { channelName = "[" + channel.getName() + "]"; @@ -183,7 +181,7 @@ /** * Attempts to bind using the provided port and if that fails attempts to bind to each of the ports from portstart * to (portstart + retries -1) until either there are no more ports or the bind is successful. The address to bind - * to is obtained via a call to {link {@link #getBind()}. + * to is obtained via a call to {@link #getBind()}. * * @param socket The socket to bind * @param portstart Starting port for bind attempts @@ -202,11 +200,11 @@ setPort(port); log.info(sm.getString("receiverBase.socket.bind", addr)); retries = 0; - } catch (IOException x) { + } catch (IOException ioe) { retries--; if (retries <= 0) { log.info(sm.getString("receiverBase.unable.bind", addr)); - throw x; + throw ioe; } port++; } @@ -234,11 +232,11 @@ setUdpPort(portstart); log.info(sm.getString("receiverBase.udp.bind", addr)); return 0; - } catch (IOException x) { + } catch (IOException ioe) { retries--; if (retries <= 0) { log.info(sm.getString("receiverBase.unable.bind.udp", addr)); - throw x; + throw ioe; } portstart++; try { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/RxTaskPool.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/RxTaskPool.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/RxTaskPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/RxTaskPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,10 +48,6 @@ protected void configureTask(AbstractRxTask task) { synchronized (task) { task.setTaskPool(this); - // task.setName(task.getClass().getName() + "[" + inc() + "]"); - // task.setDaemon(true); - // task.setPriority(Thread.MAX_PRIORITY); - // task.start(); } } @@ -64,12 +60,11 @@ AbstractRxTask worker = null; synchronized (mutex) { while (worker == null && running) { - if (idle.size() > 0) { + if (!idle.isEmpty()) { try { worker = idle.remove(0); - } catch (java.util.NoSuchElementException x) { - // this means that there are no available workers - worker = null; + } catch (java.util.NoSuchElementException ignore) { + // Should never happen as access to idle is always synchronized on mutex } } else if (used.size() < this.maxTasks && creator != null) { worker = creator.createRxTask(); @@ -81,7 +76,7 @@ Thread.currentThread().interrupt(); } } - } // while + } if (worker != null) { used.add(worker); } @@ -104,7 +99,6 @@ if (running) { synchronized (mutex) { used.remove(worker); - // if ( idle.size() < minThreads && !idle.contains(worker)) idle.add(worker); if (idle.size() < maxTasks && !idle.contains(worker)) { idle.add(worker); // let max be the upper limit } else { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/SenderState.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/SenderState.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/SenderState.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/SenderState.java 2026-01-23 19:33:36.000000000 +0000 @@ -52,7 +52,7 @@ // ----------------------------------------------------- Instance Variables - private volatile int state = READY; + private volatile int state; // ----------------------------------------------------- Constructor diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/NioReceiver.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/NioReceiver.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/NioReceiver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/NioReceiver.java 2026-01-23 19:33:36.000000000 +0000 @@ -52,7 +52,7 @@ private volatile boolean running = false; - private AtomicReference selector = new AtomicReference<>(); + private final AtomicReference selector = new AtomicReference<>(); private ServerSocketChannel serverChannel = null; private DatagramChannel datagramChannel = null; @@ -72,12 +72,12 @@ super.start(); try { setPool(new RxTaskPool(getMaxThreads(), getMinThreads(), this)); - } catch (Exception x) { - log.fatal(sm.getString("nioReceiver.threadpool.fail"), x); - if (x instanceof IOException) { - throw (IOException) x; + } catch (Exception e) { + log.fatal(sm.getString("nioReceiver.threadpool.fail"), e); + if (e instanceof IOException) { + throw (IOException) e; } else { - throw new IOException(x.getMessage()); + throw new IOException(e.getMessage()); } } try { @@ -90,12 +90,12 @@ Thread t = new Thread(this, "NioReceiver" + channelName); t.setDaemon(true); t.start(); - } catch (Exception x) { - log.fatal(sm.getString("nioReceiver.start.fail"), x); - if (x instanceof IOException) { - throw (IOException) x; + } catch (Exception e) { + log.fatal(sm.getString("nioReceiver.start.fail"), e); + if (e instanceof IOException) { + throw (IOException) e; } else { - throw new IOException(x.getMessage()); + throw new IOException(e.getMessage()); } } } @@ -128,13 +128,13 @@ // set up the datagram channel if (this.getUdpPort() > 0) { datagramChannel = DatagramChannel.open(); - configureDatagraChannel(); + configureDatagramChannel(); // bind to the address to avoid security checks bindUdp(datagramChannel.socket(), getUdpPort(), getAutoBind()); } } - private void configureDatagraChannel() throws IOException { + private void configureDatagramChannel() throws IOException { datagramChannel.configureBlocking(false); datagramChannel.socket().setSendBufferSize(getUdpTxBufSize()); datagramChannel.socket().setReceiveBufferSize(getUdpRxBufSize()); @@ -160,15 +160,15 @@ if (events.isEmpty()) { return; } - Runnable r = null; + Runnable r; while ((r = events.pollFirst()) != null) { try { if (log.isTraceEnabled()) { log.trace("Processing event in selector:" + r); } r.run(); - } catch (Exception x) { - log.error(sm.getString("nioReceiver.eventsError"), x); + } catch (Exception e) { + log.error(sm.getString("nioReceiver.eventsError"), e); } } } @@ -184,9 +184,9 @@ if (key.channel() instanceof SocketChannel) { try { ((SocketChannel) key.channel()).socket().close(); - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(sm.getString("nioReceiver.closeError"), e); + log.debug(sm.getString("nioReceiver.closeError"), ioe); } } } @@ -201,9 +201,9 @@ } try { key.channel().close(); - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(sm.getString("nioReceiver.closeError"), e); + log.debug(sm.getString("nioReceiver.closeError"), ioe); } } @@ -303,7 +303,7 @@ // get an iterator over the set of selected keys Iterator it = selector.selectedKeys().iterator(); // look at each key in the selected set - while (it != null && it.hasNext()) { + while (it.hasNext()) { SelectionKey key = it.next(); // Is a new connection coming in? if (key.isAcceptable()) { @@ -344,9 +344,9 @@ if (datagramChannel != null) { try { datagramChannel.close(); - } catch (Exception iox) { + } catch (Exception e) { if (log.isDebugEnabled()) { - log.debug(sm.getString("nioReceiver.closeError"), iox); + log.debug(sm.getString("nioReceiver.closeError"), e); } } datagramChannel = null; @@ -377,8 +377,8 @@ log.warn(sm.getString("nioReceiver.stop.threadRunning")); } closeSelector(); - } catch (Exception x) { - log.error(sm.getString("nioReceiver.stop.fail"), x); + } catch (Exception e) { + log.error(sm.getString("nioReceiver.stop.fail"), e); } finally { this.selector.set(null); } @@ -397,9 +397,9 @@ key.attach(null); key.cancel(); } - } catch (IOException ignore) { + } catch (IOException ioe) { if (log.isWarnEnabled()) { - log.warn(sm.getString("nioReceiver.cleanup.fail"), ignore); + log.warn(sm.getString("nioReceiver.cleanup.fail"), ioe); } } catch (ClosedSelectorException ignore) { // Ignore @@ -444,8 +444,8 @@ running = true; try { listen(); - } catch (Exception x) { - log.error(sm.getString("nioReceiver.run.fail"), x); + } catch (Exception e) { + log.error(sm.getString("nioReceiver.run.fail"), e); } finally { running = false; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/NioReplicationTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -199,7 +199,7 @@ ChannelMessage[] msgs = pkgcnt == 0 ? ChannelData.EMPTY_DATA_ARRAY : reader.execute(); - registerForRead(key, reader);// register to read new data, before we send it off to avoid dead locks + registerForRead(key, reader);// register to read new data, before we send it off to avoid deadlocks for (ChannelMessage msg : msgs) { /* @@ -214,7 +214,8 @@ try { Logs.MESSAGES.trace("NioReplicationThread - Received msg:" + new UniqueId(msg.getUniqueId()) + " at " + new java.sql.Timestamp(System.currentTimeMillis())); - } catch (Throwable t) { + } catch (Throwable ignore) { + // Ignore } } // process the message @@ -228,7 +229,7 @@ } } catch (RemoteProcessException e) { if (log.isDebugEnabled()) { - log.error(sm.getString("nioReplicationTask.process.clusterMsg.failed"), e); + log.debug(sm.getString("nioReplicationTask.process.clusterMsg.failed"), e); } if (ChannelData.sendAckSync(msg.getOptions())) { sendAck(key, (WritableByteChannel) channel, Constants.FAIL_ACK_COMMAND, saddr); @@ -280,8 +281,8 @@ log.trace("CKX Cancelling key:" + key); } - } catch (Exception x) { - log.error(sm.getString("nioReplicationTask.error.register.key", key), x); + } catch (Exception e) { + log.error(sm.getString("nioReplicationTask.error.register.key", key), e); } }; receiver.addEvent(r); @@ -339,8 +340,12 @@ ((channel instanceof SocketChannel) ? ((SocketChannel) channel).socket().getInetAddress() : ((DatagramChannel) channel).socket().getInetAddress())); } - } catch (IOException x) { - log.warn(sm.getString("nioReplicationTask.unable.ack", x.getMessage())); + } catch (IOException ioe) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("nioReplicationTask.unable.ack", ioe.getMessage()), ioe); + } else { + log.warn(sm.getString("nioReplicationTask.unable.ack", ioe.getMessage())); + } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/NioSender.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/NioSender.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/NioSender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/NioSender.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,8 +33,8 @@ import org.apache.juli.logging.LogFactory; /** - * This class is NOT thread safe and should never be used with more than one thread at a time This is a state machine, - * handled by the process method States are: + * This class is NOT thread safe and should never be used with more than one thread at a time. This is a state machine, + * handled by the process method. States are: *

              *
            • NOT_CONNECTED -> connect() -> CONNECTED
            • *
            • CONNECTED -> setMessage() -> READY TO WRITE
            • @@ -42,6 +42,7 @@ *
            • READY_TO_READ -> read() -> READY_TO_READ | TRANSFER_COMPLETE
            • *
            • TRANSFER_COMPLETE -> CONNECTED
            • *
            + * Thread-safety / synchronisation is managed by ParallelNioSender */ public class NioSender extends AbstractSender { @@ -96,12 +97,11 @@ if (current != null) { key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); } - return false; } else { // wait for the connection to finish key.interestOps(key.interestOps() | SelectionKey.OP_CONNECT); - return false; - } // end if + } + return false; } else if (key.isWritable()) { boolean writecomplete = write(); if (writecomplete) { @@ -120,7 +120,7 @@ } else { // we are not complete, lets write some more key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); - } // end if + } } else if (key.isReadable()) { boolean readcomplete = read(); if (readcomplete) { @@ -221,7 +221,7 @@ } @Override - public synchronized void connect() throws IOException { + public void connect() throws IOException { if (connecting || isConnected()) { return; } @@ -277,16 +277,13 @@ try { try { socketChannel.socket().close(); - } catch (Exception x) { + } catch (Exception e) { // Ignore } // error free close, all the way - // try {socket.shutdownOutput();}catch ( Exception x){} - // try {socket.shutdownInput();}catch ( Exception x){} - // try {socket.close();}catch ( Exception x){} try { socketChannel.close(); - } catch (Exception x) { + } catch (Exception e) { // Ignore } } finally { @@ -297,26 +294,23 @@ try { try { dataChannel.socket().close(); - } catch (Exception x) { + } catch (Exception e) { // Ignore } // error free close, all the way - // try {socket.shutdownOutput();}catch ( Exception x){} - // try {socket.shutdownInput();}catch ( Exception x){} - // try {socket.close();}catch ( Exception x){} try { dataChannel.close(); - } catch (Exception x) { + } catch (Exception e) { // Ignore } } finally { dataChannel = null; } } - } catch (Exception x) { - log.error(sm.getString("nioSender.unable.disconnect", x.getMessage())); + } catch (Exception e) { + log.error(sm.getString("nioSender.unable.disconnect", e.getMessage())); if (log.isDebugEnabled()) { - log.debug(sm.getString("nioSender.unable.disconnect", x.getMessage()), x); + log.debug(sm.getString("nioSender.unable.disconnect", e.getMessage()), e); } } } @@ -364,28 +358,26 @@ public void setMessage(byte[] data, int offset, int length) throws IOException { if (data != null) { - synchronized (this) { - current = data; - remaining = length; - ackbuf.clear(); - if (writebuf != null) { - writebuf.clear(); - } else { - writebuf = getBuffer(length); - } - if (writebuf.capacity() < length) { - writebuf = getBuffer(length); - } + current = data; + remaining = length; + ackbuf.clear(); + if (writebuf != null) { + writebuf.clear(); + } else { + writebuf = getBuffer(length); + } + if (writebuf.capacity() < length) { + writebuf = getBuffer(length); + } - // TODO use ByteBuffer.wrap to avoid copying the data. - writebuf.put(data, offset, length); - writebuf.flip(); - if (isConnected()) { - if (isUdpBased()) { - dataChannel.register(getSelector(), SelectionKey.OP_WRITE, this); - } else { - socketChannel.register(getSelector(), SelectionKey.OP_WRITE, this); - } + // TODO use ByteBuffer.wrap to avoid copying the data. + writebuf.put(data, offset, length); + writebuf.flip(); + if (isConnected()) { + if (isUdpBased()) { + dataChannel.register(getSelector(), SelectionKey.OP_WRITE, this); + } else { + socketChannel.register(getSelector(), SelectionKey.OP_WRITE, this); } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/ParallelNioSender.java 2026-01-23 19:33:36.000000000 +0000 @@ -88,20 +88,20 @@ cx.addFaultyMember(result.getFailed().getFaultyMembers()); } } - } catch (Exception x) { + } catch (Exception e) { if (log.isTraceEnabled()) { - log.trace("Error sending message", x); + log.trace("Error sending message", e); } if (cx == null) { - if (x instanceof ChannelException) { - cx = (ChannelException) x; + if (e instanceof ChannelException) { + cx = (ChannelException) e; } else { - cx = new ChannelException(sm.getString("parallelNioSender.send.failed"), x); + cx = new ChannelException(sm.getString("parallelNioSender.send.failed"), e); } } for (NioSender sender : senders) { if (!sender.isComplete()) { - cx.addFaultyMember(sender.getDestination(), x); + cx.addFaultyMember(sender.getDestination(), e); } } throw cx; @@ -126,16 +126,16 @@ // there was an error throw cx; } - } catch (Exception x) { + } catch (Exception e) { try { this.disconnect(); - } catch (Exception e) { + } catch (Exception ignore) { // Ignore } - if (x instanceof ChannelException) { - throw (ChannelException) x; + if (e instanceof ChannelException) { + throw (ChannelException) e; } else { - throw new ChannelException(x); + throw new ChannelException(e); } } @@ -173,9 +173,9 @@ } SenderState.getSenderState(sender.getDestination()).setReady(); } // end if - } catch (Exception x) { + } catch (Exception e) { if (log.isTraceEnabled()) { - log.trace("Error while processing send to " + sender.getDestination().getName(), x); + log.trace("Error while processing send to " + sender.getDestination().getName(), e); } SenderState state = SenderState.getSenderState(sender.getDestination()); int attempt = sender.getAttempt() + 1; @@ -192,7 +192,7 @@ log.warn(sm.getString("parallelNioSender.send.fail.retrying", sender.getDestination().getName())); } else { - log.warn(sm.getString("parallelNioSender.send.fail", sender.getDestination().getName()), x); + log.warn(sm.getString("parallelNioSender.send.fail", sender.getDestination().getName()), e); } } } @@ -200,8 +200,8 @@ log.warn(sm.getString("parallelNioSender.sender.disconnected.notRetry", sender.getDestination().getName())); ChannelException cx = - new ChannelException(sm.getString("parallelNioSender.sender.disconnected.sendFailed"), x); - cx.addFaultyMember(sender.getDestination(), x); + new ChannelException(sm.getString("parallelNioSender.sender.disconnected.sendFailed"), e); + cx.addFaultyMember(sender.getDestination(), e); result.failed(cx); break; } @@ -218,8 +218,8 @@ } } else { ChannelException cx = new ChannelException(sm.getString("parallelNioSender.sendFailed.attempt", - Integer.toString(sender.getAttempt()), Integer.toString(maxAttempts)), x); - cx.addFaultyMember(sender.getDestination(), x); + Integer.toString(sender.getAttempt()), Integer.toString(maxAttempts)), e); + cx.addFaultyMember(sender.getDestination(), e); result.failed(cx); } // end if } @@ -228,7 +228,7 @@ } private static class SendResult { - private List completeSenders = new ArrayList<>(); + private final List completeSenders = new ArrayList<>(); private ChannelException exception = null; private void complete(NioSender sender) { @@ -366,7 +366,7 @@ setConnected(false); try { close(); - } catch (Exception x) { + } catch (Exception ignore) { // Ignore } } @@ -384,14 +384,14 @@ } else { try { sender.read(); - } catch (IOException x) { + } catch (IOException ioe) { sender.disconnect(); sender.reset(); // nioSenders.remove(entry.getKey()); i.remove(); result = true; - } catch (Exception x) { - log.warn(sm.getString("parallelNioSender.error.keepalive", sender), x); + } catch (Exception e) { + log.warn(sm.getString("parallelNioSender.error.keepalive", sender), e); } } } @@ -399,8 +399,9 @@ if (result) { try { state.selector.selectNow(); - } catch (Exception e) { - /* Ignore */} + } catch (Exception ignore) { + // Ignore + } } return result; } @@ -422,7 +423,7 @@ NioSender nioSender = iter.next(); try { nioSender.disconnect(); - } catch (Exception e) { + } catch (Exception ignore) { // Ignore } iter.remove(); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/transport/nio/PooledParallelSender.java 2026-01-23 19:33:36.000000000 +0000 @@ -64,8 +64,8 @@ ParallelNioSender sender = new ParallelNioSender(); transferProperties(this, sender); return sender; - } catch (IOException x) { - throw new RuntimeException(sm.getString("pooledParallelSender.unable.open"), x); + } catch (IOException ioe) { + throw new RuntimeException(sm.getString("pooledParallelSender.unable.open"), ioe); } } } \ No newline at end of file diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/util/Arrays.java tomcat10-10.1.52/java/org/apache/catalina/tribes/util/Arrays.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/util/Arrays.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/util/Arrays.java 2026-01-23 19:33:36.000000000 +0000 @@ -214,7 +214,7 @@ return idx; } - public static int hashCode(byte a[]) { + public static int hashCode(byte[] a) { if (a == null) { return 0; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/util/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/tribes/util/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/tribes/util/LocalStrings_ru.properties 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/util/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,19 @@ +# 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. + +# Do not edit this file directly. +# To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations + +arrays.length.outOfBounds=Недостаточно элементов данных в ключе, длина вне границ. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/util/StringManager.java tomcat10-10.1.52/java/org/apache/catalina/tribes/util/StringManager.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/util/StringManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/util/StringManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,15 +39,11 @@ *

            * Please see the documentation for java.util.ResourceBundle for more information. * - * @author James Duncan Davidson [duncan@eng.sun.com] - * @author James Todd [gonzo@eng.sun.com] - * @author Mel Martinez [mmartinez@g1440.com] - * * @see java.util.ResourceBundle */ public class StringManager { - private static int LOCALE_CACHE_SIZE = 10; + private static final int LOCALE_CACHE_SIZE = 10; /** * The ResourceBundle for this StringManager. @@ -68,14 +64,15 @@ try { bnd = ResourceBundle.getBundle(bundleName, locale); } catch (MissingResourceException ex) { - // Try from the current loader (that's the case for trusted apps) - // Should only be required if using a TC5 style classloader structure - // where common != shared != server + /* + * Try from the current loader (that's the case for trusted apps). Should only be required if using a class + * loader structure where common != shared != server + */ ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl != null) { try { bnd = ResourceBundle.getBundle(bundleName, locale, cl); - } catch (MissingResourceException ex2) { + } catch (MissingResourceException ignore) { // Ignore } } @@ -106,8 +103,7 @@ */ public String getString(String key) { if (key == null) { - String msg = "key may not have a null value"; - throw new IllegalArgumentException(msg); + throw new IllegalArgumentException("key may not have a null value"); } String str = null; @@ -117,7 +113,7 @@ if (bundle != null) { str = bundle.getString(key); } - } catch (MissingResourceException mre) { + } catch (MissingResourceException ignore) { // bad: shouldn't mask an exception the following way: // str = "[cannot find message associated with key '" + key + // "' due to " + mre + "]"; @@ -129,7 +125,6 @@ // better: consistent with container pattern to // simply return null. Calling code can then do // a null check. - str = null; } return str; @@ -181,7 +176,7 @@ * * @return The StringManager for the given class. */ - public static final StringManager getManager(Class clazz) { + public static StringManager getManager(Class clazz) { return getManager(clazz.getPackage().getName()); } @@ -194,7 +189,7 @@ * * @return The StringManager for the given package. */ - public static final StringManager getManager(String packageName) { + public static StringManager getManager(String packageName) { return getManager(packageName, Locale.getDefault()); } @@ -208,7 +203,7 @@ * * @return The StringManager for a particular package and Locale */ - public static final synchronized StringManager getManager(String packageName, Locale locale) { + public static synchronized StringManager getManager(String packageName, Locale locale) { Map map = managers.get(packageName); if (map == null) { @@ -222,10 +217,7 @@ @Override protected boolean removeEldestEntry(Map.Entry eldest) { - if (size() > (LOCALE_CACHE_SIZE - 1)) { - return true; - } - return false; + return size() > (LOCALE_CACHE_SIZE - 1); } }; managers.put(packageName, map); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/tribes/util/UUIDGenerator.java tomcat10-10.1.52/java/org/apache/catalina/tribes/util/UUIDGenerator.java --- tomcat10-10.1.34/java/org/apache/catalina/tribes/util/UUIDGenerator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/tribes/util/UUIDGenerator.java 2026-01-23 19:33:36.000000000 +0000 @@ -64,7 +64,7 @@ into[6 + offset] &= 0x0F; into[6 + offset] |= (UUID_VERSION << 4); into[8 + offset] &= 0x3F; // 0011 1111 - into[8 + offset] |= 0x80; // 1000 0000 + into[8 + offset] |= (byte) 0x80; // 1000 0000 return into; } @@ -77,11 +77,10 @@ * @param r Random */ public static void nextBytes(byte[] into, int offset, int length, Random r) { - int numRequested = length; int numGot = 0, rnd = 0; while (true) { for (int i = 0; i < BYTES_PER_INT; i++) { - if (numGot == numRequested) { + if (numGot == length) { return; } rnd = (i == 0 ? r.nextInt() : rnd >> BITS_PER_BYTE); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/AbstractGroup.java tomcat10-10.1.52/java/org/apache/catalina/users/AbstractGroup.java --- tomcat10-10.1.34/java/org/apache/catalina/users/AbstractGroup.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/AbstractGroup.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ * Convenience base class for {@link Group} implementations. *

            * - * @author Craig R. McClanahan - * * @since 4.1 */ public abstract class AbstractGroup implements Group { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/AbstractRole.java tomcat10-10.1.52/java/org/apache/catalina/users/AbstractRole.java --- tomcat10-10.1.34/java/org/apache/catalina/users/AbstractRole.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/AbstractRole.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ * Convenience base class for {@link Role} implementations. *

            * - * @author Craig R. McClanahan - * * @since 4.1 */ public abstract class AbstractRole implements Role { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/AbstractUser.java tomcat10-10.1.52/java/org/apache/catalina/users/AbstractUser.java --- tomcat10-10.1.34/java/org/apache/catalina/users/AbstractUser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/AbstractUser.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ * Convenience base class for {@link User} implementations. *

            * - * @author Craig R. McClanahan - * * @since 4.1 */ public abstract class AbstractUser implements User { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/Constants.java tomcat10-10.1.52/java/org/apache/catalina/users/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/users/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Manifest constants for this Java package. * - * @author Craig R. McClanahan - * * @since 4.1 */ public final class Constants { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/DataSourceUserDatabase.java tomcat10-10.1.52/java/org/apache/catalina/users/DataSourceUserDatabase.java --- tomcat10-10.1.34/java/org/apache/catalina/users/DataSourceUserDatabase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/DataSourceUserDatabase.java 2026-01-23 19:33:36.000000000 +0000 @@ -322,7 +322,7 @@ } /** - * @return the table that holds user data.. + * @return the table that holds user data */ public String getUserTable() { return userTable; @@ -871,8 +871,7 @@ } } - User user = new GenericUser<>(this, userName, dbCredentials, fullName, groups, roles); - return user; + return new GenericUser<>(this, userName, dbCredentials, fullName, groups, roles); } @Override @@ -1124,7 +1123,7 @@ protected void saveInternal(Connection dbConnection) { StringBuilder temp = null; - StringBuilder tempRelation = null; + StringBuilder tempRelation; StringBuilder tempRelationDelete = null; if (isRoleStoreDefined()) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/DataSourceUserDatabaseFactory.java tomcat10-10.1.52/java/org/apache/catalina/users/DataSourceUserDatabaseFactory.java --- tomcat10-10.1.34/java/org/apache/catalina/users/DataSourceUserDatabaseFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/DataSourceUserDatabaseFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,8 +40,6 @@ *
          • dataSourceName - JNDI name of the DataSource, which must be located in the same Context * environment as the UserDatabase
          • *
          - * - * @author Craig R. McClanahan */ public class DataSourceUserDatabaseFactory implements ObjectFactory { @@ -52,7 +50,7 @@ /** *

          * Create and return a new DataSourceUserDatabase instance that has been configured according to the - * properties of the specified Reference. If you instance can be created, return null + * properties of the specified Reference. If the instance cannot be created, return null * instead. *

          * @@ -69,7 +67,7 @@ // We only know how to deal with javax.naming.References // that specify a class name of "org.apache.catalina.UserDatabase" - if ((obj == null) || !(obj instanceof Reference)) { + if (!(obj instanceof Reference)) { return null; } Reference ref = (Reference) obj; @@ -79,9 +77,7 @@ DataSource dataSource = null; String dataSourceName = null; - RefAddr ra = null; - - ra = ref.get("dataSourceName"); + RefAddr ra = ref.get("dataSourceName"); if (ra != null) { dataSourceName = ra.getContent().toString(); dataSource = (DataSource) nameCtx.lookup(dataSourceName); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/GenericGroup.java tomcat10-10.1.52/java/org/apache/catalina/users/GenericGroup.java --- tomcat10-10.1.34/java/org/apache/catalina/users/GenericGroup.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/GenericGroup.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,8 +33,6 @@ *

          * * @param The specific type of UserDase with which this group is associated - * - * @author Craig R. McClanahan */ public class GenericGroup extends AbstractGroup { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/GenericRole.java tomcat10-10.1.52/java/org/apache/catalina/users/GenericRole.java --- tomcat10-10.1.34/java/org/apache/catalina/users/GenericRole.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/GenericRole.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ *

          * * @param The specific type of UserDase with which this role is associated - * - * @author Craig R. McClanahan */ public class GenericRole extends AbstractRole { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/GenericUser.java tomcat10-10.1.52/java/org/apache/catalina/users/GenericUser.java --- tomcat10-10.1.34/java/org/apache/catalina/users/GenericUser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/GenericUser.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,6 @@ *

          * * @param The specific type of UserDase with which this role is associated - * - * @author Craig R. McClanahan */ public class GenericUser extends AbstractUser { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/MemoryGroup.java tomcat10-10.1.52/java/org/apache/catalina/users/MemoryGroup.java --- tomcat10-10.1.34/java/org/apache/catalina/users/MemoryGroup.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/MemoryGroup.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,6 @@ * {@link UserDatabase}. *

          * - * @author Craig R. McClanahan - * * @since 4.1 * * @deprecated Use {@link GenericGroup} instead. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/MemoryRole.java tomcat10-10.1.52/java/org/apache/catalina/users/MemoryRole.java --- tomcat10-10.1.34/java/org/apache/catalina/users/MemoryRole.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/MemoryRole.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,6 @@ * {@link UserDatabase}. *

          * - * @author Craig R. McClanahan - * * @since 4.1 * * @deprecated Use {@link GenericRole} instead. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/MemoryUser.java tomcat10-10.1.52/java/org/apache/catalina/users/MemoryUser.java --- tomcat10-10.1.34/java/org/apache/catalina/users/MemoryUser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/MemoryUser.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,8 +27,6 @@ * {@link UserDatabase}. *

          * - * @author Craig R. McClanahan - * * @since 4.1 * * @deprecated Use {@link GenericUser} instead. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/MemoryUserDatabase.java tomcat10-10.1.52/java/org/apache/catalina/users/MemoryUserDatabase.java --- tomcat10-10.1.34/java/org/apache/catalina/users/MemoryUserDatabase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/MemoryUserDatabase.java 2026-01-23 19:33:36.000000000 +0000 @@ -57,8 +57,6 @@ * This class does not enforce what, in an RDBMS, would be called referential integrity. Concurrent modifications may * result in inconsistent data such as a User retaining a reference to a Role that has been removed from the database. * - * @author Craig R. McClanahan - * * @since 4.1 */ /* @@ -139,7 +137,7 @@ protected final Map roles = new ConcurrentHashMap<>(); /** - * The set of {@link User}s defined in this database, keyed by user name. + * The set of {@link User}s defined in this database, keyed by username. */ protected final Map users = new ConcurrentHashMap<>(); @@ -259,7 +257,7 @@ @Override public Group createGroup(String groupname, String description) { - if (groupname == null || groupname.length() == 0) { + if (groupname == null || groupname.isEmpty()) { String msg = sm.getString("memoryUserDatabase.nullGroup"); log.warn(msg); throw new IllegalArgumentException(msg); @@ -278,7 +276,7 @@ @Override public Role createRole(String rolename, String description) { - if (rolename == null || rolename.length() == 0) { + if (rolename == null || rolename.isEmpty()) { String msg = sm.getString("memoryUserDatabase.nullRole"); log.warn(msg); throw new IllegalArgumentException(msg); @@ -298,7 +296,7 @@ @Override public User createUser(String username, String password, String fullName) { - if (username == null || username.length() == 0) { + if (username == null || username.isEmpty()) { String msg = sm.getString("memoryUserDatabase.nullUser"); log.warn(msg); throw new IllegalArgumentException(msg); @@ -481,15 +479,12 @@ // Print the file prolog writer.println(""); writer.println(""); // Print entries for each defined role, group, and user - Iterator values = null; - values = getRoles(); + Iterator values = getRoles(); while (values.hasNext()) { Role role = (Role) values.next(); writer.print(" 0) { + if (!rolename.isEmpty()) { Role role = database.findRole(rolename); if (role == null) { role = database.createRole(rolename, null); @@ -780,8 +765,8 @@ String roles = attributes.getValue("roles"); User user = database.createUser(username, password, fullName); if (groups != null) { - while (groups.length() > 0) { - String groupname = null; + while (!groups.isEmpty()) { + String groupname; int comma = groups.indexOf(','); if (comma >= 0) { groupname = groups.substring(0, comma).trim(); @@ -790,7 +775,7 @@ groupname = groups.trim(); groups = ""; } - if (groupname.length() > 0) { + if (!groupname.isEmpty()) { Group group = database.findGroup(groupname); if (group == null) { group = database.createGroup(groupname, null); @@ -800,8 +785,8 @@ } } if (roles != null) { - while (roles.length() > 0) { - String rolename = null; + while (!roles.isEmpty()) { + String rolename; int comma = roles.indexOf(','); if (comma >= 0) { rolename = roles.substring(0, comma).trim(); @@ -810,7 +795,7 @@ rolename = roles.trim(); roles = ""; } - if (rolename.length() > 0) { + if (!rolename.isEmpty()) { Role role = database.findRole(rolename); if (role == null) { role = database.createRole(rolename, null); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java tomcat10-10.1.52/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java --- tomcat10-10.1.34/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/users/MemoryUserDatabaseFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,8 +41,6 @@ * to which it is stored. [conf/tomcat-users.xml] *
        * - * @author Craig R. McClanahan - * * @since 4.1 */ public class MemoryUserDatabaseFactory implements ObjectFactory { @@ -54,7 +52,7 @@ /** *

        * Create and return a new MemoryUserDatabase instance that has been configured according to the - * properties of the specified Reference. If you instance can be created, return null + * properties of the specified Reference. If the instance cannot be created, return null * instead. *

        * @@ -71,7 +69,7 @@ // We only know how to deal with javax.naming.References // that specify a class name of "org.apache.catalina.UserDatabase" - if ((obj == null) || !(obj instanceof Reference)) { + if (!(obj instanceof Reference)) { return null; } Reference ref = (Reference) obj; @@ -82,9 +80,7 @@ // Create and configure a MemoryUserDatabase instance based on the // RefAddr values associated with this Reference MemoryUserDatabase database = new MemoryUserDatabase(name.toString()); - RefAddr ra = null; - - ra = ref.get("pathname"); + RefAddr ra = ref.get("pathname"); if (ra != null) { database.setPathname(ra.getContent().toString()); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/CharsetMapper.java tomcat10-10.1.52/java/org/apache/catalina/util/CharsetMapper.java --- tomcat10-10.1.34/java/org/apache/catalina/util/CharsetMapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/CharsetMapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,8 +30,6 @@ * text (or generating output text) when the Content-Type header does not include one. You can customize the behavior of * this class by modifying the mapping data it loads, or by subclassing it (to change the algorithm) and then using your * own version for a particular web application. - * - * @author Craig R. McClanahan */ public class CharsetMapper { @@ -57,7 +55,7 @@ /** - * Construct a new CharsetMapper using the specified properties resource. + * Construct a new CharsetMapper using the specified properties' resource. * * @param name Name of a properties resource to be loaded * @@ -83,7 +81,7 @@ /** * The mapping properties that have been initialized from the specified or default properties resource. */ - private Properties map = new Properties(); + private final Properties map = new Properties(); // ------------------------------------------------------- Public Methods diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/ContextName.java tomcat10-10.1.52/java/org/apache/catalina/util/ContextName.java --- tomcat10-10.1.34/java/org/apache/catalina/util/ContextName.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/ContextName.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ package org.apache.catalina.util; import java.util.Locale; +import java.util.Objects; /** * Utility class to manage context names so there is one place where the conversions between baseName, path and version @@ -106,11 +107,7 @@ } // Version should never be null - if (version == null) { - this.version = ""; - } else { - this.version = version; - } + this.version = Objects.requireNonNullElse(version, ""); // Name is path + version if (this.version.isEmpty()) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/CustomObjectInputStream.java tomcat10-10.1.52/java/org/apache/catalina/util/CustomObjectInputStream.java --- tomcat10-10.1.34/java/org/apache/catalina/util/CustomObjectInputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/CustomObjectInputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,9 +33,6 @@ /** * Custom subclass of ObjectInputStream that loads from the class loader for this web application. This * allows classes defined only with the web application to be found correctly. - * - * @author Craig R. McClanahan - * @author Bip Thelin */ public final class CustomObjectInputStream extends ObjectInputStream { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/DOMWriter.java tomcat10-10.1.52/java/org/apache/catalina/util/DOMWriter.java --- tomcat10-10.1.34/java/org/apache/catalina/util/DOMWriter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/DOMWriter.java 2026-01-23 19:33:36.000000000 +0000 @@ -63,7 +63,7 @@ case Node.ELEMENT_NODE: out.print('<'); out.print(node.getLocalName()); - Attr attrs[] = sortAttributes(node.getAttributes()); + Attr[] attrs = sortAttributes(node.getAttributes()); boolean xmlns = false; for (Attr attr : attrs) { if ("xmlns".equals(attr.getPrefix())) { @@ -109,7 +109,7 @@ out.print(node.getLocalName()); String data = node.getNodeValue(); - if (data != null && data.length() > 0) { + if (data != null && !data.isEmpty()) { out.print(' '); out.print(data); } @@ -152,17 +152,15 @@ } int len = attrs.getLength(); - Attr array[] = new Attr[len]; + Attr[] array = new Attr[len]; for (int i = 0; i < len; i++) { array[i] = (Attr) attrs.item(i); } for (int i = 0; i < len - 1; i++) { - String name = null; - name = array[i].getLocalName(); + String name = array[i].getLocalName(); int index = i; for (int j = i + 1; j < len; j++) { - String curName = null; - curName = array[j].getLocalName(); + String curName = array[j].getLocalName(); if (curName.compareTo(name) < 0) { name = curName; index = j; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/ErrorPageSupport.java tomcat10-10.1.52/java/org/apache/catalina/util/ErrorPageSupport.java --- tomcat10-10.1.34/java/org/apache/catalina/util/ErrorPageSupport.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/ErrorPageSupport.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,10 +29,10 @@ public class ErrorPageSupport { // Fully qualified class name to error page - private Map exceptionPages = new ConcurrentHashMap<>(); + private final Map exceptionPages = new ConcurrentHashMap<>(); // HTTP status code to error page - private Map statusPages = new ConcurrentHashMap<>(); + private final Map statusPages = new ConcurrentHashMap<>(); public void add(ErrorPage errorPage) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/ExactRateLimiter.java tomcat10-10.1.52/java/org/apache/catalina/util/ExactRateLimiter.java --- tomcat10-10.1.34/java/org/apache/catalina/util/ExactRateLimiter.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/ExactRateLimiter.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.util; + +import java.util.concurrent.ScheduledExecutorService; + +/** + * A RateLimiter that compromises efficiency for accuracy in order to provide exact rate limiting. + */ +public class ExactRateLimiter extends RateLimiterBase { + + @Override + protected String getDefaultPolicyName() { + return "exact"; + } + + + @Override + protected TimeBucketCounterBase newCounterInstance(int duration, ScheduledExecutorService executorService) { + return new ExactTimeBucketCounter(duration, executorService); + } + + + /** + * An accurate counter with exact bucket index, but slightly less efficient than another fast counter provided with + * the {@link FastRateLimiter}. + */ + static class ExactTimeBucketCounter extends TimeBucketCounterBase { + + ExactTimeBucketCounter(int bucketDuration, ScheduledExecutorService executorService) { + super(bucketDuration, executorService); + } + + @Override + public long getBucketIndex(long timestamp) { + return (timestamp / 1000) / getBucketDuration(); + } + + @Override + public double getRatio() { + // Actual value is exactly the same as declared + return 1.0d; + } + + @Override + public long getMillisUntilNextBucket() { + long millis = System.currentTimeMillis(); + long nextTimeBucketMillis = (getBucketIndex(millis) + 1) * getBucketDuration() * 1000; + return nextTimeBucketMillis - millis; + } + } +} diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/FastRateLimiter.java tomcat10-10.1.52/java/org/apache/catalina/util/FastRateLimiter.java --- tomcat10-10.1.34/java/org/apache/catalina/util/FastRateLimiter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/FastRateLimiter.java 2026-01-23 19:33:36.000000000 +0000 @@ -14,92 +14,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.catalina.util; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicInteger; - -import jakarta.servlet.FilterConfig; - -import org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor; /** * A RateLimiter that compromises accuracy for speed in order to provide maximum throughput. */ -public class FastRateLimiter implements RateLimiter { - - private static AtomicInteger index = new AtomicInteger(); - - TimeBucketCounter bucketCounter; - - int duration; - - int requests; - - int actualRequests; - - int actualDuration; - - // Initial policy name can be rewritten by setPolicyName() - private String policyName = "fast-" + index.incrementAndGet(); - - @Override - public String getPolicyName() { - return policyName; - } - - @Override - public void setPolicyName(String name) { - this.policyName = name; - } - - @Override - public int getDuration() { - return actualDuration; - } +public class FastRateLimiter extends RateLimiterBase { @Override - public void setDuration(int duration) { - this.duration = duration; + protected String getDefaultPolicyName() { + return "fast"; } - @Override - public int getRequests() { - return actualRequests; - } @Override - public void setRequests(int requests) { - this.requests = requests; + protected TimeBucketCounterBase newCounterInstance(int duration, ScheduledExecutorService executorService) { + return new TimeBucketCounter(duration, executorService); } - @Override - public int increment(String ipAddress) { - return bucketCounter.increment(ipAddress); - } @Override - public void destroy() { - bucketCounter.destroy(); - } - - @Override - public void setFilterConfig(FilterConfig filterConfig) { - - ScheduledExecutorService executorService = (ScheduledExecutorService) filterConfig.getServletContext() - .getAttribute(ScheduledThreadPoolExecutor.class.getName()); - - if (executorService == null) { - executorService = new java.util.concurrent.ScheduledThreadPoolExecutor(1); - } - - bucketCounter = new TimeBucketCounter(duration, executorService); - actualRequests = (int) Math.round(bucketCounter.getRatio() * requests); - actualDuration = bucketCounter.getActualDuration() / 1000; - } - public TimeBucketCounter getBucketCounter() { - return bucketCounter; + return (TimeBucketCounter) bucketCounter; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/FilterUtil.java tomcat10-10.1.52/java/org/apache/catalina/util/FilterUtil.java --- tomcat10-10.1.34/java/org/apache/catalina/util/FilterUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/FilterUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -79,6 +79,11 @@ return false; } + /* + * Note: Order does not matter here in terms of specification compliance because this is Filter mapping. If any + * rule matches then this method returns true. Order would matter if this was Servlet mapping. + */ + // Case 1 - Exact Match if (testPath.equals(requestPath)) { return true; @@ -92,8 +97,8 @@ if (testPath.regionMatches(0, requestPath, 0, testPath.length() - 2)) { if (requestPath.length() == (testPath.length() - 2)) { return true; - } else if ('/' == requestPath.charAt(testPath.length() - 2)) { - return true; + } else { + return '/' == requestPath.charAt(testPath.length() - 2); } } return false; @@ -109,7 +114,12 @@ } } - // Case 4 - "Default" Match + // Case 4 - Context Root + if (testPath.isEmpty() && requestPath.equals("/")) { + return true; + } + + // Case 5 - "Default" Match return false; // NOTE - Not relevant for selecting filters } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/IOTools.java tomcat10-10.1.52/java/org/apache/catalina/util/IOTools.java --- tomcat10-10.1.34/java/org/apache/catalina/util/IOTools.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/IOTools.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,9 +24,7 @@ /** - * Contains commonly needed I/O-related methods - * - * @author Dan Sandberg + * Contains commonly needed I/O-related methods. */ public class IOTools { protected static final int DEFAULT_BUFFER_SIZE = 4 * 1024; // 4k diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/Introspection.java tomcat10-10.1.52/java/org/apache/catalina/util/Introspection.java --- tomcat10-10.1.34/java/org/apache/catalina/util/Introspection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/Introspection.java 2026-01-23 19:33:36.000000000 +0000 @@ -59,11 +59,8 @@ * @return true if the method does have a valid name and signature, else false */ public static boolean isValidSetter(Method method) { - if (method.getName().startsWith("set") && method.getName().length() > 3 && - method.getParameterTypes().length == 1 && method.getReturnType().getName().equals("void")) { - return true; - } - return false; + return method.getName().startsWith("set") && method.getName().length() > 3 && + method.getParameterTypes().length == 1 && method.getReturnType().getName().equals("void"); } /** @@ -74,11 +71,8 @@ * @return true if the method is a valid lifecycle callback method, else false */ public static boolean isValidLifecycleCallback(Method method) { - if (method.getParameterTypes().length != 0 || Modifier.isStatic(method.getModifiers()) || - method.getExceptionTypes().length > 0 || !method.getReturnType().getName().equals("void")) { - return false; - } - return true; + return method.getParameterTypes().length == 0 && !Modifier.isStatic(method.getModifiers()) && + method.getExceptionTypes().length == 0 && method.getReturnType().getName().equals("void"); } /** diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/LifecycleBase.java tomcat10-10.1.52/java/org/apache/catalina/util/LifecycleBase.java --- tomcat10-10.1.34/java/org/apache/catalina/util/LifecycleBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/LifecycleBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -56,7 +56,7 @@ /** - * Will a {@link LifecycleException} thrown by a sub-class during {@link #initInternal()}, {@link #startInternal()}, + * Will a {@link LifecycleException} thrown by a subclass during {@link #initInternal()}, {@link #startInternal()}, * {@link #stopInternal()} or {@link #destroyInternal()} be re-thrown for the caller to handle or will it be logged * instead? * @@ -68,7 +68,7 @@ /** - * Configure if a {@link LifecycleException} thrown by a sub-class during {@link #initInternal()}, + * Configure if a {@link LifecycleException} thrown by a subclass during {@link #initInternal()}, * {@link #startInternal()}, {@link #stopInternal()} or {@link #destroyInternal()} will be re-thrown for the caller * to handle or if it will be logged instead. * @@ -98,7 +98,7 @@ /** - * Allow sub classes to fire {@link Lifecycle} events. + * Allow subclasses to fire {@link Lifecycle} events. * * @param type Event type * @param data Data associated with event. @@ -128,7 +128,7 @@ /** - * Sub-classes implement this method to perform any instance initialisation required. + * Subclasses implement this method to perform any instance initialisation required. * * @throws LifecycleException If the initialisation fails */ @@ -167,7 +167,7 @@ // FAILED state so call stop() to complete the clean-up. stop(); } else if (!state.equals(LifecycleState.STARTING)) { - // Shouldn't be necessary but acts as a check that sub-classes are + // Shouldn't be necessary but acts as a check that subclasses are // doing what they are supposed to. invalidTransition(AFTER_START_EVENT); } else { @@ -182,10 +182,10 @@ /** - * Sub-classes must ensure that the state is changed to {@link LifecycleState#STARTING} during the execution of this + * Subclasses must ensure that the state is changed to {@link LifecycleState#STARTING} during the execution of this * method. Changing state will trigger the {@link Lifecycle#START_EVENT} event. If a component fails to start it may - * either throw a {@link LifecycleException} which will cause it's parent to fail to start or it can place itself in - * the error state in which case {@link #stop()} will be called on the failed component but the parent component + * either throw a {@link LifecycleException} which will cause it's parent to fail to start, or it can place itself + * in the error state in which case {@link #stop()} will be called on the failed component but the parent component * will continue to start normally. * * @throws LifecycleException Start error occurred @@ -234,7 +234,7 @@ stopInternal(); - // Shouldn't be necessary but acts as a check that sub-classes are + // Shouldn't be necessary but acts as a check that subclasses are // doing what they are supposed to. if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) { invalidTransition(AFTER_STOP_EVENT); @@ -254,7 +254,7 @@ /** - * Sub-classes must ensure that the state is changed to {@link LifecycleState#STOPPING} during the execution of this + * Subclasses must ensure that the state is changed to {@link LifecycleState#STOPPING} during the execution of this * method. Changing state will trigger the {@link Lifecycle#STOP_EVENT} event. * * @throws LifecycleException Stop error occurred @@ -304,7 +304,7 @@ /** - * Sub-classes implement this method to perform any instance destruction required. + * Subclasses implement this method to perform any instance destruction required. * * @throws LifecycleException If the destruction fails */ @@ -324,9 +324,9 @@ /** - * Provides a mechanism for sub-classes to update the component state. Calling this method will automatically fire + * Provides a mechanism for subclasses to update the component state. Calling this method will automatically fire * any associated {@link Lifecycle} event. It will also check that any attempted state transition is valid for a - * sub-class. + * subclass. * * @param state The new state for this component * @@ -338,9 +338,9 @@ /** - * Provides a mechanism for sub-classes to update the component state. Calling this method will automatically fire + * Provides a mechanism for subclasses to update the component state. Calling this method will automatically fire * any associated {@link Lifecycle} event. It will also check that any attempted state transition is valid for a - * sub-class. + * subclass. * * @param state The new state for this component * @param data The data to pass to the associated {@link Lifecycle} event @@ -392,22 +392,20 @@ private void invalidTransition(String type) throws LifecycleException { - String msg = sm.getString("lifecycleBase.invalidTransition", type, toString(), state); - throw new LifecycleException(msg); + throw new LifecycleException(sm.getString("lifecycleBase.invalidTransition", type, toString(), state)); } private void handleSubClassException(Throwable t, String key, Object... args) throws LifecycleException { setStateInternal(LifecycleState.FAILED, null, false); ExceptionUtils.handleThrowable(t); - String msg = sm.getString(key, args); if (getThrowOnFailure()) { if (!(t instanceof LifecycleException)) { - t = new LifecycleException(msg, t); + t = new LifecycleException(sm.getString(key, args), t); } throw (LifecycleException) t; } else { - log.error(msg, t); + log.error(sm.getString(key, args), t); } } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/LifecycleMBeanBase.java tomcat10-10.1.52/java/org/apache/catalina/util/LifecycleMBeanBase.java --- tomcat10-10.1.34/java/org/apache/catalina/util/LifecycleMBeanBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/LifecycleMBeanBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -75,7 +75,7 @@ /** - * Method implemented by sub-classes to identify the domain in which MBeans should be registered. + * Method implemented by subclasses to identify the domain in which MBeans should be registered. * * @return The name of the domain to use to register MBeans. */ @@ -89,7 +89,7 @@ /** - * Allow sub-classes to specify the key properties component of the {@link ObjectName} that will be used to register + * Allow subclasses to specify the key properties component of the {@link ObjectName} that will be used to register * this component. * * @return The string representation of the key properties component of the desired {@link ObjectName} @@ -98,7 +98,7 @@ /** - * Utility method to enable sub-classes to easily register additional components that don't implement + * Utility method to enable subclasses to easily register additional components that don't implement * {@link JmxEnabled} with an MBean server.
        * Note: This method should only be used once {@link #initInternal()} has been called and before * {@link #destroyInternal()} has been called. @@ -119,7 +119,7 @@ try { on = new ObjectName(name.toString()); - Registry.getRegistry(null, null).registerComponent(obj, on, null); + Registry.getRegistry(null).registerComponent(obj, on, null); } catch (Exception e) { log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name), e); } @@ -129,7 +129,7 @@ /** - * Utility method to enable sub-classes to easily unregister additional components that don't implement + * Utility method to enable subclasses to easily unregister additional components that don't implement * {@link JmxEnabled} with an MBean server.
        * Note: This method should only be used once {@link #initInternal()} has been called and before * {@link #destroyInternal()} has been called. @@ -138,15 +138,13 @@ */ protected final void unregister(String objectNameKeyProperties) { // Construct an object name with the right domain - StringBuilder name = new StringBuilder(getDomain()); - name.append(':'); - name.append(objectNameKeyProperties); - Registry.getRegistry(null, null).unregisterComponent(name.toString()); + String name = getDomain() + ':' + objectNameKeyProperties; + Registry.getRegistry(null).unregisterComponent(name); } /** - * Utility method to enable sub-classes to easily unregister additional components that don't implement + * Utility method to enable subclasses to easily unregister additional components that don't implement * {@link JmxEnabled} with an MBean server.
        * Note: This method should only be used once {@link #initInternal()} has been called and before * {@link #destroyInternal()} has been called. @@ -154,7 +152,7 @@ * @param on The name of the component to unregister */ protected final void unregister(ObjectName on) { - Registry.getRegistry(null, null).unregisterComponent(on); + Registry.getRegistry(null).unregisterComponent(on); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/NetMask.java tomcat10-10.1.52/java/org/apache/catalina/util/NetMask.java --- tomcat10-10.1.34/java/org/apache/catalina/util/NetMask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/NetMask.java 2026-01-23 19:33:36.000000000 +0000 @@ -236,7 +236,7 @@ * * But prior to that, a simple test can be done: we deal with IP addresses here, which means IPv4 and IPv6. IPv4 * addresses are encoded on 4 bytes, IPv6 addresses are encoded on 16 bytes. If the candidate address length is - * different than this NetMask's address, we don't have a match. + * different from this NetMask's address, we don't have a match. */ if (candidate.length != netaddr.length) { return false; @@ -245,7 +245,7 @@ /* * Now do the byte-compare. The constructor has recorded the number of bytes to compare in nrBytes, use that. If - * any of the byte we have to compare is different than what we expect, we don't have a match. + * any of the byte we have to compare is different from what we expect, we don't have a match. * * If, on the opposite, after this loop, all bytes have been deemed equal, then the loop variable i will point * to the byte right after that -- which we will need... @@ -302,8 +302,7 @@ @Override public int hashCode() { - int result = 31 * Arrays.hashCode(netaddr) + lastByteShift; - return result; + return 31 * Arrays.hashCode(netaddr) + lastByteShift; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/ParameterMap.java tomcat10-10.1.52/java/org/apache/catalina/util/ParameterMap.java --- tomcat10-10.1.34/java/org/apache/catalina/util/ParameterMap.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/ParameterMap.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,8 +32,6 @@ * * @param The type of Key * @param The type of Value - * - * @author Craig R. McClanahan */ public final class ParameterMap implements Map, Serializable { @@ -82,7 +80,12 @@ * @param map Map whose contents are duplicated in the new map */ public ParameterMap(Map map) { - delegatedMap = new LinkedHashMap<>(map); + // Unroll loop for performance - https://bz.apache.org/bugzilla/show_bug.cgi?id=69820 + int mapSize = map.size(); + delegatedMap = new LinkedHashMap<>((int) (mapSize * 1.5)); + for (Map.Entry entry : map.entrySet()) { + delegatedMap.put(entry.getKey(), entry.getValue()); + } unmodifiableDelegatedMap = Collections.unmodifiableMap(delegatedMap); } @@ -95,7 +98,12 @@ * @param map Map whose contents are duplicated in the new map */ public ParameterMap(ParameterMap map) { - delegatedMap = new LinkedHashMap<>(map.delegatedMap); + // Unroll loop for performance - https://bz.apache.org/bugzilla/show_bug.cgi?id=69820 + int mapSize = map.size(); + delegatedMap = new LinkedHashMap<>((int) (mapSize * 1.5)); + for (Map.Entry entry : map.entrySet()) { + delegatedMap.put(entry.getKey(), entry.getValue()); + } unmodifiableDelegatedMap = Collections.unmodifiableMap(delegatedMap); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/RateLimiter.java tomcat10-10.1.52/java/org/apache/catalina/util/RateLimiter.java --- tomcat10-10.1.34/java/org/apache/catalina/util/RateLimiter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/RateLimiter.java 2026-01-23 19:33:36.000000000 +0000 @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.catalina.util; import jakarta.servlet.FilterConfig; @@ -46,13 +45,13 @@ void setRequests(int requests); /** - * Increments the number of requests by the given ipAddress in the current time window. + * Increments the number of requests by the given identifier in the current time window. * - * @param ipAddress the ip address + * @param identifier the identifier for which the number of associated requests should be incremented * * @return the new value after incrementing */ - int increment(String ipAddress); + int increment(String identifier); /** * Cleanup no longer needed resources. @@ -92,7 +91,7 @@ /** * Provide the quota header for this rate limit for a given request count within the current time window. * - * @param requestCount The request count within the current time window + * @param requestCount The request count within the current time window * * @return the quota header for the given value of request count * diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/RateLimiterBase.java tomcat10-10.1.52/java/org/apache/catalina/util/RateLimiterBase.java --- tomcat10-10.1.34/java/org/apache/catalina/util/RateLimiterBase.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/RateLimiterBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.util; + +import java.util.Objects; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.servlet.FilterConfig; + +/** + * Base implementation of {@link RateLimiter}, provides runtime data maintenance mechanism monitor. + */ +public abstract class RateLimiterBase implements RateLimiter { + + private static final AtomicInteger index = new AtomicInteger(); + + TimeBucketCounterBase bucketCounter; + + int requests; + int actualRequests; + + int duration; + int actualDuration; + + // Initial policy name can be rewritten by setPolicyName() + private String policyName = null; + + /* + * The self-owned utility executor, will be instantiated only when ScheduledThreadPoolExecutor is absent during + * filter configure phase. + */ + private ScheduledThreadPoolExecutor internalExecutorService = null; + + /** + * If policy name has not been specified, the first call of {@link #getPolicyName()} returns an auto-generated + * policy name using the default policy name as prefix and followed by auto-increase index. + * + * @return default policy name, as a prefix of auto-generated policy name. + */ + protected abstract String getDefaultPolicyName(); + + + @Override + public String getPolicyName() { + if (policyName == null) { + policyName = getDefaultPolicyName() + "-" + index.incrementAndGet(); + } + return policyName; + } + + + @Override + public void setPolicyName(String name) { + Objects.requireNonNull(name); + this.policyName = name; + } + + + @Override + public int getDuration() { + return actualDuration; + } + + + @Override + public void setDuration(int duration) { + this.duration = duration; + } + + + @Override + public int getRequests() { + return actualRequests; + } + + + @Override + public void setRequests(int requests) { + this.requests = requests; + } + + + @Override + public int increment(String identifier) { + return bucketCounter.increment(identifier); + } + + + @Override + public void destroy() { + bucketCounter.destroy(); + if (internalExecutorService != null) { + try { + internalExecutorService.shutdown(); + } catch (SecurityException e) { + // ignore + } + } + } + + + /** + * Instantiate an instance of {@link TimeBucketCounterBase} for specific time bucket size. Concrete classes + * determine its counter policy by returning different implementation instances. + * + * @param duration size of each time bucket in seconds + * @param utilityExecutor the executor + * + * @return counter instance of {@link TimeBucketCounterBase} + */ + protected abstract TimeBucketCounterBase newCounterInstance(int duration, ScheduledExecutorService utilityExecutor); + + + @Override + public void setFilterConfig(FilterConfig filterConfig) { + + ScheduledExecutorService executorService = (ScheduledExecutorService) filterConfig.getServletContext() + .getAttribute(ScheduledThreadPoolExecutor.class.getName()); + + if (executorService == null) { + internalExecutorService = new java.util.concurrent.ScheduledThreadPoolExecutor(1); + executorService = internalExecutorService; + } + + bucketCounter = newCounterInstance(duration, executorService); + actualDuration = bucketCounter.getBucketDuration(); + actualRequests = (int) Math.round(bucketCounter.getRatio() * requests); + } + + + /** + * Returns the internal instance of {@link TimeBucketCounterBase}. + * + * @return instance of {@link TimeBucketCounterBase} + */ + public TimeBucketCounterBase getBucketCounter() { + return bucketCounter; + } +} diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/RequestUtil.java tomcat10-10.1.52/java/org/apache/catalina/util/RequestUtil.java --- tomcat10-10.1.34/java/org/apache/catalina/util/RequestUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/RequestUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,11 +18,10 @@ import jakarta.servlet.http.HttpServletRequest; +import org.apache.catalina.connector.Request; + /** * General purpose request parsing and encoding utility methods. - * - * @author Craig R. McClanahan - * @author Tim Tye */ public final class RequestUtil { @@ -54,4 +53,51 @@ return url; } + + + /** + * Strip parameters for given path. + * + * @param input the input path + * @param request the request to add the parameters to + * + * @return the cleaned path + */ + public static String stripPathParams(String input, Request request) { + // Shortcut + if (input.indexOf(';') < 0) { + return input; + } + + StringBuilder sb = new StringBuilder(input.length()); + int pos = 0; + int limit = input.length(); + while (pos < limit) { + int nextSemiColon = input.indexOf(';', pos); + if (nextSemiColon < 0) { + nextSemiColon = limit; + } + sb.append(input, pos, nextSemiColon); + int followingSlash = input.indexOf('/', nextSemiColon); + if (followingSlash < 0) { + pos = limit; + } else { + pos = followingSlash; + } + if (request != null && nextSemiColon + 1 < pos) { + String pathVariablesString = input.substring(nextSemiColon + 1, pos); + String[] pathVariables = pathVariablesString.split(";"); + for (String pathVariable : pathVariables) { + int equals = pathVariable.indexOf('='); + if (equals > -1 && equals + 1 < pathVariable.length()) { + String name = pathVariable.substring(0, equals); + String value = pathVariable.substring(equals + 1); + request.addPathParameter(name, value); + } + } + } + } + + return sb.toString(); + } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/ResourceSet.java tomcat10-10.1.52/java/org/apache/catalina/util/ResourceSet.java --- tomcat10-10.1.34/java/org/apache/catalina/util/ResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/ResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,6 @@ * modifications. When first created, a ResourceMap is not locked. * * @param The type of elements in the Set - * - * @author Craig R. McClanahan */ public final class ResourceSet extends HashSet { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/ServerInfo.java tomcat10-10.1.52/java/org/apache/catalina/util/ServerInfo.java --- tomcat10-10.1.34/java/org/apache/catalina/util/ServerInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/ServerInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Simple utility module to make it easy to plug in the server identifier when integrating Tomcat. - * - * @author Craig R. McClanahan */ public class ServerInfo { @@ -122,7 +120,7 @@ return serverNumber; } - public static void main(String args[]) { + public static void main(String[] args) { System.out.println("Server version: " + getServerInfo()); System.out.println("Server built: " + getServerBuilt()); System.out.println("Server number: " + getServerNumber()); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/SessionConfig.java tomcat10-10.1.52/java/org/apache/catalina/util/SessionConfig.java --- tomcat10-10.1.34/java/org/apache/catalina/util/SessionConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/SessionConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -55,13 +55,13 @@ // 3. Default defined by spec if (context != null) { String cookieName = context.getSessionCookieName(); - if (cookieName != null && cookieName.length() > 0) { + if (cookieName != null && !cookieName.isEmpty()) { return cookieName; } SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig(); cookieName = scc.getName(); - if (cookieName != null && cookieName.length() > 0) { + if (cookieName != null && !cookieName.isEmpty()) { return cookieName; } } @@ -81,10 +81,10 @@ SessionCookieConfig scc = context.getServletContext().getSessionCookieConfig(); String contextPath = context.getSessionCookiePath(); - if (contextPath == null || contextPath.length() == 0) { + if (contextPath == null || contextPath.isEmpty()) { contextPath = scc.getPath(); } - if (contextPath == null || contextPath.length() == 0) { + if (contextPath == null || contextPath.isEmpty()) { contextPath = context.getEncodedPath(); } if (context.getSessionCookiePathUsesTrailingSlash()) { @@ -98,7 +98,7 @@ } else { // Only handle special case of ROOT context where cookies require a // path of '/' but the servlet spec uses an empty string - if (contextPath.length() == 0) { + if (contextPath.isEmpty()) { contextPath = "/"; } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/SessionIdGeneratorBase.java tomcat10-10.1.52/java/org/apache/catalina/util/SessionIdGeneratorBase.java --- tomcat10-10.1.34/java/org/apache/catalina/util/SessionIdGeneratorBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/SessionIdGeneratorBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -179,7 +179,7 @@ } - protected void getRandomBytes(byte bytes[]) { + protected void getRandomBytes(byte[] bytes) { SecureRandom random = randoms.poll(); if (random == null) { @@ -212,9 +212,9 @@ if (result == null) { // No secureRandomClass or creation failed. Use SecureRandom. try { - if (secureRandomProvider != null && secureRandomProvider.length() > 0) { + if (secureRandomProvider != null && !secureRandomProvider.isEmpty()) { result = SecureRandom.getInstance(secureRandomAlgorithm, secureRandomProvider); - } else if (secureRandomAlgorithm != null && secureRandomAlgorithm.length() > 0) { + } else if (secureRandomAlgorithm != null && !secureRandomAlgorithm.isEmpty()) { result = SecureRandom.getInstance(secureRandomAlgorithm); } } catch (NoSuchAlgorithmException e) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/StandardSessionIdGenerator.java tomcat10-10.1.52/java/org/apache/catalina/util/StandardSessionIdGenerator.java --- tomcat10-10.1.34/java/org/apache/catalina/util/StandardSessionIdGenerator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/StandardSessionIdGenerator.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,7 @@ @Override public String generateSessionId(String route) { - byte random[] = new byte[16]; + byte[] random = new byte[16]; int sessionIdLength = getSessionIdLength(); // Render the result as a String of hexadecimal digits @@ -49,11 +49,11 @@ } } - if (route != null && route.length() > 0) { + if (route != null && !route.isEmpty()) { buffer.append('.').append(route); } else { String jvmRoute = getJvmRoute(); - if (jvmRoute != null && jvmRoute.length() > 0) { + if (jvmRoute != null && !jvmRoute.isEmpty()) { buffer.append('.').append(jvmRoute); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/Strftime.java tomcat10-10.1.52/java/org/apache/catalina/util/Strftime.java --- tomcat10-10.1.34/java/org/apache/catalina/util/Strftime.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/Strftime.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,9 +34,6 @@ *
      • The interface looks like a subset of DateFormat. Maybe someday someone will make this class extend * DateFormat.
      • *
      - * - * @author Bip Thelin - * @author Dan Sandberg */ public class Strftime { protected static final Properties translate; diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/StringUtil.java tomcat10-10.1.52/java/org/apache/catalina/util/StringUtil.java --- tomcat10-10.1.34/java/org/apache/catalina/util/StringUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/StringUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,7 +33,7 @@ * @return An array of String values. */ public static String[] splitCommaSeparated(String s) { - return (s == null || s.length() == 0) ? new String[0] : commaSeparatedValuesPattern.split(s); + return (s == null || s.isEmpty()) ? new String[0] : commaSeparatedValuesPattern.split(s); } } \ No newline at end of file diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/TimeBucketCounter.java tomcat10-10.1.52/java/org/apache/catalina/util/TimeBucketCounter.java --- tomcat10-10.1.34/java/org/apache/catalina/util/TimeBucketCounter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/TimeBucketCounter.java 2026-01-23 19:33:36.000000000 +0000 @@ -14,122 +14,91 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.apache.catalina.util; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.juli.logging.Log; -import org.apache.juli.logging.LogFactory; -import org.apache.tomcat.util.res.StringManager; /** - * This class maintains a thread safe hash map that has timestamp-based buckets followed by a string for a key, and a - * counter for a value. each time the increment() method is called it adds the key if it does not exist, increments its - * value and returns it. a maintenance thread cleans up keys that are prefixed by previous timestamp buckets. + * A fast counter that optimizes efficiency at the expense of approximate bucket indexing. */ -public class TimeBucketCounter { - - private static final Log log = LogFactory.getLog(TimeBucketCounter.class); - private static final StringManager sm = StringManager.getManager(TimeBucketCounter.class); - - /** - * Map to hold the buckets - */ - private final ConcurrentHashMap map = new ConcurrentHashMap<>(); +public class TimeBucketCounter extends TimeBucketCounterBase { - /** - * Milliseconds bucket size as a Power of 2 for bit shift math, e.g. 16 for 65_536ms which is about 1:05 minute - */ + // Milliseconds bucket size as a Power of 2 for bit shift math, e.g. 16 for 65_536ms which is about 1:05 minute. private final int numBits; - /** - * Ratio of actual duration to config duration - */ + // Ratio of actual duration to config duration private final double ratio; - /** - * The future allowing control of the background processor. - */ - private ScheduledFuture maintenanceFuture; - private ScheduledFuture monitorFuture; - private final ScheduledExecutorService executorService; - private final long sleeptime; - /** - * Creates a new TimeBucketCounter with the specified lifetime. - * - * @param bucketDuration duration in seconds, e.g. for 1 minute pass 60 - * @param executorService the executor service which will be used to run the maintenance - */ public TimeBucketCounter(int bucketDuration, ScheduledExecutorService executorService) { + super(getActualDuration(bucketDuration), executorService); + this.numBits = determineShiftBitsOfDuration(bucketDuration); + this.ratio = ratioToPowerOf2(bucketDuration * 1000); + } - this.executorService = executorService; - - int durationMillis = bucketDuration * 1000; - - int bits = 0; - int pof2 = nextPowerOf2(durationMillis); - int bitCheck = pof2; - while (bitCheck > 1) { - bitCheck = pof2 >> ++bits; - } - - this.numBits = bits; - this.ratio = ratioToPowerOf2(durationMillis); + /** + * {@inheritDoc} + *

      + * Calculates the current time bucket index by shifting bits for fast division, e.g. shift 16 bits is the same as + * dividing by 65,536 which is about 1:05m. + */ + @Override + public long getBucketIndex(long timestamp) { + return timestamp >> this.numBits; + } - int cleanupsPerBucketDuration = (durationMillis >= 60_000) ? 6 : 3; - sleeptime = durationMillis / cleanupsPerBucketDuration; - // Start our thread - if (sleeptime > 0) { - monitorFuture = executorService.scheduleWithFixedDelay(new MaintenanceMonitor(), 0, 60, TimeUnit.SECONDS); - } + public int getNumBits() { + return numBits; } + /** - * Increments the counter for the passed identifier in the current time bucket and returns the new value. - * - * @param identifier an identifier for which we want to maintain count, e.g. IP Address + * The actual duration may differ from the configured duration because it is set to the next power of 2 value in + * order to perform very fast bit shift arithmetic. * - * @return the count within the current time bucket + * @return the actual bucket duration in milliseconds */ - public final int increment(String identifier) { - String key = getCurrentBucketPrefix() + "-" + identifier; - AtomicInteger ai = map.computeIfAbsent(key, v -> new AtomicInteger()); - return ai.incrementAndGet(); + public int getActualDuration() { + return (int) Math.pow(2, getNumBits()); } + /** - * Calculates the current time bucket prefix by shifting bits for fast division, e.g. shift 16 bits is the same as - * dividing by 65,536 which is about 1:05m. + * Determines the bits of shift for the specific bucket duration in seconds, which used to figure out the correct + * bucket index. + * + * @param duration bucket duration in seconds * - * @return The current bucket prefix. + * @return bits to be shifted */ - public final int getCurrentBucketPrefix() { - return (int) (System.currentTimeMillis() >> this.numBits); + protected static int determineShiftBitsOfDuration(int duration) { + int bits = 0; + int pof2 = nextPowerOf2(duration * 1000); + int bitCheck = pof2; + while (bitCheck > 1) { + bitCheck = pof2 >> ++bits; + } + return bits; } - public int getNumBits() { - return numBits; - } /** * The actual duration may differ from the configured duration because it is set to the next power of 2 value in * order to perform very fast bit shift arithmetic. * - * @return the actual bucket duration in milliseconds + * @param duration in seconds + * + * @return the actual bucket duration in seconds + * + * @see TimeBucketCounter#determineShiftBitsOfDuration(int) */ - public int getActualDuration() { - return (int) Math.pow(2, getNumBits()); + private static int getActualDuration(int duration) { + return (int) (1L << determineShiftBitsOfDuration(duration)) / 1000; } + /** * Returns the ratio between the configured duration param and the actual duration which will be set to the next * power of 2. We then multiply the configured requests param by the same ratio in order to compensate for the added @@ -137,18 +106,25 @@ * * @return the ratio, e.g. 1.092 if the actual duration is 65_536 for the configured duration of 60_000 */ + @Override public double getRatio() { return ratio; } + /** * Returns the ratio to the next power of 2 so that we can adjust the value. + * + * @param value of target duration in seconds + * + * @return the ratio to the next power of 2 so that we can adjust the value */ static double ratioToPowerOf2(int value) { double nextPO2 = nextPowerOf2(value); return Math.round((1000 * nextPO2 / value)) / 1000d; } + /** * Returns the next power of 2 given a value, e.g. 256 for 250, or 1024, for 1000. */ @@ -161,59 +137,11 @@ return valueOfHighestBit << 1; } - /** - * When we want to test a full bucket duration we need to sleep until the next bucket starts. - * - * @return the number of milliseconds until the next bucket - */ + + @Override public long getMillisUntilNextBucket() { long millis = System.currentTimeMillis(); long nextTimeBucketMillis = ((millis + (long) Math.pow(2, numBits)) >> numBits) << numBits; - long delta = nextTimeBucketMillis - millis; - return delta; - } - - /** - * Sets isRunning to false to terminate the maintenance thread. - */ - public void destroy() { - // Stop our thread - if (monitorFuture != null) { - monitorFuture.cancel(true); - monitorFuture = null; - } - if (maintenanceFuture != null) { - maintenanceFuture.cancel(true); - maintenanceFuture = null; - } - } - - private class Maintenance implements Runnable { - @Override - public void run() { - String currentBucketPrefix = String.valueOf(getCurrentBucketPrefix()); - ConcurrentHashMap.KeySetView keys = map.keySet(); - // remove obsolete keys - keys.removeIf(k -> !k.startsWith(currentBucketPrefix)); - } + return nextTimeBucketMillis - millis; } - - private class MaintenanceMonitor implements Runnable { - @Override - public void run() { - if (sleeptime > 0 && (maintenanceFuture == null || maintenanceFuture.isDone())) { - if (maintenanceFuture != null && maintenanceFuture.isDone()) { - // There was an error executing the scheduled task, get it and log it - try { - maintenanceFuture.get(); - } catch (InterruptedException | ExecutionException e) { - log.error(sm.getString("timebucket.maintenance.error"), e); - } - } - maintenanceFuture = executorService.scheduleWithFixedDelay(new Maintenance(), sleeptime, sleeptime, - TimeUnit.MILLISECONDS); - } - } - } - } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/TimeBucketCounterBase.java tomcat10-10.1.52/java/org/apache/catalina/util/TimeBucketCounterBase.java --- tomcat10-10.1.34/java/org/apache/catalina/util/TimeBucketCounterBase.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/TimeBucketCounterBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,222 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.util; + +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.res.StringManager; + +/** + * This class maintains a thread safe hash map that has timestamp-based buckets followed by a string for a key, and a + * counter for an integer value. Each time the increment() method is called it adds the key if it does not exist, + * increments its value and returns it. + */ +public abstract class TimeBucketCounterBase { + + private static final Log log = LogFactory.getLog(TimeBucketCounterBase.class); + private static final StringManager sm = StringManager.getManager(TimeBucketCounterBase.class); + + private static final String BUCKET_KEY_DELIMITER = "-"; + + // Map to hold the buckets + private final ConcurrentHashMap map = new ConcurrentHashMap<>(); + + // The future allowing control of the background processor. + private ScheduledFuture maintenanceFuture; + private ScheduledFuture monitorFuture; + private final ScheduledExecutorService executorService; + private final long sleeptime; + private final int bucketDuration; + + + /** + * Creates a new TimeBucketCounter with the specified lifetime. + * + * @param bucketDuration duration in seconds, e.g. for 1 minute pass 60 + * @param executorService the executor service that will be used to run the maintenance task + * + * @throws NullPointerException if executorService is null. + */ + public TimeBucketCounterBase(int bucketDuration, ScheduledExecutorService executorService) { + Objects.requireNonNull(executorService); + this.executorService = executorService; + this.bucketDuration = bucketDuration; + + int cleanupsPerBucketDuration = (bucketDuration >= 60) ? 6 : 3; + sleeptime = bucketDuration * 1000L / cleanupsPerBucketDuration; + + // Start our thread + if (sleeptime > 0) { + monitorFuture = executorService.scheduleWithFixedDelay(new MaintenanceMonitor(), 0, 60, TimeUnit.SECONDS); + } + } + + + /** + * @return bucketDuration in seconds + */ + public int getBucketDuration() { + return bucketDuration; + } + + + /** + * Returns the ratio between the configured duration param and the actual duration. + * + * @return the ratio between the configured duration param and the actual duration. + */ + public abstract double getRatio(); + + + /** + * Increments the counter for the passed identifier in the current time bucket and returns the new value. + * + * @param identifier an identifier for which we want to maintain count, e.g. IP Address + * + * @return the count within the current time bucket + */ + public final int increment(String identifier) { + String key = genKey(identifier); + AtomicInteger ai = map.computeIfAbsent(key, v -> new AtomicInteger()); + return ai.incrementAndGet(); + } + + + /** + * Generates the key of timeBucket counter maps with the specific identifier, and the timestamp is implicitly + * equivalent to "now". + * + * @param identifier an identifier for which we want to maintain count + * + * @return key of timeBucket counter maps + */ + protected final String genKey(String identifier) { + return genKey(identifier, System.currentTimeMillis()); + } + + + /** + * Generates the key of timeBucket counter maps with the specific identifier and timestamp. + * + * @param identifier of target request + * @param timestamp when target request received + * + * @return key of timeBucket counter maps + */ + protected final String genKey(String identifier, long timestamp) { + return getBucketIndex(timestamp) + BUCKET_KEY_DELIMITER + identifier; + } + + + /** + * Calculate the bucket index for the specific timestamp. + * + * @param timestamp the specific timestamp in milliseconds + * + * @return prefix the bucket key prefix for the specific timestamp + */ + protected abstract long getBucketIndex(long timestamp); + + + /** + * Returns current bucket prefix + * + * @return bucket index + */ + public int getCurrentBucketPrefix() { + return (int) getBucketIndex(System.currentTimeMillis()); + } + + + /** + * When we want to test a full bucket duration we need to sleep until the next bucket starts. + *

      + * WARNING: This method is used for test purpose. + * + * @return the number of milliseconds until the next bucket + */ + public abstract long getMillisUntilNextBucket(); + + + /** + * Stops threads created by this object and cleans up resources. + */ + public void destroy() { + map.clear(); + if (monitorFuture != null) { + monitorFuture.cancel(true); + monitorFuture = null; + } + if (maintenanceFuture != null) { + maintenanceFuture.cancel(true); + maintenanceFuture = null; + } + } + + + /** + * Periodic evict, perform removal of obsolete bucket items. Absence of this operation may result in OOM after a + * long run. + */ + public void periodicEvict() { + /* + * The implementation of this method assumes that the time taken for eviction is less than 1 bucket duration. It + * is possible that the eviction process starts in one bucket but finishes in another. Therefore, keys for the + * current bucket and the next bucket when the eviction process starts are excluded from eviction. + */ + long currentBucketIndex = getCurrentBucketPrefix(); + String currentBucketPrefix = String.valueOf(currentBucketIndex); + String nextBucketPrefix = String.valueOf(currentBucketIndex + 1); + ConcurrentHashMap.KeySetView keys = map.keySet(); + // remove obsolete keys + keys.removeIf(k -> !k.startsWith(currentBucketPrefix) && !k.startsWith(nextBucketPrefix)); + } + + + private class Maintenance implements Runnable { + @Override + public void run() { + periodicEvict(); + } + } + + private class MaintenanceMonitor implements Runnable { + @Override + public void run() { + if (sleeptime > 0 && (maintenanceFuture == null || maintenanceFuture.isDone())) { + if (maintenanceFuture != null && maintenanceFuture.isDone()) { + // There was an error executing the scheduled task, get it and log it + try { + maintenanceFuture.get(); + } catch (InterruptedException | ExecutionException e) { + log.error(sm.getString("timebucket.maintenance.error"), e); + } + } + maintenanceFuture = executorService.scheduleWithFixedDelay(new Maintenance(), sleeptime, sleeptime, + TimeUnit.MILLISECONDS); + } + } + } +} diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/ToStringUtil.java tomcat10-10.1.52/java/org/apache/catalina/util/ToStringUtil.java --- tomcat10-10.1.34/java/org/apache/catalina/util/ToStringUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/ToStringUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,17 +30,17 @@ } - public static final String toString(Contained contained) { + public static String toString(Contained contained) { return toString(contained, contained.getContainer()); } - public static final String toString(Object obj, Container container) { + public static String toString(Object obj, Container container) { return containedToString(obj, container, "Container"); } - public static final String toString(Object obj, Manager manager) { + public static String toString(Object obj, Manager manager) { return containedToString(obj, manager, "Manager"); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/URLEncoder.java tomcat10-10.1.52/java/org/apache/catalina/util/URLEncoder.java --- tomcat10-10.1.34/java/org/apache/catalina/util/URLEncoder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/URLEncoder.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,15 +20,13 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.nio.charset.Charset; +import java.nio.charset.CodingErrorAction; import java.util.BitSet; /** * This class is very similar to the java.net.URLEncoder class. Unfortunately, with java.net.URLEncoder there is no way * to specify to the java.net.URLEncoder which characters should NOT be encoded. This code was moved from * DefaultServlet.java - * - * @author Craig R. McClanahan - * @author Remy Maucherat */ public final class URLEncoder implements Cloneable { @@ -149,7 +147,15 @@ int maxBytesPerChar = 10; StringBuilder rewrittenPath = new StringBuilder(path.length()); ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar); - OutputStreamWriter writer = new OutputStreamWriter(buf, charset); + /* + * Most calls to this method use UTF-8 where malformed input and unmappable character issues are not expected to + * happen. The only Tomcat code that currently (January 2026) might call this method with something other than + * UTF-8 is the rewrite valve. In that case, the rewrite rules should be consistent with the configured URI + * encoding on the Connector. Given all of this, the IAE is only expected to be thrown as a result of + * configuration errors. + */ + OutputStreamWriter writer = new OutputStreamWriter(buf, charset.newEncoder() + .onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT)); for (int i = 0; i < path.length(); i++) { int c = path.charAt(i); @@ -162,9 +168,8 @@ try { writer.write((char) c); writer.flush(); - } catch (IOException e) { - buf.reset(); - continue; + } catch (IOException ioe) { + throw new IllegalArgumentException(ioe); } byte[] ba = buf.toByteArray(); for (byte toEncode : ba) { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/util/XMLWriter.java tomcat10-10.1.52/java/org/apache/catalina/util/XMLWriter.java --- tomcat10-10.1.34/java/org/apache/catalina/util/XMLWriter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/util/XMLWriter.java 2026-01-23 19:33:36.000000000 +0000 @@ -133,22 +133,22 @@ * @param type Element type */ public void writeElement(String namespace, String namespaceInfo, String name, int type) { - if ((namespace != null) && (namespace.length() > 0)) { + if ((namespace != null) && (!namespace.isEmpty())) { switch (type) { case OPENING: if (lastWriteWasOpen) { buffer.append('\n'); } if (namespaceInfo != null) { - buffer.append( - "<" + namespace + ":" + name + " xmlns:" + namespace + "=\"" + namespaceInfo + "\">"); + buffer.append("<").append(namespace).append(":").append(name).append(" xmlns:") + .append(namespace).append("=\"").append(namespaceInfo).append("\">"); } else { - buffer.append("<" + namespace + ":" + name + ">"); + buffer.append("<").append(namespace).append(":").append(name).append(">"); } lastWriteWasOpen = true; break; case CLOSING: - buffer.append("\n"); + buffer.append("\n"); lastWriteWasOpen = false; break; case NO_CONTENT: @@ -157,25 +157,25 @@ buffer.append('\n'); } if (namespaceInfo != null) { - buffer.append("<" + namespace + ":" + name + " xmlns:" + namespace + "=\"" + namespaceInfo + - "\"/>\n"); + buffer.append("<").append(namespace).append(":").append(name).append(" xmlns:") + .append(namespace).append("=\"").append(namespaceInfo).append("\"/>\n"); } else { - buffer.append("<" + namespace + ":" + name + "/>\n"); + buffer.append("<").append(namespace).append(":").append(name).append("/>\n"); } lastWriteWasOpen = false; break; } - } else if ((namespaceInfo != null) && (namespaceInfo.length() > 0)) { + } else if ((namespaceInfo != null) && (!namespaceInfo.isEmpty())) { switch (type) { case OPENING: if (lastWriteWasOpen) { buffer.append('\n'); } - buffer.append("<" + name + " xmlns=\"" + namespaceInfo + "\">"); + buffer.append("<").append(name).append(" xmlns=\"").append(namespaceInfo).append("\">"); lastWriteWasOpen = true; break; case CLOSING: - buffer.append("\n"); + buffer.append("\n"); lastWriteWasOpen = false; break; case NO_CONTENT: @@ -183,7 +183,7 @@ if (lastWriteWasOpen) { buffer.append('\n'); } - buffer.append("<" + name + " xmlns=\"" + namespaceInfo + "\"/>\n"); + buffer.append("<").append(name).append(" xmlns=\"").append(namespaceInfo).append("\"/>\n"); lastWriteWasOpen = false; break; } @@ -193,11 +193,11 @@ if (lastWriteWasOpen) { buffer.append('\n'); } - buffer.append("<" + name + ">"); + buffer.append("<").append(name).append(">"); lastWriteWasOpen = true; break; case CLOSING: - buffer.append("\n"); + buffer.append("\n"); lastWriteWasOpen = false; break; case NO_CONTENT: @@ -205,7 +205,7 @@ if (lastWriteWasOpen) { buffer.append('\n'); } - buffer.append("<" + name + "/>\n"); + buffer.append("<").append(name).append("/>\n"); lastWriteWasOpen = false; break; } @@ -239,7 +239,7 @@ * @param data Data to append */ public void writeData(String data) { - buffer.append(""); + buffer.append(""); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/AbstractAccessLogValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/AbstractAccessLogValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/AbstractAccessLogValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/AbstractAccessLogValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,6 +21,7 @@ import java.io.IOException; import java.net.InetAddress; import java.text.SimpleDateFormat; +import java.time.Instant; import java.util.ArrayList; import java.util.Date; import java.util.Enumeration; @@ -56,15 +57,12 @@ /** - *

      * Abstract implementation of the Valve interface that generates a web server access log with the detailed line * contents matching a configurable pattern. The syntax of the available patterns is similar to that supported by the * Apache HTTP Server mod_log_config module. - *

      *

      * Patterns for the logged message may include constant text or any of the following replacement strings, for which the * corresponding information from the specified Response is substituted: - *

      *
        *
      • %a - Remote IP address *
      • %A - Local IP address @@ -97,7 +95,6 @@ *
      *

      * In addition, the caller can specify one of the following aliases for commonly utilized patterns: - *

      *
        *
      • common - %h %l %u %t "%r" %s %b *
      • combined - %h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" @@ -106,7 +103,6 @@ * There is also support to write information from the cookie, incoming header, the Session or something else in the * ServletRequest.
        * It is modeled after the Apache HTTP Server log configuration syntax: - *

        *
          *
        • %{xxx}i for incoming headers *
        • %{xxx}o for outgoing response headers @@ -115,6 +111,8 @@ *
        • %{xxx}s xxx is an attribute in the HttpSession *
        • %{xxx}t xxx is an enhanced SimpleDateFormat pattern (see Configuration Reference document for * details on supported time patterns) + *
        • %{xxx}L xxx is the identifier to log (see Configuration Reference document for details on supported + * identifiers) *
        • %{xxx}T xxx is the unit for the time taken to process the request (see Configuration Reference * document for details on supported units) *
        @@ -124,17 +122,9 @@ * non-null value, the logging will be skipped. If the value returned from ServletRequest.getAttribute(conditionIf) * yields the null value, the logging will be skipped. The condition attribute is synonym for * conditionUnless and is provided for backwards compatibility. - *

        *

        * For extended attributes coming from a getAttribute() call, it is you responsibility to ensure there are no newline or * control characters. - *

        - * - * @author Craig R. McClanahan - * @author Jason Brittain - * @author Remy Maucherat - * @author Takayuki Kaneko - * @author Peter Rossbach */ public abstract class AbstractAccessLogValve extends ValveBase implements AccessLog { @@ -167,7 +157,12 @@ PEER } - // ------------------------------------------------------ Constructor + private enum IdentifierType { + CONNECTION, + UNKNOWN + } + + public AbstractAccessLogValve() { super(true); } @@ -250,11 +245,11 @@ /* Helper object to be able to call SimpleDateFormat.format(). */ private final Date currentDate = new Date(); - protected final String cache[]; - private SimpleDateFormat formatter; + protected final String[] cache; + private final SimpleDateFormat formatter; private boolean isCLF = false; - private Cache parent = null; + private final Cache parent; private Cache(Cache parent) { this(null, parent); @@ -344,11 +339,7 @@ currentDate.setTime(time); previousFormat = formatter.format(currentDate); if (isCLF) { - StringBuilder current = new StringBuilder(32); - current.append('['); - current.append(previousFormat); - current.append(']'); - previousFormat = current.toString(); + previousFormat = "[" + previousFormat + "]"; } } cache[index] = previousFormat; @@ -364,10 +355,10 @@ protected final Cache cLFCache; private final Map formatCache = new HashMap<>(); - protected DateFormatCache(int size, Locale loc, DateFormatCache parent) { + protected DateFormatCache(int size, Locale loc, DateFormatCache parentFC) { cacheSize = size; cacheDefaultLocale = loc; - this.parent = parent; + parent = parentFC; Cache parentCache = null; if (parent != null) { synchronized (parent) { @@ -425,12 +416,12 @@ private static final ThreadLocal localDate = ThreadLocal.withInitial(Date::new); /** - * Are we doing conditional logging. default null. It is the value of conditionUnless property. + * Are we doing conditional logging ? default null. It is the value of conditionUnless property. */ protected String condition = null; /** - * Are we doing conditional logging. default null. It is the value of conditionIf property. + * Are we doing conditional logging ? default null. It is the value of conditionIf property. */ protected String conditionIf = null; @@ -466,7 +457,7 @@ /** * Buffer pool used for log message generation. Pool used to reduce garbage generation. */ - private SynchronizedStack charArrayWriters = new SynchronizedStack<>(); + private final SynchronizedStack charArrayWriters = new SynchronizedStack<>(); /** * Log message buffers are usually recycled and re-used. To prevent excessive memory usage, if a buffer grows beyond @@ -672,16 +663,13 @@ return; } - // Date for access log should be the beginning of the request - Date date = getDate(request.getCoyoteRequest().getStartTime()); - CharArrayWriter result = charArrayWriters.pop(); if (result == null) { result = new CharArrayWriter(128); } for (AccessLogElement logElement : logElements) { - logElement.addElement(result, date, request, response, time); + logElement.addElement(result, request, response, time); } log(result); @@ -705,7 +693,7 @@ /** * This method returns a Date object that is accurate to within one second. If a thread calls this method to get a - * Date and it's been less than 1 second since a new Date was created, this method simply gives out the same Date + * Date, and it's been less than 1 second since a new Date was created, this method simply gives out the same Date * again so that the system doesn't spend time creating Date objects unnecessarily. * * @param systime The time @@ -744,13 +732,46 @@ /** * AccessLogElement writes the partial message into the buffer. + *

        + * At least one method must be implemented else a loop will occur. + *

        + * When the deprecated method is removed in Tomcat 12, the default implementation for + * {@link #addElement(CharArrayWriter, Request, Response, long)} will also be removed. */ protected interface AccessLogElement { - void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time); + /** + * Called to create an access log entry. + * + * @param buf The buffer to which the log element should be added + * @param date The time stamp for the start of the request + * @param request The request that triggered this access log entry + * @param response The response to the request that triggered this access log entry + * @param time The time taken in nanoseconds to process the request + * + * @deprecated Unused. Will be removed in Tomcat 12. Use + * {@link #addElement(CharArrayWriter, Request, Response, long)} + */ + @Deprecated + default void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + addElement(buf, request, response, time); + } + + /** + * Called to create an access log entry. + * + * @param buf The buffer to which the log element should be added + * @param request The request that triggered this access log entry + * @param response The response to the request that triggered this access log entry + * @param time The time taken in nanoseconds to process the request + */ + default void addElement(CharArrayWriter buf, Request request, Response response, long time) { + Date date = getDate(request.getCoyoteRequest().getStartTime()); + addElement(buf, date, request, response, time); + } } /** - * Marks an AccessLogElement as needing to be have the value cached at the start of the request rather than just + * Marks an AccessLogElement as needing to have the value cached at the start of the request rather than just * recorded at the end as the source data for the element may not be available at the end of the request. This * typically occurs for remote network information, such as ports, IP addresses etc. when the connection is closed * unexpectedly. These elements take advantage of these values being cached elsewhere on first request and do not @@ -765,7 +786,7 @@ */ protected static class ThreadNameElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { RequestInfo info = request.getCoyoteRequest().getRequestProcessor(); if (info != null) { buf.append(info.getWorkerThreadName()); @@ -786,8 +807,8 @@ String init; try { init = InetAddress.getLocalHost().getHostAddress(); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); init = "127.0.0.1"; } @@ -799,7 +820,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { buf.append(localAddrValue); } } @@ -836,7 +857,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { String value = null; if (remoteAddressType == RemoteAddressType.PEER) { value = request.getPeerAddr(); @@ -876,7 +897,7 @@ */ protected class HostElement implements AccessLogElement, CachedElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { String value = null; if (requestAttributesEnabled) { Object host = request.getAttribute(REMOTE_HOST_ATTRIBUTE); @@ -910,7 +931,7 @@ */ protected static class LogicalUserNameElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { buf.append('-'); } } @@ -920,7 +941,7 @@ */ protected class ProtocolElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (requestAttributesEnabled) { Object proto = request.getAttribute(PROTOCOL_ATTRIBUTE); if (proto == null) { @@ -939,7 +960,7 @@ */ protected static class UserElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (request != null) { String value = request.getRemoteUser(); if (value != null) { @@ -1082,11 +1103,11 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - long timestamp = date.getTime(); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + Instant requestStartInstant = Instant.from(request.getCoyoteRequest().getStartInstant()); long frac; if (!usesBegin) { - timestamp += TimeUnit.NANOSECONDS.toMillis(time); + requestStartInstant = requestStartInstant.plusNanos(time); } /* * Implementation note: This is deliberately not implemented using switch. If a switch is used the compiler @@ -1096,13 +1117,13 @@ * pre-loading up to date as the name changes is error prone. */ if (type == FormatType.CLF) { - buf.append(localDateCache.get().getFormat(timestamp)); + buf.append(localDateCache.get().getFormat(requestStartInstant.toEpochMilli())); } else if (type == FormatType.SEC) { - buf.append(Long.toString(timestamp / 1000)); + buf.append(Long.toString(requestStartInstant.getEpochSecond())); } else if (type == FormatType.MSEC) { - buf.append(Long.toString(timestamp)); + buf.append(Long.toString(requestStartInstant.toEpochMilli())); } else if (type == FormatType.MSEC_FRAC) { - frac = timestamp % 1000; + frac = requestStartInstant.toEpochMilli() % 1000; if (frac < 100) { if (frac < 10) { buf.append('0'); @@ -1114,16 +1135,15 @@ buf.append(Long.toString(frac)); } else { // FormatType.SDF + long timestamp = requestStartInstant.toEpochMilli(); String temp = localDateCache.get().getFormat(format, locale, timestamp); if (usesMsecs) { frac = timestamp % 1000; StringBuilder tripleMsec = new StringBuilder(4); if (frac < 100) { + tripleMsec.append('0'); if (frac < 10) { tripleMsec.append('0'); - tripleMsec.append('0'); - } else { - tripleMsec.append('0'); } } tripleMsec.append(frac); @@ -1144,7 +1164,7 @@ */ protected static class RequestElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (request != null) { String method = request.getMethod(); if (method == null) { @@ -1172,7 +1192,7 @@ */ protected static class HttpStatusCodeElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (response != null) { // This approach is used to reduce GC from toString conversion int status = response.getStatus(); @@ -1221,7 +1241,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (requestAttributesEnabled && portType == PortType.LOCAL) { Object port = request.getAttribute(SERVER_PORT_ATTRIBUTE); if (port == null) { @@ -1260,7 +1280,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { // Don't need to flush since trigger for log message is after the // response has been committed long length = response.getBytesWritten(false); @@ -1288,7 +1308,7 @@ */ protected static class MethodElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (request != null) { buf.append(request.getMethod()); } @@ -1299,7 +1319,7 @@ * write time taken to process the request - %D, %T */ protected static class ElapsedTimeElement implements AccessLogElement { - enum Style { + public enum Style { SECONDS { @Override public void append(CharArrayWriter buf, long time) { @@ -1368,7 +1388,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { style.append(buf, time); } } @@ -1378,7 +1398,7 @@ */ protected static class FirstByteTimeElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { long commitTime = response.getCoyoteResponse().getCommitTimeNanos(); if (commitTime == -1) { buf.append('-'); @@ -1394,7 +1414,7 @@ */ protected static class QueryElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { String query = null; if (request != null) { query = request.getQueryString(); @@ -1411,7 +1431,7 @@ */ protected static class SessionIdElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (request == null) { buf.append('-'); } else { @@ -1430,7 +1450,7 @@ */ protected static class RequestURIElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (request != null) { buf.append(request.getRequestURI()); } else { @@ -1444,7 +1464,7 @@ */ protected class LocalServerNameElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { String value = null; if (requestAttributesEnabled) { Object serverName = request.getAttribute(SERVER_NAME_ATTRIBUTE); @@ -1477,7 +1497,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { buf.append(str); } } @@ -1493,7 +1513,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { Enumeration iter = request.getHeaders(header); if (iter.hasMoreElements()) { escapeAndAppend(iter.nextElement(), buf); @@ -1518,7 +1538,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { StringBuilder value = null; boolean first = true; Cookie[] cookies = request.getCookies(); @@ -1556,7 +1576,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (null != response) { Iterator iter = response.getHeaders(header).iterator(); if (iter.hasNext()) { @@ -1583,7 +1603,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { Object value = null; if (request != null) { value = request.getAttribute(attribute); @@ -1613,7 +1633,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { Object value = null; if (null != request) { HttpSession sess = request.getSession(false); @@ -1640,7 +1660,7 @@ */ protected static class ConnectionStatusElement implements AccessLogElement { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (response != null && request != null) { boolean statusFound = false; @@ -1677,6 +1697,47 @@ } } + + /** + * Write identifier element %{xxx}L + */ + protected static class IdentifierElement implements AccessLogElement { + + /** + * Type of identifier to log + */ + private final IdentifierType identifierType; + + public IdentifierElement() { + this(""); + } + + + public IdentifierElement(String type) { + switch (type) { + case "c": + identifierType = IdentifierType.CONNECTION; + break; + default: + log.error(sm.getString("accessLogValve.invalidIdentifierType", type)); + identifierType = IdentifierType.UNKNOWN; + break; + } + } + + @Override + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + switch (identifierType) { + case CONNECTION: + buf.append(request.getServletConnection().getConnectionId()); + break; + case UNKNOWN: + buf.append("???"); + } + } + } + + /** * Parse pattern string and create the array of AccessLogElement. * @@ -1749,14 +1810,16 @@ */ protected AccessLogElement createAccessLogElement(String name, char pattern) { switch (pattern) { - case 'i': - return new HeaderElement(name); + case 'a': + return new RemoteAddrElement(name); case 'c': return new CookieElement(name); + case 'i': + return new HeaderElement(name); + case 'L': + return new IdentifierElement(name); case 'o': return new ResponseHeaderElement(name); - case 'a': - return new RemoteAddrElement(name); case 'p': return new PortElement(name); case 'r': @@ -1875,6 +1938,10 @@ * encoding which may not be true for Tomcat so Tomcat uses the Java \\uXXXX encoding. */ protected static void escapeAndAppend(String input, CharArrayWriter dest) { + escapeAndAppend(input, dest, false); + } + + protected static void escapeAndAppend(String input, CharArrayWriter dest, boolean escapeQuoteAsDouble) { if (input == null || input.isEmpty()) { dest.append('-'); return; @@ -1910,7 +1977,11 @@ dest.write(input, next, current - next); } next = current + 1; - dest.append("\\\""); + if (escapeQuoteAsDouble) { + dest.append("\"\""); + } else { + dest.append("\\\""); + } break; // Don't output individual unchanged chars, // write the sub string only when the first char to encode diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/AccessLogValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/AccessLogValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/AccessLogValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/AccessLogValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,6 +30,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; +import java.util.Objects; import java.util.TimeZone; import org.apache.catalina.LifecycleException; @@ -317,11 +318,7 @@ */ public void setFileDateFormat(String fileDateFormat) { String newFormat; - if (fileDateFormat == null) { - newFormat = ""; - } else { - newFormat = fileDateFormat; - } + newFormat = Objects.requireNonNullElse(fileDateFormat, ""); this.fileDateFormat = newFormat; synchronized (this) { @@ -345,7 +342,7 @@ * @param encoding The name of the character set. */ public void setEncoding(String encoding) { - if (encoding != null && encoding.length() > 0) { + if (encoding != null && !encoding.isEmpty()) { this.encoding = encoding; } else { this.encoding = null; @@ -377,14 +374,14 @@ for (String oldAccessLog : oldAccessLogs) { boolean match = false; - if (prefix != null && prefix.length() > 0) { + if (prefix != null && !prefix.isEmpty()) { if (!oldAccessLog.startsWith(prefix)) { continue; } match = true; } - if (suffix != null && suffix.length() > 0) { + if (suffix != null && !suffix.isEmpty()) { if (!oldAccessLog.endsWith(suffix)) { continue; } @@ -449,9 +446,9 @@ close(false); try { holder.renameTo(new File(newFileName)); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - log.error(sm.getString("accessLogValve.rotateFail"), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("accessLogValve.rotateFail"), t); } /* Make sure date is correct */ @@ -518,9 +515,9 @@ if (!rotatedLogFile.renameTo(newLogFile)) { log.error(sm.getString("accessLogValve.renameFail", rotatedLogFile, newLogFile)); } - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - log.error(sm.getString("accessLogValve.renameFail", rotatedLogFile, newLogFile), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("accessLogValve.renameFail", rotatedLogFile, newLogFile), t); } } } @@ -544,9 +541,9 @@ if (!currentLogFile.renameTo(newLogFile)) { log.error(sm.getString("accessLogValve.renameFail", currentLogFile, newLogFile)); } - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - log.error(sm.getString("accessLogValve.renameFail", currentLogFile, newLogFile), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("accessLogValve.renameFail", currentLogFile, newLogFile), t); } } else { log.error(sm.getString("accessLogValve.alreadyExists", currentLogFile, newLogFile)); @@ -569,9 +566,9 @@ if (currentLogFile != null && !currentLogFile.exists()) { try { close(false); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); - log.info(sm.getString("accessLogValve.closeFail"), e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.info(sm.getString("accessLogValve.closeFail"), t); } /* Make sure date is correct */ @@ -625,10 +622,10 @@ false); currentLogFile = pathname; - } catch (IOException e) { + } catch (IOException ioe) { writer = null; currentLogFile = null; - log.error(sm.getString("accessLogValve.openFail", pathname, System.getProperty("user.name")), e); + log.error(sm.getString("accessLogValve.openFail", pathname, System.getProperty("user.name")), ioe); } // Rotating a log file will always trigger a new file to be opened so // when a new file is opened, check to see if any old files need to be diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/Constants.java tomcat10-10.1.52/java/org/apache/catalina/valves/Constants.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,8 +19,6 @@ /** * Manifest constants for the org.apache.catalina.valves package. - * - * @author Craig R. McClanahan */ public final class Constants { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/CrawlerSessionManagerValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/CrawlerSessionManagerValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/CrawlerSessionManagerValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/CrawlerSessionManagerValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,6 +21,7 @@ import java.util.Enumeration; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.regex.Pattern; import jakarta.servlet.ServletException; @@ -46,7 +47,6 @@ private static final Log log = LogFactory.getLog(CrawlerSessionManagerValve.class); private final Map clientIdSessionId = new ConcurrentHashMap<>(); - private final Map sessionIdClientId = new ConcurrentHashMap<>(); private String crawlerUserAgents = ".*[bB]ot.*|.*Yahoo! Slurp.*|.*Feedfetcher-Google.*"; private Pattern uaPattern = null; @@ -60,6 +60,8 @@ private boolean isContextAware = true; + private Function clientIdentifierFunction = this::getClientIdentifier; + /** * Specifies a default constructor so async support can be configured. @@ -77,7 +79,7 @@ */ public void setCrawlerUserAgents(String crawlerUserAgents) { this.crawlerUserAgents = crawlerUserAgents; - if (crawlerUserAgents == null || crawlerUserAgents.length() == 0) { + if (crawlerUserAgents == null || crawlerUserAgents.isEmpty()) { uaPattern = null; } else { uaPattern = Pattern.compile(crawlerUserAgents); @@ -102,7 +104,7 @@ */ public void setCrawlerIps(String crawlerIps) { this.crawlerIps = crawlerIps; - if (crawlerIps == null || crawlerIps.length() == 0) { + if (crawlerIps == null || crawlerIps.isEmpty()) { ipPattern = null; } else { ipPattern = Pattern.compile(crawlerIps); @@ -163,6 +165,15 @@ this.isContextAware = isContextAware; } + /** + * Specify the clientIdentifier function that will be used to identify unique clients. The default is to use the + * client IP address, optionally combined with the host name and context name. + * + * @param clientIdentifierFunction The new function used to build identifiers for clients. + */ + public void setClientIdentifierFunction(Function clientIdentifierFunction) { + this.clientIdentifierFunction = clientIdentifierFunction; + } @Override protected void initInternal() throws LifecycleException { @@ -185,7 +196,7 @@ boolean isBot = false; String sessionId = null; String clientIp = request.getRemoteAddr(); - String clientIdentifier = getClientIdentifier(host, request.getContext(), clientIp); + String clientIdentifier = clientIdentifierFunction.apply(request); if (log.isTraceEnabled()) { log.trace(request.hashCode() + ": ClientIdentifier=" + clientIdentifier + ", RequestedSessionId=" + @@ -246,7 +257,6 @@ HttpSession s = request.getSession(false); if (s != null) { clientIdSessionId.put(clientIdentifier, s.getId()); - sessionIdClientId.put(s.getId(), clientIdentifier); // #valueUnbound() will be called on session expiration s.setAttribute(this.getClass().getName(), new CrawlerHttpSessionBindingListener(clientIdSessionId, clientIdentifier)); @@ -264,12 +274,12 @@ } } - - private String getClientIdentifier(Host host, Context context, String clientIp) { - StringBuilder result = new StringBuilder(clientIp); + private String getClientIdentifier(Request request) { + StringBuilder result = new StringBuilder(request.getRemoteAddr()); if (isHostAware) { - result.append('-').append(host.getName()); + result.append('-').append(request.getHost().getName()); } + Context context = request.getContext(); if (isContextAware && context != null) { result.append(context.getName()); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/ErrorReportValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/ErrorReportValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/ErrorReportValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/ErrorReportValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,21 +42,11 @@ import org.apache.tomcat.util.security.Escape; /** - *

        * Implementation of a Valve that outputs HTML error pages. - *

        *

        * This Valve should be attached at the Host level, although it will work if attached to a Context. - *

        *

        * HTML code from the Cocoon 2 project. - *

        - * - * @author Remy Maucherat - * @author Craig R. McClanahan - * @author Nicola Ken Barozzi Aisa - * @author Stefano Mazzocchi - * @author Yoav Shapira */ public class ErrorReportValve extends ValveBase { @@ -94,7 +84,7 @@ if (response.isCommitted()) { if (response.setErrorReported()) { - // Error wasn't previously reported but we can't write an error + // Error wasn't previously reported, but we can't write an error // page because the response has already been committed. // See if IO is allowed @@ -217,7 +207,7 @@ if (message == null) { if (throwable != null) { String exceptionMessage = throwable.getMessage(); - if (exceptionMessage != null && exceptionMessage.length() > 0) { + if (exceptionMessage != null && !exceptionMessage.isEmpty()) { try (Scanner scanner = new Scanner(exceptionMessage)) { message = Escape.htmlElementContent(scanner.nextLine()); } @@ -389,10 +379,10 @@ response.setContentType("text/html"); response.setCharacterEncoding("UTF-8"); - try (OutputStream os = response.getOutputStream(); InputStream is = new FileInputStream(file);) { + try (OutputStream os = response.getOutputStream(); InputStream is = new FileInputStream(file)) { IOTools.flow(is, os); - } catch (IOException e) { - getContainer().getLogger().warn(sm.getString("errorReportValve.errorPageIOException", location), e); + } catch (IOException ioe) { + getContainer().getLogger().warn(sm.getString("errorReportValve.errorPageIOException", location), ioe); return false; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/ExtendedAccessLogValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/ExtendedAccessLogValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/ExtendedAccessLogValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/ExtendedAccessLogValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,8 +41,9 @@ import org.apache.tomcat.util.ExceptionUtils; /** - * An implementation of the W3c Extended Log File Format. See http://www.w3.org/TR/WD-logfile.html for more information - * about the format. The following fields are supported: + * An implementation of the W3c Extended Log File Format. See + * WD-logfile-960323 for more information about the format. The + * following fields are supported: *
          *
        • c-dns: Client hostname (or ip address if enableLookups for the connector is false)
        • *
        • c-ip: Client ip address
        • @@ -70,6 +71,7 @@ *
        • For any of the x-H(...) the following method will be called from the HttpServletRequest object
        • *
        • x-H(authType): getAuthType
        • *
        • x-H(characterEncoding): getCharacterEncoding
        • + *
        • x-H(connectionId): getConnectionId
        • *
        • x-H(contentLength): getContentLength
        • *
        • x-H(locale): getLocale
        • *
        • x-H(protocol): getProtocol
        • @@ -80,28 +82,6 @@ *
        • x-H(scheme): getScheme
        • *
        • x-H(secure): isSecure
        • *
        - *

        - * Log rotation can be on or off. This is dictated by the rotatable property. - *

        - *

        - * For UNIX users, another field called checkExists is also available. If set to true, the log file's - * existence will be checked before each logging. This way an external log rotator can move the file somewhere and - * Tomcat will start with a new file. - *

        - *

        - * For JMX junkies, a public method called rotate has been made available to allow you to tell this - * instance to move the existing log file to somewhere else and start writing a new log file. - *

        - *

        - * Conditional logging is also supported. This can be done with the condition property. If the value - * returned from ServletRequest.getAttribute(condition) yields a non-null value, the logging will be skipped. - *

        - *

        - * For extended attributes coming from a getAttribute() call, it is you responsibility to ensure there are no newline or - * control characters. - *

        - * - * @author Peter Rossbach */ public class ExtendedAccessLogValve extends AccessLogValve { @@ -110,47 +90,35 @@ // -------------------------------------------------------- Private Methods /** - * Wrap the incoming value with double quotes (") and escape any double quotes appearing in the value using two - * double quotes (""). + * Calls toString() on the object, wraps the result with double quotes (") and writes the result to the buffer. Any + * double quotes appearing in the value are escaped using two double quotes (""). If the value is null or if + * toString() fails, '-' will be written to the buffer. * * @param value - The value to wrap - * - * @return '-' if null. Otherwise, toString() will be called on the object and the value will be wrapped in quotes - * and any quotes will be escaped with 2 sets of quotes. + * @param buf the buffer to write to */ - static String wrap(Object value) { + static void wrap(Object value, CharArrayWriter buf) { String svalue; - // Does the value contain a " ? If so must encode it if (value == null || "-".equals(value)) { - return "-"; + buf.append('-'); + return; } try { svalue = value.toString(); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); /* Log error */ - return "-"; + buf.append('-'); + return; } - /* Wrap all values in double quotes. */ - StringBuilder buffer = new StringBuilder(svalue.length() + 2); - buffer.append('\"'); - int i = 0; - while (i < svalue.length()) { - int j = svalue.indexOf('\"', i); - if (j == -1) { - buffer.append(svalue.substring(i)); - i = svalue.length(); - } else { - buffer.append(svalue.substring(i, j + 1)); - buffer.append('"'); - i = j + 1; - } + buf.append('\"'); + if (!svalue.isEmpty()) { + // Does the value contain a " ? If so must encode it + escapeAndAppend(svalue, buf, true); } - - buffer.append('\"'); - return buffer.toString(); + buf.append('\"'); } @Override @@ -175,11 +143,12 @@ ThreadLocal.withInitial(() -> new ElementTimestampStruct("yyyy-MM-dd")); @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { ElementTimestampStruct eds = currentDate.get(); long millis = eds.currentTimestamp.getTime(); - if (date.getTime() > (millis + INTERVAL - 1) || date.getTime() < millis) { - eds.currentTimestamp.setTime(date.getTime() - (date.getTime() % INTERVAL)); + long epochMilli = request.getCoyoteRequest().getStartInstant().toEpochMilli(); + if (epochMilli > (millis + INTERVAL - 1) || epochMilli < millis) { + eds.currentTimestamp.setTime(epochMilli - (epochMilli % INTERVAL)); eds.currentTimestampString = eds.currentTimestampFormat.format(eds.currentTimestamp); } buf.append(eds.currentTimestampString); @@ -194,11 +163,12 @@ ThreadLocal.withInitial(() -> new ElementTimestampStruct("HH:mm:ss")); @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { ElementTimestampStruct eds = currentTime.get(); long millis = eds.currentTimestamp.getTime(); - if (date.getTime() > (millis + INTERVAL - 1) || date.getTime() < millis) { - eds.currentTimestamp.setTime(date.getTime() - (date.getTime() % INTERVAL)); + long epochMilli = request.getCoyoteRequest().getStartInstant().toEpochMilli(); + if (epochMilli > (millis + INTERVAL - 1) || epochMilli < millis) { + eds.currentTimestamp.setTime(epochMilli - (epochMilli % INTERVAL)); eds.currentTimestampString = eds.currentTimestampFormat.format(eds.currentTimestamp); } buf.append(eds.currentTimestampString); @@ -213,8 +183,8 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(request.getHeader(header))); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getHeader(header), buf); } } @@ -226,8 +196,8 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(response.getHeader(header))); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(response.getHeader(header), buf); } } @@ -239,8 +209,8 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(request.getContext().getServletContext().getAttribute(attribute))); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getContext().getServletContext().getAttribute(attribute), buf); } } @@ -252,7 +222,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { StringBuilder value = new StringBuilder(); boolean first = true; Cookie[] c = request.getCookies(); @@ -269,7 +239,7 @@ if (value.length() == 0) { buf.append('-'); } else { - buf.append(wrap(value.toString())); + wrap(value, buf); } } } @@ -285,7 +255,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { if (null != response) { Iterator iter = response.getHeaders(header).iterator(); if (iter.hasNext()) { @@ -299,7 +269,9 @@ } buffer.append(iter.next()); } - buf.append(wrap(buffer.toString())); + wrap(buffer, buf); + } else { + buf.append('-'); } return; } @@ -315,8 +287,8 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(request.getAttribute(attribute))); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getAttribute(attribute), buf); } } @@ -328,12 +300,12 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { HttpSession session = null; if (request != null) { session = request.getSession(false); if (session != null) { - buf.append(wrap(session.getAttribute(attribute))); + wrap(session.getAttribute(attribute), buf); } } } @@ -357,8 +329,14 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(urlEncode(request.getParameter(parameter)))); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + String parameterValue; + try { + parameterValue = request.getParameter(parameter); + } catch (IllegalStateException ise) { + parameterValue = null; + } + wrap(urlEncode(parameterValue), buf); } } @@ -519,8 +497,8 @@ log.trace("finished decoding with element size of: " + list.size()); } return list.toArray(new AccessLogElement[0]); - } catch (IOException e) { - log.error(sm.getString("extendedAccessLogValve.patternParseError", pattern), e); + } catch (IOException ioe) { + log.error(sm.getString("extendedAccessLogValve.patternParseError", pattern), ioe); return null; } } @@ -532,16 +510,19 @@ if (tokenizer.hasSubToken()) { String nextToken = tokenizer.getToken(); if ("taken".equals(nextToken)) { - nextToken = tokenizer.getToken(); - - if ("ns".equals(nextToken)) { - return new ElapsedTimeElement(ElapsedTimeElement.Style.NANOSECONDS); - } else if ("us".equals(nextToken)) { - return new ElapsedTimeElement(ElapsedTimeElement.Style.MICROSECONDS); - } else if ("ms".equals(nextToken)) { - return new ElapsedTimeElement(ElapsedTimeElement.Style.MILLISECONDS); - } else if ("fracsec".equals(nextToken)) { - return new ElapsedTimeElement(ElapsedTimeElement.Style.SECONDS_FRACTIONAL); + if (tokenizer.hasSubToken()) { + nextToken = tokenizer.getToken(); + if ("ns".equals(nextToken)) { + return new ElapsedTimeElement(ElapsedTimeElement.Style.NANOSECONDS); + } else if ("us".equals(nextToken)) { + return new ElapsedTimeElement(ElapsedTimeElement.Style.MICROSECONDS); + } else if ("ms".equals(nextToken)) { + return new ElapsedTimeElement(ElapsedTimeElement.Style.MILLISECONDS); + } else if ("fracsec".equals(nextToken)) { + return new ElapsedTimeElement(ElapsedTimeElement.Style.SECONDS_FRACTIONAL); + } else { + return new ElapsedTimeElement(ElapsedTimeElement.Style.SECONDS); + } } else { return new ElapsedTimeElement(ElapsedTimeElement.Style.SECONDS); } @@ -568,13 +549,13 @@ } else if ("dns".equals(nextToken)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { String value; try { value = InetAddress.getLocalHost().getHostName(); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); value = "localhost"; } buf.append(value); @@ -607,7 +588,7 @@ } else if ("query".equals(token)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { String query = request.getQueryString(); if (query != null) { @@ -621,13 +602,11 @@ } else { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { String query = request.getQueryString(); - if (query == null) { - buf.append(request.getRequestURI()); - } else { - buf.append(request.getRequestURI()); + buf.append(request.getRequestURI()); + if (query != null) { buf.append('?'); buf.append(request.getQueryString()); } @@ -668,7 +647,6 @@ } protected AccessLogElement getProxyElement(PatternTokenizer tokenizer) throws IOException { - String token = null; if (tokenizer.hasSubToken()) { tokenizer.getToken(); return new StringElement("-"); @@ -676,7 +654,7 @@ tokenizer.getParameter(); return new StringElement("-"); } - log.error(sm.getString("extendedAccessLogValve.decodeError", token)); + log.error(sm.getString("extendedAccessLogValve.decodeError", tokenizer.getRemains())); return null; } @@ -722,78 +700,85 @@ if ("authType".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(request.getAuthType())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getAuthType(), buf); } }; } else if ("remoteUser".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(request.getRemoteUser())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getRemoteUser(), buf); } }; } else if ("requestedSessionId".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(request.getRequestedSessionId())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getRequestedSessionId(), buf); } }; } else if ("requestedSessionIdFromCookie".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap("" + request.isRequestedSessionIdFromCookie())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(String.valueOf(request.isRequestedSessionIdFromCookie()), buf); } }; } else if ("requestedSessionIdValid".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap("" + request.isRequestedSessionIdValid())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(String.valueOf(request.isRequestedSessionIdValid()), buf); } }; } else if ("contentLength".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap("" + request.getContentLengthLong())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(String.valueOf(request.getContentLengthLong()), buf); + } + }; + } else if ("connectionId".equals(parameter)) { + return new AccessLogElement() { + @Override + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getServletConnection().getConnectionId(), buf); } }; } else if ("characterEncoding".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(request.getCharacterEncoding())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getCharacterEncoding(), buf); } }; } else if ("locale".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(request.getLocale())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getLocale(), buf); } }; } else if ("protocol".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap(request.getProtocol())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(request.getProtocol(), buf); } }; } else if ("scheme".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { buf.append(request.getScheme()); } }; } else if ("secure".equals(parameter)) { return new AccessLogElement() { @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { - buf.append(wrap("" + request.isSecure())); + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { + wrap(Boolean.valueOf(request.isSecure()), buf); } }; } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/HealthCheckValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/HealthCheckValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/HealthCheckValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/HealthCheckValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,7 +26,6 @@ import org.apache.catalina.LifecycleException; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; -import org.apache.catalina.util.LifecycleBase; import org.apache.tomcat.util.buf.MessageBytes; @@ -99,11 +98,7 @@ return false; } } - if (container instanceof LifecycleBase) { - return ((LifecycleBase) container).getState().isAvailable(); - } else { - return true; - } + return container.getState().isAvailable(); } } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/JDBCAccessLogValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/JDBCAccessLogValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/JDBCAccessLogValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/JDBCAccessLogValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -96,8 +96,6 @@ * remoteHost, user, timeStamp, query, status, bytes *

        * - * @author Andre de Jesus - * @author Peter Rossbach * @deprecated Non scalable design, and not documented. Will be removed in Tomcat 12. */ @Deprecated @@ -577,6 +575,7 @@ /** * Prepare tables for processing. Used by subclasses for testing. + * * @throws SQLException if an exception occurs */ protected void prepare() throws SQLException { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/JsonAccessLogValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/JsonAccessLogValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/JsonAccessLogValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/JsonAccessLogValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,7 +20,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.ListIterator; @@ -67,8 +66,10 @@ *
      • %{xxx}o: responseHeaders
      • *
      • %{xxx}r: requestAttributes
      • *
      • %{xxx}s: sessionAttributes
      • + *
      • %{xxx}L: identifier
      • *
      - * The attribute list is based on https://github.com/fluent/fluentd/blob/master/lib/fluent/plugin/parser_apache2.rb#L72 + * The attribute list is based on + * parser_apache2.rb */ public class JsonAccessLogValve extends AccessLogValve { @@ -108,6 +109,7 @@ pattern2AttributeName.put(Character.valueOf('o'), "responseHeaders"); pattern2AttributeName.put(Character.valueOf('r'), "requestAttributes"); pattern2AttributeName.put(Character.valueOf('s'), "sessionAttributes"); + pattern2AttributeName.put(Character.valueOf('L'), "identifier"); SUB_OBJECT_PATTERNS = Collections.unmodifiableMap(pattern2AttributeName); } @@ -122,7 +124,7 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { buf.write(ch); } } @@ -179,6 +181,9 @@ } else if (ale instanceof CookieElement) { subTypeLists.get(Character.valueOf('c')).add(wrappedLogElement); lit.remove(); + } else if (ale instanceof IdentifierElement) { + subTypeLists.get(Character.valueOf('L')).add(wrappedLogElement); + lit.remove(); } else { // Keep the simple items and add separator lit.add(new CharElement(',')); @@ -216,9 +221,9 @@ private static class JsonWrappedElement implements AccessLogElement, CachedElement { - private CharSequence attributeName; - private boolean quoteValue; - private AccessLogElement delegate; + private final CharSequence attributeName; + private final boolean quoteValue; + private final AccessLogElement delegate; private CharSequence escapeJsonString(CharSequence nonEscaped) { return JSONFilter.escape(nonEscaped); @@ -231,7 +236,7 @@ if (patternAttribute == null) { patternAttribute = "other-" + Character.toString(pattern); } - if (key != null && !"".equals(key)) { + if (key != null && !key.isEmpty()) { if (SUB_OBJECT_PATTERNS.containsKey(Character.valueOf(pattern))) { this.attributeName = escapeJsonString(key); } else { @@ -251,12 +256,12 @@ } @Override - public void addElement(CharArrayWriter buf, Date date, Request request, Response response, long time) { + public void addElement(CharArrayWriter buf, Request request, Response response, long time) { buf.append('"').append(attributeName).append('"').append(':'); if (quoteValue) { buf.append('"'); } - delegate.addElement(buf, date, request, response, time); + delegate.addElement(buf, request, response, time); if (quoteValue) { buf.append('"'); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/JsonErrorReportValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/JsonErrorReportValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/JsonErrorReportValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/JsonErrorReportValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,12 +28,8 @@ import org.apache.tomcat.util.res.StringManager; /** - *

      - * Implementation of a Valve that outputs error jsons. - *

      - *

      - * This Valve should be attached at the Host level, although it will work if attached to a Context. - *

      + * Implementation of a Valve that outputs error JSON. This Valve should be attached at the Host level, although it will + * work if attached to a Context. */ public class JsonErrorReportValve extends ErrorReportValve { @@ -64,7 +60,7 @@ StringManager smClient = StringManager.getManager(Constants.Package, request.getLocales()); response.setLocale(smClient.getLocale()); - String type = null; + String type; if (throwable != null) { type = smClient.getString("errorReportValve.exceptionReport"); } else { @@ -74,18 +70,61 @@ if (message == null && throwable != null) { message = throwable.getMessage(); } - String description = null; - description = smClient.getString("http." + statusCode + ".desc"); + if (message == null) { + message = ""; + } + String description = smClient.getString("http." + statusCode + ".desc"); if (description == null) { - if (message == null || message.isEmpty()) { + if (message.isEmpty()) { return; } else { description = smClient.getString("errorReportValve.noDescription"); } } - String jsonReport = "{\n" + " \"type\": \"" + JSONFilter.escape(type) + "\",\n" + " \"message\": \"" + - JSONFilter.escape(message) + "\",\n" + " \"description\": \"" + JSONFilter.escape(description) + - "\"\n" + "}"; + StringBuilder sb = new StringBuilder(); + sb.append("{\n \"type\": \"").append(JSONFilter.escape(type)).append("\",\n"); + sb.append(" \"status\": ").append(statusCode).append(",\n"); + sb.append(" \"message\": \"").append(JSONFilter.escape(message)).append("\",\n"); + sb.append(" \"description\": \"").append(JSONFilter.escape(description)); + + if (throwable != null) { + sb.append("\",\n"); + + // Stack trace + sb.append(" \"throwable\": ["); + boolean first = true; + do { + if (!first) { + sb.append(','); + } else { + first = false; + } + sb.append('\"').append(JSONFilter.escape(throwable.toString())).append('\"'); + + StackTraceElement[] elements = throwable.getStackTrace(); + int pos = elements.length; + for (int i = elements.length - 1; i >= 0; i--) { + if ((elements[i].getClassName().startsWith("org.apache.catalina.core.ApplicationFilterChain")) && + (elements[i].getMethodName().equals("internalDoFilter"))) { + pos = i; + break; + } + } + for (int i = 0; i < pos; i++) { + if (!(elements[i].getClassName().startsWith("org.apache.catalina.core."))) { + sb.append(',').append('\"').append(' ').append(JSONFilter.escape(elements[i].toString())) + .append('\"'); + } + } + + throwable = throwable.getCause(); + } while (throwable != null); + sb.append("]\n}"); + + } else { + sb.append("\"\n}"); + } + try { try { response.setContentType("application/json"); @@ -98,12 +137,12 @@ } Writer writer = response.getReporter(); if (writer != null) { - writer.write(jsonReport); + writer.write(sb.toString()); response.finishResponse(); - return; } } catch (IOException | IllegalStateException e) { // Ignore } } + } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings.properties tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -135,6 +135,13 @@ loadBalancerDrainingValve.draining=Load-balancer is in DISABLED state, draining this node loadBalancerDrainingValve.skip=Client is presenting a valid [{0}] cookie, re-balancing is being skipped +parameterLimitValve.closeError=Error closing configuration +parameterLimitValve.invalidLimits=Each limit configuration must contain either a single integer or three, comma-separated integers. Invalid limit string [{0}] +parameterLimitValve.invalidLine=Each line must contain at least one '=' character. Invalid line [{0}] +parameterLimitValve.noConfiguration=No configuration resource found [{0}] +parameterLimitValve.readConfiguration=Read configuration from [/WEB-INF/{0}] +parameterLimitValve.readError=Error reading configurationpatternTokenizer.unexpectedParenthesis=Unexpected ')' in pattern + patternTokenizer.unexpectedParenthesis=Unexpected ')' in pattern persistentValve.acquireFailed=The request for [{0}] did not obtain the per session Semaphore as no permit was available diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -135,6 +135,13 @@ loadBalancerDrainingValve.draining=Le balanceur de charge est dans l'étât DISABLED, le changement de répartition de la charge est sauté loadBalancerDrainingValve.skip=Le client a envoyé un cookie [{0}] valide, le le changement de répartition de la charge est sauté +parameterLimitValve.closeError=Erreur lors de la fermeture de la configuration +parameterLimitValve.invalidLimits=Chaque configuration d''une limite doit contenir soit un seul nombre entier, soit trois, séparés par des virgules. Limite invalide [{0}] +parameterLimitValve.invalidLine=Chaque ligne doit contenir au moins un caractère ''='', la ligne invalide est [{0}] +parameterLimitValve.noConfiguration=La ressource de configuration [{0}] n''a pas été trouvée +parameterLimitValve.readConfiguration=Lecture de la configuration à partir de [/WEB-INF/{0}] +parameterLimitValve.readError=Erreur lors de la lecture de la configuration + patternTokenizer.unexpectedParenthesis=')' inattendu dans le modèle persistentValve.acquireFailed=La requête pour [{0}] n''a pas obtenu le sémaphore associé à la session car aucun permis n''était disponible @@ -163,6 +170,6 @@ sslValve.invalidProvider=Le fournisseur SSL spécifié pour le connecteur associé avec cette requête de [{0}] est invalide, le certificat n''a pas pu être traité stuckThreadDetectionValve.interrupted=Le fil d'exécution a été interrompu après la fin de la requête, cela sera ignoré -stuckThreadDetectionValve.notifyStuckThreadCompleted=Le Thread [{0}] (id=[{3}]) qui a été préalablement rapporté comme étant bloqué s''est terminé, il a été actif pendant approximativement [{1}] millisecondes, il y a [{2}] thread(s) au total qui sont surveillés par cette valve et qui pourraient être bloqués +stuckThreadDetectionValve.notifyStuckThreadCompleted=Le Thread [{0}] (id=[{3}]) qui a été préalablement rapporté comme étant bloqué s''est terminé, il a été actif pendant approximativement [{1}] millisecondes. {2,choice,0#|0< Il y a [{2}] thread(s) au total qui sont surveillés par cette valve et qui pourraient être bloqués.} stuckThreadDetectionValve.notifyStuckThreadDetected=Le Thread [{0}] (id=[{6}]) a été actif depuis [{1}] millisecondes (depuis [{2}]) pour traiter la même requête pour [{4}] et pourrait être bloqué (le seuil configurable est de [{5}] secondes pour cette StuckThreadDetectionValve), il y a [{3}] thread(s) au total qui sont surveillés par cette valve et qui pourraient être bloqués stuckThreadDetectionValve.notifyStuckThreadInterrupted=Le Thread [{0}] (id=[{5}]) a été interrompu car il a été actif depuis [{1}] millisecondes (depuis [{2}]) pour traiter la même requête pour [{3}] et était probablement bloqué (le seuil configurable est de [{4}] secondes pour cette StuckThreadDetectionValve) diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,7 @@ accessLogValve.invalidPortType=不正なポート種類 [{0}] の代わりにサーバーのローカルポートを使用します。 accessLogValve.invalidRemoteAddressType=リモート (非ピア) アドレスを使用している無効なリモートアドレスタイプ [{0}] accessLogValve.openDirFail=アクセスログのディレクトリ[{0}]の作成に失敗しました -accessLogValve.openFail=アクセスログファイル [{0}] を開けません。 +accessLogValve.openFail=アクセスログファイル [{0}] を開けません。注: ユーザー [{1}] として実行しています accessLogValve.renameFail=[{0}]から[{1}]へのアクセスログの名前の変更に失敗しました。 accessLogValve.rotateFail=アクセスログのローテーションに失敗しました accessLogValve.unsupportedEncoding=文字エンコーディングに [{0}] を指定できません。システムの既定値を使用します。 @@ -135,6 +135,13 @@ loadBalancerDrainingValve.draining=ロードバランサは DISABLED 状態にあり、このノードを使用していません loadBalancerDrainingValve.skip=クライアントは有効な [{0}] Cookie を提示しているため、リバランスはスキップされています +parameterLimitValve.closeError=設定をクローズする際のエラー +parameterLimitValve.invalidLimits=各制限構成には、1 つの整数かコンマで区切られた 3 つの整数が含まれている必要があります。無効な制限文字列 [{0}] +parameterLimitValve.invalidLine=各行には少なくとも 1 つの ''='' が含まれている必要があります。無効な行 [{0}] +parameterLimitValve.noConfiguration=構成リソースが見つかりません [{0}] +parameterLimitValve.readConfiguration=[/WEB-INF/{0}] から設定を読み取ります +parameterLimitValve.readError=設定の読み込みエラー + patternTokenizer.unexpectedParenthesis=パターンに予期しない ')' があります persistentValve.acquireFailed=リクエスト [{0}] は、利用可能な許可がなかったため、セッションごとのセマフォを取得できませんでした diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings_ru.properties tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_ru.properties --- tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings_ru.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_ru.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,11 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +accessLogValve.invalidLocale=Невозможно установить локаль [{0}] + +errorReportValve.description=Описание +errorReportValve.rootCauseInLogs=Полная трассировка стека первопричины ошибки доступна в логах сервера. + http.401.reason=Не авторизовано http.402.reason=Требуется оплата http.403.desc=Сервер получил запрос, но отказался его авторизовать. diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/catalina/valves/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -149,6 +149,6 @@ sslValve.certError=无法处理证书字符串[{0}]以创建java.security.cert.X509Certificate对象 sslValve.invalidProvider=与此[{0}]请求关联的连接器上指定的SSL提供程序无效。 无法处理证书数据。 -stuckThreadDetectionValve.notifyStuckThreadCompleted=线程[{0}](id=[{3}])之前报告为卡住,但是已经完成。它活跃了大概[{1}]毫秒。{2,选择,0#|0< 仍有[{2}]个被Valve监控的线程可能卡住} +stuckThreadDetectionValve.notifyStuckThreadCompleted=线程[{0}](id=[{3}])之前报告为卡住,但是已经完成。它活跃了大概[{1}]毫秒。{2,choice,0#|0< 仍有[{2}]个被Valve监控的线程可能卡住} stuckThreadDetectionValve.notifyStuckThreadDetected=线程[{0}](id=[{6}])已处于活动状态[{1}]毫秒(自[{2}]起),以便为[{4}]提供相同的请求,并且可能被卡住(此StuckThreadDetectionValve的配置阈值为[{5}]秒)。总共有[{3}]个线程受此阀监视,可能被卡住。 stuckThreadDetectionValve.notifyStuckThreadInterrupted=线程[{0}](id=[{5}])已被中断,因为它在[{1}]毫秒(自[{2}]起)内处于活动状态,以便为[{3}]提供相同的请求,并且可能被卡住(此StuckThreadDetectionValve的配置中断阈值为[{4}]秒)。 diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/ParameterLimitValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/ParameterLimitValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/ParameterLimitValve.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/ParameterLimitValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,282 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.valves; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +import jakarta.servlet.ServletException; + +import org.apache.catalina.Container; +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.buf.UDecoder; +import org.apache.tomcat.util.file.ConfigFileLoader; +import org.apache.tomcat.util.file.ConfigurationSource; + +/** + * This is a concrete implementation of {@link ValveBase} that allows alternative values for the + * Connector attributes {@code maxParameterCount}, {@code maxPartCount} and {@code maxPartHeaderSize} + * to be applied to a request. The features of this implementation include: + *
        + *
      • URL-specific parameter limits that can be defined using regular expressions
      • + *
      • Configurable through Tomcat's server.xml or context.xml
      • + *
      • Requires a parameter_limit.config file containing the URL-specific parameter limits. It must be + * placed in the Host configuration folder or in the WEB-INF folder of the web application.
      • + *
      + *

      + * The default limit, specified by Connector's value, applies to all requests unless a more specific URL pattern is + * matched. URL patterns and their corresponding limits can be configured via a regular expression mapping through the + * urlPatternLimits attribute. + *

      + * The Valve checks each incoming request and enforces the appropriate limit. If a request exceeds the allowed number of + * parameters, a 400 Bad Request response is returned. + *

      + * Example, configuration in context.xml: + * + *

      + * {@code
      + * 
      + *     
      + * }
      + * and in parameter_limit.config:
      + * 
      + * + *
      + * {@code
      + * /api/.*=150
      + * /admin/.*=50
      + * /upload/.*=30,5,1024
      + * }
      + * 
      + *

      + * The configuration allows for flexible control over different sections of your application, such as applying higher + * limits for API endpoints and stricter limits for admin areas. + *

      + * If a single integer is provided, it is used for {@code maxParameterCount}. + *

      + * If three integers are provided, they are applied to {@code maxParameterCount}, {@code maxPartCount} and + * {@code maxPartHeaderSize} respectively. + */ + +public class ParameterLimitValve extends ValveBase { + + /** + * Map for URL-specific limits. + */ + private Map urlPatternLimits = new ConcurrentHashMap<>(); + + /** + * Relative path to the configuration file. Note: If the valve's container is a context, this will be relative to + * /WEB-INF/. + */ + private String resourcePath = "parameter_limit.config"; + + /** + * Will be set to true if the valve is associated with a context. + */ + private boolean context = false; + + public ParameterLimitValve() { + super(true); + } + + public String getResourcePath() { + return resourcePath; + } + + public void setResourcePath(String resourcePath) { + this.resourcePath = resourcePath; + } + + @Override + protected void initInternal() throws LifecycleException { + super.initInternal(); + containerLog = LogFactory.getLog(getContainer().getLogName() + ".parameterLimit"); + } + + @Override + protected void startInternal() throws LifecycleException { + + super.startInternal(); + + InputStream is = null; + + // Process configuration file for this valve + if (getContainer() instanceof Context) { + context = true; + String webInfResourcePath = "/WEB-INF/" + resourcePath; + is = ((Context) getContainer()).getServletContext().getResourceAsStream(webInfResourcePath); + if (containerLog.isDebugEnabled()) { + if (is == null) { + containerLog.debug(sm.getString("parameterLimitValve.noConfiguration", webInfResourcePath)); + } else { + containerLog.debug(sm.getString("parameterLimitValve.readConfiguration", webInfResourcePath)); + } + } + } else { + String resourceName = Container.getConfigPath(getContainer(), resourcePath); + try { + ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getResource(resourceName); + is = resource.getInputStream(); + } catch (IOException ioe) { + if (containerLog.isDebugEnabled()) { + containerLog.debug(sm.getString("parameterLimitValve.noConfiguration", resourceName), ioe); + } + } + } + + if (is == null) { + // Will use management operations to configure the valve dynamically + return; + } + + try (InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8); + BufferedReader reader = new BufferedReader(isr)) { + setUrlPatternLimits(reader); + } catch (IOException ioe) { + containerLog.error(sm.getString("parameterLimitValve.closeError"), ioe); + } finally { + try { + is.close(); + } catch (IOException ioe) { + containerLog.error(sm.getString("parameterLimitValve.closeError"), ioe); + } + } + + } + + public void setUrlPatternLimits(String urlPatternConfig) { + urlPatternLimits.clear(); + setUrlPatternLimits(new BufferedReader(new StringReader(urlPatternConfig))); + } + + /** + * Set the mapping of URL patterns to their corresponding parameter limits. The input should be provided line by + * line, where each line contains a pattern and a limit, separated by the last '='. + *

      + * Example: + * + *

      +     * /api/.*=50
      +     * /api======/.*=150
      +     * /urlEncoded%20api=2
      +     * # This is a comment
      +     * 
      + * + * @param reader A BufferedReader containing URL pattern to parameter limit mappings, with each pair on a separate + * line. + */ + public void setUrlPatternLimits(BufferedReader reader) { + if (containerLog == null && getContainer() != null) { + containerLog = LogFactory.getLog(getContainer().getLogName() + ".parameterLimit"); + } + try { + String line; + while ((line = reader.readLine()) != null) { + // Trim whitespace from the line + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { + // Skip empty lines or comments + continue; + } + + int lastEqualsIndex = line.lastIndexOf('='); + if (lastEqualsIndex == -1) { + throw new IllegalArgumentException(sm.getString("parameterLimitValve.invalidLine", line)); + } + + String patternString = line.substring(0, lastEqualsIndex).trim(); + String limitsString = line.substring(lastEqualsIndex + 1).trim(); + + Pattern pattern = Pattern.compile(UDecoder.URLDecode(patternString, StandardCharsets.UTF_8)); + String[] limits = limitsString.split(","); + if (limits.length == 1) { + urlPatternLimits.put(pattern, new Integer[] { Integer.valueOf(limits[0]), null, null }); + } else if (limits.length == 3) { + urlPatternLimits.put(pattern, new Integer[] { Integer.valueOf(limits[0]), + Integer.valueOf(limits[1]), Integer.valueOf(limits[2]) }); + } else { + throw new IllegalArgumentException( + sm.getString("parameterLimitValve.invalidLimitsString", limitsString)); + } + if (containerLog != null && containerLog.isTraceEnabled()) { + containerLog.trace("Add pattern " + pattern + " and limit(s) " + limitsString); + } + } + } catch (IOException ioe) { + if (containerLog != null) { + containerLog.error(sm.getString("parameterLimitValve.readError"), ioe); + } + } + } + + @Override + protected void stopInternal() throws LifecycleException { + super.stopInternal(); + urlPatternLimits.clear(); + } + + /** + * Checks if any of the defined patterns matches the URI of the request and if it does, enforces the corresponding + * parameter limit for the request. Then invoke the next Valve in the sequence. + * + * @param request The servlet request to be processed + * @param response The servlet response to be created + * + * @exception IOException if an input/output error occurs + * @exception ServletException if a servlet error occurs + */ + @Override + public void invoke(Request request, Response response) throws IOException, ServletException { + + if (urlPatternLimits.isEmpty()) { + getNext().invoke(request, response); + return; + } + + String requestURI = context ? request.getRequestPathMB().toString() : request.getDecodedRequestURI(); + + // Iterate over the URL patterns and apply corresponding limits + for (Map.Entry entry : urlPatternLimits.entrySet()) { + if (entry.getKey().matcher(requestURI).matches()) { + Integer[] limits = entry.getValue(); + // maxParameterCount should always be present + request.setMaxParameterCount(limits[0].intValue()); + if (limits[1] != null) { + request.setMaxPartCount(limits[1].intValue()); + request.setMaxPartHeaderSize(limits[2].intValue()); + } + break; + } + } + + // Invoke the next valve to continue processing the request + getNext().invoke(request, response); + } +} diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/PersistentValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/PersistentValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/PersistentValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/PersistentValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -77,7 +77,7 @@ protected Pattern filter = null; - private ConcurrentMap sessionToSemaphoreMap = new ConcurrentHashMap<>(); + private final ConcurrentMap sessionToSemaphoreMap = new ConcurrentHashMap<>(); private boolean semaphoreFairness = true; @@ -94,11 +94,7 @@ @Override public void setContainer(Container container) { super.setContainer(container); - if (container instanceof Engine || container instanceof Host) { - clBindRequired = true; - } else { - clBindRequired = false; - } + clBindRequired = container instanceof Engine || container instanceof Host; } @@ -203,7 +199,7 @@ Session hsess; try { hsess = request.getSessionInternal(false); - } catch (Exception ex) { + } catch (Exception e) { hsess = null; } String newsessionId = null; @@ -290,9 +286,7 @@ int maxInactiveInterval = session.getMaxInactiveInterval(); if (maxInactiveInterval > 0) { int timeIdle = (int) (session.getIdleTimeInternal() / 1000L); - if (timeIdle >= maxInactiveInterval) { - return true; - } + return timeIdle >= maxInactiveInterval; } } @@ -326,7 +320,7 @@ } public void setFilter(String filter) { - if (filter == null || filter.length() == 0) { + if (filter == null || filter.isEmpty()) { this.filter = null; } else { try { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/ProxyErrorReportValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/ProxyErrorReportValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/ProxyErrorReportValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/ProxyErrorReportValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -147,7 +147,7 @@ } StringBuilder stringBuilder = new StringBuilder(urlString); - if (urlString.indexOf("?") > -1) { + if (urlString.contains("?")) { stringBuilder.append('&'); } else { stringBuilder.append('?'); @@ -193,7 +193,7 @@ } try { response.sendRedirect(urlString); - } catch (IOException e) { + } catch (IOException ioe) { // Ignore } } else { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/RemoteAddrValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteAddrValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/RemoteAddrValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteAddrValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,9 @@ * Concrete implementation of RequestFilterValve that filters based on the string representation of the * remote client's IP address optionally combined with the server connector port number. * - * @author Craig R. McClanahan + * @deprecated This Valve will be removed in Tomcat 12 onwards. Use {@link RemoteCIDRValve} instead. */ +@Deprecated public final class RemoteAddrValve extends RequestFilterValve { private static final Log log = LogFactory.getLog(RemoteAddrValve.class); diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/RemoteCIDRValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteCIDRValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/RemoteCIDRValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteCIDRValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -203,12 +203,8 @@ } // Allow if deny is specified but allow isn't - if (!deny.isEmpty() && allow.isEmpty()) { - return true; - } - - // Deny this request - return false; + // Otherwise deny this request + return !deny.isEmpty() && allow.isEmpty(); } diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/RemoteHostValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteHostValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/RemoteHostValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteHostValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,6 @@ /** * Concrete implementation of RequestFilterValve that filters based on the remote client's host name * optionally combined with the server connector port number. - * - * @author Craig R. McClanahan */ public final class RemoteHostValve extends RequestFilterValve { diff -Nru tomcat10-10.1.34/java/org/apache/catalina/valves/RemoteIpValve.java tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteIpValve.java --- tomcat10-10.1.34/java/org/apache/catalina/valves/RemoteIpValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/catalina/valves/RemoteIpValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,7 +53,7 @@ * If the incoming request.getRemoteAddr() matches the valve's list of internal or trusted proxies: *

      *
        - *
      • Loop on the comma delimited list of IPs and hostnames passed by the preceding load balancer or proxy in the given + *
      • Loop on the comma-delimited list of IPs and hostnames passed by the preceding load balancer or proxy in the given * request's Http header named $remoteIpHeader (default value x-forwarded-for). Values are * processed in right-to-left order.
      • *
      • For each ip/host of the list: @@ -129,7 +129,7 @@ *

      protocolHeaderHttpsValueValue of the protocolHeader to indicate that it is an Https requestValue of the protocolHeader to indicate that it is a Https requestN/AString like https or ONhttps
      @@ -266,7 +266,7 @@ nested <a> elements will not match this pattern -- we will describe means to support this kind of matching later.

      -

      The next step up in matching pattern complexity is "a/b". This pattern will +

      The next step-up in matching pattern complexity is "a/b". This pattern will be matched when a <b> element is found nested inside a top-level <a> element. Again, this match can occur as many times as desired, depending on the content of the XML document being parsed. @@ -300,7 +300,7 @@

      It is quite possible that, when a particular XML element is being parsed, the pattern for more than one registered processing rule will be matched either because you registered more than one processing rule with the same -matching pattern, or because one more more exact pattern matches and wildcard +matching pattern, or because one more exact pattern matches and wildcard pattern matches are satisfied by the same element.

      When this occurs, the corresponding processing rules will all be fired in order. @@ -529,7 +529,7 @@ mechanism for processing the contents of the struts-config.xml configuration that describes nearly every aspect of a Struts-based application. Because of this, the controller servlet contains a comprehensive, real world, -example of how the Digester can be employed for this type of a use case. +example of how the Digester can be employed for this type of use case. See the initDigester() method of class org.apache.struts.action.ActionServlet for the code that creates and configures the Digester to be used, and the initMapping() @@ -839,7 +839,7 @@ implementation which does not build on the default pattern matching rules. It uses a pluggable RegexMatcher implementation to test if a path matches the pattern for a Rule. All matching rules are returned -(note that this behaviour differs from longest matching rule of the default +(note that this behaviour differs from the longest matching rule of the default pattern matching rules). See the Java Docs for more details.

      @@ -1001,7 +1001,7 @@

    • PUBLIC public-identifier system-identifier
    • -The system-identifier is an URI from which the resource can be obtained +The system-identifier is a URI from which the resource can be obtained (either directly or indirectly). Many valid URIs may identify the same resource. The public-identifier is an additional free identifier which may be used (by the parser) to locate the resource. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/file/ConfigFileLoader.java tomcat10-10.1.52/java/org/apache/tomcat/util/file/ConfigFileLoader.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/file/ConfigFileLoader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/file/ConfigFileLoader.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,20 +20,20 @@ import java.io.InputStream; /** - * This class is used to obtain {@link InputStream}s for configuration files - * from a given location String. This allows greater flexibility than these - * files having to be loaded directly from a file system. + * This class is used to obtain {@link InputStream}s for configuration files from a given location String. This allows + * greater flexibility than these files having to be loaded directly from a file system. */ public class ConfigFileLoader { private static ConfigurationSource source; /** - * Get the configured configuration source. If none has been configured, - * a default source based on the calling directory will be used. + * Get the configured configuration source. If none has been configured, a default source based on the calling + * directory will be used. + * * @return the configuration source in use */ - public static final ConfigurationSource getSource() { + public static ConfigurationSource getSource() { if (source == null) { return ConfigurationSource.DEFAULT; } @@ -41,11 +41,11 @@ } /** - * Set the configuration source used by Tomcat to locate various - * configuration resources. + * Set the configuration source used by Tomcat to locate various configuration resources. + * * @param source The source */ - public static final void setSource(ConfigurationSource source) { + public static void setSource(ConfigurationSource source) { if (ConfigFileLoader.source == null) { ConfigFileLoader.source = source; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/file/ConfigurationSource.java tomcat10-10.1.52/java/org/apache/tomcat/util/file/ConfigurationSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/file/ConfigurationSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/file/ConfigurationSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,17 +29,16 @@ import org.apache.tomcat.util.buf.UriUtil; /** - * Abstracts configuration file storage. Allows Tomcat embedding using the regular - * configuration style. - * This abstraction aims to be very simple and does not cover resource listing, - * which is usually used for dynamic deployments that are usually not used when - * embedding, as well as resource writing. + * Abstracts configuration file storage. Allows Tomcat embedding using the regular configuration style. This abstraction + * aims to be very simple and does not cover resource listing, which is usually used for dynamic deployments that are + * usually not used when embedding, as well as resource writing. */ public interface ConfigurationSource { ConfigurationSource DEFAULT = new ConfigurationSource() { - protected final File userDir = new File(System.getProperty("user.dir")); - protected final URI userDirUri = userDir.toURI(); + private final File userDir = new File(System.getProperty("user.dir")); + private final URI userDirUri = userDir.toURI(); + @Override public Resource getResource(String name) throws IOException { if (!UriUtil.isAbsoluteURI(name)) { @@ -52,7 +51,7 @@ return new Resource(fis, f.toURI()); } } - URI uri = null; + URI uri; try { uri = userDirUri.resolve(name); } catch (IllegalArgumentException e) { @@ -65,6 +64,7 @@ throw new FileNotFoundException(name); } } + @Override public URI getURI(String name) { if (!UriUtil.isAbsoluteURI(name)) { @@ -81,24 +81,26 @@ }; /** - * Represents a resource: a stream to the resource associated with - * its URI. + * Represents a resource: a stream to the resource associated with its URI. */ class Resource implements AutoCloseable { private final InputStream inputStream; private final URI uri; + public Resource(InputStream inputStream, URI uri) { this.inputStream = inputStream; this.uri = uri; } + public InputStream getInputStream() { return inputStream; } + public URI getURI() { return uri; } - public long getLastModified() - throws MalformedURLException, IOException { + + public long getLastModified() throws MalformedURLException, IOException { URLConnection connection = null; try { connection = uri.toURL().openConnection(); @@ -109,6 +111,7 @@ } } } + @Override public void close() throws IOException { if (inputStream != null) { @@ -119,50 +122,58 @@ /** * Returns the contents of the main conf/server.xml file. + * * @return the server.xml as an InputStream + * * @throws IOException if an error occurs or if the resource does not exist */ - default Resource getServerXml() - throws IOException { + default Resource getServerXml() throws IOException { return getConfResource("server.xml"); } /** - * Returns the contents of the shared conf/web.xml file. This usually - * contains the declaration of the default and JSP servlets. + * Returns the contents of the shared conf/web.xml file. This usually contains the declaration of the default and + * JSP servlets. + * * @return the web.xml as an InputStream + * * @throws IOException if an error occurs or if the resource does not exist */ - default Resource getSharedWebXml() - throws IOException { + default Resource getSharedWebXml() throws IOException { return getConfResource("web.xml"); } /** * Get a resource, based on the conf path. + * * @param name The resource name + * * @return the resource as an InputStream + * * @throws IOException if an error occurs or if the resource does not exist */ - default Resource getConfResource(String name) - throws IOException { + default Resource getConfResource(String name) throws IOException { String fullName = "conf/" + name; return getResource(fullName); } /** * Get a resource, not based on the conf path. + * * @param name The resource name + * * @return the resource + * * @throws IOException if an error occurs or if the resource does not exist */ - Resource getResource(String name) - throws IOException; + Resource getResource(String name) throws IOException; /** - * Get a URI to the given resource. Unlike getResource, this will also - * return URIs to locations where no resource exists. + * Get a URI to the given resource. Unlike getResource, this will also return URIs to locations where no resource + * exists. + * * @param name The resource name + * * @return a URI representing the resource location */ URI getURI(String name); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/file/Matcher.java tomcat10-10.1.52/java/org/apache/tomcat/util/file/Matcher.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/file/Matcher.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/file/Matcher.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,32 +20,32 @@ import java.util.Set; /** - *

      This is a utility class to match file globs. - * The class has been derived from + *

      + * This is a utility class to match file globs. The class has been derived from * org.apache.tools.ant.types.selectors.SelectorUtils. *

      - *

      All methods are static.

      + *

      + * All methods are static. + *

      */ public final class Matcher { /** - * Tests whether or not a given file name matches any file name pattern in - * the given set. The match is performed case-sensitively. + * Tests whether or not a given file name matches any file name pattern in the given set. The match is performed + * case-sensitively. * * @see #match(String, String, boolean) * - * @param patternSet The pattern set to match against. Must not be - * null. - * @param fileName The file name to match, as a String. Must not be - * null. It must be just a file name, without - * a path. + * @param patternSet The pattern set to match against. Must not be null. + * @param fileName The file name to match, as a String. Must not be null. It must be just a file + * name, without a path. * - * @return true if any pattern in the set matches against the - * file name, or false otherwise. + * @return true if any pattern in the set matches against the file name, or false + * otherwise. */ public static boolean matchName(Set patternSet, String fileName) { char[] fileNameArray = fileName.toCharArray(); - for (String pattern: patternSet) { + for (String pattern : patternSet) { if (match(pattern, fileNameArray, true)) { return true; } @@ -55,48 +55,35 @@ /** - * Tests whether or not a string matches against a pattern. - * The pattern may contain two special characters:
      + * Tests whether or not a string matches against a pattern. The pattern may contain two special characters:
      * '*' means zero or more characters
      * '?' means one and only one character * - * @param pattern The pattern to match against. - * Must not be null. - * @param str The string which must be matched against the - * pattern. Must not be null. - * @param caseSensitive Whether or not matching should be performed - * case sensitively. - * + * @param pattern The pattern to match against. Must not be null. + * @param str The string which must be matched against the pattern. Must not be null. + * @param caseSensitive Whether or not matching should be performed case sensitively. * - * @return true if the string matches against the pattern, - * or false otherwise. + * @return true if the string matches against the pattern, or false otherwise. */ - public static boolean match(String pattern, String str, - boolean caseSensitive) { + public static boolean match(String pattern, String str, boolean caseSensitive) { return match(pattern, str.toCharArray(), caseSensitive); } /** - * Tests whether or not a string matches against a pattern. - * The pattern may contain two special characters:
      + * Tests whether or not a string matches against a pattern. The pattern may contain two special characters:
      * '*' means zero or more characters
      * '?' means one and only one character * - * @param pattern The pattern to match against. - * Must not be null. - * @param strArr The character array which must be matched against the - * pattern. Must not be null. - * @param caseSensitive Whether or not matching should be performed - * case sensitively. - * + * @param pattern The pattern to match against. Must not be null. + * @param strArr The character array which must be matched against the pattern. Must not be + * null. + * @param caseSensitive Whether or not matching should be performed case sensitively. * - * @return true if the string matches against the pattern, - * or false otherwise. + * @return true if the string matches against the pattern, or false otherwise. */ - private static boolean match(String pattern, char[] strArr, - boolean caseSensitive) { + private static boolean match(String pattern, char[] strArr, boolean caseSensitive) { char[] patArr = pattern.toCharArray(); int patIdxStart = 0; int patIdxEnd = patArr.length - 1; @@ -197,8 +184,7 @@ for (int j = 0; j < patLength; j++) { ch = patArr[patIdxStart + j + 1]; if (ch != '?') { - if (different(caseSensitive, ch, - strArr[strIdxStart + i + j])) { + if (different(caseSensitive, ch, strArr[strIdxStart + i + j])) { continue strLoop; } } @@ -230,11 +216,8 @@ return true; } - private static boolean different( - boolean caseSensitive, char ch, char other) { - return caseSensitive - ? ch != other - : Character.toUpperCase(ch) != Character.toUpperCase(other); + private static boolean different(boolean caseSensitive, char ch, char other) { + return caseSensitive ? ch != other : Character.toUpperCase(ch) != Character.toUpperCase(other); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/CookieProcessor.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/CookieProcessor.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/CookieProcessor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/CookieProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,7 +33,7 @@ /** * Generate the {@code Set-Cookie} HTTP header value for the given Cookie. This method receives as parameter the - * servlet request so that it can make decisions based on request properties. One such use-case is decide if the + * servlet request so that it can make decisions based on request properties. One such use-case is deciding if the * SameSite attribute should be added to the cookie based on the User-Agent or other request header because there * are browser versions incompatible with the SameSite attribute. This is described by * the Chromium project. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/CookiesWithoutEquals.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/CookiesWithoutEquals.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/CookiesWithoutEquals.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/CookiesWithoutEquals.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,13 +24,49 @@ IGNORE("ignore"), NAME("name"); /* + * @formatter:off * There is no VALUE option since the Servlet specification does not permit the creation of a Cookie with a name * that is either null or the zero length string. * - * The historical intention (from the user agent perspective) of using a name-value-pair without an equals sign has - * been to indicate a cookie with a name but no value. Tomcat has done the opposite. The current RFC6265bis text - * treats a name-value-pair without an equals sign as a cookie with a value but no name. Supporting this will - * require changes to the Servlet specifcation. + * In RFC 2019, cookie name and value were defined as follows: + * cookie = NAME "=" VALUE *(";" cookie-av) + * NAME = attr + * VALUE = value + * attr = token + * value = word + * And from RFC 2068 + * token = 1* + * word = *TEXT + * Set-Cookie and Cookie used the same definition. + * Name had to be at least one character, equals sign was required, value could be the empty string. + * + * In RFC 2965, the definition of value changed to: + * value = token | quoted-string + * Set-Cookie2 and Cookie use the same definition. + * Name had to be at least one character, equals sign was required, value could not be the empty string (it could + * be ""). + * + * In RFC6265, which aimed to document actual usage, cookie name and value are defined as follows: + * cookie-pair = cookie-name "=" cookie-value + * cookie-name = token + * cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) + * For the user agent, the equals sign was required and cookies with no name were ignored. + * + * In RFC6265bis, the definitions are unchanged. + * For the user agent: + * - a name-value-pair without an equals sign is treated as the value of a cookie with an empty name. + * - both empty name and empty value are allowed but if both are empty the cookie will be ignored. + * @formatter:on + * + * To see how RFC6265 arrived at his behaviour, see https://github.com/httpwg/http-extensions/issues/159 + * + * Historically, the users agents settled on using a name-value-pair without an equals sign to indicate a cookie + * with a value but no name. Tomcat did the opposite. That arose from addressing this bug: + * https://bz.apache.org/bugzilla/show_bug.cgi?id=49000 which was based on observed but not understood client + * behaviour. + * + * The current RFC6265bis text explicitly treats a name-value-pair without an equals sign as a cookie with a value + * but no name. There are currently no plans for the Servlet specification to support nameless cookies. */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/FastHttpDateFormat.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/FastHttpDateFormat.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/FastHttpDateFormat.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/FastHttpDateFormat.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ /** * Utility class to generate HTTP dates. - * - * @author Remy Maucherat */ public final class FastHttpDateFormat { @@ -75,8 +73,10 @@ /** * Formatter cache. + *

      + * Note: This needs to be a ConcurrentHashMap for correct operation so declare it as such (rather than as Map). */ - private static final Map formatCache = new ConcurrentHashMap<>(CACHE_SIZE); + private static final ConcurrentHashMap formatCache = new ConcurrentHashMap<>(CACHE_SIZE); /** diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/HeaderUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/HeaderUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/HeaderUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/HeaderUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,7 +20,7 @@ /** * Converts an HTTP header line in byte form to a printable String. Bytes corresponding to visible ASCII characters - * will converted to those characters. All other bytes (0x00 to 0x1F, 0x7F to OxFF) will be represented in 0xNN + * will be converted to those characters. All other bytes (0x00 to 0x1F, 0x7F to 0xFF) will be represented in 0xNN * form. * * @param bytes Contains an HTTP header line diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/tomcat/util/http/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -31,6 +31,7 @@ parameters.copyFail=无法创建以调试日志记录为目的的原始参数值的副本 parameters.decodeFail.debug=字符解码失败.参数 [{0}]和值 [{1}]被忽略 parameters.decodeFail.info=字符解码失败。值为[{1}]的参数[{0}]已被忽略。请注意,此处引用的名称和值可能由于解码失败而损坏。使用调试级别日志记录查看原始的、未损坏的值。 +parameters.duplicateFail=创建查询参数副本失败 parameters.emptyChunk=忽略空参数块 parameters.fallToDebug=\n\ \ 注:更多的参数错误将以DEBUG级别日志进行记录。 diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/Method.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/Method.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/Method.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/Method.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.http; + +public class Method { + + /* + * This class was originally created to hold the bytes to String conversion method. It turns out that these + * constants are just as much of a benefit to performance - if used consistently. + * + * If the String constants for the methods are used throughout the code-base, that allows String.equals() to use the + * 'same object shortcut' when checking if a request is (or is not) using a particular method. That is faster than a + * character by character comparison. That results in a further performance improvement that is as big - or possibly + * slightly bigger - than the improvement obtained by using the optimised conversion. + */ + + // Standard HTTP methods supported by HttpServlet + public static final String GET = "GET"; + public static final String POST = "POST"; + public static final String PUT = "PUT"; + public static final String PATCH = "PATCH"; + public static final String HEAD = "HEAD"; + public static final String OPTIONS = "OPTIONS"; + public static final String DELETE = "DELETE"; + public static final String TRACE = "TRACE"; + // Additional WebDAV methods + public static final String PROPFIND = "PROPFIND"; + public static final String PROPPATCH = "PROPPATCH"; + public static final String MKCOL = "MKCOL"; + public static final String COPY = "COPY"; + public static final String MOVE = "MOVE"; + public static final String LOCK = "LOCK"; + public static final String UNLOCK = "UNLOCK"; + // Other methods recognised by Tomcat + public static final String CONNECT = "CONNECT"; + + + /** + * Provides optimised conversion from bytes to Strings for known HTTP methods. The bytes are assumed to be an + * ISO-8859-1 encoded representation of an HTTP method. The method is not validated as being a token, but only valid + * HTTP method names will be returned. + *

      + * Doing it this way is ~10x faster than using MessageBytes.toStringType() saving ~40ns per request which is ~1% of + * the processing time for a minimal "Hello World" type servlet. For non-standard methods there is an additional + * overhead of ~2.5ns per request. + *

      + * Pretty much every request ends up converting the method to a String so it is more efficient to do this straight + * away and always use Strings. + * + * @param buf The byte buffer containing the HTTP method to convert + * @param start The first byte of the HTTP method + * @param len The number of bytes to convert + * + * @return The HTTP method as a String or {@code null} if the method is not recognised. + */ + public static String bytesToString(byte[] buf, int start, int len) { + switch (buf[start]) { + case 'G': { + if (len == 3 && buf[start + 1] == 'E' && buf[start + 2] == 'T') { + return GET; + } + break; + } + case 'P': { + if (len == 4 && buf[start + 1] == 'O' && buf[start + 2] == 'S' && buf[start + 3] == 'T') { + return POST; + } else if (len == 3 && buf[start + 1] == 'U' && buf[start + 2] == 'T') { + return PUT; + } else if (len == 5 && buf[start + 1] == 'A' && buf[start + 2] == 'T' && buf[start + 3] == 'C' && + buf[start + 4] == 'H') { + return PATCH; + } else if (len == 8 && buf[start + 1] == 'R' && buf[start + 2] == 'O' && buf[start + 3] == 'P' && + buf[start + 4] == 'F' && buf[start + 5] == 'I' && buf[start + 6] == 'N' && + buf[start + 7] == 'D') { + return PROPFIND; + } else if (len == 9 && buf[start + 1] == 'R' && buf[start + 2] == 'O' && buf[start + 3] == 'P' && + buf[start + 4] == 'P' && buf[start + 5] == 'A' && buf[start + 6] == 'T' && + buf[start + 7] == 'C' && buf[start + 8] == 'H') { + return PROPPATCH; + } + break; + } + case 'H': { + if (len == 4 && buf[start + 1] == 'E' && buf[start + 2] == 'A' && buf[start + 3] == 'D') { + return HEAD; + } + break; + } + case 'O': { + if (len == 7 && buf[start + 1] == 'P' && buf[start + 2] == 'T' && buf[start + 3] == 'I' && + buf[start + 4] == 'O' && buf[start + 5] == 'N' && buf[start + 6] == 'S') { + return OPTIONS; + } + break; + } + case 'D': { + if (len == 6 && buf[start + 1] == 'E' && buf[start + 2] == 'L' && buf[start + 3] == 'E' && + buf[start + 4] == 'T' && buf[start + 5] == 'E') { + return DELETE; + } + break; + } + case 'T': { + if (len == 5 && buf[start + 1] == 'R' && buf[start + 2] == 'A' && buf[start + 3] == 'C' && + buf[start + 4] == 'E') { + return TRACE; + } + break; + } + case 'M': { + if (len == 5 && buf[start + 1] == 'K' && buf[start + 2] == 'C' && buf[start + 3] == 'O' && + buf[start + 4] == 'L') { + return MKCOL; + } else if (len == 4 && buf[start + 1] == 'O' && buf[start + 2] == 'V' && buf[start + 3] == 'E') { + return MOVE; + } + break; + } + case 'C': { + if (len == 4 && buf[start + 1] == 'O' && buf[start + 2] == 'P' && buf[start + 3] == 'Y') { + return COPY; + } else if (len == 7 && buf[start + 1] == 'O' && buf[start + 2] == 'N' && buf[start + 3] == 'N' && + buf[start + 4] == 'E' && buf[start + 5] == 'C' && buf[start + 6] == 'T') { + return CONNECT; + } + break; + } + case 'L': { + if (len == 4 && buf[start + 1] == 'O' && buf[start + 2] == 'C' && buf[start + 3] == 'K') { + return LOCK; + } + break; + } + case 'U': { + if (len == 6 && buf[start + 1] == 'N' && buf[start + 2] == 'L' && buf[start + 3] == 'O' && + buf[start + 4] == 'C' && buf[start + 5] == 'K') { + return UNLOCK; + } + break; + } + } + + return null; + } + + + private Method() { + // Utility class - hide default constructor + } +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/MimeHeaders.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/MimeHeaders.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/MimeHeaders.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/MimeHeaders.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -39,39 +40,26 @@ * calls header methods, but is easy to avoid inside tomcat. The goal is to use _only_ MessageByte-based Fields, and * reduce to 0 the memory overhead of tomcat. *

      - * This class is used to contain standard internet message headers, - * used for SMTP (RFC822) and HTTP (RFC2068) messages as well as for - * MIME (RFC 2045) applications such as transferring typed data and - * grouping related items in multipart message bodies. + * This class is used to contain standard internet message headers, used for SMTP (RFC822) and HTTP (RFC2068) messages + * as well as for MIME (RFC 2045) applications such as transferring typed data and grouping related items in multipart + * message bodies. *

      - * Message headers, as specified in RFC822, include a field name - * and a field body. Order has no semantic significance, and several - * fields with the same name may exist. However, most fields do not - * (and should not) exist more than once in a header. + * Message headers, as specified in RFC822, include a field name and a field body. Order has no semantic significance, + * and several fields with the same name may exist. However, most fields do not (and should not) exist more than once in + * a header. *

      - * Many kinds of field body must conform to a specified syntax, - * including the standard parenthesized comment syntax. This class - * supports only two simple syntaxes, for dates and integers. + * Many kinds of field body must conform to a specified syntax, including the standard parenthesized comment syntax. + * This class supports only two simple syntaxes, for dates and integers. *

      - * When processing headers, care must be taken to handle the case of - * multiple same-name fields correctly. The values of such fields are - * only available as strings. They may be accessed by index (treating - * the header as an array of fields), or by name (returning an array - * of string values). + * When processing headers, care must be taken to handle the case of multiple same-name fields correctly. The values of + * such fields are only available as strings. They may be accessed by index (treating the header as an array of fields), + * or by name (returning an array of string values). *

      - * Headers are first parsed and stored in the order they are - * received. This is based on the fact that most servlets will not - * directly access all headers, and most headers are single-valued. - * (the alternative - a hash or similar data structure - will add - * an overhead that is not needed in most cases) + * Headers are first parsed and stored in the order they are received. This is based on the fact that most servlets will + * not directly access all headers, and most headers are single-valued. (the alternative - a hash or similar data + * structure - will add an overhead that is not needed in most cases) *

      - * Apache seems to be using a similar method for storing and manipulating - * headers. - * - * @author dac@eng.sun.com - * @author James Todd [gonzo@eng.sun.com] - * @author Costin Manolache - * @author kevin seguin + * Apache seems to be using a similar method for storing and manipulating headers. */ public class MimeHeaders { @@ -113,7 +101,7 @@ this.limit = limit; if (limit > 0 && headers.length > limit && count < limit) { // shrink header list array - MimeHeaderField tmp[] = new MimeHeaderField[limit]; + MimeHeaderField[] tmp = new MimeHeaderField[limit]; System.arraycopy(headers, 0, tmp, 0, count); headers = tmp; } @@ -172,7 +160,7 @@ int j = -1; for (int i = 0; i < count; i++) { String name = headers[i].getName().toStringType(); - if (allowedHeaders.contains(name)) { + if (allowedHeaders.contains(name.trim().toLowerCase(Locale.ENGLISH))) { ++j; if (j != i) { headers[j] = headers[i]; @@ -279,7 +267,7 @@ if (limit > 0 && newLength > limit) { newLength = limit; } - MimeHeaderField tmp[] = new MimeHeaderField[newLength]; + MimeHeaderField[] tmp = new MimeHeaderField[newLength]; System.arraycopy(headers, 0, tmp, 0, len); headers = tmp; } @@ -313,7 +301,7 @@ * * @return the message bytes container for the value */ - public MessageBytes addValue(byte b[], int startN, int len) { + public MessageBytes addValue(byte[] b, int startN, int len) { MimeHeaderField mhf = createHeader(); mhf.getName().setBytes(b, startN, len); return mhf.getValue(); @@ -387,7 +375,7 @@ public String getHeader(String name) { MessageBytes mh = getValue(name); - return mh != null ? mh.toString() : null; + return mh != null ? mh.toStringType() : null; } // -------------------- Removing -------------------- diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/Parameters.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/Parameters.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/Parameters.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/Parameters.java 2026-01-23 19:33:36.000000000 +0000 @@ -157,7 +157,7 @@ handleQueryParameters(); ArrayList values = paramHashValues.get(name); if (values != null) { - if (values.size() == 0) { + if (values.isEmpty()) { return ""; } return values.get(0); @@ -187,9 +187,9 @@ try { decodedQuery.duplicate(queryMB); - } catch (IOException e) { + } catch (IOException ioe) { // Can't happen, as decodedQuery can't overflow - log.error(sm.getString("parameters.copyFail"), e); + log.error(sm.getString("parameters.copyFail"), ioe); } processParameters(decodedQuery, queryStringCharset); } @@ -227,11 +227,11 @@ private static final Charset DEFAULT_URI_CHARSET = StandardCharsets.UTF_8; - public void processParameters(byte bytes[], int start, int len) { + public void processParameters(byte[] bytes, int start, int len) { processParameters(bytes, start, len, charset); } - private void processParameters(byte bytes[], int start, int len, Charset charset) { + private void processParameters(byte[] bytes, int start, int len, Charset charset) { if (log.isTraceEnabled()) { log.trace(sm.getString("parameters.bytes", new String(bytes, start, len, DEFAULT_BODY_CHARSET))); @@ -407,14 +407,14 @@ } break; } - } catch (IOException e) { + } catch (IOException ioe) { setParseFailedReason(FailReason.URL_DECODING); decodeFailCount++; if (decodeFailCount == 1 || log.isDebugEnabled()) { if (log.isDebugEnabled()) { log.debug( sm.getString("parameters.decodeFail.debug", origName.toString(), origValue.toString()), - e); + ioe); } else if (log.isInfoEnabled()) { UserDataHelper.Mode logMode = userDataLog.getNextMode(); if (logMode != null) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/RequestUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/RequestUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/RequestUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/RequestUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,13 +30,13 @@ /** - * Normalize a relative URI path that may have relative values ("/./", "/../", and so on ) it it. - * WARNING - This method is useful only for normalizing application-generated paths. It does not - * try to perform security checks for malicious input. + * Normalize a relative URI path. This method normalizes "/./", "/../", "//" and "\". If the input path is an + * attempt to 'escape the root' (e.g. /../input.txt) then {@code null} is returned to prevent attempts to 'escape + * the root'. WARNING - No other URI validation checks are performed. * * @param path Relative path to be normalized * - * @return The normalized path or null if the path cannot be normalized + * @return The normalized path or {@code null} if the input path attempts to 'escape the root'. */ public static String normalize(String path) { return normalize(path, true); @@ -44,14 +44,14 @@ /** - * Normalize a relative URI path that may have relative values ("/./", "/../", and so on ) it it. - * WARNING - This method is useful only for normalizing application-generated paths. It does not - * try to perform security checks for malicious input. + * Normalize a relative URI path. This method normalizes "/./", "/../" and "//". This method optionally normalizes + * "\". If the input path is an attempt to 'escape the root' (e.g. /../input.txt) then {@code null} is returned to + * prevent attempts to 'escape the root'. WARNING - No other URI validation checks are performed. * * @param path Relative path to be normalized - * @param replaceBackSlash Should '\\' be replaced with '/' + * @param replaceBackSlash Should '\\' be normalized to '/' * - * @return The normalized path or null if the path cannot be normalized + * @return The normalized path or {@code null} if the input path attempts to 'escape the root'. */ public static String normalize(String path, boolean replaceBackSlash) { @@ -123,26 +123,19 @@ // Build scheme://host:port from request StringBuilder target = new StringBuilder(); String scheme = request.getScheme(); - if (scheme == null) { - return false; - } else { - scheme = scheme.toLowerCase(Locale.ENGLISH); - } - target.append(scheme); - target.append("://"); - String host = request.getServerName(); - if (host == null) { + if (scheme == null || host == null) { return false; } - target.append(host); + scheme = scheme.toLowerCase(Locale.ENGLISH); + target.append(scheme).append("://").append(host); int port = request.getServerPort(); // Origin may or may not include the (default) port. // At this point target doesn't include a port. if (target.length() == origin.length()) { // origin and target can only be equal if both are using default - // ports. Therefore only append the port to the target if a + // ports. Therefore, only append the port to the target if a // non-default port is used. if (("http".equals(scheme) || "ws".equals(scheme)) && port != 80 || ("https".equals(scheme) || "wss".equals(scheme)) && port != 443) { @@ -161,7 +154,7 @@ // Both scheme and host are case-insensitive but the CORS spec states // this check should be case-sensitive - return origin.equals(target.toString()); + return origin.contentEquals(target); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/ResponseUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/ResponseUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/ResponseUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/ResponseUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -60,7 +60,7 @@ } // Short-cut if no headers have been set - if (varyHeaders.size() == 0) { + if (varyHeaders.isEmpty()) { adapter.addHeader(VARY_HEADER, name); return; } @@ -82,8 +82,8 @@ StringReader input = new StringReader(varyHeader); try { TokenList.parseTokenList(input, fieldNames); - } catch (IOException ioe) { - // Should never happen + } catch (IOException ignore) { + // Should never happen because a StringReader is used. } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -80,9 +80,8 @@ if (cookieValue != null && !cookieValue.isNull()) { if (cookieValue.getType() != MessageBytes.T_BYTES) { if (log.isDebugEnabled()) { - Exception e = new Exception(); // TODO: Review this in light of HTTP/2 - log.debug(sm.getString("rfc6265CookieProcessor.expectedBytes"), e); + log.debug(sm.getString("rfc6265CookieProcessor.expectedBytes"), new Exception()); } cookieValue.toBytes(); } @@ -115,7 +114,7 @@ header.append(cookie.getName()); header.append('='); String value = cookie.getValue(); - if (value != null && value.length() > 0) { + if (value != null && !value.isEmpty()) { validateCookieValue(value); header.append(value); } @@ -143,14 +142,14 @@ } String domain = cookie.getDomain(); - if (domain != null && domain.length() > 0) { + if (domain != null && !domain.isEmpty()) { validateDomain(domain); header.append("; Domain="); header.append(domain); } String path = cookie.getPath(); - if (path != null && path.length() > 0) { + if (path != null && !path.isEmpty()) { validatePath(path); header.append("; Path="); header.append(path); @@ -239,7 +238,7 @@ private void validateDomain(String domain) { int i = 0; - int prev = -1; + int prev; int cur = -1; char[] chars = domain.toCharArray(); while (i < chars.length) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/ServerCookies.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/ServerCookies.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/ServerCookies.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/ServerCookies.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,7 +49,7 @@ if (cookieCount >= serverCookies.length) { int newSize = limit > -1 ? Math.min(2 * cookieCount, limit) : 2 * cookieCount; - ServerCookie scookiesTmp[] = new ServerCookie[newSize]; + ServerCookie[] scookiesTmp = new ServerCookie[newSize]; System.arraycopy(serverCookies, 0, scookiesTmp, 0, cookieCount); serverCookies = scookiesTmp; } @@ -78,7 +78,7 @@ this.limit = limit; if (limit > -1 && serverCookies.length > limit && cookieCount <= limit) { // shrink cookie list array - ServerCookie scookiesTmp[] = new ServerCookie[limit]; + ServerCookie[] scookiesTmp = new ServerCookie[limit]; System.arraycopy(serverCookies, 0, scookiesTmp, 0, cookieCount); serverCookies = scookiesTmp; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/WebdavIfHeader.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/WebdavIfHeader.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/WebdavIfHeader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/WebdavIfHeader.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,9 +30,9 @@ import org.apache.tomcat.util.res.StringManager; /** - * The IfHeader class represents the state lists defined - * through the HTTP If header, which is specified in RFC 2518 as - * follows : + * The IfHeader class represents the state lists defined through the HTTP If header, which is + * specified in RFC 2518 as follows : + * *

        *    If = "If" ":" ( 1*No-tag-list | 1*Tagged-list)
        *    No-tag-list = List
      @@ -43,10 +43,10 @@
        *    Coded-URL = "<" absoluteURI ">"
        * 
      *

      - * Reformulating this specification into proper EBNF as specified by N. Wirth - * we get the following productions, which map to the parse METHODS of this - * class. Any whitespace is ignored except for white space surrounding and - * within words which is considered significant. + * Reformulating this specification into proper EBNF as specified by N. Wirth we get the following productions, which + * map to the parse METHODS of this class. Any whitespace is ignored except for white space surrounding and within words + * which is considered significant. + * *

        *    If = "If:" ( Tagged | Untagged ).
        *    Tagged = { "<" Word ">" Untagged } .
      @@ -55,34 +55,25 @@
        *    Word = characters .
        * 
      *

      - * An If header either contains untagged IfList entries or - * tagged IfList entries but not a mixture of both. An If - * header containing tagged entries is said to be of tagged type while - * an If header containing untagged entries is said to be of - * untagged type. + * An If header either contains untagged IfList entries or tagged IfList entries but not a + * mixture of both. An If header containing tagged entries is said to be of tagged type while an + * If header containing untagged entries is said to be of untagged type. *

      - * An IfList is a list of tokens - words enclosed in < > - * - and etags - words enclosed in [ ]. An IfList matches a - * (token, etag) tuple if all entries in the list match. If an entry in the list - * is prefixed with the word Not (parsed case insensitively) the entry - * must not match the concrete token or etag. + * An IfList is a list of tokens - words enclosed in < > - and etags - words enclosed in [ + * ]. An IfList matches a (token, etag) tuple if all entries in the list match. If an entry in the list is + * prefixed with the word Not (parsed case insensitively) the entry must not match the concrete token or etag. + *

      + * Example: The ifList (<token> [etag]) only matches if the concrete token has the value + * token and the concrete etag has the value etag. On the other hand, the ifList + * (Not <notoken>) matches any token which is not notoken (in this case the concrete + * value of the etag is not taken into consideration). *

      - * Example: The ifList (<token> [etag]) only matches - * if the concret token has the value token and the conrete etag - * has the value etag. On the other hand, the ifList - * (Not <notoken>) matches any token which is not - * notoken (in this case the concrete value of the etag is - * not taken into consideration). - * * This class was contributed by Apache Jackrabbit - * - * @author Felix Meschberger */ public class WebdavIfHeader { private static final Log log = LogFactory.getLog(WebdavIfHeader.class); - private static final StringManager sm = - StringManager.getManager(WebdavIfHeader.class.getPackage().getName()); + private static final StringManager sm = StringManager.getManager(WebdavIfHeader.class.getPackage().getName()); /** * The string representation of the header value @@ -97,22 +88,22 @@ /** * The list of resources present in the If header. */ - private List resources = new ArrayList<>(); + private final List resources = new ArrayList<>(); /** * The list of all positive tokens present in the If header. */ - private List allTokens = new ArrayList<>(); + private final List allTokens = new ArrayList<>(); /** * The list of all NOT tokens present in the If header. */ - private List allNotTokens = new ArrayList<>(); + private final List allNotTokens = new ArrayList<>(); private String uriPrefix; /** - * Create a Untagged IfHeader if the given lock tokens. + * Create an Untagged IfHeader if the given lock tokens. * * @param tokens the tokens * @@ -120,7 +111,7 @@ */ public WebdavIfHeader(String[] tokens) throws IOException { allTokens.addAll(Arrays.asList(tokens)); - StringBuffer b = new StringBuffer(); + StringBuilder b = new StringBuilder(); for (String token : tokens) { b.append("(").append("<"); b.append(token); @@ -131,10 +122,9 @@ } /** - * Parses the If header and creates and internal representation - * which is easy to query. + * Parses the If header and creates and internal representation which is easy to query. * - * @param uriPrefix The uri prefix to use for the absolute href + * @param uriPrefix The uri prefix to use for the absolute href * @param ifHeaderValue the if header * * @throws IOException If the parsing of the IfHeader fails @@ -153,8 +143,7 @@ } /** - * Return the String representation of the If header present on - * the given request or null. + * Return the String representation of the If header present on the given request or null. * * @return If header value as String or null. */ @@ -165,32 +154,28 @@ /** * Returns true if an If header was present in the given request. False otherwise. * - * @return true if an If header was present. + * @return true if an If header was present. */ public boolean hasValue() { return ifHeader != null; } /** - * Tries to match the contents of the If header with the given - * token and etag values with the restriction to only check for the tag. + * Tries to match the contents of the If header with the given token and etag values with the restriction + * to only check for the tag. *

      - * If the If header is of untagged type, the untagged IfList - * is matched against the token and etag given: A match of the token and - * etag is found if at least one of the IfList entries match the - * token and etag tuple. + * If the If header is of untagged type, the untagged IfList is matched against the token and etag + * given: A match of the token and etag is found if at least one of the IfList entries match the token and + * etag tuple. * - * @param tag The tag to identify the IfList to match the token - * and etag against. + * @param tag The tag to identify the IfList to match the token and etag against. * @param tokens The tokens to compare. - * @param etag The ETag value to compare. + * @param etag The ETag value to compare. * - * @return If the If header is of untagged type the result is - * true if any of the IfList entries matches - * the token and etag values. For tagged type If header the - * result is true if either no entry for the given tag - * exists in the If header or if the IfList for the - * given tag matches the token and etag given. + * @return If the If header is of untagged type the result is true if any of the + * IfList entries matches the token and etag values. For tagged type If header the + * result is true if either no entry for the given tag exists in the If header or + * if the IfList for the given tag matches the token and etag given. */ public boolean matches(String tag, List tokens, String etag) { if (ifHeader == null) { @@ -211,42 +196,38 @@ } /** - * @return an iterator over all tokens present in the if header, that were - * not denied by a leading NOT statement. + * @return an iterator over all tokens present in the if header, that were not denied by a leading NOT statement. */ public Iterator getAllTokens() { return allTokens.iterator(); } /** - * @return an iterator over all NOT tokens present in the if header, that - * were explicitly denied. + * @return an iterator over all NOT tokens present in the if header, that were explicitly denied. */ public Iterator getAllNotTokens() { return allNotTokens.iterator(); } /** - * Parse the original header value and build the internal IfHeaderInterface - * object that is easy to query. + * Parse the original header value and build the internal IfHeaderInterface object that is easy to query. */ - private IfHeaderInterface parse() - throws IOException { + private IfHeaderInterface parse() throws IOException { IfHeaderInterface ifHeader; - if (headerValue != null && headerValue.length() > 0) { - StringReader reader = null; - int firstChar = 0; + if (headerValue != null && !headerValue.isEmpty()) { - try { - reader = new StringReader(headerValue); + int firstChar = 0; + try (StringReader reader = new StringReader(headerValue)) { // get the first character to decide - expect '(' or '<' try { reader.mark(1); firstChar = readWhiteSpace(reader); reader.reset(); } catch (IOException ignore) { - // may be thrown according to API but is only thrown by the - // StringReader class if the reader is already closed. + /* + * May be thrown according to API but is only thrown by the StringReader class if the reader is + * already closed. + */ } if (firstChar == '(') { @@ -257,11 +238,6 @@ logIllegalState("If", firstChar, "(<", null); ifHeader = null; } - - } finally { - if (reader != null) { - reader.close(); - } } } else { @@ -273,19 +249,20 @@ return ifHeader; } - //---------- internal IF header parser ------------------------------------- + // ---------- internal IF header parser ------------------------------------- /** - * Parses a tagged type If header. This method implements the - * Tagged production given in the class comment : + * Parses a tagged type If header. This method implements the Tagged production given in the class + * comment : + * *

            *    Tagged = { "<" Word ">" Untagged } .
            * 
      * * @param reader the reader + * * @return the parsed map */ - private IfHeaderMap parseTagged(StringReader reader) - throws IOException { + private IfHeaderMap parseTagged(StringReader reader) throws IOException { IfHeaderMap map = new IfHeaderMap(); while (true) { // read next non-white space @@ -314,8 +291,9 @@ } /** - * Parses an untagged type If header. This method implements the - * Untagged production given in the class comment : + * Parses an untagged type If header. This method implements the Untagged production given in the + * class comment : + * *
            *    Untagged = { "(" IfList ")" } .
            * 
      @@ -324,11 +302,10 @@ * * @return An ArrayList of {@link IfList} entries. */ - private IfHeaderList parseUntagged(StringReader reader) - throws IOException { + private IfHeaderList parseUntagged(StringReader reader) throws IOException { IfHeaderList list = new IfHeaderList(); while (true) { - // read next non white space + // read next non whitespace reader.mark(1); int c = readWhiteSpace(reader); if (c < 0) { @@ -354,8 +331,9 @@ } /** - * Parses an IfList in the If header. This method - * implements the Tagged production given in the class comment : + * Parses an IfList in the If header. This method implements the Tagged production given + * in the class comment : + * *
            *    IfList = { [ "Not" ] ( ("<" Word ">" ) | ( "[" Word "]" ) ) } .
            * 
      @@ -388,7 +366,7 @@ // check whether t or T not = reader.read(); - if (not !='t' && not != 'T') { + if (not != 't' && not != 'T') { logIllegalState("IfList-Not", not, "t", null); break; } @@ -445,8 +423,7 @@ } /** - * Returns the first non-whitespace character from the reader or -1 if - * the end of the reader is encountered. + * Returns the first non-whitespace character from the reader or -1 if the end of the reader is encountered. * * @param reader The Reader to read from * @@ -457,34 +434,31 @@ private int readWhiteSpace(Reader reader) throws IOException { int c = reader.read(); while (c >= 0 && Character.isWhitespace((char) c)) { - c = reader.read(); + c = reader.read(); } return c; } /** - * Reads from the input until the end character is encountered and returns - * the string up to but not including this end character. If the end of input - * is reached before reading the end character null is - * returned. + * Reads from the input until the end character is encountered and returns the string up to but not including this + * end character. If the end of input is reached before reading the end character null is returned. *

      * Note that this method does not support any escaping. * * @param reader The Reader to read from - * @param end The ending character limiting the word. + * @param end The ending character limiting the word. * - * @return The string read up to but not including the ending character or - * null if the end of input is reached before the ending - * character has been read. + * @return The string read up to but not including the ending character or null if the end of input is + * reached before the ending character has been read. * * @throws IOException if a problem occurs during reading. */ private String readWord(Reader reader, char end) throws IOException { - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); // read the word value int c = reader.read(); - for (; c >= 0 && c != end; c=reader.read()) { + for (; c >= 0 && c != end; c = reader.read()) { buf.append((char) c); } @@ -499,22 +473,18 @@ } /** - * Logs an unexpected character with the corresponding state and list of - * expected characters. If the reader parameter is not null, characters - * are read until either the end of the input is reached or any of the - * characters in the expChar string is read. - * - * @param state The name of the current parse state. This method logs this - * name in the message. The intended value would probably be the - * name of the EBNF production during which the error occurs. + * Logs an unexpected character with the corresponding state and list of expected characters. If the reader + * parameter is not null, characters are read until either the end of the input is reached or any of the characters + * in the expChar string is read. + * + * @param state The name of the current parse state. This method logs this name in the message. The intended value + * would probably be the name of the EBNF production during which the error occurs. * @param effChar The effective character read. * @param expChar The list of characters acceptable in the current state. - * @param reader The reader to be caught up to any of the expected - * characters. If null the input is not caught up to - * any of the expected characters (of course ;-). + * @param reader The reader to be caught up to any of the expected characters. If null the input is + * not caught up to any of the expected characters (of course ;-). */ - private void logIllegalState(String state, int effChar, String expChar, - StringReader reader) { + private void logIllegalState(String state, int effChar, String expChar, StringReader reader) { // format the effective character to be logged String effString = (effChar < 0) ? "" : String.valueOf((char) effChar); @@ -526,7 +496,7 @@ if (reader != null && effChar >= 0) { try { if (log.isTraceEnabled()) { - log.trace("logIllegalState: Catch up to any of "+expChar); + log.trace("logIllegalState: Catch up to any of " + expChar); } do { reader.mark(1); @@ -541,20 +511,17 @@ } } - //---------- internal If header structure ---------------------------------- + // ---------- internal If header structure ---------------------------------- /** - * The IfListEntry abstract class is the base class for - * entries in an IfList production. This abstract base class - * provides common functionality to both types of entries, namely tokens - * enclosed in angle brackets (< >) and etags enclosed - * in square brackets ([ ]). + * The IfListEntry abstract class is the base class for entries in an IfList production. This + * abstract base class provides common functionality to both types of entries, namely tokens enclosed in angle + * brackets (< >) and etags enclosed in square brackets ([ ]). */ private abstract static class IfListEntry { /** - * The entry string value - the semantics of this value depends on the - * implementing class. + * The entry string value - the semantics of this value depends on the implementing class. */ protected final String value; @@ -565,11 +532,10 @@ protected String stringValue; /** - * Sets up the final fields of this abstract class. The meaning of - * value parameter depends solely on the implementing class. From the - * point of view of this abstract class, it is simply a string value. + * Sets up the final fields of this abstract class. The meaning of value parameter depends solely on the + * implementing class. From the point of view of this abstract class, it is simply a string value. * - * @param value The string value of this instance + * @param value The string value of this instance * @param positive true if matches are positive */ protected IfListEntry(String value, boolean positive) { @@ -578,47 +544,39 @@ } /** - * Matches the value from the parameter to the internal string value. - * If the parameter and the {@link #value} field match, the method - * returns true for positive matches and false - * for negative matches. + * Matches the value from the parameter to the internal string value. If the parameter and the {@link #value} + * field match, the method returns true for positive matches and false for negative + * matches. *

      - * This helper method can be called by implementations to evaluate the - * concrete match on the correct value parameter. See - * {@link #match(String, String)} for the external API method. - * - * @param value The string value to compare to the {@link #value} - * field. - * - * @return true if the value parameter and the - * {@link #value} field match and the {@link #positive} field is - * true or if the values do not match and the - * {@link #positive} field is false. + * This helper method can be called by implementations to evaluate the concrete match on the correct value + * parameter. See {@link #match(String, String)} for the external API method. + * + * @param value The string value to compare to the {@link #value} field. + * + * @return true if the value parameter and the {@link #value} field match and the {@link #positive} + * field is true or if the values do not match and the {@link #positive} field is + * false. */ protected boolean match(String value) { return positive == this.value.equals(value); } /** - * Matches the entry's value to the the token or etag. Depending on the - * concrete implementation, only one of the parameters may be evaluated - * while the other may be ignored. + * Matches the entry's value to the token or etag. Depending on the concrete implementation, only one of the + * parameters may be evaluated while the other may be ignored. *

      - * Implementing METHODS may call the helper method {@link #match(String)} - * for the actual matching. + * Implementing METHODS may call the helper method {@link #match(String)} for the actual matching. * * @param token The token value to compare - * @param etag The etag value to compare + * @param etag The etag value to compare * - * @return true if the token/etag matches the IfList - * entry. + * @return true if the token/etag matches the IfList entry. */ public abstract boolean match(String token, String etag); /** - * Returns a short type name for the implementation. This method is - * used by the {@link #toString} method to build the string representation - * if the instance. + * Returns a short type name for the implementation. This method is used by the {@link #toString} method to + * build the string representation if the instance. * * @return The type name of the implementation. */ @@ -632,8 +590,8 @@ } /** - * Returns the String representation of this entry. This method uses the - * {@link #getType} to build the string representation. + * Returns the String representation of this entry. This method uses the {@link #getType} to build the string + * representation. * * @return the String representation of this entry. */ @@ -647,15 +605,15 @@ } /** - * The IfListEntryToken extends the {@link IfListEntry} - * abstract class to represent an entry for token matching. + * The IfListEntryToken extends the {@link IfListEntry} abstract class to represent an entry for token + * matching. */ private static class IfListEntryToken extends IfListEntry { /** * Creates a token matching entry. * - * @param token The token value pertinent to this instance. + * @param token The token value pertinent to this instance. * @param positive true if this is a positive match entry. */ IfListEntryToken(String token, boolean positive) { @@ -663,17 +621,13 @@ } /** - * Matches the token parameter to the stored token value and returns - * true if the values match and if the match is positive. - * true is also returned for negative matches if the values - * do not match. + * Matches the token parameter to the stored token value and returns true if the values match and + * if the match is positive. true is also returned for negative matches if the values do not match. * * @param token The token value to compare - * @param etag The etag value to compare, which is ignored in this - * implementation. + * @param etag The etag value to compare, which is ignored in this implementation. * - * @return true if the token matches the IfList - * entry's token value. + * @return true if the token matches the IfList entry's token value. */ @Override public boolean match(String token, String etag) { @@ -681,8 +635,7 @@ } /** - * Returns the type name of this implementation, which is fixed to - * be Token. + * Returns the type name of this implementation, which is fixed to be Token. * * @return The fixed string Token as the type name. */ @@ -693,15 +646,15 @@ } /** - * The IfListEntryToken extends the {@link IfListEntry} - * abstract class to represent an entry for etag matching. + * The IfListEntryToken extends the {@link IfListEntry} abstract class to represent an entry for etag + * matching. */ private static class IfListEntryEtag extends IfListEntry { /** * Creates an etag matching entry. * - * @param etag The etag value pertinent to this instance. + * @param etag The etag value pertinent to this instance. * @param positive true if this is a positive match entry. */ IfListEntryEtag(String etag, boolean positive) { @@ -709,17 +662,13 @@ } /** - * Matches the etag parameter to the stored etag value and returns - * true if the values match and if the match is positive. - * true is also returned for negative matches if the values - * do not match. - * - * @param token The token value to compare, which is ignored in this - * implementation. - * @param etag The etag value to compare + * Matches the etag parameter to the stored etag value and returns true if the values match and if + * the match is positive. true is also returned for negative matches if the values do not match. + * + * @param token The token value to compare, which is ignored in this implementation. + * @param etag The etag value to compare * - * @return true if the etag matches the IfList - * entry's etag value. + * @return true if the etag matches the IfList entry's etag value. */ @Override public boolean match(String token, String etag) { @@ -727,8 +676,7 @@ } /** - * Returns the type name of this implementation, which is fixed to - * be ETag. + * Returns the type name of this implementation, which is fixed to be ETag. * * @return The fixed string ETag as the type name. */ @@ -739,12 +687,11 @@ } /** - * The IfList class extends the ArrayList class - * with the limitation to only support adding {@link IfListEntry} objects - * and adding a {@link #match} method. + * The IfList class extends the ArrayList class with the limitation to only support adding + * {@link IfListEntry} objects and adding a {@link #match} method. *

      - * This class is a container for data contained in the If - * production IfList + * This class is a container for data contained in the If production IfList + * *

            *    IfList = { [ "Not" ] ( ("<" Word ">" ) | ( "[" Word "]" ) ) } .
            * 
      @@ -772,8 +719,7 @@ * @param index the index * @param entry the entry * - * @throws IndexOutOfBoundsException if index is out of range - * (index < 0 || index > size()). + * @throws IndexOutOfBoundsException if index is out of range (index < 0 || index > size()). */ @Override public void add(int index, IfListEntry entry) { @@ -781,21 +727,19 @@ } /** - * Returns true if all {@link IfListEntry} objects in the - * list match the given token and etag. If the list is entry, it is - * considered to match the token and etag. + * Returns true if all {@link IfListEntry} objects in the list match the given token and etag. If + * the list is entry, it is considered to match the token and etag. * * @param tokens The token to compare. - * @param etag The etag to compare. + * @param etag The etag to compare. * - * @return true if all entries in the list match the - * given tag and token. + * @return true if all entries in the list match the given tag and token. */ public boolean match(List tokens, String etag) { if (log.isTraceEnabled()) { log.trace("match: Trying to match token=" + tokens + ", etag=" + etag); } - for (int i=0; i < size(); i++) { + for (int i = 0; i < size(); i++) { IfListEntry ile = get(i); boolean match = false; for (String token : tokens) { @@ -817,35 +761,31 @@ } /** - * The IfHeaderInterface interface abstracts away the difference of - * tagged and untagged If header lists. The single method provided - * by this interface is to check whether a request may be applied to a + * The IfHeaderInterface interface abstracts away the difference of tagged and untagged If + * header lists. The single method provided by this interface is to check whether a request may be applied to a * resource with given token and etag. */ private interface IfHeaderInterface { /** - * Matches the resource, token, and etag against this - * IfHeaderInterface instance. + * Matches the resource, token, and etag against this IfHeaderInterface instance. + * + * @param resource The resource to match this instance against. This must be absolute URI of the resource as + * defined in Section 3 (URI Syntactic Components) of RFC 2396 Uniform Resource Identifiers + * (URI): Generic Syntax. + * @param tokens The resource's lock token to match + * @param etag The resource's etag to match * - * @param resource The resource to match this instance against. This - * must be absolute URI of the resource as defined in Section 3 - * (URI Syntactic Components) of RFC 2396 Uniform Resource - * Identifiers (URI): Generic Syntax. - * @param tokens The resource's lock token to match - * @param etag The resource's etag to match - * - * @return true if the header matches the resource with - * token and etag, which means that the request is applicable - * to the resource according to the If header. + * @return true if the header matches the resource with token and etag, which means that the + * request is applicable to the resource according to the If header. */ boolean matches(String resource, List tokens, String etag); } /** - * The IfHeaderList class implements the {@link IfHeaderInterface} - * interface to support untagged lists of {@link IfList}s. This class - * implements the data container for the production : + * The IfHeaderList class implements the {@link IfHeaderInterface} interface to support untagged lists + * of {@link IfList}s. This class implements the data container for the production : + * *
            *    Untagged = { "(" IfList ")" } .
            * 
      @@ -855,19 +795,17 @@ private static final long serialVersionUID = 1L; /** - * Matches a list of {@link IfList}s against the token and etag. If any of - * the {@link IfList}s matches, the method returns true. - * On the other hand false is only returned if non of the + * Matches a list of {@link IfList}s against the token and etag. If any of the {@link IfList}s matches, the + * method returns true. On the other hand false is only returned if none of the * {@link IfList}s match. * - * @param resource The resource to match, which is ignored by this - * implementation. A value of null is therefor - * acceptable. - * @param tokens The tokens to compare. - * @param etag The ETag value to compare. + * @param resource The resource to match, which is ignored by this implementation. A value of null + * is therefor acceptable. + * @param tokens The tokens to compare. + * @param etag The ETag value to compare. * - * @return True if any of the {@link IfList}s matches the token - * and etag, else false is returned. + * @return True if any of the {@link IfList}s matches the token and etag, else false + * is returned. */ @Override public boolean matches(String resource, List tokens, String etag) { @@ -890,29 +828,27 @@ } /** - * The IfHeaderMap class implements the {@link IfHeaderInterface} - * interface to support tagged lists of {@link IfList}s. This class - * implements the data container for the production : + * The IfHeaderMap class implements the {@link IfHeaderInterface} interface to support tagged lists of + * {@link IfList}s. This class implements the data container for the production : + * *
            *    Tagged = { "<" Word ">" "(" IfList ")" } .
            * 
      */ - private class IfHeaderMap extends HashMap implements IfHeaderInterface { + private class IfHeaderMap extends HashMap implements IfHeaderInterface { private static final long serialVersionUID = 1L; /** - * Matches the token and etag for the given resource. If the resource is - * not mentioned in the header, a match is assumed and true - * is returned in this case. - * - * @param resource The absolute URI of the resource for which to find - * a match. - * @param tokens The tokens to compare. - * @param etag The etag to compare. + * Matches the token and etag for the given resource. If the resource is not mentioned in the header, a match is + * assumed and true is returned in this case. + * + * @param resource The absolute URI of the resource for which to find a match. + * @param tokens The tokens to compare. + * @param etag The etag to compare. * - * @return true if either no entry exists for the resource - * or if the entry for the resource matches the token and etag. + * @return true if either no entry exists for the resource or if the entry for the resource matches + * the token and etag. */ @Override public boolean matches(String resource, List tokens, String etag) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileItem.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileItem.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileItem.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileItem.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,7 +20,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.UncheckedIOException; import java.io.UnsupportedEncodingException; /** @@ -47,18 +46,21 @@ */ public interface FileItem extends FileItemHeadersSupport { - // ------------------------------- Methods from javax.activation.DataSource + /** + * Deletes the underlying storage for a file item, including deleting any + * associated temporary disk file. Although this storage will be deleted + * automatically when the {@code FileItem} instance is garbage + * collected, this method can be used to ensure that this is done at an + * earlier time, thus preserving system resources. + */ + void delete(); /** - * Returns an {@link java.io.InputStream InputStream} that can be - * used to retrieve the contents of the file. - * - * @return An {@link java.io.InputStream InputStream} that can be - * used to retrieve the contents of the file. + * Returns the contents of the file item as an array of bytes. * - * @throws IOException if an error occurs. + * @return The contents of the file item as an array of bytes. */ - InputStream getInputStream() throws IOException; + byte[] get(); /** * Returns the content type passed by the browser or {@code null} if @@ -70,6 +72,25 @@ String getContentType(); /** + * Returns the name of the field in the multipart form corresponding to + * this file item. + * + * @return The name of the form field. + */ + String getFieldName(); + + /** + * Returns an {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @return An {@link java.io.InputStream InputStream} that can be + * used to retrieve the contents of the file. + * + * @throws IOException if an error occurs. + */ + InputStream getInputStream() throws IOException; + + /** * Returns the original file name in the client's file system, as provided by * the browser (or other client software). In most cases, this will be the * base file name, without path information. However, some clients, such as @@ -83,16 +104,16 @@ */ String getName(); - // ------------------------------------------------------- FileItem methods - /** - * Provides a hint as to whether or not the file contents will be read - * from memory. + * Returns an {@link java.io.OutputStream OutputStream} that can + * be used for storing the contents of the file. * - * @return {@code true} if the file contents will be read from memory; - * {@code false} otherwise. + * @return An {@link java.io.OutputStream OutputStream} that can be used + * for storing the contents of the file. + * + * @throws IOException if an error occurs. */ - boolean isInMemory(); + OutputStream getOutputStream() throws IOException; /** * Returns the size of the file item. @@ -102,13 +123,13 @@ long getSize(); /** - * Returns the contents of the file item as an array of bytes. - * - * @return The contents of the file item as an array of bytes. + * Returns the contents of the file item as a String, using the default + * character encoding. This method uses {@link #get()} to retrieve the + * contents of the item. * - * @throws UncheckedIOException if an I/O error occurs + * @return The contents of the item, as a string. */ - byte[] get() throws UncheckedIOException; + String getString(); /** * Returns the contents of the file item as a String, using the specified @@ -121,53 +142,26 @@ * * @throws UnsupportedEncodingException if the requested character * encoding is not available. - * @throws IOException if an I/O error occurs - */ - String getString(String encoding) throws UnsupportedEncodingException, IOException; - - /** - * Returns the contents of the file item as a String, using the default - * character encoding. This method uses {@link #get()} to retrieve the - * contents of the item. - * - * @return The contents of the item, as a string. */ - String getString(); + String getString(String encoding) throws UnsupportedEncodingException; /** - * A convenience method to write an uploaded item to disk. The client code - * is not concerned with whether or not the item is stored in memory, or on - * disk in a temporary location. They just want to write the uploaded item - * to a file. - *

      - * This method is not guaranteed to succeed if called more than once for - * the same item. This allows a particular implementation to use, for - * example, file renaming, where possible, rather than copying all of the - * underlying data, thus gaining a significant performance benefit. - * - * @param file The {@code File} into which the uploaded item should - * be stored. + * Determines whether or not a {@code FileItem} instance represents + * a simple form field. * - * @throws Exception if an error occurs. - */ - void write(File file) throws Exception; - - /** - * Deletes the underlying storage for a file item, including deleting any - * associated temporary disk file. Although this storage will be deleted - * automatically when the {@code FileItem} instance is garbage - * collected, this method can be used to ensure that this is done at an - * earlier time, thus preserving system resources. + * @return {@code true} if the instance represents a simple form + * field; {@code false} if it represents an uploaded file. */ - void delete(); + boolean isFormField(); /** - * Returns the name of the field in the multipart form corresponding to - * this file item. + * Provides a hint as to whether or not the file contents will be read + * from memory. * - * @return The name of the form field. + * @return {@code true} if the file contents will be read from memory; + * {@code false} otherwise. */ - String getFieldName(); + boolean isInMemory(); /** * Sets the field name used to reference this file item. @@ -177,15 +171,6 @@ void setFieldName(String name); /** - * Determines whether or not a {@code FileItem} instance represents - * a simple form field. - * - * @return {@code true} if the instance represents a simple form - * field; {@code false} if it represents an uploaded file. - */ - boolean isFormField(); - - /** * Specifies whether or not a {@code FileItem} instance represents * a simple form field. * @@ -195,14 +180,21 @@ void setFormField(boolean state); /** - * Returns an {@link java.io.OutputStream OutputStream} that can - * be used for storing the contents of the file. + * A convenience method to write an uploaded item to disk. The client code + * is not concerned with whether or not the item is stored in memory, or on + * disk in a temporary location. They just want to write the uploaded item + * to a file. + *

      + * This method is not guaranteed to succeed if called more than once for + * the same item. This allows a particular implementation to use, for + * example, file renaming, where possible, rather than copying all of the + * underlying data, thus gaining a significant performance benefit. * - * @return An {@link java.io.OutputStream OutputStream} that can be used - * for storing the contents of the file. + * @param file The {@code File} into which the uploaded item should + * be stored. * - * @throws IOException if an error occurs. + * @throws Exception if an error occurs. */ - OutputStream getOutputStream() throws IOException; + void write(File file) throws Exception; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileItemHeaders.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileItemHeaders.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileItemHeaders.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileItemHeaders.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,6 +44,17 @@ /** *

      + * Returns an {@code Iterator} of all the header names. + *

      + * + * @return an {@code Iterator} containing all of the names of + * headers provided with this file item. If the item does not have + * any headers return an empty {@code Iterator} + */ + Iterator getHeaderNames(); + + /** + *

      * Returns all the values of the specified item header as an * {@code Iterator} of {@code String} objects. *

      @@ -60,15 +71,4 @@ */ Iterator getHeaders(String name); - /** - *

      - * Returns an {@code Iterator} of all the header names. - *

      - * - * @return an {@code Iterator} containing all of the names of - * headers provided with this file item. If the item does not have - * any headers return an empty {@code Iterator} - */ - Iterator getHeaderNames(); - } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileItemStream.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileItemStream.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileItemStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileItemStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,20 +48,14 @@ */ private static final long serialVersionUID = -7280778431581963740L; - } + /** + * Constructs a new instance. + */ + public ItemSkippedException() { + // empty + } - /** - * Creates an {@link InputStream}, which allows to read the - * items contents. - * - * @return The input stream, from which the items data may - * be read. - * @throws IllegalStateException The method was already invoked on - * this item. It is not possible to recreate the data stream. - * @throws IOException An I/O error occurred. - * @see ItemSkippedException - */ - InputStream openStream() throws IOException; + } /** * Returns the content type passed by the browser or {@code null} if @@ -73,6 +67,14 @@ String getContentType(); /** + * Returns the name of the field in the multipart form corresponding to + * this file item. + * + * @return The name of the form field. + */ + String getFieldName(); + + /** * Returns the original file name in the client's file system, as provided by * the browser (or other client software). In most cases, this will be the * base file name, without path information. However, some clients, such as @@ -83,14 +85,6 @@ String getName(); /** - * Returns the name of the field in the multipart form corresponding to - * this file item. - * - * @return The name of the form field. - */ - String getFieldName(); - - /** * Determines whether or not a {@code FileItem} instance represents * a simple form field. * @@ -99,4 +93,17 @@ */ boolean isFormField(); + /** + * Creates an {@link InputStream}, which allows to read the + * items contents. + * + * @return The input stream, from which the items data may + * be read. + * @throws IllegalStateException The method was already invoked on + * this item. It is not possible to recreate the data stream. + * @throws IOException An I/O error occurred. + * @see ItemSkippedException + */ + InputStream openStream() throws IOException; + } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileUpload.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileUpload.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileUpload.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileUpload.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,15 +33,11 @@ public class FileUpload extends FileUploadBase { - // ----------------------------------------------------------- Data members - /** * The factory to use to create new form items. */ private FileItemFactory fileItemFactory; - // ----------------------------------------------------------- Constructors - /** * Constructs an uninitialized instance of this class. * @@ -52,8 +48,6 @@ public FileUpload() { } - // ----------------------------------------------------- Property accessors - /** * Returns the factory class used when creating file items. * @@ -67,11 +61,11 @@ /** * Sets the factory class to use when creating file items. * - * @param factory The factory class for new file items. + * @param fileItemFactory The factory class for new file items. */ @Override - public void setFileItemFactory(final FileItemFactory factory) { - this.fileItemFactory = factory; + public void setFileItemFactory(final FileItemFactory fileItemFactory) { + this.fileItemFactory = fileItemFactory; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileUploadBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,9 +33,10 @@ /** - *

      High level API for processing file uploads.

      + * High level API for processing file uploads. * - *

      This class handles multiple files per single HTML widget, sent using + *

      + * This class handles multiple files per single HTML widget, sent using * {@code multipart/mixed} encoding type, as specified by * RFC 1867. Use {@link * #parseRequest(RequestContext)} to acquire a list of {@link @@ -48,7 +49,15 @@ */ public abstract class FileUploadBase { - // ----------------------------------------------------- Manifest constants + /** + * Line feed. + */ + private static final char LF = '\n'; + + /** + * Carriage return. + */ + private static final char CR = '\r'; /** * HTTP content type header name. @@ -90,7 +99,12 @@ */ public static final String MULTIPART_MIXED = "multipart/mixed"; - // ----------------------------------------------------------- Data members + /** + * Default per part header size limit in bytes. + * + * @since FileUpload 1.6.0 + */ + public static final int DEFAULT_PART_HEADER_SIZE_MAX = 512; /** * The maximum size permitted for the complete request, as opposed to @@ -111,6 +125,11 @@ private long fileCountMax = -1; /** + * The maximum permitted size of the headers provided with a single part in bytes. + */ + private int partHeaderSizeMax = DEFAULT_PART_HEADER_SIZE_MAX; + + /** * The content encoding to use when reading part headers. */ private String headerEncoding; @@ -120,70 +139,65 @@ */ private ProgressListener listener; - // ----------------------------------------------------- Property accessors - /** - * Returns the factory class used when creating file items. - * - * @return The factory class for new file items. + * Constructs a new instance. */ - public abstract FileItemFactory getFileItemFactory(); - - /** - * Sets the factory class to use when creating file items. - * - * @param factory The factory class for new file items. - */ - public abstract void setFileItemFactory(FileItemFactory factory); - - /** - * Returns the maximum allowed size of a complete request, as opposed - * to {@link #getFileSizeMax()}. - * - * @return The maximum allowed size, in bytes. The default value of - * -1 indicates, that there is no limit. - * - * @see #setSizeMax(long) - * - */ - public long getSizeMax() { - return sizeMax; + public FileUploadBase() { + // empty } /** - * Sets the maximum allowed size of a complete request, as opposed - * to {@link #setFileSizeMax(long)}. - * - * @param sizeMax The maximum allowed size, in bytes. The default value of - * -1 indicates, that there is no limit. + * Gets the boundary from the {@code Content-type} header. * - * @see #getSizeMax() + * @param contentType The value of the content type header from which to + * extract the boundary value. * + * @return The boundary, as a byte array. */ - public void setSizeMax(final long sizeMax) { - this.sizeMax = sizeMax; + public byte[] getBoundary(final String contentType) { + final ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + final Map params = parser.parse(contentType, new char[] {';', ','}); + final String boundaryStr = params.get("boundary"); + + if (boundaryStr == null) { + return null; + } + return boundaryStr.getBytes(StandardCharsets.ISO_8859_1); } /** - * Returns the maximum allowed size of a single uploaded file, - * as opposed to {@link #getSizeMax()}. + * Gets the field name from the {@code Content-disposition} + * header. * - * @see #setFileSizeMax(long) - * @return Maximum size of a single uploaded file. + * @param headers A {@code Map} containing the HTTP request headers. + * + * @return The field name for the current {@code encapsulation}. */ - public long getFileSizeMax() { - return fileSizeMax; + public String getFieldName(final FileItemHeaders headers) { + return getFieldName(headers.getHeader(CONTENT_DISPOSITION)); } /** - * Sets the maximum allowed size of a single uploaded file, - * as opposed to {@link #getSizeMax()}. - * - * @see #getFileSizeMax() - * @param fileSizeMax Maximum size of a single uploaded file. + * Returns the field name, which is given by the content-disposition + * header. + * @param contentDisposition The content-dispositions header value. + * @return The field name. */ - public void setFileSizeMax(final long fileSizeMax) { - this.fileSizeMax = fileSizeMax; + private String getFieldName(final String contentDisposition) { + String fieldName = null; + if (contentDisposition != null && contentDisposition.toLowerCase(Locale.ROOT).startsWith(FORM_DATA)) { + final ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + final Map params = parser.parse(contentDisposition, ';'); + fieldName = params.get("name"); + if (fieldName != null) { + fieldName = fieldName.trim(); + } + } + return fieldName; } /** @@ -196,155 +210,14 @@ } /** - * Sets the maximum number of files allowed per request. - * - * @param fileCountMax The new limit. {@code -1} means no limit. - */ - public void setFileCountMax(final long fileCountMax) { - this.fileCountMax = fileCountMax; - } - - /** - * Retrieves the character encoding used when reading the headers of an - * individual part. When not specified, or {@code null}, the request - * encoding is used. If that is also not specified, or {@code null}, - * the platform default encoding is used. - * - * @return The encoding used to read part headers. - */ - public String getHeaderEncoding() { - return headerEncoding; - } - - /** - * Specifies the character encoding to be used when reading the headers of - * individual part. When not specified, or {@code null}, the request - * encoding is used. If that is also not specified, or {@code null}, - * the platform default encoding is used. - * - * @param encoding The encoding used to read part headers. - */ - public void setHeaderEncoding(final String encoding) { - headerEncoding = encoding; - } - - // --------------------------------------------------------- Public methods - - /** - * Processes an RFC 1867 - * compliant {@code multipart/form-data} stream. - * - * @param ctx The context for the request to be parsed. - * - * @return An iterator to instances of {@code FileItemStream} - * parsed from the request, in the order that they were - * transmitted. - * - * @throws FileUploadException if there are problems reading/parsing - * the request or storing files. - * @throws IOException An I/O error occurred. This may be a network - * error while communicating with the client or a problem while - * storing the uploaded content. - */ - public FileItemIterator getItemIterator(final RequestContext ctx) - throws FileUploadException, IOException { - try { - return new FileItemIteratorImpl(this, ctx); - } catch (final FileUploadIOException e) { - // unwrap encapsulated SizeException - throw (FileUploadException) e.getCause(); - } - } - - /** - * Processes an RFC 1867 - * compliant {@code multipart/form-data} stream. - * - * @param ctx The context for the request to be parsed. - * - * @return A list of {@code FileItem} instances parsed from the - * request, in the order that they were transmitted. - * - * @throws FileUploadException if there are problems reading/parsing - * the request or storing files. - */ - public List parseRequest(final RequestContext ctx) - throws FileUploadException { - final List items = new ArrayList<>(); - boolean successful = false; - try { - final FileItemIterator iter = getItemIterator(ctx); - final FileItemFactory fileItemFactory = Objects.requireNonNull(getFileItemFactory(), - "No FileItemFactory has been set."); - final byte[] buffer = new byte[Streams.DEFAULT_BUFFER_SIZE]; - while (iter.hasNext()) { - if (items.size() == fileCountMax) { - // The next item will exceed the limit. - throw new FileCountLimitExceededException(ATTACHMENT, getFileCountMax()); - } - final FileItemStream item = iter.next(); - // Don't use getName() here to prevent an InvalidFileNameException. - final String fileName = item.getName(); - final FileItem fileItem = fileItemFactory.createItem(item.getFieldName(), item.getContentType(), - item.isFormField(), fileName); - items.add(fileItem); - try { - Streams.copy(item.openStream(), fileItem.getOutputStream(), true, buffer); - } catch (final FileUploadIOException e) { - throw (FileUploadException) e.getCause(); - } catch (final IOException e) { - throw new IOFileUploadException(String.format("Processing of %s request failed. %s", - MULTIPART_FORM_DATA, e.getMessage()), e); - } - final FileItemHeaders fih = item.getHeaders(); - fileItem.setHeaders(fih); - } - successful = true; - return items; - } catch (final FileUploadException e) { - throw e; - } catch (final IOException e) { - throw new FileUploadException(e.getMessage(), e); - } finally { - if (!successful) { - for (final FileItem fileItem : items) { - try { - fileItem.delete(); - } catch (final Exception ignored) { - // ignored TODO perhaps add to tracker delete failure list somehow? - } - } - } - } - } - - // ------------------------------------------------------ Protected methods - - /** - * Retrieves the boundary from the {@code Content-type} header. - * - * @param contentType The value of the content type header from which to - * extract the boundary value. + * Returns the factory class used when creating file items. * - * @return The boundary, as a byte array. + * @return The factory class for new file items. */ - public byte[] getBoundary(final String contentType) { - final ParameterParser parser = new ParameterParser(); - parser.setLowerCaseNames(true); - // Parameter parser can handle null input - final Map params = parser.parse(contentType, new char[] {';', ','}); - final String boundaryStr = params.get("boundary"); - - if (boundaryStr == null) { - return null; - } - final byte[] boundary; - boundary = boundaryStr.getBytes(StandardCharsets.ISO_8859_1); - return boundary; - } + public abstract FileItemFactory getFileItemFactory(); /** - * Retrieves the file name from the {@code Content-disposition} + * Gets the file name from the {@code Content-disposition} * header. * * @param headers The HTTP headers object. @@ -357,18 +230,18 @@ /** * Returns the given content-disposition headers file name. - * @param pContentDisposition The content-disposition headers value. + * @param contentDisposition The content-disposition headers value. * @return The file name */ - private String getFileName(final String pContentDisposition) { + private String getFileName(final String contentDisposition) { String fileName = null; - if (pContentDisposition != null) { - final String cdl = pContentDisposition.toLowerCase(Locale.ENGLISH); + if (contentDisposition != null) { + final String cdl = contentDisposition.toLowerCase(Locale.ROOT); if (cdl.startsWith(FORM_DATA) || cdl.startsWith(ATTACHMENT)) { final ParameterParser parser = new ParameterParser(); parser.setLowerCaseNames(true); // Parameter parser can handle null input - final Map params = parser.parse(pContentDisposition, ';'); + final Map params = parser.parse(contentDisposition, ';'); if (params.containsKey("filename")) { fileName = params.get("filename"); if (fileName != null) { @@ -386,37 +259,52 @@ } /** - * Retrieves the field name from the {@code Content-disposition} - * header. + * Returns the maximum allowed size of a single uploaded file, + * as opposed to {@link #getSizeMax()}. * - * @param headers A {@code Map} containing the HTTP request headers. + * @see #setFileSizeMax(long) + * @return Maximum size of a single uploaded file. + */ + public long getFileSizeMax() { + return fileSizeMax; + } + + /** + * Gets the character encoding used when reading the headers of an + * individual part. When not specified, or {@code null}, the request + * encoding is used. If that is also not specified, or {@code null}, + * the platform default encoding is used. * - * @return The field name for the current {@code encapsulation}. + * @return The encoding used to read part headers. */ - public String getFieldName(final FileItemHeaders headers) { - return getFieldName(headers.getHeader(CONTENT_DISPOSITION)); + public String getHeaderEncoding() { + return headerEncoding; } /** - * Returns the field name, which is given by the content-disposition - * header. - * @param pContentDisposition The content-dispositions header value. - * @return The field jake + * Processes an RFC 1867 + * compliant {@code multipart/form-data} stream. + * + * @param ctx The context for the request to be parsed. + * + * @return An iterator to instances of {@code FileItemStream} + * parsed from the request, in the order that they were + * transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. + * @throws IOException An I/O error occurred. This may be a network + * error while communicating with the client or a problem while + * storing the uploaded content. */ - private String getFieldName(final String pContentDisposition) { - String fieldName = null; - if (pContentDisposition != null - && pContentDisposition.toLowerCase(Locale.ENGLISH).startsWith(FORM_DATA)) { - final ParameterParser parser = new ParameterParser(); - parser.setLowerCaseNames(true); - // Parameter parser can handle null input - final Map params = parser.parse(pContentDisposition, ';'); - fieldName = params.get("name"); - if (fieldName != null) { - fieldName = fieldName.trim(); - } + public FileItemIterator getItemIterator(final RequestContext ctx) + throws FileUploadException, IOException { + try { + return new FileItemIteratorImpl(this, ctx); + } catch (final FileUploadIOException e) { + // unwrap encapsulated SizeException + throw (FileUploadException) e.getCause(); } - return fieldName; } /** @@ -465,6 +353,40 @@ } /** + * Obtain the per part size limit for headers. + * + * @return The maximum size of the headers for a single part in bytes. + * + * @since FileUpload 1.6.0 + */ + public int getPartHeaderSizeMax() { + return partHeaderSizeMax; + } + + /** + * Returns the progress listener. + * + * @return The progress listener, if any, or null. + */ + public ProgressListener getProgressListener() { + return listener; + } + + /** + * Returns the maximum allowed size of a complete request, as opposed + * to {@link #getFileSizeMax()}. + * + * @return The maximum allowed size, in bytes. The default value of + * -1 indicates, that there is no limit. + * + * @see #setSizeMax(long) + * + */ + public long getSizeMax() { + return sizeMax; + } + + /** * Creates a new instance of {@link FileItemHeaders}. * @return The new instance. */ @@ -483,12 +405,12 @@ private int parseEndOfLine(final String headerPart, final int end) { int index = end; for (;;) { - final int offset = headerPart.indexOf('\r', index); + final int offset = headerPart.indexOf(CR, index); if (offset == -1 || offset + 1 >= headerPart.length()) { throw new IllegalStateException( "Expected headers to be terminated by an empty line."); } - if (headerPart.charAt(offset + 1) == '\n') { + if (headerPart.charAt(offset + 1) == LF) { return offset; } index = offset + 1; @@ -512,21 +434,137 @@ } /** - * Returns the progress listener. + * Processes an RFC 1867 + * compliant {@code multipart/form-data} stream. * - * @return The progress listener, if any, or null. + * @param ctx The context for the request to be parsed. + * + * @return A list of {@code FileItem} instances parsed from the + * request, in the order that they were transmitted. + * + * @throws FileUploadException if there are problems reading/parsing + * the request or storing files. */ - public ProgressListener getProgressListener() { - return listener; + public List parseRequest(final RequestContext ctx) throws FileUploadException { + final List items = new ArrayList<>(); + boolean successful = false; + try { + final FileItemIterator iter = getItemIterator(ctx); + final FileItemFactory fileItemFactory = getFileItemFactory(); + Objects.requireNonNull(fileItemFactory, "getFileItemFactory()"); + final byte[] buffer = new byte[Streams.DEFAULT_BUFFER_SIZE]; + while (iter.hasNext()) { + if (items.size() == fileCountMax) { + // The next item will exceed the limit. + throw new FileCountLimitExceededException(ATTACHMENT, getFileCountMax()); + } + final FileItemStream item = iter.next(); + // Don't use getName() here to prevent an InvalidFileNameException. + final String fileName = item.getName(); + final FileItem fileItem = fileItemFactory.createItem(item.getFieldName(), item.getContentType(), + item.isFormField(), fileName); + items.add(fileItem); + try { + Streams.copy(item.openStream(), fileItem.getOutputStream(), true, buffer); + } catch (final FileUploadIOException e) { + throw (FileUploadException) e.getCause(); + } catch (final IOException e) { + throw new IOFileUploadException(String.format("Processing of %s request failed. %s", + MULTIPART_FORM_DATA, e.getMessage()), e); + } + final FileItemHeaders fih = item.getHeaders(); + fileItem.setHeaders(fih); + } + successful = true; + return items; + } catch (final FileUploadException e) { + throw e; + } catch (final IOException e) { + throw new FileUploadException(e.getMessage(), e); + } finally { + if (!successful) { + for (final FileItem fileItem : items) { + try { + fileItem.delete(); + } catch (final Exception ignored) { + // ignored TODO perhaps add to tracker delete failure list somehow? + } + } + } + } + } + + /** + * Sets the maximum number of files allowed per request. + * + * @param fileCountMax The new limit. {@code -1} means no limit. + */ + public void setFileCountMax(final long fileCountMax) { + this.fileCountMax = fileCountMax; + } + + /** + * Sets the factory class to use when creating file items. + * + * @param factory The factory class for new file items. + */ + public abstract void setFileItemFactory(FileItemFactory factory); + + /** + * Sets the maximum allowed size of a single uploaded file, + * as opposed to {@link #getSizeMax()}. + * + * @see #getFileSizeMax() + * @param fileSizeMax Maximum size of a single uploaded file. + */ + public void setFileSizeMax(final long fileSizeMax) { + this.fileSizeMax = fileSizeMax; + } + + /** + * Specifies the character encoding to be used when reading the headers of + * individual part. When not specified, or {@code null}, the request + * encoding is used. If that is also not specified, or {@code null}, + * the platform default encoding is used. + * + * @param encoding The encoding used to read part headers. + */ + public void setHeaderEncoding(final String encoding) { + headerEncoding = encoding; + } + + /** + * Sets the per part size limit for headers. + * + * @param partHeaderSizeMax The maximum size of the headers in bytes. + * + * @since FileUpload 1.6.0 + */ + public void setPartHeaderSizeMax(final int partHeaderSizeMax) { + this.partHeaderSizeMax = partHeaderSizeMax; } /** * Sets the progress listener. * - * @param pListener The progress listener, if any. Defaults to null. + * @param listener The progress listener, if any. Defaults to null. + */ + public void setProgressListener(final ProgressListener listener) { + this.listener = listener; + } + + /** + * Sets the maximum allowed size of a complete request, as opposed + * to {@link #setFileSizeMax(long)}. + * + * @param sizeMax The maximum allowed size, in bytes. The default value of + * -1 indicates, that there is no limit. + * + * @see #getSizeMax() + * */ - public void setProgressListener(final ProgressListener pListener) { - listener = pListener; + public void setSizeMax(final long sizeMax) { + this.sizeMax = sizeMax; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileUploadException.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileUploadException.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/FileUploadException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/FileUploadException.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,20 +36,20 @@ * Constructs a new {@code FileUploadException} with specified detail * message. * - * @param msg the error message. + * @param message the error message. */ - public FileUploadException(final String msg) { - super(msg); + public FileUploadException(final String message) { + super(message); } /** * Creates a new {@code FileUploadException} with the given * detail message and cause. * - * @param msg The exceptions detail message. + * @param message The exceptions detail message. * @param cause The exceptions cause. */ - public FileUploadException(final String msg, final Throwable cause) { - super(msg, cause); + public FileUploadException(final String message, final Throwable cause) { + super(message, cause); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/InvalidFileNameException.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/InvalidFileNameException.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/InvalidFileNameException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/InvalidFileNameException.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,12 +42,12 @@ /** * Creates a new instance. * - * @param pName The file name causing the exception. - * @param pMessage A human readable error message. + * @param name The file name causing the exception. + * @param message A human readable error message. */ - public InvalidFileNameException(final String pName, final String pMessage) { - super(pMessage); - name = pName; + public InvalidFileNameException(final String name, final String message) { + super(message); + this.name = name; } /** diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/MultipartStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,11 +23,12 @@ import java.io.UnsupportedEncodingException; import org.apache.tomcat.util.http.fileupload.impl.FileUploadIOException; +import org.apache.tomcat.util.http.fileupload.impl.SizeLimitExceededException; import org.apache.tomcat.util.http.fileupload.util.Closeable; import org.apache.tomcat.util.http.fileupload.util.Streams; /** - *

      Low level API for processing file uploads. + * Low level API for processing file uploads. * *

      This class can be used to process data streams conforming to MIME * 'multipart' format as defined in @@ -58,7 +59,9 @@ * boundary token of the same length as the parent stream (see {@link * #setBoundary(byte[])}). * - *

      Here is an example of usage of this class.
      + *

      + * Here is an example of usage of this class. + *

      * *
        *   try {
      @@ -74,7 +77,7 @@
        *     }
        *   } catch(MultipartStream.MalformedStreamException e) {
        *     // the stream failed to follow required syntax
      - *   } catch(IOException e) {
      + *   } catch(IOException ioe) {
        *     // a read or write error occurred
        *   }
        * 
      @@ -82,6 +85,316 @@ public class MultipartStream { /** + * Thrown upon attempt of setting an invalid boundary token. + */ + public static class IllegalBoundaryException extends IOException { + + /** + * The UID to use when serializing this instance. + */ + private static final long serialVersionUID = -161533165102632918L; + + /** + * Constructs an {@code IllegalBoundaryException} with no + * detail message. + */ + public IllegalBoundaryException() { + } + + /** + * Constructs an {@code IllegalBoundaryException} with + * the specified detail message. + * + * @param message The detail message. + */ + public IllegalBoundaryException(final String message) { + super(message); + } + + } + + /** + * An {@link InputStream} for reading an items contents. + */ + public class ItemInputStream extends InputStream implements Closeable { + + /** + * Offset when converting negative bytes to integers. + */ + private static final int BYTE_POSITIVE_OFFSET = 256; + + /** + * The number of bytes, which have been read so far. + */ + private long total; + + /** + * The number of bytes, which must be hold, because + * they might be a part of the boundary. + */ + private int pad; + + /** + * The current offset in the buffer. + */ + private int pos; + + /** + * Whether the stream is already closed. + */ + private boolean closed; + + /** + * Creates a new instance. + */ + ItemInputStream() { + findSeparator(); + } + + /** + * Returns the number of bytes, which are currently + * available, without blocking. + * + * @throws IOException An I/O error occurs. + * @return Number of bytes in the buffer. + */ + @Override + public int available() throws IOException { + if (pos == -1) { + return tail - head - pad; + } + return pos - head; + } + + /** + * Closes the input stream. + * + * @throws IOException An I/O error occurred. + */ + @Override + public void close() throws IOException { + close(false); + } + + /** + * Closes the input stream. + * + * @param closeUnderlying Whether to close the underlying stream (hard close) + * @throws IOException An I/O error occurred. + */ + public void close(final boolean closeUnderlying) throws IOException { + if (closed) { + return; + } + if (closeUnderlying) { + closed = true; + input.close(); + } else { + for (;;) { + int available = available(); + if (available == 0) { + available = makeAvailable(); + if (available == 0) { + break; + } + } + if (skip(available) != available) { + // TODO log or throw? + } + } + } + closed = true; + } + + /** + * Called for finding the separator. + */ + private void findSeparator() { + pos = MultipartStream.this.findSeparator(); + if (pos == -1) { + if (tail - head > keepRegion) { + pad = keepRegion; + } else { + pad = tail - head; + } + } + } + + /** + * Returns the number of bytes, which have been read + * by the stream. + * + * @return Number of bytes, which have been read so far. + */ + public long getBytesRead() { + return total; + } + + /** + * Returns, whether the stream is closed. + * + * @return True, if the stream is closed, otherwise false. + */ + @Override + public boolean isClosed() { + return closed; + } + + /** + * Attempts to read more data. + * + * @return Number of available bytes + * @throws IOException An I/O error occurred. + */ + private int makeAvailable() throws IOException { + if (pos != -1) { + return 0; + } + + // Move the data to the beginning of the buffer. + total += tail - head - pad; + System.arraycopy(buffer, tail - pad, buffer, 0, pad); + + // Refill buffer with new data. + head = 0; + tail = pad; + + for (;;) { + final int bytesRead = input.read(buffer, tail, bufSize - tail); + if (bytesRead == -1) { + // The last pad amount is left in the buffer. + // Boundary can't be in there so signal an error + // condition. + final String msg = "Stream ended unexpectedly"; + throw new MalformedStreamException(msg); + } + if (notifier != null) { + notifier.noteBytesRead(bytesRead); + } + tail += bytesRead; + + findSeparator(); + final int av = available(); + + if (av > 0 || pos != -1) { + return av; + } + } + } + + /** + * Returns the next byte in the stream. + * + * @return The next byte in the stream, as a non-negative + * integer, or -1 for EOF. + * @throws IOException An I/O error occurred. + */ + @Override + public int read() throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } + if (available() == 0 && makeAvailable() == 0) { + return -1; + } + ++total; + final int b = buffer[head++]; + if (b >= 0) { + return b; + } + return b + BYTE_POSITIVE_OFFSET; + } + + /** + * Reads bytes into the given buffer. + * + * @param b The destination buffer, where to write to. + * @param off Offset of the first byte in the buffer. + * @param len Maximum number of bytes to read. + * @return Number of bytes, which have been actually read, + * or -1 for EOF. + * @throws IOException An I/O error occurred. + */ + @Override + public int read(final byte[] b, final int off, final int len) throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } + if (len == 0) { + return 0; + } + int res = available(); + if (res == 0) { + res = makeAvailable(); + if (res == 0) { + return -1; + } + } + res = Math.min(res, len); + System.arraycopy(buffer, head, b, off, res); + head += res; + total += res; + return res; + } + + /** + * Skips the given number of bytes. + * + * @param bytes Number of bytes to skip. + * @return The number of bytes, which have actually been + * skipped. + * @throws IOException An I/O error occurred. + */ + @Override + public long skip(final long bytes) throws IOException { + if (closed) { + throw new FileItemStream.ItemSkippedException(); + } + int av = available(); + if (av == 0) { + av = makeAvailable(); + if (av == 0) { + return 0; + } + } + final long res = Math.min(av, bytes); + head += res; + return res; + } + + } + + /** + * Thrown to indicate that the input stream fails to follow the + * required syntax. + */ + public static class MalformedStreamException extends IOException { + + /** + * The UID to use when serializing this instance. + */ + private static final long serialVersionUID = 6466926458059796677L; + + /** + * Constructs a {@code MalformedStreamException} with no + * detail message. + */ + public MalformedStreamException() { + } + + /** + * Constructs an {@code MalformedStreamException} with + * the specified detail message. + * + * @param message The detail message. + */ + public MalformedStreamException(final String message) { + super(message); + } + + } + + /** * Internal class, which is used to invoke the * {@link ProgressListener}. */ @@ -111,24 +424,24 @@ * Creates a new instance with the given listener * and content length. * - * @param pListener The listener to invoke. - * @param pContentLength The expected content length. + * @param listener The listener to invoke. + * @param contentLength The expected content length. */ - public ProgressNotifier(final ProgressListener pListener, final long pContentLength) { - listener = pListener; - contentLength = pContentLength; + public ProgressNotifier(final ProgressListener listener, final long contentLength) { + this.listener = listener; + this.contentLength = contentLength; } /** * Called to indicate that bytes have been read. * - * @param pBytes Number of bytes, which have been read. + * @param count Number of bytes, which have been read. */ - void noteBytesRead(final int pBytes) { + void noteBytesRead(final int count) { /* Indicates, that the given number of bytes have been read from * the input stream. */ - bytesRead += pBytes; + bytesRead += count; notifyListener(); } @@ -151,8 +464,6 @@ } - // ----------------------------------------------------- Manifest constants - /** * The Carriage Return ASCII character value. */ @@ -169,12 +480,6 @@ public static final byte DASH = 0x2D; /** - * The maximum length of {@code header-part} that will be - * processed (10 kilobytes = 10240 bytes.). - */ - public static final int HEADER_PART_SIZE_MAX = 10240; - - /** * The default length of the buffer used for processing a request. */ protected static final int DEFAULT_BUFSIZE = 4096; @@ -186,13 +491,13 @@ protected static final byte[] HEADER_SEPARATOR = {CR, LF, CR, LF}; /** - * A byte sequence that that follows a delimiter that will be + * A byte sequence that follows a delimiter that will be * followed by an encapsulation ({@code CRLF}). */ protected static final byte[] FIELD_SEPARATOR = {CR, LF}; /** - * A byte sequence that that follows a delimiter of the last + * A byte sequence that follows a delimiter of the last * encapsulation in the stream ({@code --}). */ protected static final byte[] STREAM_TERMINATOR = {DASH, DASH}; @@ -202,7 +507,27 @@ */ protected static final byte[] BOUNDARY_PREFIX = {CR, LF, DASH, DASH}; - // ----------------------------------------------------------- Data members + /** + * Compares {@code count} first bytes in the arrays + * {@code a} and {@code b}. + * + * @param a The first array to compare. + * @param b The second array to compare. + * @param count How many bytes should be compared. + * + * @return {@code true} if {@code count} first bytes in arrays + * {@code a} and {@code b} are equal. + */ + public static boolean arrayequals(final byte[] a, + final byte[] b, + final int count) { + for (int i = 0; i < count; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } /** * The input stream from which data is read. @@ -264,31 +589,28 @@ */ private final ProgressNotifier notifier; - // ----------------------------------------------------------- Constructors + /** + * The maximum permitted size of the headers provided with a single part in bytes. + */ + private int partHeaderSizeMax = FileUploadBase.DEFAULT_PART_HEADER_SIZE_MAX; /** - *

      Constructs a {@code MultipartStream} with a custom size buffer. - * - *

      Note that the buffer must be at least big enough to contain the - * boundary string, plus 4 characters for CR/LF and double dash, plus at - * least one byte of data. Too small a buffer size setting will degrade - * performance. + * Constructs a {@code MultipartStream} with a custom size buffer. + *

      + * Note that the buffer must be at least big enough to contain the boundary string, plus 4 characters for CR/LF and + * double dash, plus at least one byte of data. Too small a buffer size setting will degrade performance. * * @param input The {@code InputStream} to serve as a data source. - * @param boundary The token used for dividing the stream into - * {@code encapsulations}. + * @param boundary The token used for dividing the stream into {@code encapsulations}. * @param bufSize The size of the buffer to be used, in bytes. - * @param pNotifier The notifier, which is used for calling the - * progress listener, if any. + * @param notifier The notifier, which is used for calling the progress listener, if any. * * @throws IllegalArgumentException If the buffer size is too small * * @since FileUpload 1.3.1 */ - public MultipartStream(final InputStream input, - final byte[] boundary, - final int bufSize, - final ProgressNotifier pNotifier) { + public MultipartStream(final InputStream input, final byte[] boundary, final int bufSize, + final ProgressNotifier notifier) { if (boundary == null) { throw new IllegalArgumentException("boundary may not be null"); @@ -297,50 +619,103 @@ // body-data tokens. this.boundaryLength = boundary.length + BOUNDARY_PREFIX.length; if (bufSize < this.boundaryLength + 1) { - throw new IllegalArgumentException( - "The buffer size specified for the MultipartStream is too small"); + throw new IllegalArgumentException("The buffer size specified for the MultipartStream is too small"); } - this.input = input; this.bufSize = Math.max(bufSize, boundaryLength * 2); this.buffer = new byte[this.bufSize]; - this.notifier = pNotifier; - + this.notifier = notifier; this.boundary = new byte[this.boundaryLength]; this.boundaryTable = new int[this.boundaryLength + 1]; this.keepRegion = this.boundary.length; - - System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, - BOUNDARY_PREFIX.length); - System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, - boundary.length); + System.arraycopy(BOUNDARY_PREFIX, 0, this.boundary, 0, BOUNDARY_PREFIX.length); + System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length); computeBoundaryTable(); - head = 0; tail = 0; } /** - *

      Constructs a {@code MultipartStream} with a default size buffer. + * Constructs a {@code MultipartStream} with a default size buffer. * * @param input The {@code InputStream} to serve as a data source. * @param boundary The token used for dividing the stream into * {@code encapsulations}. - * @param pNotifier An object for calling the progress listener, if any. + * @param notifier An object for calling the progress listener, if any. * * * @see #MultipartStream(InputStream, byte[], int, ProgressNotifier) */ - public MultipartStream(final InputStream input, - final byte[] boundary, - final ProgressNotifier pNotifier) { - this(input, boundary, DEFAULT_BUFSIZE, pNotifier); + public MultipartStream(final InputStream input, final byte[] boundary, final ProgressNotifier notifier) { + this(input, boundary, DEFAULT_BUFSIZE, notifier); } - // --------------------------------------------------------- Public methods + /** + * Compute the table used for Knuth-Morris-Pratt search algorithm. + */ + private void computeBoundaryTable() { + int position = 2; + int candidate = 0; + + boundaryTable[0] = -1; + boundaryTable[1] = 0; + + while (position <= boundaryLength) { + if (boundary[position - 1] == boundary[candidate]) { + boundaryTable[position] = candidate + 1; + candidate++; + position++; + } else if (candidate > 0) { + candidate = boundaryTable[candidate]; + } else { + boundaryTable[position] = 0; + position++; + } + } + } + + /** + * Reads {@code body-data} from the current {@code encapsulation} and discards it. + *

      + * Use this method to skip encapsulations you don't need or don't understand. + * + * @return The amount of data discarded. + * + * @throws MalformedStreamException if the stream ends unexpectedly. + * @throws IOException if an i/o error occurs. + */ + public int discardBodyData() throws MalformedStreamException, IOException { + return readBodyData(null); + } + + /** + * Searches for the {@code boundary} in the {@code buffer} + * region delimited by {@code head} and {@code tail}. + * + * @return The position of the boundary found, counting from the + * beginning of the {@code buffer}, or {@code -1} if + * not found. + */ + protected int findSeparator() { + + int bufferPos = head; + int tablePos = 0; + + while (bufferPos < tail) { + while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos]) { + tablePos = boundaryTable[tablePos]; + } + bufferPos++; + tablePos++; + if (tablePos == boundaryLength) { + return bufferPos - boundaryLength; + } + } + return -1; + } /** - * Retrieves the character encoding used when reading the headers of an + * Gets the character encoding used when reading the headers of an * individual part. When not specified, or {@code null}, the platform * default encoding is used. * @@ -351,39 +726,42 @@ } /** - * Specifies the character encoding to be used when reading the headers of - * individual parts. When not specified, or {@code null}, the platform - * default encoding is used. + * Obtain the per part size limit for headers. * - * @param encoding The encoding used to read part headers. + * @return The maximum size of the headers for a single part in bytes. + * + * @since 1.6.0 */ - public void setHeaderEncoding(final String encoding) { - headerEncoding = encoding; + public int getPartHeaderSizeMax() { + return partHeaderSizeMax; } /** - * Reads a byte from the {@code buffer}, and refills it as - * necessary. + * Creates a new {@link ItemInputStream}. + * @return A new instance of {@link ItemInputStream}. + */ + public ItemInputStream newInputStream() { + return new ItemInputStream(); + } + + /** + * Reads {@code body-data} from the current {@code encapsulation} and writes its contents into the output + * {@code Stream}. + *

      + * Arbitrary large amounts of data can be processed by this method using a constant size buffer. (see {@link + * #MultipartStream(InputStream,byte[],int, MultipartStream.ProgressNotifier) constructor}). * - * @return The next byte from the input stream. + * @param output The {@code Stream} to write data into. May be null, in which case this method is equivalent to + * {@link #discardBodyData()}. * - * @throws IOException if there is no more data available. + * @return the amount of data written. + * + * @throws MalformedStreamException if the stream ends unexpectedly. + * @throws IOException if an i/o error occurs. */ - public byte readByte() throws IOException { - // Buffer depleted ? - if (head == tail) { - head = 0; - // Refill. - tail = input.read(buffer, head, bufSize); - if (tail == -1) { - // No more data available. - throw new IOException("No more data is available"); - } - if (notifier != null) { - notifier.noteBytesRead(tail); - } - } - return buffer[head++]; + public int readBodyData(final OutputStream output) + throws MalformedStreamException, IOException { + return (int) Streams.copy(newInputStream(), output, false); // Streams.copy closes the input stream } /** @@ -434,69 +812,35 @@ } /** - *

      Changes the boundary token used for partitioning the stream. - * - *

      This method allows single pass processing of nested multipart - * streams. - * - *

      The boundary token of the nested stream is {@code required} - * to be of the same length as the boundary token in parent stream. - * - *

      Restoring the parent stream boundary token after processing of a - * nested stream is left to the application. + * Reads a byte from the {@code buffer}, and refills it as + * necessary. * - * @param boundary The boundary to be used for parsing of the nested - * stream. + * @return The next byte from the input stream. * - * @throws IllegalBoundaryException if the {@code boundary} - * has a different length than the one - * being currently parsed. - */ - public void setBoundary(final byte[] boundary) - throws IllegalBoundaryException { - if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) { - throw new IllegalBoundaryException( - "The length of a boundary token cannot be changed"); - } - System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, - boundary.length); - computeBoundaryTable(); - } - - /** - * Compute the table used for Knuth-Morris-Pratt search algorithm. + * @throws IOException if there is no more data available. */ - private void computeBoundaryTable() { - int position = 2; - int candidate = 0; - - boundaryTable[0] = -1; - boundaryTable[1] = 0; - - while (position <= boundaryLength) { - if (boundary[position - 1] == boundary[candidate]) { - boundaryTable[position] = candidate + 1; - candidate++; - position++; - } else if (candidate > 0) { - candidate = boundaryTable[candidate]; - } else { - boundaryTable[position] = 0; - position++; + public byte readByte() throws IOException { + // Buffer depleted ? + if (head == tail) { + head = 0; + // Refill. + tail = input.read(buffer, head, bufSize); + if (tail == -1) { + // No more data available. + throw new IOException("No more data is available"); + } + if (notifier != null) { + notifier.noteBytesRead(tail); } } + return buffer[head++]; } /** - *

      Reads the {@code header-part} of the current - * {@code encapsulation}. - * - *

      Headers are returned verbatim to the input stream, including the - * trailing {@code CRLF} marker. Parsing is left to the - * application. - * - *

      TODO allow limiting maximum header size to - * protect against abuse. + * Reads the {@code header-part} of the current {@code encapsulation}. + *

      + * Headers are returned verbatim to the input stream, including the trailing {@code CRLF} marker. Parsing is left to + * the application. * * @return The {@code header-part} of the current encapsulation. * @@ -518,10 +862,11 @@ } catch (final IOException e) { throw new MalformedStreamException("Stream ended unexpectedly"); } - if (++size > HEADER_PART_SIZE_MAX) { - throw new MalformedStreamException(String.format( - "Header section has more than %s bytes (maybe it is not properly terminated)", - Integer.valueOf(HEADER_PART_SIZE_MAX))); + size++; + if (getPartHeaderSizeMax() != -1 && size > getPartHeaderSizeMax()) { + throw new FileUploadIOException(new SizeLimitExceededException( + String.format("Header section has more than %s bytes (maybe it is not properly terminated)", Integer.valueOf(getPartHeaderSizeMax())), + size, getPartHeaderSizeMax())); } if (b == HEADER_SEPARATOR[i]) { i++; @@ -530,7 +875,6 @@ } baos.write(b); } - String headers; if (headerEncoding != null) { try { @@ -543,56 +887,52 @@ } else { headers = baos.toString(); } - return headers; } /** - *

      Reads {@code body-data} from the current - * {@code encapsulation} and writes its contents into the - * output {@code Stream}. - * - *

      Arbitrary large amounts of data can be processed by this - * method using a constant size buffer. (see {@link - * #MultipartStream(InputStream,byte[],int, - * MultipartStream.ProgressNotifier) constructor}). - * - * @param output The {@code Stream} to write data into. May - * be null, in which case this method is equivalent - * to {@link #discardBodyData()}. + * Changes the boundary token used for partitioning the stream. + *

      + * This method allows single pass processing of nested multipart streams. + *

      + * The boundary token of the nested stream is {@code required} to be of the same length as the boundary token in + * parent stream. + *

      + * Restoring the parent stream boundary token after processing of a nested stream is left to the application. * - * @return the amount of data written. + * @param boundary The boundary to be used for parsing of the nested stream. * - * @throws MalformedStreamException if the stream ends unexpectedly. - * @throws IOException if an i/o error occurs. + * @throws IllegalBoundaryException if the {@code boundary} has a different length than the one being currently + * parsed. */ - public int readBodyData(final OutputStream output) - throws MalformedStreamException, IOException { - return (int) Streams.copy(newInputStream(), output, false); // N.B. Streams.copy closes the input stream + public void setBoundary(final byte[] boundary) throws IllegalBoundaryException { + if (boundary.length != boundaryLength - BOUNDARY_PREFIX.length) { + throw new IllegalBoundaryException("The length of a boundary token cannot be changed"); + } + System.arraycopy(boundary, 0, this.boundary, BOUNDARY_PREFIX.length, boundary.length); + computeBoundaryTable(); } /** - * Creates a new {@link ItemInputStream}. - * @return A new instance of {@link ItemInputStream}. + * Specifies the character encoding to be used when reading the headers of + * individual parts. When not specified, or {@code null}, the platform + * default encoding is used. + * + * @param encoding The encoding used to read part headers. */ - public ItemInputStream newInputStream() { - return new ItemInputStream(); + public void setHeaderEncoding(final String encoding) { + headerEncoding = encoding; } /** - *

      Reads {@code body-data} from the current - * {@code encapsulation} and discards it. + * Sets the per part size limit for headers. * - *

      Use this method to skip encapsulations you don't need or don't - * understand. + * @param partHeaderSizeMax The maximum size of the headers in bytes. * - * @return The amount of data discarded. - * - * @throws MalformedStreamException if the stream ends unexpectedly. - * @throws IOException if an i/o error occurs. + * @since 1.6.0 */ - public int discardBodyData() throws MalformedStreamException, IOException { - return readBodyData(null); + public void setPartHeaderSizeMax(final int partHeaderSizeMax) { + this.partHeaderSizeMax = partHeaderSizeMax; } /** @@ -627,361 +967,4 @@ } } - /** - * Compares {@code count} first bytes in the arrays - * {@code a} and {@code b}. - * - * @param a The first array to compare. - * @param b The second array to compare. - * @param count How many bytes should be compared. - * - * @return {@code true} if {@code count} first bytes in arrays - * {@code a} and {@code b} are equal. - */ - public static boolean arrayequals(final byte[] a, - final byte[] b, - final int count) { - for (int i = 0; i < count; i++) { - if (a[i] != b[i]) { - return false; - } - } - return true; - } - - /** - * Searches for the {@code boundary} in the {@code buffer} - * region delimited by {@code head} and {@code tail}. - * - * @return The position of the boundary found, counting from the - * beginning of the {@code buffer}, or {@code -1} if - * not found. - */ - protected int findSeparator() { - - int bufferPos = this.head; - int tablePos = 0; - - while (bufferPos < this.tail) { - while (tablePos >= 0 && buffer[bufferPos] != boundary[tablePos]) { - tablePos = boundaryTable[tablePos]; - } - bufferPos++; - tablePos++; - if (tablePos == boundaryLength) { - return bufferPos - boundaryLength; - } - } - return -1; - } - - /** - * Thrown to indicate that the input stream fails to follow the - * required syntax. - */ - public static class MalformedStreamException extends IOException { - - /** - * The UID to use when serializing this instance. - */ - private static final long serialVersionUID = 6466926458059796677L; - - /** - * Constructs a {@code MalformedStreamException} with no - * detail message. - */ - public MalformedStreamException() { - } - - /** - * Constructs an {@code MalformedStreamException} with - * the specified detail message. - * - * @param message The detail message. - */ - public MalformedStreamException(final String message) { - super(message); - } - - } - - /** - * Thrown upon attempt of setting an invalid boundary token. - */ - public static class IllegalBoundaryException extends IOException { - - /** - * The UID to use when serializing this instance. - */ - private static final long serialVersionUID = -161533165102632918L; - - /** - * Constructs an {@code IllegalBoundaryException} with no - * detail message. - */ - public IllegalBoundaryException() { - } - - /** - * Constructs an {@code IllegalBoundaryException} with - * the specified detail message. - * - * @param message The detail message. - */ - public IllegalBoundaryException(final String message) { - super(message); - } - - } - - /** - * An {@link InputStream} for reading an items contents. - */ - public class ItemInputStream extends InputStream implements Closeable { - - /** - * The number of bytes, which have been read so far. - */ - private long total; - - /** - * The number of bytes, which must be hold, because - * they might be a part of the boundary. - */ - private int pad; - - /** - * The current offset in the buffer. - */ - private int pos; - - /** - * Whether the stream is already closed. - */ - private boolean closed; - - /** - * Creates a new instance. - */ - ItemInputStream() { - findSeparator(); - } - - /** - * Called for finding the separator. - */ - private void findSeparator() { - pos = MultipartStream.this.findSeparator(); - if (pos == -1) { - if (tail - head > keepRegion) { - pad = keepRegion; - } else { - pad = tail - head; - } - } - } - - /** - * Returns the number of bytes, which have been read - * by the stream. - * - * @return Number of bytes, which have been read so far. - */ - public long getBytesRead() { - return total; - } - - /** - * Returns the number of bytes, which are currently - * available, without blocking. - * - * @throws IOException An I/O error occurs. - * @return Number of bytes in the buffer. - */ - @Override - public int available() throws IOException { - if (pos == -1) { - return tail - head - pad; - } - return pos - head; - } - - /** - * Offset when converting negative bytes to integers. - */ - private static final int BYTE_POSITIVE_OFFSET = 256; - - /** - * Returns the next byte in the stream. - * - * @return The next byte in the stream, as a non-negative - * integer, or -1 for EOF. - * @throws IOException An I/O error occurred. - */ - @Override - public int read() throws IOException { - if (closed) { - throw new FileItemStream.ItemSkippedException(); - } - if (available() == 0 && makeAvailable() == 0) { - return -1; - } - ++total; - final int b = buffer[head++]; - if (b >= 0) { - return b; - } - return b + BYTE_POSITIVE_OFFSET; - } - - /** - * Reads bytes into the given buffer. - * - * @param b The destination buffer, where to write to. - * @param off Offset of the first byte in the buffer. - * @param len Maximum number of bytes to read. - * @return Number of bytes, which have been actually read, - * or -1 for EOF. - * @throws IOException An I/O error occurred. - */ - @Override - public int read(final byte[] b, final int off, final int len) throws IOException { - if (closed) { - throw new FileItemStream.ItemSkippedException(); - } - if (len == 0) { - return 0; - } - int res = available(); - if (res == 0) { - res = makeAvailable(); - if (res == 0) { - return -1; - } - } - res = Math.min(res, len); - System.arraycopy(buffer, head, b, off, res); - head += res; - total += res; - return res; - } - - /** - * Closes the input stream. - * - * @throws IOException An I/O error occurred. - */ - @Override - public void close() throws IOException { - close(false); - } - - /** - * Closes the input stream. - * - * @param pCloseUnderlying Whether to close the underlying stream - * (hard close) - * @throws IOException An I/O error occurred. - */ - public void close(final boolean pCloseUnderlying) throws IOException { - if (closed) { - return; - } - if (pCloseUnderlying) { - closed = true; - input.close(); - } else { - for (;;) { - int av = available(); - if (av == 0) { - av = makeAvailable(); - if (av == 0) { - break; - } - } - skip(av); - } - } - closed = true; - } - - /** - * Skips the given number of bytes. - * - * @param bytes Number of bytes to skip. - * @return The number of bytes, which have actually been - * skipped. - * @throws IOException An I/O error occurred. - */ - @Override - public long skip(final long bytes) throws IOException { - if (closed) { - throw new FileItemStream.ItemSkippedException(); - } - int av = available(); - if (av == 0) { - av = makeAvailable(); - if (av == 0) { - return 0; - } - } - final long res = Math.min(av, bytes); - head += res; - return res; - } - - /** - * Attempts to read more data. - * - * @return Number of available bytes - * @throws IOException An I/O error occurred. - */ - private int makeAvailable() throws IOException { - if (pos != -1) { - return 0; - } - - // Move the data to the beginning of the buffer. - total += tail - head - pad; - System.arraycopy(buffer, tail - pad, buffer, 0, pad); - - // Refill buffer with new data. - head = 0; - tail = pad; - - for (;;) { - final int bytesRead = input.read(buffer, tail, bufSize - tail); - if (bytesRead == -1) { - // The last pad amount is left in the buffer. - // Boundary can't be in there so signal an error - // condition. - final String msg = "Stream ended unexpectedly"; - throw new MalformedStreamException(msg); - } - if (notifier != null) { - notifier.noteBytesRead(bytesRead); - } - tail += bytesRead; - - findSeparator(); - final int av = available(); - - if (av > 0 || pos != -1) { - return av; - } - } - } - - /** - * Returns, whether the stream is closed. - * - * @return True, if the stream is closed, otherwise false. - */ - @Override - public boolean isClosed() { - return closed; - } - - } - } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/ParameterParser.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/ParameterParser.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/ParameterParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/ParameterParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,7 +22,6 @@ import java.util.Map; import org.apache.tomcat.util.http.fileupload.util.mime.MimeUtility; -import org.apache.tomcat.util.http.fileupload.util.mime.RFC2231Utility; /** * A simple parser intended to parse sequences of name/value pairs. @@ -40,32 +39,32 @@ /** * String to be parsed. */ - private char[] chars = null; + private char[] chars; /** * Current position in the string. */ - private int pos = 0; + private int pos; /** * Maximum position in the string. */ - private int len = 0; + private int len; /** * Start of a token. */ - private int i1 = 0; + private int i1; /** * End of a token. */ - private int i2 = 0; + private int i2; /** * Whether names stored in the map should be converted to lower case. */ - private boolean lowerCaseNames = false; + private boolean lowerCaseNames; /** * Default ParameterParser constructor. @@ -74,16 +73,6 @@ } /** - * Are there any characters left to parse? - * - * @return {@code true} if there are unparsed characters, - * {@code false} otherwise. - */ - private boolean hasChar() { - return this.pos < this.len; - } - - /** * A helper method to process the parsed token. This method removes * leading and trailing blanks as well as enclosing quotation marks, * when necessary. @@ -94,18 +83,18 @@ */ private String getToken(final boolean quoted) { // Trim leading white spaces - while ((i1 < i2) && (Character.isWhitespace(chars[i1]))) { + while (i1 < i2 && Character.isWhitespace(chars[i1])) { i1++; } // Trim trailing white spaces - while ((i2 > i1) && (Character.isWhitespace(chars[i2 - 1]))) { + while (i2 > i1 && Character.isWhitespace(chars[i2 - 1])) { i2--; } // Strip away quotation marks if necessary if (quoted - && ((i2 - i1) >= 2) - && (chars[i1] == '"') - && (chars[i2 - 1] == '"')) { + && i2 - i1 >= 2 + && chars[i1] == '"' + && chars[i2 - 1] == '"') { i1++; i2--; } @@ -117,6 +106,28 @@ } /** + * Are there any characters left to parse? + * + * @return {@code true} if there are unparsed characters, + * {@code false} otherwise. + */ + private boolean hasChar() { + return pos < len; + } + + /** + * Returns {@code true} if parameter names are to be converted to lower + * case when name/value pairs are parsed. + * + * @return {@code true} if parameter names are to be + * converted to lower case when name/value pairs are parsed. + * Otherwise returns {@code false} + */ + public boolean isLowerCaseNames() { + return lowerCaseNames; + } + + /** * Tests if the given character is present in the array of characters. * * @param ch the character to test for presence in the array of characters @@ -137,83 +148,83 @@ } /** - * Parses out a token until any of the given terminators - * is encountered. + * Extracts a map of name/value pairs from the given array of + * characters. Names are expected to be unique. * - * @param terminators the array of terminating characters. Any of these - * characters when encountered signify the end of the token + * @param charArray the array of characters that contains a sequence of + * name/value pairs + * @param separator the name/value pairs separator * - * @return the token + * @return a map of name/value pairs */ - private String parseToken(final char[] terminators) { - char ch; - i1 = pos; - i2 = pos; - while (hasChar()) { - ch = chars[pos]; - if (isOneOf(ch, terminators)) { - break; - } - i2++; - pos++; + public Map parse(final char[] charArray, final char separator) { + if (charArray == null) { + return new HashMap<>(); } - return getToken(false); + return parse(charArray, 0, charArray.length, separator); } /** - * Parses out a token until any of the given terminators - * is encountered outside the quotation marks. + * Extracts a map of name/value pairs from the given array of characters. Names are expected to be unique. * - * @param terminators the array of terminating characters. Any of these - * characters when encountered outside the quotation marks signify the end - * of the token + * @param charArray the array of characters that contains a sequence of name/value pairs + * @param offset the initial offset. + * @param length the length. + * @param separator the name/value pairs separator * - * @return the token + * @return a map of name/value pairs */ - private String parseQuotedToken(final char[] terminators) { - char ch; - i1 = pos; - i2 = pos; - boolean quoted = false; - boolean charEscaped = false; + public Map parse(final char[] charArray, final int offset, final int length, final char separator) { + if (charArray == null) { + return new HashMap<>(); + } + final HashMap params = new HashMap<>(); + chars = charArray.clone(); + pos = offset; + len = length; while (hasChar()) { - ch = chars[pos]; - if (!quoted && isOneOf(ch, terminators)) { - break; + String paramName = parseToken(new char[] { '=', separator }); + String paramValue = null; + if (hasChar() && charArray[pos] == '=') { + pos++; // skip '=' + paramValue = parseQuotedToken(new char[] { separator }); + if (paramValue != null) { + try { + paramValue = RFC2231Utility.hasEncodedValue(paramName) ? RFC2231Utility.decodeText(paramValue) + : MimeUtility.decodeText(paramValue); + } catch (final UnsupportedEncodingException e) { + // let's keep the original value in this case + } + } } - if (!charEscaped && ch == '"') { - quoted = !quoted; + if (hasChar() && charArray[pos] == separator) { + pos++; // skip separator + } + if (paramName != null && !paramName.isEmpty()) { + paramName = RFC2231Utility.stripDelimiter(paramName); + if (lowerCaseNames) { + paramName = paramName.toLowerCase(Locale.ROOT); + } + params.put(paramName, paramValue); } - charEscaped = (!charEscaped && ch == '\\'); - i2++; - pos++; - } - return getToken(true); + return params; } /** - * Returns {@code true} if parameter names are to be converted to lower - * case when name/value pairs are parsed. + * Extracts a map of name/value pairs from the given string. Names are + * expected to be unique. * - * @return {@code true} if parameter names are to be - * converted to lower case when name/value pairs are parsed. - * Otherwise returns {@code false} - */ - public boolean isLowerCaseNames() { - return this.lowerCaseNames; - } - - /** - * Sets the flag if parameter names are to be converted to lower case when - * name/value pairs are parsed. + * @param str the string that contains a sequence of name/value pairs + * @param separator the name/value pairs separator * - * @param b {@code true} if parameter names are to be - * converted to lower case when name/value pairs are parsed. - * {@code false} otherwise. + * @return a map of name/value pairs */ - public void setLowerCaseNames(final boolean b) { - this.lowerCaseNames = b; + public Map parse(final String str, final char separator) { + if (str == null) { + return new HashMap<>(); + } + return parse(str.toCharArray(), separator); } /** @@ -245,96 +256,71 @@ } /** - * Extracts a map of name/value pairs from the given string. Names are - * expected to be unique. + * Parses out a token until any of the given terminators + * is encountered outside the quotation marks. * - * @param str the string that contains a sequence of name/value pairs - * @param separator the name/value pairs separator + * @param terminators the array of terminating characters. Any of these + * characters when encountered outside the quotation marks signify the end + * of the token * - * @return a map of name/value pairs + * @return the token */ - public Map parse(final String str, final char separator) { - if (str == null) { - return new HashMap<>(); + private String parseQuotedToken(final char[] terminators) { + char ch; + i1 = pos; + i2 = pos; + boolean quoted = false; + boolean charEscaped = false; + while (hasChar()) { + ch = chars[pos]; + if (!quoted && isOneOf(ch, terminators)) { + break; + } + if (!charEscaped && ch == '"') { + quoted = !quoted; + } + charEscaped = !charEscaped && ch == '\\'; + i2++; + pos++; + } - return parse(str.toCharArray(), separator); + return getToken(true); } /** - * Extracts a map of name/value pairs from the given array of - * characters. Names are expected to be unique. + * Parses out a token until any of the given terminators + * is encountered. * - * @param charArray the array of characters that contains a sequence of - * name/value pairs - * @param separator the name/value pairs separator + * @param terminators the array of terminating characters. Any of these + * characters when encountered signify the end of the token * - * @return a map of name/value pairs + * @return the token */ - public Map parse(final char[] charArray, final char separator) { - if (charArray == null) { - return new HashMap<>(); + private String parseToken(final char[] terminators) { + char ch; + i1 = pos; + i2 = pos; + while (hasChar()) { + ch = chars[pos]; + if (isOneOf(ch, terminators)) { + break; + } + i2++; + pos++; } - return parse(charArray, 0, charArray.length, separator); + return getToken(false); } /** - * Extracts a map of name/value pairs from the given array of - * characters. Names are expected to be unique. - * - * @param charArray the array of characters that contains a sequence of - * name/value pairs - * @param offset - the initial offset. - * @param length - the length. - * @param separator the name/value pairs separator + * Sets the flag if parameter names are to be converted to lower case when + * name/value pairs are parsed. * - * @return a map of name/value pairs + * @param b {@code true} if parameter names are to be + * converted to lower case when name/value pairs are parsed. + * {@code false} otherwise. */ - public Map parse( - final char[] charArray, - final int offset, - final int length, - final char separator) { - - if (charArray == null) { - return new HashMap<>(); - } - final HashMap params = new HashMap<>(); - this.chars = charArray.clone(); - this.pos = offset; - this.len = length; - - String paramName; - String paramValue; - while (hasChar()) { - paramName = parseToken(new char[] { - '=', separator }); - paramValue = null; - if (hasChar() && (charArray[pos] == '=')) { - pos++; // skip '=' - paramValue = parseQuotedToken(new char[] { - separator }); - - if (paramValue != null) { - try { - paramValue = RFC2231Utility.hasEncodedValue(paramName) ? RFC2231Utility.decodeText(paramValue) - : MimeUtility.decodeText(paramValue); - } catch (final UnsupportedEncodingException e) { - // let's keep the original value in this case - } - } - } - if (hasChar() && (charArray[pos] == separator)) { - pos++; // skip separator - } - if ((paramName != null) && !paramName.isEmpty()) { - paramName = RFC2231Utility.stripDelimiter(paramName); - if (this.lowerCaseNames) { - paramName = paramName.toLowerCase(Locale.ENGLISH); - } - params.put(paramName, paramValue); - } - } - return params; + public void setLowerCaseNames(final boolean b) { + lowerCaseNames = b; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/ProgressListener.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/ProgressListener.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/ProgressListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/ProgressListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,13 +25,13 @@ /** * Updates the listeners status information. * - * @param pBytesRead The total number of bytes, which have been read + * @param bytesRead The total number of bytes, which have been read * so far. - * @param pContentLength The total number of bytes, which are being + * @param contentLength The total number of bytes, which are being * read. May be -1, if this number is unknown. - * @param pItems The number of the field, which is currently being + * @param items The number of the field, which is currently being * read. (0 = no item so far, 1 = first item is being read, ...) */ - void update(long pBytesRead, long pContentLength, int pItems); + void update(long bytesRead, long contentLength, int items); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/RFC2231Utility.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/RFC2231Utility.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/RFC2231Utility.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/RFC2231Utility.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.http.fileupload; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; + +/** + * Utility class to decode/encode character set on HTTP Header fields based on RFC 2231. This implementation adheres to RFC 5987 in particular, which was + * defined for HTTP headers. + *

      + * RFC 5987 builds on RFC 2231, but has lesser scope like mandatory charset definition and + * no parameter continuation + *

      + * + * @see RFC 2231 + * @see RFC 5987 + */ +final class RFC2231Utility { + + /** + * Percent character '{@value}'. + */ + private static final char PERCENT = '%'; + /** + * The Hexadecimal values char array. + */ + private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); + /** + * The Hexadecimal representation of 127. + */ + private static final byte MASK = 0x7f; + /** + * The Hexadecimal representation of 128. + */ + private static final int MASK_128 = 0x80; + /** + * The Hexadecimal decode value. + */ + private static final byte[] HEX_DECODE = new byte[MASK_128]; + // create a ASCII decoded array of Hexadecimal values + static { + for (int i = 0; i < HEX_DIGITS.length; i++) { + HEX_DECODE[HEX_DIGITS[i]] = (byte) i; + HEX_DECODE[Character.toLowerCase(HEX_DIGITS[i])] = (byte) i; + } + } + + /** + * Decodes a string of text obtained from a HTTP header as per RFC 2231 + *

      + * Eg 1. {@code us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A} will be decoded to {@code This is ***fun***} + *

      + *

      + * Eg 2. {@code iso-8859-1'en'%A3%20rate} will be decoded to {@code £ rate} + *

      + *

      + * Eg 3. {@code UTF-8''%c2%a3%20and%20%e2%82%ac%20rates} will be decoded to {@code £ and € rates} + *

      + * + * @param encodedText Text to be decoded has a format of {@code ''} and ASCII only + * @return Decoded text based on charset encoding + * @throws UnsupportedEncodingException The requested character set wasn't found. + */ + static String decodeText(final String encodedText) throws UnsupportedEncodingException { + final int langDelimitStart = encodedText.indexOf('\''); + if (langDelimitStart == -1) { + // missing charset + return encodedText; + } + final String mimeCharset = encodedText.substring(0, langDelimitStart); + final int langDelimitEnd = encodedText.indexOf('\'', langDelimitStart + 1); + if (langDelimitEnd == -1) { + // missing language + return encodedText; + } + final byte[] bytes = fromHex(encodedText.substring(langDelimitEnd + 1)); + return new String(bytes, getJavaCharset(mimeCharset)); + } + + /** + * Converts {@code text} to their corresponding Hex value. + * + * @param text ASCII text input + * @return Byte array of characters decoded from ASCII table + */ + private static byte[] fromHex(final String text) { + final int shift = 4; + final ByteArrayOutputStream out = new ByteArrayOutputStream(text.length()); + for (int i = 0; i < text.length();) { + final char c = text.charAt(i++); + if (c == PERCENT) { + if (i > text.length() - 2) { + break; // unterminated sequence + } + final byte b1 = HEX_DECODE[text.charAt(i++) & MASK]; + final byte b2 = HEX_DECODE[text.charAt(i++) & MASK]; + out.write(b1 << shift | b2); + } else { + out.write((byte) c); + } + } + return out.toByteArray(); + } + + private static String getJavaCharset(final String mimeCharset) { + // good enough for standard values + return mimeCharset; + } + + /** + * Checks if Asterisk (*) at the end of parameter name to indicate, if it has charset and language information to decode the value. + * + * @param paramName The parameter, which is being checked. + * @return {@code true}, if encoded as per RFC 2231, {@code false} otherwise + */ + static boolean hasEncodedValue(final String paramName) { + if (paramName != null) { + return paramName.lastIndexOf('*') == paramName.length() - 1; + } + return false; + } + + /** + * If {@code paramName} has Asterisk (*) at the end, it will be stripped off, else the passed value will be returned. + * + * @param paramName The parameter, which is being inspected. + * @return stripped {@code paramName} of Asterisk (*), if RFC2231 encoded + */ + static String stripDelimiter(final String paramName) { + if (hasEncodedValue(paramName)) { + final StringBuilder paramBuilder = new StringBuilder(paramName); + paramBuilder.deleteCharAt(paramName.lastIndexOf('*')); + return paramBuilder.toString(); + } + return paramName; + } + + /** + * Private constructor so that no instances can be created. This class contains only static utility methods. + */ + private RFC2231Utility() { + } +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItem.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,259 +37,175 @@ import org.apache.tomcat.util.http.fileupload.FileItemHeaders; import org.apache.tomcat.util.http.fileupload.FileUploadException; import org.apache.tomcat.util.http.fileupload.IOUtils; +import org.apache.tomcat.util.http.fileupload.InvalidFileNameException; import org.apache.tomcat.util.http.fileupload.ParameterParser; import org.apache.tomcat.util.http.fileupload.util.Streams; /** - *

      The default implementation of the - * {@link org.apache.tomcat.util.http.fileupload.FileItem FileItem} interface. - * - *

      After retrieving an instance of this class from a {@link - * org.apache.tomcat.util.http.fileupload.FileUpload FileUpload} instance (see + * The default implementation of the {@link org.apache.tomcat.util.http.fileupload.FileItem FileItem} interface. + *

      + * After retrieving an instance of this class from a {@link DiskFileItemFactory} instance (see * {@link org.apache.tomcat.util.http.fileupload.FileUpload - * #parseRequest(org.apache.tomcat.util.http.fileupload.RequestContext)}), you - * may either request all contents of file at once using {@link #get()} or - * request an {@link java.io.InputStream InputStream} with - * {@link #getInputStream()} and process the file without attempting to load - * it into memory, which may come handy with large files. - * - *

      Temporary files, which are created for file items, will be deleted when - * the associated request is recycled.

      + * #parseRequest(org.apache.tomcat.util.http.fileupload.RequestContext)}), you may either request all contents of file + * at once using {@link #get()} or request an {@link java.io.InputStream InputStream} with {@link #getInputStream()} and + * process the file without attempting to load it into memory, which may come handy with large files. + *

      + * Temporary files, which are created for file items, will be deleted when the associated request is recycled. + *

      * * @since FileUpload 1.1 */ -public class DiskFileItem - implements FileItem { +public class DiskFileItem implements FileItem { - // ----------------------------------------------------- Manifest constants + /** + * Counter used in unique identifier generation. + */ + private static final AtomicInteger COUNTER = new AtomicInteger(0); /** - * Default content charset to be used when no explicit charset - * parameter is provided by the sender. Media subtypes of the - * "text" type are defined to have a default charset value of - * "ISO-8859-1" when received via HTTP. + * Default content charset to be used when no explicit charset parameter is provided by the sender. Media subtypes + * of the "text" type are defined to have a default charset value of "ISO-8859-1" when received via HTTP. */ public static final String DEFAULT_CHARSET = "ISO-8859-1"; - // ----------------------------------------------------------- Data members - /** * UID used in unique file name generation. */ - private static final String UID = - UUID.randomUUID().toString().replace('-', '_'); + private static final String UID = UUID.randomUUID().toString().replace('-', '_'); /** - * Counter used in unique identifier generation. + * Returns an identifier that is unique within the class loader used to load this class, but does not have + * random-like appearance. + * + * @return A String with the non-random looking instance identifier. */ - private static final AtomicInteger COUNTER = new AtomicInteger(0); + private static String getUniqueId() { + final int limit = 100000000; + final int current = COUNTER.getAndIncrement(); + String id = Integer.toString(current); + + // If you manage to get more than 100 million of ids, you'll + // start getting ids longer than 8 characters. + if (current < limit) { + id = ("00000000" + id).substring(id.length()); + } + return id; + } /** - * The name of the form field as provided by the browser. + * Cached contents of the file. */ - private String fieldName; + private byte[] cachedContent; /** - * The content type passed by the browser, or {@code null} if - * not defined. + * The content type passed by the browser, or {@code null} if not defined. */ private final String contentType; /** - * Whether or not this item is a simple form field. + * Default content charset to be used when no explicit charset parameter is provided by the sender. */ - private boolean isFormField; + private String defaultCharset = DEFAULT_CHARSET; /** - * The original file name in the user's file system. + * Output stream for this item. */ - private final String fileName; + private transient DeferredFileOutputStream dfos; /** - * The size of the item, in bytes. This is used to cache the size when a - * file item is moved from its original location. + * The name of the form field as provided by the browser. */ - private long size = -1; - + private String fieldName; /** - * The threshold above which uploads will be stored on disk. + * The original file name in the user's file system. */ - private final int sizeThreshold; + private final String fileName; /** - * The directory in which uploaded files will be stored, if stored on disk. + * The file items headers. */ - private final File repository; + private FileItemHeaders headers; /** - * Cached contents of the file. + * Whether or not this item is a simple form field. */ - private byte[] cachedContent; + private boolean formField; /** - * Output stream for this item. + * The directory in which uploaded files will be stored, if stored on disk. */ - private transient DeferredFileOutputStream dfos; + private final File repository; /** - * The temporary file to use. + * The size of the item, in bytes. This is used to cache the size when a file item is moved from its original + * location. */ - private transient File tempFile; + private long size = -1; /** - * The file items headers. + * The threshold above which uploads will be stored on disk. */ - private FileItemHeaders headers; + private final int sizeThreshold; /** - * Default content charset to be used when no explicit charset - * parameter is provided by the sender. + * The temporary file to use. */ - private String defaultCharset = DEFAULT_CHARSET; - - // ----------------------------------------------------------- Constructors + private transient File tempFile; /** * Constructs a new {@code DiskFileItem} instance. * * @param fieldName The name of the form field. - * @param contentType The content type passed by the browser or - * {@code null} if not specified. - * @param isFormField Whether or not this item is a plain form field, as - * opposed to a file upload. - * @param fileName The original file name in the user's file system, or - * {@code null} if not specified. - * @param sizeThreshold The threshold, in bytes, below which items will be - * retained in memory and above which they will be - * stored as a file. - * @param repository The data repository, which is the directory in - * which files will be created, should the item size - * exceed the threshold. - */ - public DiskFileItem(final String fieldName, - final String contentType, final boolean isFormField, final String fileName, - final int sizeThreshold, final File repository) { + * @param contentType The content type passed by the browser or {@code null} if not specified. + * @param isFormField Whether or not this item is a plain form field, as opposed to a file upload. + * @param fileName The original file name in the user's file system, or {@code null} if not specified. + * @param sizeThreshold The threshold, in bytes, below which items will be retained in memory and above which they + * will be stored as a file. + * @param repository The data repository, which is the directory in which files will be created, should the item + * size exceed the threshold. + */ + public DiskFileItem(final String fieldName, final String contentType, final boolean isFormField, + final String fileName, final int sizeThreshold, final File repository) { this.fieldName = fieldName; this.contentType = contentType; - this.isFormField = isFormField; + this.formField = isFormField; this.fileName = fileName; this.sizeThreshold = sizeThreshold; this.repository = repository; } - // ------------------------------- Methods from javax.activation.DataSource - /** - * Returns an {@link java.io.InputStream InputStream} that can be - * used to retrieve the contents of the file. - * - * @return An {@link java.io.InputStream InputStream} that can be - * used to retrieve the contents of the file. - * - * @throws IOException if an error occurs. + * Clears the cache. */ - @Override - public InputStream getInputStream() - throws IOException { - if (!isInMemory()) { - return Files.newInputStream(dfos.getFile().toPath()); - } - - if (cachedContent == null) { - cachedContent = dfos.getData(); - } - return new ByteArrayInputStream(cachedContent); + private void clear() { + cachedContent = null; // NOPMD } /** - * Returns the content type passed by the agent or {@code null} if - * not defined. - * - * @return The content type passed by the agent or {@code null} if - * not defined. + * Deletes the underlying storage for a file item, including deleting any associated temporary disk file. This + * method can be used to ensure that this is done at an earlier time, thus preserving system resources. */ @Override - public String getContentType() { - return contentType; - } - - /** - * Returns the content charset passed by the agent or {@code null} if - * not defined. - * - * @return The content charset passed by the agent or {@code null} if - * not defined. - */ - public String getCharSet() { - final ParameterParser parser = new ParameterParser(); - parser.setLowerCaseNames(true); - // Parameter parser can handle null input - final Map params = parser.parse(getContentType(), ';'); - return params.get("charset"); - } - - /** - * Returns the original file name in the client's file system. - * - * @return The original file name in the client's file system. - * @throws org.apache.tomcat.util.http.fileupload.InvalidFileNameException - * The file name contains a NUL character, which might be an indicator of - * a security attack. If you intend to use the file name anyways, catch - * the exception and use {@link - * org.apache.tomcat.util.http.fileupload.InvalidFileNameException#getName()}. - */ - @Override - public String getName() { - return Streams.checkFileName(fileName); - } - - // ------------------------------------------------------- FileItem methods - - /** - * Provides a hint as to whether or not the file contents will be read - * from memory. - * - * @return {@code true} if the file contents will be read - * from memory; {@code false} otherwise. - */ - @Override - public boolean isInMemory() { - if (cachedContent != null) { - return true; - } - return dfos.isInMemory(); - } - - /** - * Returns the size of the file. - * - * @return The size of the file, in bytes. - */ - @Override - public long getSize() { - if (size >= 0) { - return size; - } - if (cachedContent != null) { - return cachedContent.length; - } - if (dfos.isInMemory()) { - return dfos.getData().length; + public void delete() { + clear(); + final File outputFile = getStoreLocation(); + if (outputFile != null && !isInMemory() && outputFile.exists()) { + if (!outputFile.delete()) { + final String desc = "Cannot delete " + outputFile.toString(); + throw new UncheckedIOException(desc, new IOException(desc)); + } } - return dfos.getFile().length(); } /** - * Returns the contents of the file as an array of bytes. If the - * contents of the file were not yet cached in memory, they will be - * loaded from the disk storage and cached. + * Gets the contents of the file as an array of bytes. If the contents of the file were not yet cached in memory, + * they will be loaded from the disk storage and cached. * - * @return The contents of the file as an array of bytes - * or {@code null} if the data cannot be read + * @return The contents of the file as an array of bytes or {@code null} if the data cannot be read. * * @throws UncheckedIOException if an I/O error occurs - * @throws ArithmeticException if the file {@code size} overflows an int + * @throws ArithmeticException if the file {@code size} overflows an int */ @Override public byte[] get() throws UncheckedIOException { @@ -311,132 +227,43 @@ } /** - * Returns the contents of the file as a String, using the specified - * encoding. This method uses {@link #get()} to retrieve the - * contents of the file. - * - * @param charset The charset to use. + * Gets the content charset passed by the agent or {@code null} if not defined. * - * @return The contents of the file, as a string. - * - * @throws UnsupportedEncodingException if the requested character - * encoding is not available. + * @return The content charset passed by the agent or {@code null} if not defined. */ - @Override - public String getString(final String charset) - throws UnsupportedEncodingException, IOException { - return new String(get(), charset); + public String getCharSet() { + final ParameterParser parser = new ParameterParser(); + parser.setLowerCaseNames(true); + // Parameter parser can handle null input + final Map params = parser.parse(getContentType(), ';'); + return params.get("charset"); } /** - * Returns the contents of the file as a String, using the default - * character encoding. This method uses {@link #get()} to retrieve the - * contents of the file. - * - * TODO Consider making this method throw UnsupportedEncodingException. + * Gets the content type passed by the agent or {@code null} if not defined. * - * @return The contents of the file, as a string. + * @return The content type passed by the agent or {@code null} if not defined. */ @Override - public String getString() { - try { - final byte[] rawData = get(); - String charset = getCharSet(); - if (charset == null) { - charset = defaultCharset; - } - return new String(rawData, charset); - } catch (final IOException e) { - return ""; - } + public String getContentType() { + return contentType; } /** - * A convenience method to write an uploaded item to disk. The client code - * is not concerned with whether or not the item is stored in memory, or on - * disk in a temporary location. They just want to write the uploaded item - * to a file. - *

      - * This implementation first attempts to rename the uploaded item to the - * specified destination file, if the item was originally written to disk. - * Otherwise, the data will be copied to the specified file. - *

      - * This method is only guaranteed to work once, the first time it - * is invoked for a particular item. This is because, in the event that the - * method renames a temporary file, that file will no longer be available - * to copy or rename again at a later time. - * - * @param file The {@code File} into which the uploaded item should - * be stored. + * Gets the default charset for use when no explicit charset parameter is provided by the sender. * - * @throws Exception if an error occurs. - */ - @Override - public void write(final File file) throws Exception { - if (isInMemory()) { - try (OutputStream fout = Files.newOutputStream(file.toPath())) { - fout.write(get()); - } - } else { - final File outputFile = getStoreLocation(); - if (outputFile == null) { - /* - * For whatever reason we cannot write the - * file to disk. - */ - throw new FileUploadException( - "Cannot write uploaded file to disk!"); - } - // Save the length of the file - size = outputFile.length(); - /* - * The uploaded file is being stored on disk - * in a temporary location so move it to the - * desired file. - */ - if (file.exists() && !file.delete()) { - throw new FileUploadException("Cannot write uploaded file to disk!"); - } - if (!outputFile.renameTo(file)) { - BufferedInputStream in = null; - BufferedOutputStream out = null; - try { - in = new BufferedInputStream(new FileInputStream(outputFile)); - out = new BufferedOutputStream(new FileOutputStream(file)); - IOUtils.copy(in, out); - out.close(); - } finally { - IOUtils.closeQuietly(in); - IOUtils.closeQuietly(out); - } - } - } - } - - /** - * Deletes the underlying storage for a file item, including deleting any associated temporary disk file. - * This method can be used to ensure that this is done at an earlier time, thus preserving system resources. + * @return the default charset */ - @Override - public void delete() { - cachedContent = null; - final File outputFile = getStoreLocation(); - if (outputFile != null && !isInMemory() && outputFile.exists()) { - if (!outputFile.delete()) { - final String desc = "Cannot delete " + outputFile.toString(); - throw new UncheckedIOException(desc, new IOException(desc)); - } - } + public String getDefaultCharset() { + return defaultCharset; } /** - * Returns the name of the field in the multipart form corresponding to - * this file item. + * Gets the name of the field in the multipart form corresponding to this file item. * * @return The name of the form field. * * @see #setFieldName(String) - * */ @Override public String getFieldName() { @@ -444,55 +271,52 @@ } /** - * Sets the field name used to reference this file item. - * - * @param fieldName The name of the form field. - * - * @see #getFieldName() + * Gets the file item headers. * + * @return The file items headers. */ @Override - public void setFieldName(final String fieldName) { - this.fieldName = fieldName; + public FileItemHeaders getHeaders() { + return headers; } /** - * Determines whether or not a {@code FileItem} instance represents - * a simple form field. - * - * @return {@code true} if the instance represents a simple form - * field; {@code false} if it represents an uploaded file. + * Gets an {@link java.io.InputStream InputStream} that can be used to retrieve the contents of the file. * - * @see #setFormField(boolean) + * @return An {@link java.io.InputStream InputStream} that can be used to retrieve the contents of the file. * + * @throws IOException if an error occurs. */ @Override - public boolean isFormField() { - return isFormField; + public InputStream getInputStream() throws IOException { + if (!isInMemory()) { + return Files.newInputStream(dfos.getFile().toPath()); + } + if (cachedContent == null) { + cachedContent = dfos.getData(); + } + return new ByteArrayInputStream(cachedContent); } /** - * Specifies whether or not a {@code FileItem} instance represents - * a simple form field. - * - * @param state {@code true} if the instance represents a simple form - * field; {@code false} if it represents an uploaded file. + * Gets the original file name in the client's file system. * - * @see #isFormField() + * @return The original file name in the client's file system. * + * @throws InvalidFileNameException The file name contains a NUL character, which might be an indicator of a + * security attack. If you intend to use the file name anyways, catch the + * exception and use + * {@link org.apache.tomcat.util.http.fileupload.InvalidFileNameException#getName()}. */ @Override - public void setFormField(final boolean state) { - isFormField = state; + public String getName() { + return Streams.checkFileName(fileName); } /** - * Returns an {@link java.io.OutputStream OutputStream} that can - * be used for storing the contents of the file. - * - * @return An {@link java.io.OutputStream OutputStream} that can be used - * for storing the contents of the file. + * Gets an {@link java.io.OutputStream OutputStream} that can be used for storing the contents of the file. * + * @return An {@link java.io.OutputStream OutputStream} that can be used for storing the contents of the file. */ @Override public OutputStream getOutputStream() { @@ -503,20 +327,32 @@ return dfos; } - // --------------------------------------------------------- Public methods + /** + * Gets the size of the file. + * + * @return The size of the file, in bytes. + */ + @Override + public long getSize() { + if (size >= 0) { + return size; + } + if (cachedContent != null) { + return cachedContent.length; + } + if (dfos.isInMemory()) { + return dfos.getData().length; + } + return dfos.getFile().length(); + } /** - * Returns the {@link java.io.File} object for the {@code FileItem}'s - * data's temporary location on the disk. Note that for - * {@code FileItem}s that have their data stored in memory, - * this method will return {@code null}. When handling large - * files, you can use {@link java.io.File#renameTo(java.io.File)} to - * move the file to new location without copying the data, if the - * source and destination locations reside within the same logical - * volume. + * Gets the {@link java.io.File} object for the {@code FileItem}'s data's temporary location on the disk. Note + * that for {@code FileItem}s that have their data stored in memory, this method will return {@code null}. When + * handling large files, you can use {@link java.io.File#renameTo(java.io.File)} to move the file to new location + * without copying the data, if the source and destination locations reside within the same logical volume. * - * @return The data file, or {@code null} if the data is stored in - * memory. + * @return The data file, or {@code null} if the data is stored in memory. */ public File getStoreLocation() { if (dfos == null) { @@ -528,16 +364,49 @@ return dfos.getFile(); } - // ------------------------------------------------------ Protected methods + /** + * Gets the contents of the file as a String, using the default character encoding. This method uses + * {@link #get()} to retrieve the contents of the file. + *

      + * TODO Consider making this method throw UnsupportedEncodingException. + * + * @return The contents of the file, as a string. + */ + @Override + public String getString() { + final byte[] rawData = get(); + String charset = getCharSet(); + if (charset == null) { + charset = defaultCharset; + } + try { + return new String(rawData, charset); + } catch (final UnsupportedEncodingException e) { + return ""; + } + } /** - * Creates and returns a {@link java.io.File File} representing a uniquely - * named temporary file in the configured repository path. The lifetime of - * the file is tied to the lifetime of the {@code FileItem} instance; - * the file will be deleted when the instance is garbage collected. + * Gets the contents of the file as a String, using the specified encoding. This method uses {@link #get()} to + * retrieve the contents of the file. + * + * @param charset The charset to use. + * + * @return The contents of the file, as a string. + * + * @throws UnsupportedEncodingException if the requested character encoding is not available. + */ + @Override + public String getString(final String charset) throws UnsupportedEncodingException { + return new String(get(), charset); + } + + /** + * Creates and returns a {@link java.io.File File} representing a uniquely named temporary file in the configured + * repository path. The lifetime of the file is tied to the lifetime of the {@code FileItem} instance; the file will + * be deleted when the instance is garbage collected. *

      - * Note: Subclasses that override this method must ensure that they return the - * same File each time. + * Note: Subclasses that override this method must ensure that they return the same File each time. * * @return The {@link java.io.File File} to be used for temporary storage. */ @@ -555,71 +424,138 @@ return tempFile; } - // -------------------------------------------------------- Private methods - /** - * Returns an identifier that is unique within the class loader used to - * load this class, but does not have random-like appearance. + * Tests whether or not a {@code FileItem} instance represents a simple form field. * - * @return A String with the non-random looking instance identifier. + * @return {@code true} if the instance represents a simple form field; {@code false} if it represents an uploaded + * file. + * + * @see #setFormField(boolean) */ - private static String getUniqueId() { - final int limit = 100000000; - final int current = COUNTER.getAndIncrement(); - String id = Integer.toString(current); + @Override + public boolean isFormField() { + return formField; + } - // If you manage to get more than 100 million of ids, you'll - // start getting ids longer than 8 characters. - if (current < limit) { - id = ("00000000" + id).substring(id.length()); + /** + * Provides a hint as to whether or not the file contents will be read from memory. + * + * @return {@code true} if the file contents will be read from memory; {@code false} otherwise. + */ + @Override + public boolean isInMemory() { + if (cachedContent != null) { + return true; } - return id; + return dfos.isInMemory(); } /** - * Returns a string representation of this object. + * Sets the default charset for use when no explicit charset parameter is provided by the sender. * - * @return a string representation of this object. + * @param charset the default charset + */ + public void setDefaultCharset(final String charset) { + defaultCharset = charset; + } + + /** + * Sets the field name used to reference this file item. + * + * @param fieldName The name of the form field. + * + * @see #getFieldName() */ @Override - public String toString() { - return String.format("name=%s, StoreLocation=%s, size=%s bytes, isFormField=%s, FieldName=%s", - getName(), getStoreLocation(), Long.valueOf(getSize()), Boolean.valueOf(isFormField()), getFieldName()); + public void setFieldName(final String fieldName) { + this.fieldName = fieldName; } /** - * Returns the file item headers. - * @return The file items headers. + * Sets whether or not a {@code FileItem} instance represents a simple form field. + * + * @param formField {@code true} if the instance represents a simple form field; {@code false} if it represents an + * uploaded file. + * + * @see #isFormField() */ @Override - public FileItemHeaders getHeaders() { - return headers; + public void setFormField(final boolean formField) { + this.formField = formField; } /** * Sets the file item headers. - * @param pHeaders The file items headers. + * + * @param headers The file items headers. */ @Override - public void setHeaders(final FileItemHeaders pHeaders) { - headers = pHeaders; + public void setHeaders(final FileItemHeaders headers) { + this.headers = headers; } /** - * Returns the default charset for use when no explicit charset - * parameter is provided by the sender. - * @return the default charset + * Returns a string representation of this object. + * + * @return a string representation of this object. */ - public String getDefaultCharset() { - return defaultCharset; + @Override + public String toString() { + return String.format("name=%s, StoreLocation=%s, size=%s bytes, isFormField=%s, FieldName=%s", getName(), + getStoreLocation(), Long.valueOf(getSize()), Boolean.valueOf(isFormField()), getFieldName()); } /** - * Sets the default charset for use when no explicit charset - * parameter is provided by the sender. - * @param charset the default charset + * A convenience method to write an uploaded item to disk. The client code is not concerned with whether or not the + * item is stored in memory, or on disk in a temporary location. They just want to write the uploaded item to a + * file. + *

      + * This implementation first attempts to rename the uploaded item to the specified destination file, if the item was + * originally written to disk. Otherwise, the data will be copied to the specified file. + *

      + * This method is only guaranteed to work once, the first time it is invoked for a particular item. This is + * because, in the event that the method renames a temporary file, that file will no longer be available to copy or + * rename again at a later time. + * + * @param file The {@code File} into which the uploaded item should be stored. + * + * @throws Exception if an error occurs. */ - public void setDefaultCharset(final String charset) { - defaultCharset = charset; + @Override + public void write(final File file) throws Exception { + if (isInMemory()) { + try (OutputStream fout = Files.newOutputStream(file.toPath())) { + fout.write(get()); + } + } else { + final File outputFile = getStoreLocation(); + if (outputFile == null) { + /* + * For whatever reason we cannot write the file to disk. + */ + throw new FileUploadException("Cannot write uploaded file to disk!"); + } + // Save the length of the file + size = outputFile.length(); + /* + * The uploaded file is being stored on disk in a temporary location so move it to the desired file. + */ + if (file.exists() && !file.delete()) { + throw new FileUploadException("Cannot write uploaded file to disk!"); + } + if (!outputFile.renameTo(file)) { + BufferedInputStream in = null; + BufferedOutputStream out = null; + try { + in = new BufferedInputStream(new FileInputStream(outputFile)); + out = new BufferedOutputStream(new FileOutputStream(file)); + IOUtils.copy(in, out); + out.close(); + } finally { + IOUtils.closeQuietly(in); + IOUtils.closeQuietly(out); + } + } + } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/disk/DiskFileItemFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,7 +39,7 @@ * {@code System.getProperty("java.io.tmpdir")}. *

    *

    - * NOTE: Files are created in the system default temp directory with + * NOTE: Files are created in the system default temp directory with * predictable names. This means that a local attacker with write access to that * directory can perform a TOUTOC attack to replace any uploaded file with a * file of the attackers choice. The implications of this will depend on how the @@ -58,15 +58,11 @@ */ public class DiskFileItemFactory implements FileItemFactory { - // ----------------------------------------------------- Manifest constants - /** * The default threshold above which uploads will be stored on disk. */ public static final int DEFAULT_SIZE_THRESHOLD = 10240; - // ----------------------------------------------------- Instance Variables - /** * The directory in which uploaded files will be stored, if stored on disk. */ @@ -83,8 +79,6 @@ */ private String defaultCharset = DiskFileItem.DEFAULT_CHARSET; - // ----------------------------------------------------------- Constructors - /** * Constructs an unconfigured instance of this class. The resulting factory * may be configured by calling the appropriate setter methods. @@ -108,36 +102,53 @@ this.repository = repository; } - // ------------------------------------------------------------- Properties - /** - * Returns the directory used to temporarily store files that are larger - * than the configured size threshold. - * - * @return The directory in which temporary files will be located. + * Create a new {@link DiskFileItem} + * instance from the supplied parameters and the local factory + * configuration. * - * @see #setRepository(java.io.File) + * @param fieldName The name of the form field. + * @param contentType The content type of the form field. + * @param isFormField {@code true} if this is a plain form field; + * {@code false} otherwise. + * @param fileName The name of the uploaded file, if any, as supplied + * by the browser or other client. * + * @return The newly created file item. */ - public File getRepository() { - return repository; + @Override + public FileItem createItem(final String fieldName, final String contentType, + final boolean isFormField, final String fileName) { + final DiskFileItem result = new DiskFileItem(fieldName, contentType, + isFormField, fileName, sizeThreshold, repository); + result.setDefaultCharset(defaultCharset); + return result; } /** - * Sets the directory used to temporarily store files that are larger + * Gets the default charset for use when no explicit charset + * parameter is provided by the sender. + * @return the default charset + */ + public String getDefaultCharset() { + return defaultCharset; + } + + /** + * Gets the directory used to temporarily store files that are larger * than the configured size threshold. * - * @param repository The directory in which temporary files will be located. + * @return The directory in which temporary files will be located. * - * @see #getRepository() + * @see #setRepository(java.io.File) * */ - public void setRepository(final File repository) { - this.repository = repository; + public File getRepository() { + return repository; } /** - * Returns the size threshold beyond which files are written directly to + * Gets the size threshold beyond which files are written directly to * disk. The default value is 10240 bytes. * * @return The size threshold, in bytes. @@ -149,57 +160,37 @@ } /** - * Sets the size threshold beyond which files are written directly to disk. - * - * @param sizeThreshold The size threshold, in bytes. - * - * @see #getSizeThreshold() + * Sets the default charset for use when no explicit charset + * parameter is provided by the sender. * + * @param charset the default charset */ - public void setSizeThreshold(final int sizeThreshold) { - this.sizeThreshold = sizeThreshold; + public void setDefaultCharset(final String charset) { + this.defaultCharset = charset; } - // --------------------------------------------------------- Public Methods - /** - * Create a new {@link DiskFileItem} - * instance from the supplied parameters and the local factory - * configuration. + * Sets the directory used to temporarily store files that are larger + * than the configured size threshold. * - * @param fieldName The name of the form field. - * @param contentType The content type of the form field. - * @param isFormField {@code true} if this is a plain form field; - * {@code false} otherwise. - * @param fileName The name of the uploaded file, if any, as supplied - * by the browser or other client. + * @param repository The directory in which temporary files will be located. + * + * @see #getRepository() * - * @return The newly created file item. - */ - @Override - public FileItem createItem(final String fieldName, final String contentType, - final boolean isFormField, final String fileName) { - final DiskFileItem result = new DiskFileItem(fieldName, contentType, - isFormField, fileName, sizeThreshold, repository); - result.setDefaultCharset(defaultCharset); - return result; - } - - /** - * Returns the default charset for use when no explicit charset - * parameter is provided by the sender. - * @return the default charset */ - public String getDefaultCharset() { - return defaultCharset; + public void setRepository(final File repository) { + this.repository = repository; } /** - * Sets the default charset for use when no explicit charset - * parameter is provided by the sender. - * @param pCharset the default charset + * Sets the size threshold beyond which files are written directly to disk. + * + * @param sizeThreshold The size threshold, in bytes. + * + * @see #getSizeThreshold() + * */ - public void setDefaultCharset(final String pCharset) { - defaultCharset = pCharset; + public void setSizeThreshold(final int sizeThreshold) { + this.sizeThreshold = sizeThreshold; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/impl/FileItemIteratorImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -198,6 +198,7 @@ String.format("The boundary specified in the %s header is too long", FileUploadBase.CONTENT_TYPE), iae); } multiPartStream.setHeaderEncoding(charEncoding); + multiPartStream.setPartHeaderSizeMax(fileUploadBase.getPartHeaderSizeMax()); } public MultipartStream getMultiPartStream() throws FileUploadException, IOException { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/package-info.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/package-info.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/package-info.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/package-info.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,8 +16,8 @@ */ /** - *

    NOTE: This code has been copied from commons-fileupload trunk - * 1.3 and commons-io 1.4 and package renamed to avoid clashes with + *

    NOTE: This code has been copied from commons-fileupload + * 1.x and commons-io 1.4 and package renamed to avoid clashes with * any web apps that may wish to use these libraries. *

    *

    diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/servlet/ServletRequestContext.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/servlet/ServletRequestContext.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/servlet/ServletRequestContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/servlet/ServletRequestContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,15 +32,11 @@ */ public class ServletRequestContext implements UploadContext { - // ----------------------------------------------------- Instance Variables - /** * The request for which the context is being provided. */ private final HttpServletRequest request; - // ----------------------------------------------------------- Constructors - /** * Construct a context for this request. * @@ -50,7 +46,22 @@ this.request = request; } - // --------------------------------------------------------- Public Methods + /** + * Retrieve the content length of the request. + * + * @return The content length of the request. + * @since FileUpload 1.3 + */ + @Override + public long contentLength() { + long size; + try { + size = Long.parseLong(request.getHeader(FileUploadBase.CONTENT_LENGTH)); + } catch (final NumberFormatException e) { + size = request.getContentLength(); + } + return size; + } /** * Retrieve the character encoding for the request. @@ -73,23 +84,6 @@ } /** - * Retrieve the content length of the request. - * - * @return The content length of the request. - * @since FileUpload 1.3 - */ - @Override - public long contentLength() { - long size; - try { - size = Long.parseLong(request.getHeader(FileUploadBase.CONTENT_LENGTH)); - } catch (final NumberFormatException e) { - size = request.getContentLength(); - } - return size; - } - - /** * Retrieve the input stream for the request. * * @return The input stream for the request. @@ -109,7 +103,7 @@ @Override public String toString() { return String.format("ContentLength=%s, ContentType=%s", - Long.valueOf(this.contentLength()), this.getContentType()); + Long.valueOf(contentLength()), getContentType()); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/FileItemHeadersImpl.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/FileItemHeadersImpl.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/FileItemHeadersImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/FileItemHeadersImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,9 +45,29 @@ */ private final Map> headerNameToValueListMap = new LinkedHashMap<>(); + /** + * Constructs a new instance. + */ + public FileItemHeadersImpl() { + // empty + } + + /** + * Method to add header values to this instance. + * + * @param name name of this header + * @param value value of this header + */ + public synchronized void addHeader(final String name, final String value) { + final String nameLower = name.toLowerCase(Locale.ROOT); + final List headerValueList = headerNameToValueListMap. + computeIfAbsent(nameLower, k -> new ArrayList<>()); + headerValueList.add(value); + } + @Override public String getHeader(final String name) { - final String nameLower = name.toLowerCase(Locale.ENGLISH); + final String nameLower = name.toLowerCase(Locale.ROOT); final List headerValueList = headerNameToValueListMap.get(nameLower); if (null == headerValueList) { return null; @@ -62,7 +82,7 @@ @Override public Iterator getHeaders(final String name) { - final String nameLower = name.toLowerCase(Locale.ENGLISH); + final String nameLower = name.toLowerCase(Locale.ROOT); List headerValueList = headerNameToValueListMap.get(nameLower); if (null == headerValueList) { headerValueList = Collections.emptyList(); @@ -70,17 +90,4 @@ return headerValueList.iterator(); } - /** - * Method to add header values to this instance. - * - * @param name name of this header - * @param value value of this header - */ - public synchronized void addHeader(final String name, final String value) { - final String nameLower = name.toLowerCase(Locale.ENGLISH); - final List headerValueList = headerNameToValueListMap. - computeIfAbsent(nameLower, k -> new ArrayList<>()); - headerValueList.add(value); - } - } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/LimitedInputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,27 +45,15 @@ * Creates a new instance. * * @param inputStream The input stream, which shall be limited. - * @param pSizeMax The limit; no more than this number of bytes + * @param sizeMax The limit; no more than this number of bytes * shall be returned by the source stream. */ - public LimitedInputStream(final InputStream inputStream, final long pSizeMax) { + public LimitedInputStream(final InputStream inputStream, final long sizeMax) { super(inputStream); - sizeMax = pSizeMax; + this.sizeMax = sizeMax; } /** - * Called to indicate, that the input streams limit has - * been exceeded. - * - * @param pSizeMax The input streams limit, in bytes. - * @param pCount The actual number of bytes. - * @throws IOException The called method is expected - * to raise an IOException. - */ - protected abstract void raiseError(long pSizeMax, long pCount) - throws IOException; - - /** * Called to check, whether the input streams * limit is reached. * @@ -78,6 +66,43 @@ } /** + * Closes this input stream and releases any system resources + * associated with the stream. + * This + * method simply performs {@code in.close()}. + * + * @throws IOException if an I/O error occurs. + * @see java.io.FilterInputStream#in + */ + @Override + public void close() throws IOException { + closed = true; + super.close(); + } + + /** + * Returns, whether this stream is already closed. + * + * @return True, if the stream is closed, otherwise false. + * @throws IOException An I/O error occurred. + */ + @Override + public boolean isClosed() throws IOException { + return closed; + } + + /** + * Called to indicate, that the input streams limit has + * been exceeded. + * + * @param sizeMax The input streams limit, in bytes. + * @param count The actual number of bytes. + * @throws IOException The called method is expected + * to raise an IOException. + */ + protected abstract void raiseError(long sizeMax, long count) throws IOException; + + /** * Reads the next byte of data from this input stream. The value * byte is returned as an {@code int} in the range * {@code 0} to {@code 255}. If no byte is available @@ -137,30 +162,4 @@ return res; } - /** - * Returns, whether this stream is already closed. - * - * @return True, if the stream is closed, otherwise false. - * @throws IOException An I/O error occurred. - */ - @Override - public boolean isClosed() throws IOException { - return closed; - } - - /** - * Closes this input stream and releases any system resources - * associated with the stream. - * This - * method simply performs {@code in.close()}. - * - * @throws IOException if an I/O error occurs. - * @see java.io.FilterInputStream#in - */ - @Override - public void close() throws IOException { - closed = true; - super.close(); - } - } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/Streams.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/Streams.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/Streams.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/Streams.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,24 +28,47 @@ public final class Streams { /** - * Private constructor, to prevent instantiation. - * This class has only static methods. - */ - private Streams() { - // Does nothing - } - - /** * Default buffer size for use in * {@link #copy(InputStream, OutputStream, boolean)}. */ public static final int DEFAULT_BUFFER_SIZE = 8192; /** + * Checks, whether the given file name is valid in the sense, + * that it doesn't contain any NUL characters. If the file name + * is valid, it will be returned without any modifications. Otherwise, + * an {@link InvalidFileNameException} is raised. + * + * @param fileName The file name to check + * @return Unmodified file name, if valid. + * @throws InvalidFileNameException The file name was found to be invalid. + */ + public static String checkFileName(final String fileName) { + if (fileName != null && fileName.indexOf('\u0000') != -1) { + // fileName.replace("\u0000", "\\0") + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < fileName.length(); i++) { + final char c = fileName.charAt(i); + switch (c) { + case 0: + sb.append("\\0"); + break; + default: + sb.append(c); + break; + } + } + throw new InvalidFileNameException(fileName, + "Invalid file name: " + sb); + } + return fileName; + } + + /** * Copies the contents of the given {@link InputStream} * to the given {@link OutputStream}. Shortcut for *

    -     *   copy(pInputStream, pOutputStream, new byte[8192]);
    +     *   copy(pInputStream, outputStream, new byte[8192]);
          * 
    * * @param inputStream The input stream, which is being read. @@ -116,35 +139,12 @@ } } - /** - * Checks, whether the given file name is valid in the sense, - * that it doesn't contain any NUL characters. If the file name - * is valid, it will be returned without any modifications. Otherwise, - * an {@link InvalidFileNameException} is raised. - * - * @param fileName The file name to check - * @return Unmodified file name, if valid. - * @throws InvalidFileNameException The file name was found to be invalid. + /** + * Private constructor, to prevent instantiation. + * This class has only static methods. */ - public static String checkFileName(final String fileName) { - if (fileName != null && fileName.indexOf('\u0000') != -1) { - // pFileName.replace("\u0000", "\\0") - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < fileName.length(); i++) { - final char c = fileName.charAt(i); - switch (c) { - case 0: - sb.append("\\0"); - break; - default: - sb.append(c); - break; - } - } - throw new InvalidFileNameException(fileName, - "Invalid file name: " + sb); - } - return fileName; + private Streams() { + // Does nothing } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/mime/MimeUtility.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/MimeUtility.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/mime/MimeUtility.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/MimeUtility.java 2026-01-23 19:33:36.000000000 +0000 @@ -76,13 +76,6 @@ } /** - * Hidden constructor, this class must not be instantiated. - */ - private MimeUtility() { - // do nothing - } - - /** * Decode a string of text obtained from a mail header into * its proper form. The text generally will consist of a * string of tokens, some of which may be encoded using @@ -207,7 +200,7 @@ } // pull out the character set information (this is the MIME name at this point). - final String charset = word.substring(2, charsetPos).toLowerCase(Locale.ENGLISH); + final String charset = word.substring(2, charsetPos).toLowerCase(Locale.ROOT); // now pull out the encoding token the same way. final int encodingPos = word.indexOf('?', charsetPos + 1); @@ -266,7 +259,7 @@ return null; } - final String mappedCharset = MIME2JAVA.get(charset.toLowerCase(Locale.ENGLISH)); + final String mappedCharset = MIME2JAVA.get(charset.toLowerCase(Locale.ROOT)); // if there is no mapping, then the original name is used. Many of the MIME character set // names map directly back into Java. The reverse isn't necessarily true. if (mappedCharset == null) { @@ -275,4 +268,11 @@ return mappedCharset; } + /** + * Hidden constructor, this class must not be instantiated. + */ + private MimeUtility() { + // do nothing + } + } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/mime/ParseException.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/ParseException.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/mime/ParseException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/ParseException.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,8 @@ package org.apache.tomcat.util.http.fileupload.util.mime; /** + * Thrown for a parsing problem. + * * @since FileUpload 1.3 */ final class ParseException extends Exception { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/mime/QuotedPrintableDecoder.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/QuotedPrintableDecoder.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/mime/QuotedPrintableDecoder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/QuotedPrintableDecoder.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,17 +25,35 @@ final class QuotedPrintableDecoder { /** - * The shift value required to create the upper nibble - * from the first of 2 byte values converted from ascii hex. + * Carriage return character '{@value}'. */ - private static final int UPPER_NIBBLE_SHIFT = Byte.SIZE / 2; + private static final char CR = '\r'; /** - * Hidden constructor, this class must not be instantiated. + * Equal character '{@value}'. */ - private QuotedPrintableDecoder() { - // do nothing - } + private static final char EQUAL = '='; + + /** + * Line feed character '{@value}'. + */ + private static final char LF = '\n'; + + /** + * Space character '{@value}'. + */ + private static final char SP = ' '; + + /** + * Underscore character '{@value}'. + */ + private static final char UNDERSCORE = '_'; + + /** + * The shift value required to create the upper nibble + * from the first of 2 byte values converted from ASCII hex. + */ + private static final int UPPER_NIBBLE_SHIFT = Byte.SIZE / 2; /** * Decode the encoded byte data writing it to the given output stream. @@ -56,9 +74,9 @@ final byte ch = data[off++]; // space characters were translated to '_' on encode, so we need to translate them back. - if (ch == '_') { - out.write(' '); - } else if (ch == '=') { + if (ch == UNDERSCORE) { + out.write(SP); + } else if (ch == EQUAL) { // we found an encoded character. Reduce the 3 char sequence to one. // but first, make sure we have two characters to work with. if (off + 1 >= endOffset) { @@ -69,8 +87,8 @@ final byte b2 = data[off++]; // we've found an encoded carriage return. The next char needs to be a newline - if (b1 == '\r') { - if (b2 != '\n') { + if (b1 == CR) { + if (b2 != LF) { throw new IOException("Invalid quoted printable encoding; CR must be followed by LF"); } // this was a soft linebreak inserted by the encoding. We just toss this away @@ -79,7 +97,7 @@ // this is a hex pair we need to convert back to a single byte. final int c1 = hexToBinary(b1); final int c2 = hexToBinary(b2); - out.write((c1 << UPPER_NIBBLE_SHIFT) | c2); + out.write(c1 << UPPER_NIBBLE_SHIFT | c2); // 3 bytes in, one byte out bytesWritten++; } @@ -96,7 +114,7 @@ /** * Convert a hex digit to the binary value it represents. * - * @param b the ascii hex byte to convert (0-0, A-F, a-f) + * @param b the ASCII hex byte to convert (0-0, A-F, a-f) * @return the int value of the hex byte, 0-15 * @throws IOException if the byte is not a valid hex digit. */ @@ -109,4 +127,11 @@ return i; } + /** + * Hidden constructor, this class must not be instantiated. + */ + private QuotedPrintableDecoder() { + // do nothing + } + } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/mime/RFC2231Utility.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/RFC2231Utility.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/fileupload/util/mime/RFC2231Utility.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/fileupload/util/mime/RFC2231Utility.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,154 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.tomcat.util.http.fileupload.util.mime; - -import java.io.ByteArrayOutputStream; -import java.io.UnsupportedEncodingException; -/** - * Utility class to decode/encode character set on HTTP Header fields based on RFC 2231. - * This implementation adheres to RFC 5987 in particular, which was defined for HTTP headers. - *

    - * RFC 5987 builds on RFC 2231, but has lesser scope like - * mandatory charset definition - * and no parameter continuation - * - * @see RFC 2231 - * @see RFC 5987 - */ -public final class RFC2231Utility { - /** - * The Hexadecimal values char array. - */ - private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); - /** - * The Hexadecimal representation of 127. - */ - private static final byte MASK = 0x7f; - /** - * The Hexadecimal representation of 128. - */ - private static final int MASK_128 = 0x80; - /** - * The Hexadecimal decode value. - */ - private static final byte[] HEX_DECODE = new byte[MASK_128]; - - // create a ASCII decoded array of Hexadecimal values - static { - for (int i = 0; i < HEX_DIGITS.length; i++) { - HEX_DECODE[HEX_DIGITS[i]] = (byte) i; - HEX_DECODE[Character.toLowerCase(HEX_DIGITS[i])] = (byte) i; - } - } - - /** - * Private constructor so that no instances can be created. This class - * contains only static utility methods. - */ - private RFC2231Utility() { - } - - /** - * Checks if Asterisk (*) at the end of parameter name to indicate, - * if it has charset and language information to decode the value. - * @param paramName The parameter, which is being checked. - * @return {@code true}, if encoded as per RFC 2231, {@code false} otherwise - */ - public static boolean hasEncodedValue(final String paramName) { - if (paramName != null) { - return paramName.lastIndexOf('*') == (paramName.length() - 1); - } - return false; - } - - /** - * If {@code paramName} has Asterisk (*) at the end, it will be stripped off, - * else the passed value will be returned. - * @param paramName The parameter, which is being inspected. - * @return stripped {@code paramName} of Asterisk (*), if RFC2231 encoded - */ - public static String stripDelimiter(final String paramName) { - if (hasEncodedValue(paramName)) { - final StringBuilder paramBuilder = new StringBuilder(paramName); - paramBuilder.deleteCharAt(paramName.lastIndexOf('*')); - return paramBuilder.toString(); - } - return paramName; - } - - /** - * Decode a string of text obtained from a HTTP header as per RFC 2231 - *

    - * Eg 1. {@code us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A} - * will be decoded to {@code This is ***fun***} - *

    - * Eg 2. {@code iso-8859-1'en'%A3%20rate} - * will be decoded to {@code £ rate} - *

    - * Eg 3. {@code UTF-8''%c2%a3%20and%20%e2%82%ac%20rates} - * will be decoded to {@code £ and € rates} - * - * @param encodedText - Text to be decoded has a format of {@code ''} - * and ASCII only - * @return Decoded text based on charset encoding - * @throws UnsupportedEncodingException The requested character set wasn't found. - */ - public static String decodeText(final String encodedText) throws UnsupportedEncodingException { - final int langDelimitStart = encodedText.indexOf('\''); - if (langDelimitStart == -1) { - // missing charset - return encodedText; - } - final String mimeCharset = encodedText.substring(0, langDelimitStart); - final int langDelimitEnd = encodedText.indexOf('\'', langDelimitStart + 1); - if (langDelimitEnd == -1) { - // missing language - return encodedText; - } - final byte[] bytes = fromHex(encodedText.substring(langDelimitEnd + 1)); - return new String(bytes, getJavaCharset(mimeCharset)); - } - - /** - * Convert {@code text} to their corresponding Hex value. - * @param text - ASCII text input - * @return Byte array of characters decoded from ASCII table - */ - private static byte[] fromHex(final String text) { - final int shift = 4; - final ByteArrayOutputStream out = new ByteArrayOutputStream(text.length()); - for (int i = 0; i < text.length();) { - final char c = text.charAt(i++); - if (c == '%') { - if (i > text.length() - 2) { - break; // unterminated sequence - } - final byte b1 = HEX_DECODE[text.charAt(i++) & MASK]; - final byte b2 = HEX_DECODE[text.charAt(i++) & MASK]; - out.write((b1 << shift) | b2); - } else { - out.write((byte) c); - } - } - return out.toByteArray(); - } - - private static String getJavaCharset(final String mimeCharset) { - // good enough for standard values - return mimeCharset; - } -} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/AcceptEncoding.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/AcceptEncoding.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/AcceptEncoding.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/AcceptEncoding.java 2026-01-23 19:33:36.000000000 +0000 @@ -52,7 +52,7 @@ continue; } - if (encoding.length() == 0) { + if (encoding.isEmpty()) { // No more data to read break; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/AcceptLanguage.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/AcceptLanguage.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/AcceptLanguage.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/AcceptLanguage.java 2026-01-23 19:33:36.000000000 +0000 @@ -56,7 +56,7 @@ continue; } - if (languageTag.length() == 0) { + if (languageTag.isEmpty()) { // No more data to read break; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/Authorization.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/Authorization.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/Authorization.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/Authorization.java 2026-01-23 19:33:36.000000000 +0000 @@ -82,16 +82,16 @@ if (field == null) { return null; } - while (!field.equals("")) { + while (!field.isEmpty()) { if (HttpParser.skipConstant(input, "=") != SkipResult.FOUND) { return null; } - String value = null; FieldType type = fieldTypes.get(field.toLowerCase(Locale.ENGLISH)); if (type == null) { // auth-param = token "=" ( token | quoted-string ) type = FieldType.TOKEN_OR_QUOTED_STRING; } + String value = null; switch (type) { case QUOTED_STRING: value = HttpParser.readQuotedString(input, false); @@ -131,6 +131,6 @@ QUOTED_STRING, TOKEN_OR_QUOTED_STRING, LHEX, - QUOTED_TOKEN; + QUOTED_TOKEN } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/ContentRange.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/ContentRange.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/ContentRange.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/ContentRange.java 2026-01-23 19:33:36.000000000 +0000 @@ -76,7 +76,7 @@ public static ContentRange parse(StringReader input) throws IOException { // Units (required) String units = HttpParser.readToken(input); - if (units == null || units.length() == 0) { + if (units == null || units.isEmpty()) { return null; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/Cookie.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/Cookie.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/Cookie.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/Cookie.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,8 +51,8 @@ private static final UserDataHelper invalidCookieLog = new UserDataHelper(log); private static final StringManager sm = StringManager.getManager("org.apache.tomcat.util.http.parser"); - private static final boolean isCookieOctet[] = new boolean[256]; - private static final boolean isText[] = new boolean[256]; + private static final boolean[] isCookieOctet = new boolean[256]; + private static final boolean[] isText = new boolean[256]; private static final byte[] EMPTY_BYTES = new byte[0]; private static final byte TAB_BYTE = (byte) 0x09; private static final byte SPACE_BYTE = (byte) 0x20; @@ -68,19 +68,11 @@ // %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E (RFC6265) // %x80 to %xFF (UTF-8) for (int i = 0; i < 256; i++) { - if (i < 0x21 || i == QUOTE_BYTE || i == COMMA_BYTE || i == SEMICOLON_BYTE || i == SLASH_BYTE || - i == DEL_BYTE) { - isCookieOctet[i] = false; - } else { - isCookieOctet[i] = true; - } + isCookieOctet[i] = !(i < 0x21 || i == QUOTE_BYTE || i == COMMA_BYTE || i == SEMICOLON_BYTE || + i == SLASH_BYTE || i == DEL_BYTE); } for (int i = 0; i < 256; i++) { - if (i < TAB_BYTE || (i > TAB_BYTE && i < SPACE_BYTE) || i == DEL_BYTE) { - isText[i] = false; - } else { - isText[i] = true; - } + isText[i] = !(i < TAB_BYTE || (i > TAB_BYTE && i < SPACE_BYTE) || i == DEL_BYTE); } } @@ -138,7 +130,7 @@ skipLWS(bb); value = readCookieValueRfc6265(bb); if (value == null) { - // Invalid cookie value. Skip to the next semi-colon + // Invalid cookie value. Skip to the next semicolon skipUntilSemiColon(bb); logInvalidHeader(start, bb); continue; @@ -150,7 +142,7 @@ if (skipResult == SkipResult.FOUND) { // NO-OP } else if (skipResult == SkipResult.NOT_FOUND) { - // Invalid cookie. Ignore it and skip to the next semi-colon + // Invalid cookie. Ignore it and skip to the next semicolon skipUntilSemiColon(bb); logInvalidHeader(start, bb); continue; @@ -292,8 +284,8 @@ private static class ByteBuffer { private final byte[] bytes; - private int limit; - private int position = 0; + private final int limit; + private int position; ByteBuffer(byte[] bytes, int offset, int len) { this.bytes = bytes; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/EntityTag.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/EntityTag.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/EntityTag.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/EntityTag.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,17 +35,20 @@ */ public static Boolean compareEntityTag(StringReader input, boolean compareWeak, String resourceETag) throws IOException { + + Boolean result = Boolean.FALSE; + // The resourceETag may be weak so to do weak comparison remove /W // before comparison String comparisonETag; - if (compareWeak && resourceETag.startsWith("W/")) { + if (resourceETag == null) { + comparisonETag = null; + } else if (compareWeak && resourceETag.startsWith("W/")) { comparisonETag = resourceETag.substring(2); } else { comparisonETag = resourceETag; } - Boolean result = Boolean.FALSE; - while (true) { boolean strong = false; HttpParser.skipLws(input); @@ -58,7 +61,7 @@ strong = true; break; case FOUND: - strong = false; + // Already set as strong = false; break; } @@ -71,7 +74,7 @@ } if (strong || compareWeak) { - if (comparisonETag.equals(value)) { + if (value.equals(comparisonETag)) { result = Boolean.TRUE; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/HttpHeaderParser.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/HttpHeaderParser.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/HttpHeaderParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/HttpHeaderParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,10 +33,6 @@ private static final byte SP = (byte) ' '; private static final byte HT = (byte) '\t'; private static final byte COLON = (byte) ':'; - private static final byte A = (byte) 'A'; - private static final byte a = (byte) 'a'; - private static final byte Z = (byte) 'Z'; - private static final byte LC_OFFSET = A - a; private final HeaderDataSource source; private final MimeHeaders headers; @@ -150,11 +146,6 @@ // skipLine() will handle the error return skipLine(); } - - // chr is next byte of header name. Convert to lowercase. - if (chr >= A && chr <= Z) { - source.getHeaderByteBuffer().put(pos, (byte) (chr - LC_OFFSET)); - } } // Skip the line and ignore the header diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/HttpParser.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/HttpParser.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/HttpParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/HttpParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -66,7 +66,7 @@ } // Token: Anything 0-127 that is not a control and not a separator - if (!IS_CONTROL[i] && !IS_SEPARATOR[i] && i < 128) { + if (!IS_CONTROL[i] && !IS_SEPARATOR[i]) { IS_TOKEN[i] = true; } @@ -494,7 +494,7 @@ static long readLong(Reader input) throws IOException { String digits = readDigits(input); - if (digits.length() == 0) { + if (digits.isEmpty()) { return -1; } @@ -971,7 +971,7 @@ private void relax(boolean[] flags, String relaxedChars) { - if (relaxedChars != null && relaxedChars.length() > 0) { + if (relaxedChars != null && !relaxedChars.isEmpty()) { char[] chars = relaxedChars.toCharArray(); for (char c : chars) { if (isRelaxable(c)) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/LocalStrings_es.properties tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/LocalStrings_es.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/LocalStrings_es.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/LocalStrings_es.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,5 +21,6 @@ http.illegalCharacterIpv4=El caracter [{0}] nunca es válido en una dirección IPv4.\n http.illegalCharacterIpv6=El caracter [{0}] nunca es válido en una dirección IPv6.\n http.invalidHextet=Hextet no válido. Hextet debe consistir de 4 caracteres hexadecimales o menos. +http.invalidIpv4Location=La dirección IPv6 contiene una dirección IPv4 incrustada en una ubicación no válida. http.singleColonEnd=Una dirección IPv6 no puede terminar con solo un ':'.\n http.tooManyColons=Una dirección IPv6 no puede contener más de 2 caracteres ":" seguidos diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -55,5 +55,6 @@ sf.numeric.decimalPartTooLong=小数点后超过3位 sf.numeric.decimalTooLong=小数位数超过了16个字符 sf.numeric.integerTooLong=一个整数中发现超过15位数字 +sf.numeric.integralPartTooLong=小数的整数部分超过了12位数字 sf.string.invalidCharacter=字符串中[{0}]字符无效 sf.string.invalidEscape=[{0}]字符不能被转义 diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/MediaType.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/MediaType.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/MediaType.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/MediaType.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,7 +17,7 @@ package org.apache.tomcat.util.http.parser; import java.io.IOException; -import java.io.StringReader; +import java.io.Reader; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; @@ -37,7 +37,7 @@ this.parameters = parameters; String cs = parameters.get("charset"); - if (cs != null && cs.length() > 0 && cs.charAt(0) == '"') { + if (cs != null && !cs.isEmpty() && cs.charAt(0) == '"') { cs = HttpParser.unquote(cs); } this.charset = cs; @@ -74,7 +74,7 @@ result.append(subtype); for (Map.Entry entry : parameters.entrySet()) { String value = entry.getValue(); - if (value == null || value.length() == 0) { + if (value == null || value.isEmpty()) { continue; } result.append(';'); @@ -124,11 +124,11 @@ * * @throws IOException if there was a problem reading the input */ - public static MediaType parseMediaType(StringReader input) throws IOException { + public static MediaType parseMediaType(Reader input) throws IOException { // Type (required) String type = HttpParser.readToken(input); - if (type == null || type.length() == 0) { + if (type == null || type.isEmpty()) { return null; } @@ -138,7 +138,7 @@ // Subtype (required) String subtype = HttpParser.readToken(input); - if (subtype == null || subtype.length() == 0) { + if (subtype == null || subtype.isEmpty()) { return null; } @@ -151,9 +151,11 @@ while (lookForSemiColon == SkipResult.FOUND) { String attribute = HttpParser.readToken(input); - String value = ""; + String value; if (HttpParser.skipConstant(input, "=") == SkipResult.FOUND) { value = HttpParser.readTokenOrQuotedString(input, true); + } else { + value = ""; } if (attribute != null) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/MediaTypeCache.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/MediaTypeCache.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/MediaTypeCache.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/MediaTypeCache.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,7 +51,7 @@ MediaType m = null; try { m = MediaType.parseMediaType(new StringReader(input)); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore - return null } if (m != null) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/Ranges.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/Ranges.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/Ranges.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/Ranges.java 2026-01-23 19:33:36.000000000 +0000 @@ -85,7 +85,7 @@ // Units (required) String units = HttpParser.readToken(input); - if (units == null || units.length() == 0) { + if (units == null || units.isEmpty()) { return null; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/StructuredField.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/StructuredField.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/StructuredField.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/StructuredField.java 2026-01-23 19:33:36.000000000 +0000 @@ -201,7 +201,7 @@ item = parseSfBoolean(input); } else { throw new IllegalArgumentException( - sm.getString("sf.bareitem.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); + sm.getString("sf.bareitem.invalidCharacter", String.format("\\u%04X", Integer.valueOf(c)))); } return item; @@ -209,10 +209,7 @@ static void parseSfParameters(Reader input, SfListMember listMember) throws IOException { - while (true) { - if (peek(input) != ';') { - break; - } + while (peek(input) == ';') { requireChar(input, ';'); skipSP(input); String key = parseSfKey(input); @@ -237,7 +234,7 @@ int c = input.read(); if (!isKeyFirst(c)) { throw new IllegalArgumentException( - sm.getString("sf.key.invalidFirstCharacter", String.format("\\u%40X", Integer.valueOf(c)))); + sm.getString("sf.key.invalidFirstCharacter", String.format("\\u%04X", Integer.valueOf(c)))); } while (c != -1 && isKey(c)) { @@ -267,7 +264,7 @@ if (!HttpParser.isNumeric(c)) { throw new IllegalArgumentException( - sm.getString("sf.numeric.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); + sm.getString("sf.numeric.invalidCharacter", String.format("\\u%04X", Integer.valueOf(c)))); } result.append((char) c); input.mark(1); @@ -324,7 +321,7 @@ c = input.read(); if (c != '\\' && c != '\"') { throw new IllegalArgumentException( - sm.getString("sf.string.invalidEscape", String.format("\\u%40X", Integer.valueOf(c)))); + sm.getString("sf.string.invalidEscape", String.format("\\u%04X", Integer.valueOf(c)))); } } else { if (c == '\"') { @@ -333,7 +330,7 @@ // This test also covers unexpected EOF if (c < 32 || c > 126) { throw new IllegalArgumentException( - sm.getString("sf.string.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); + sm.getString("sf.string.invalidCharacter", String.format("\\u%04X", Integer.valueOf(c)))); } } result.append((char) c); @@ -375,7 +372,7 @@ base64.append((char) c); } else { throw new IllegalArgumentException( - sm.getString("sf.base64.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); + sm.getString("sf.base64.invalidCharacter", String.format("\\u%04X", Integer.valueOf(c)))); } } @@ -393,7 +390,7 @@ return new SfBoolean(false); } else { throw new IllegalArgumentException( - sm.getString("sf.boolean.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); + sm.getString("sf.boolean.invalidCharacter", String.format("\\u%04X", Integer.valueOf(c)))); } } @@ -428,7 +425,7 @@ } } throw new IllegalArgumentException( - sm.getString("sf.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); + sm.getString("sf.invalidCharacter", String.format("\\u%04X", Integer.valueOf(c)))); } @@ -437,7 +434,7 @@ int c = input.read(); if (c == required) { throw new IllegalArgumentException( - sm.getString("sf.invalidCharacter", String.format("\\u%40X", Integer.valueOf(c)))); + sm.getString("sf.invalidCharacter", String.format("\\u%04X", Integer.valueOf(c)))); } input.reset(); } @@ -502,7 +499,7 @@ static class SfDictionary { - private Map dictionary = new LinkedHashMap<>(); + private final Map dictionary = new LinkedHashMap<>(); void addDictionaryMember(String key, SfListMember value) { dictionary.put(key, value); @@ -514,7 +511,7 @@ } static class SfList { - private List listMembers = new ArrayList<>(); + private final List listMembers = new ArrayList<>(); void addListMember(SfListMember listMember) { listMembers.add(listMember); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/TE.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/TE.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/TE.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/TE.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.http.parser; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TE { + + private final String encoding; + private final Map parameters; + private final double quality; + + protected TE(String encoding, Map parameters, double quality) { + this.encoding = encoding; + this.parameters = parameters; + this.quality = quality; + } + + public String getEncoding() { + return encoding; + } + + public Map getParameters() { + return parameters; + } + + public double getQuality() { + return quality; + } + + + public static List parse(StringReader input) throws IOException { + + List result = new ArrayList<>(); + + do { + String encoding = HttpParser.readToken(input); + if (encoding == null) { + // Invalid encoding, skip to the next one + HttpParser.skipUntil(input, 0, ','); + continue; + } + + if (encoding.isEmpty()) { + // No more data to read + break; + } + + Map parameters = new HashMap<>(); + + // Parse parameters (including optional quality) + while (HttpParser.skipConstant(input, ";") == SkipResult.FOUND) { + String name = HttpParser.readToken(input); + String value = null; + if (HttpParser.skipConstant(input, "=") == SkipResult.FOUND) { + value = HttpParser.readTokenOrQuotedString(input, true); + } + if (name != null && value != null) { + parameters.put(name, value); + } + } + + // See if a quality has been provided in the parameters + double quality = 1; + String q = parameters.remove("q"); + if (q != null) { + quality = HttpParser.readWeight(new StringReader("q=" + q), ','); + } + + if (quality > 0) { + result.add(new TE(encoding, parameters, quality)); + } + } while (true); + + return result; + } +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/TokenList.java tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/TokenList.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/http/parser/TokenList.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/http/parser/TokenList.java 2026-01-23 19:33:36.000000000 +0000 @@ -83,7 +83,7 @@ continue; } - if (element.length() == 0) { + if (element.isEmpty()) { // EOF after empty element break; } @@ -97,12 +97,10 @@ } else if (skipResult == SkipResult.FOUND) { valid = true; collection.add(element.toLowerCase(Locale.ENGLISH)); - continue; } else { // Not a token - ignore it invalid = true; HttpParser.skipUntil(input, 0, ','); - continue; } } while (true); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/json/JSONFilter.java tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONFilter.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/json/JSONFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,15 +17,16 @@ package org.apache.tomcat.util.json; /** - * Provides escaping of values so they can be included in a JSON document. - * Escaping is based on the definition of JSON found in - * RFC 8259. + * Provides escaping of values so they can be included in a JSON document. Escaping is based on the definition of JSON + * found in RFC 8259. */ public class JSONFilter { /** * Escape the given char. + * * @param c the char + * * @return a char array with the escaped sequence */ public static char[] escape(char c) { @@ -48,7 +49,9 @@ /** * Escape the given string. + * * @param input the string + * * @return the escaped string */ public static String escape(String input) { @@ -57,7 +60,9 @@ /** * Escape the given char sequence. + * * @param input the char sequence + * * @return the escaped char sequence */ public static CharSequence escape(CharSequence input) { @@ -66,16 +71,17 @@ /** * Escape the given char sequence. - * @param input the char sequence - * @param off the offset on which escaping will start + * + * @param input the char sequence + * @param off the offset on which escaping will start * @param length the length which should be escaped + * * @return the escaped char sequence corresponding to the specified range */ public static CharSequence escape(CharSequence input, int off, int length) { /* - * While any character MAY be escaped, only U+0000 to U+001F (control - * characters), U+0022 (quotation mark) and U+005C (reverse solidus) - * MUST be escaped. + * While any character MAY be escaped, only U+0000 to U+001F (control characters), U+0022 (quotation mark) and + * U+005C (reverse solidus) MUST be escaped. */ StringBuilder escaped = null; int lastUnescapedStart = off; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/json/JSONParser.java tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONParser.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/json/JSONParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,8 @@ /** * Basic JSON parser generated by JavaCC. It consumes the input provided through the constructor when - * {@code parseObject()}, {@code parseList()}, or {@code parse()} are called, and there is no way to directly - * reset the state. + * {@code parseObject()}, {@code parseList()}, or {@code parse()} are called, and there is no way to directly reset the + * state. */ @SuppressWarnings("all") // Ignore warnings in generated code public class JSONParser implements JSONParserConstants { @@ -35,8 +35,8 @@ /** * Parses a JSON object into a Java {@code Map}. */ - public java.util.LinkedHashMap parseObject() throws ParseException { - java.util.LinkedHashMap toReturn = object(); + public java.util.LinkedHashMap parseObject() throws ParseException { + java.util.LinkedHashMap toReturn = object(); if (!ensureEOF()) { throw new IllegalStateException("Expected EOF, but still had content to parse"); } @@ -81,575 +81,633 @@ return this.nativeNumbers; } - final public boolean ensureEOF() throws ParseException { - jj_consume_token(0); -{if ("" != null) { - return true; -}} - throw new Error("Missing return statement in function"); -} + final public boolean ensureEOF() throws ParseException { + jj_consume_token(0); + { + if ("" != null) { + return true; + } + } + throw new Error("Missing return statement in function"); + } - final public Object anything() throws ParseException {Object x; - switch (jj_nt.kind) { - case BRACE_OPEN:{ - x = object(); - break; - } - case BRACKET_OPEN:{ - x = list(); - break; - } - case NUMBER_INTEGER: - case NUMBER_DECIMAL: - case TRUE: - case FALSE: - case NULL: - case STRING_SINGLE_EMPTY: - case STRING_DOUBLE_EMPTY: - case STRING_SINGLE_NONEMPTY: - case STRING_DOUBLE_NONEMPTY:{ - x = value(); - break; - } - default: - jj_la1[0] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } -{if ("" != null) { - return x; -}} - throw new Error("Missing return statement in function"); -} + final public Object anything() throws ParseException { + Object x; + switch (jj_nt.kind) { + case BRACE_OPEN: { + x = object(); + break; + } + case BRACKET_OPEN: { + x = list(); + break; + } + case NUMBER_INTEGER: + case NUMBER_DECIMAL: + case TRUE: + case FALSE: + case NULL: + case STRING_SINGLE_EMPTY: + case STRING_DOUBLE_EMPTY: + case STRING_SINGLE_NONEMPTY: + case STRING_DOUBLE_NONEMPTY: { + x = value(); + break; + } + default: + jj_la1[0] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + { + if ("" != null) { + return x; + } + } + throw new Error("Missing return statement in function"); + } - final public String objectKey() throws ParseException {Object o; - String key; - switch (jj_nt.kind) { - case STRING_SINGLE_EMPTY: - case STRING_DOUBLE_EMPTY: - case STRING_SINGLE_NONEMPTY: - case STRING_DOUBLE_NONEMPTY:{ - key = string(); - break; - } - case SYMBOL:{ - key = symbol(); - break; - } - case NULL:{ - nullValue(); -key = null; - break; - } - case NUMBER_INTEGER: - case NUMBER_DECIMAL: - case TRUE: - case FALSE:{ - switch (jj_nt.kind) { - case TRUE: - case FALSE:{ - o = booleanValue(); - break; - } - case NUMBER_INTEGER: - case NUMBER_DECIMAL:{ - o = number(); - break; - } - default: - jj_la1[1] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } -key = o.toString(); - break; - } - default: - jj_la1[2] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } -{if ("" != null) { - return key; -}} - throw new Error("Missing return statement in function"); -} + final public String objectKey() throws ParseException { + Object o; + String key; + switch (jj_nt.kind) { + case STRING_SINGLE_EMPTY: + case STRING_DOUBLE_EMPTY: + case STRING_SINGLE_NONEMPTY: + case STRING_DOUBLE_NONEMPTY: { + key = string(); + break; + } + case SYMBOL: { + key = symbol(); + break; + } + case NULL: { + nullValue(); + key = null; + break; + } + case NUMBER_INTEGER: + case NUMBER_DECIMAL: + case TRUE: + case FALSE: { + switch (jj_nt.kind) { + case TRUE: + case FALSE: { + o = booleanValue(); + break; + } + case NUMBER_INTEGER: + case NUMBER_DECIMAL: { + o = number(); + break; + } + default: + jj_la1[1] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + key = o.toString(); + break; + } + default: + jj_la1[2] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + { + if ("" != null) { + return key; + } + } + throw new Error("Missing return statement in function"); + } - final public java.util.LinkedHashMap object() throws ParseException {final java.util.LinkedHashMap map = new java.util.LinkedHashMap(); - String key; - Object value; - jj_consume_token(BRACE_OPEN); - switch (jj_nt.kind) { - case NUMBER_INTEGER: - case NUMBER_DECIMAL: - case TRUE: - case FALSE: - case NULL: - case STRING_SINGLE_EMPTY: - case STRING_DOUBLE_EMPTY: - case STRING_SINGLE_NONEMPTY: - case STRING_DOUBLE_NONEMPTY: - case SYMBOL:{ - key = objectKey(); - jj_consume_token(COLON); - value = anything(); -map.put(key, value); -key = null; value = null; - label_1: - while (true) { + final public java.util.LinkedHashMap object() throws ParseException { + final java.util.LinkedHashMap map = new java.util.LinkedHashMap(); + String key; + Object value; + jj_consume_token(BRACE_OPEN); switch (jj_nt.kind) { - case COMMA:{ - ; - break; - } - default: - jj_la1[3] = jj_gen; - break label_1; - } - jj_consume_token(COMMA); - key = objectKey(); - jj_consume_token(COLON); - value = anything(); -map.put(key, value); -key = null; value = null; - } - break; - } - default: - jj_la1[4] = jj_gen; - ; - } - jj_consume_token(BRACE_CLOSE); -{if ("" != null) { - return map; -}} - throw new Error("Missing return statement in function"); -} + case NUMBER_INTEGER: + case NUMBER_DECIMAL: + case TRUE: + case FALSE: + case NULL: + case STRING_SINGLE_EMPTY: + case STRING_DOUBLE_EMPTY: + case STRING_SINGLE_NONEMPTY: + case STRING_DOUBLE_NONEMPTY: + case SYMBOL: { + key = objectKey(); + jj_consume_token(COLON); + value = anything(); + map.put(key, value); + key = null; + value = null; + label_1: + while (true) { + switch (jj_nt.kind) { + case COMMA: { + ; + break; + } + default: + jj_la1[3] = jj_gen; + break label_1; + } + jj_consume_token(COMMA); + key = objectKey(); + jj_consume_token(COLON); + value = anything(); + map.put(key, value); + key = null; + value = null; + } + break; + } + default: + jj_la1[4] = jj_gen; + ; + } + jj_consume_token(BRACE_CLOSE); + { + if ("" != null) { + return map; + } + } + throw new Error("Missing return statement in function"); + } - final public java.util.ArrayList list() throws ParseException {final java.util.ArrayList list = new java.util.ArrayList(); - Object value; - jj_consume_token(BRACKET_OPEN); - switch (jj_nt.kind) { - case BRACE_OPEN: - case BRACKET_OPEN: - case NUMBER_INTEGER: - case NUMBER_DECIMAL: - case TRUE: - case FALSE: - case NULL: - case STRING_SINGLE_EMPTY: - case STRING_DOUBLE_EMPTY: - case STRING_SINGLE_NONEMPTY: - case STRING_DOUBLE_NONEMPTY:{ - value = anything(); -list.add(value); -value = null; - label_2: - while (true) { + final public java.util.ArrayList list() throws ParseException { + final java.util.ArrayList list = new java.util.ArrayList(); + Object value; + jj_consume_token(BRACKET_OPEN); switch (jj_nt.kind) { - case COMMA:{ - ; - break; - } - default: - jj_la1[5] = jj_gen; - break label_2; - } - jj_consume_token(COMMA); - value = anything(); -list.add(value); -value = null; - } - break; - } - default: - jj_la1[6] = jj_gen; - ; - } - jj_consume_token(BRACKET_CLOSE); -list.trimToSize(); - {if ("" != null) { - return list; - }} - throw new Error("Missing return statement in function"); -} + case BRACE_OPEN: + case BRACKET_OPEN: + case NUMBER_INTEGER: + case NUMBER_DECIMAL: + case TRUE: + case FALSE: + case NULL: + case STRING_SINGLE_EMPTY: + case STRING_DOUBLE_EMPTY: + case STRING_SINGLE_NONEMPTY: + case STRING_DOUBLE_NONEMPTY: { + value = anything(); + list.add(value); + value = null; + label_2: + while (true) { + switch (jj_nt.kind) { + case COMMA: { + ; + break; + } + default: + jj_la1[5] = jj_gen; + break label_2; + } + jj_consume_token(COMMA); + value = anything(); + list.add(value); + value = null; + } + break; + } + default: + jj_la1[6] = jj_gen; + ; + } + jj_consume_token(BRACKET_CLOSE); + list.trimToSize(); + { + if ("" != null) { + return list; + } + } + throw new Error("Missing return statement in function"); + } - final public Object value() throws ParseException {Object x; - switch (jj_nt.kind) { - case STRING_SINGLE_EMPTY: - case STRING_DOUBLE_EMPTY: - case STRING_SINGLE_NONEMPTY: - case STRING_DOUBLE_NONEMPTY:{ - x = string(); - break; - } - case NUMBER_INTEGER: - case NUMBER_DECIMAL:{ - x = number(); - break; - } - case TRUE: - case FALSE:{ - x = booleanValue(); - break; - } - case NULL:{ - x = nullValue(); - break; - } - default: - jj_la1[7] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } -{if ("" != null) { - return x; -}} - throw new Error("Missing return statement in function"); -} + final public Object value() throws ParseException { + Object x; + switch (jj_nt.kind) { + case STRING_SINGLE_EMPTY: + case STRING_DOUBLE_EMPTY: + case STRING_SINGLE_NONEMPTY: + case STRING_DOUBLE_NONEMPTY: { + x = string(); + break; + } + case NUMBER_INTEGER: + case NUMBER_DECIMAL: { + x = number(); + break; + } + case TRUE: + case FALSE: { + x = booleanValue(); + break; + } + case NULL: { + x = nullValue(); + break; + } + default: + jj_la1[7] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + { + if ("" != null) { + return x; + } + } + throw new Error("Missing return statement in function"); + } - final public Object nullValue() throws ParseException { - jj_consume_token(NULL); -{if ("" != null) { - return null; -}} - throw new Error("Missing return statement in function"); -} + final public Object nullValue() throws ParseException { + jj_consume_token(NULL); + { + if ("" != null) { + return null; + } + } + throw new Error("Missing return statement in function"); + } - final public Boolean booleanValue() throws ParseException {Boolean b; - switch (jj_nt.kind) { - case TRUE:{ - jj_consume_token(TRUE); -b = Boolean.TRUE; - break; - } - case FALSE:{ - jj_consume_token(FALSE); -b = Boolean.FALSE; - break; - } - default: - jj_la1[8] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } -{if ("" != null) { - return b; -}} - throw new Error("Missing return statement in function"); -} + final public Boolean booleanValue() throws ParseException { + Boolean b; + switch (jj_nt.kind) { + case TRUE: { + jj_consume_token(TRUE); + b = Boolean.TRUE; + break; + } + case FALSE: { + jj_consume_token(FALSE); + b = Boolean.FALSE; + break; + } + default: + jj_la1[8] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + { + if ("" != null) { + return b; + } + } + throw new Error("Missing return statement in function"); + } - final public Number number() throws ParseException {Token t; - switch (jj_nt.kind) { - case NUMBER_DECIMAL:{ - t = jj_consume_token(NUMBER_DECIMAL); -if (nativeNumbers) { - {if ("" != null) { - return Long.valueOf(t.image); - }} - } else { - {if ("" != null) { - return new java.math.BigDecimal(t.image); - }} - } - break; - } - case NUMBER_INTEGER:{ - t = jj_consume_token(NUMBER_INTEGER); -if (nativeNumbers) { - {if ("" != null) { - return Double.valueOf(t.image); - }} - } else { - {if ("" != null) { - return new java.math.BigInteger(substringBefore(t.image, '.')); - }} - } - break; - } - default: - jj_la1[9] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + final public Number number() throws ParseException { + Token t; + switch (jj_nt.kind) { + case NUMBER_DECIMAL: { + t = jj_consume_token(NUMBER_DECIMAL); + if (nativeNumbers) { + { + if ("" != null) { + return Long.valueOf(t.image); + } + } + } else { + { + if ("" != null) { + return new java.math.BigDecimal(t.image); + } + } + } + break; + } + case NUMBER_INTEGER: { + t = jj_consume_token(NUMBER_INTEGER); + if (nativeNumbers) { + { + if ("" != null) { + return Double.valueOf(t.image); + } + } + } else { + { + if ("" != null) { + return new java.math.BigInteger(substringBefore(t.image, '.')); + } + } + } + break; + } + default: + jj_la1[9] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + throw new Error("Missing return statement in function"); } - throw new Error("Missing return statement in function"); -} - final public String string() throws ParseException {String s; - switch (jj_nt.kind) { - case STRING_DOUBLE_EMPTY: - case STRING_DOUBLE_NONEMPTY:{ - s = doubleQuoteString(); - break; - } - case STRING_SINGLE_EMPTY: - case STRING_SINGLE_NONEMPTY:{ - s = singleQuoteString(); - break; - } - default: - jj_la1[10] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } -{if ("" != null) { - return s; -}} - throw new Error("Missing return statement in function"); -} + final public String string() throws ParseException { + String s; + switch (jj_nt.kind) { + case STRING_DOUBLE_EMPTY: + case STRING_DOUBLE_NONEMPTY: { + s = doubleQuoteString(); + break; + } + case STRING_SINGLE_EMPTY: + case STRING_SINGLE_NONEMPTY: { + s = singleQuoteString(); + break; + } + default: + jj_la1[10] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + { + if ("" != null) { + return s; + } + } + throw new Error("Missing return statement in function"); + } - final public String doubleQuoteString() throws ParseException { - switch (jj_nt.kind) { - case STRING_DOUBLE_EMPTY:{ - jj_consume_token(STRING_DOUBLE_EMPTY); -{if ("" != null) { - return ""; -}} - break; - } - case STRING_DOUBLE_NONEMPTY:{ - jj_consume_token(STRING_DOUBLE_NONEMPTY); -String image = token.image; - {if ("" != null) { - return image.substring(1, image.length() - 1); - }} - break; - } - default: - jj_la1[11] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + final public String doubleQuoteString() throws ParseException { + switch (jj_nt.kind) { + case STRING_DOUBLE_EMPTY: { + jj_consume_token(STRING_DOUBLE_EMPTY); + { + if ("" != null) { + return ""; + } + } + break; + } + case STRING_DOUBLE_NONEMPTY: { + jj_consume_token(STRING_DOUBLE_NONEMPTY); + String image = token.image; + { + if ("" != null) { + return image.substring(1, image.length() - 1); + } + } + break; + } + default: + jj_la1[11] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + throw new Error("Missing return statement in function"); } - throw new Error("Missing return statement in function"); -} - final public String singleQuoteString() throws ParseException { - switch (jj_nt.kind) { - case STRING_SINGLE_EMPTY:{ - jj_consume_token(STRING_SINGLE_EMPTY); -{if ("" != null) { - return ""; -}} - break; - } - case STRING_SINGLE_NONEMPTY:{ - jj_consume_token(STRING_SINGLE_NONEMPTY); -String image = token.image; - {if ("" != null) { - return image.substring(1, image.length() - 1); - }} - break; - } - default: - jj_la1[12] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + final public String singleQuoteString() throws ParseException { + switch (jj_nt.kind) { + case STRING_SINGLE_EMPTY: { + jj_consume_token(STRING_SINGLE_EMPTY); + { + if ("" != null) { + return ""; + } + } + break; + } + case STRING_SINGLE_NONEMPTY: { + jj_consume_token(STRING_SINGLE_NONEMPTY); + String image = token.image; + { + if ("" != null) { + return image.substring(1, image.length() - 1); + } + } + break; + } + default: + jj_la1[12] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + throw new Error("Missing return statement in function"); } - throw new Error("Missing return statement in function"); -} - final public String symbol() throws ParseException { - jj_consume_token(SYMBOL); -{if ("" != null) { - return token.image; -}} - throw new Error("Missing return statement in function"); -} + final public String symbol() throws ParseException { + jj_consume_token(SYMBOL); + { + if ("" != null) { + return token.image; + } + } + throw new Error("Missing return statement in function"); + } - /** Generated Token Manager. */ - public JSONParserTokenManager token_source; - JavaCharStream jj_input_stream; - /** Current token. */ - public Token token; - /** Next token. */ - public Token jj_nt; - private int jj_gen; - final private int[] jj_la1 = new int[13]; - static private int[] jj_la1_0; - static { - jj_la1_init_0(); - } - private static void jj_la1_init_0() { - jj_la1_0 = new int[] {0xccf8480,0x78000,0x1ccf8000,0x40,0x1ccf8000,0x40,0xccf8480,0xccf8000,0x60000,0x18000,0xcc00000,0x8800000,0x4400000,}; - } - - /** Constructor with InputStream. */ - public JSONParser(java.io.InputStream stream) { - this(stream, null); - } - /** Constructor with InputStream and supplied encoding */ - public JSONParser(java.io.InputStream stream, String encoding) { - try { jj_input_stream = new JavaCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } - token_source = new JSONParserTokenManager(jj_input_stream); - token = new Token(); - token.next = jj_nt = token_source.getNextToken(); - jj_gen = 0; - for (int i = 0; i < 13; i++) { - jj_la1[i] = -1; - } - } - - /** Reinitialise. */ - public void ReInit(java.io.InputStream stream) { - ReInit(stream, null); - } - /** Reinitialise. */ - public void ReInit(java.io.InputStream stream, String encoding) { - try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); } - token_source.ReInit(jj_input_stream); - token = new Token(); - token.next = jj_nt = token_source.getNextToken(); - jj_gen = 0; - for (int i = 0; i < 13; i++) { - jj_la1[i] = -1; - } - } - - /** Constructor. */ - public JSONParser(java.io.Reader stream) { - jj_input_stream = new JavaCharStream(stream, 1, 1); - token_source = new JSONParserTokenManager(jj_input_stream); - token = new Token(); - token.next = jj_nt = token_source.getNextToken(); - jj_gen = 0; - for (int i = 0; i < 13; i++) { - jj_la1[i] = -1; - } - } - - /** Reinitialise. */ - public void ReInit(java.io.Reader stream) { - if (jj_input_stream == null) { - jj_input_stream = new JavaCharStream(stream, 1, 1); - } else { - jj_input_stream.ReInit(stream, 1, 1); - } - if (token_source == null) { - token_source = new JSONParserTokenManager(jj_input_stream); - } - - token_source.ReInit(jj_input_stream); - token = new Token(); - token.next = jj_nt = token_source.getNextToken(); - jj_gen = 0; - for (int i = 0; i < 13; i++) { - jj_la1[i] = -1; - } - } - - /** Constructor with generated Token Manager. */ - public JSONParser(JSONParserTokenManager tm) { - token_source = tm; - token = new Token(); - token.next = jj_nt = token_source.getNextToken(); - jj_gen = 0; - for (int i = 0; i < 13; i++) { - jj_la1[i] = -1; - } - } - - /** Reinitialise. */ - public void ReInit(JSONParserTokenManager tm) { - token_source = tm; - token = new Token(); - token.next = jj_nt = token_source.getNextToken(); - jj_gen = 0; - for (int i = 0; i < 13; i++) { - jj_la1[i] = -1; - } - } - - private Token jj_consume_token(int kind) throws ParseException { - Token oldToken = token; - if ((token = jj_nt).next != null) { - jj_nt = jj_nt.next; - } else { - jj_nt = jj_nt.next = token_source.getNextToken(); - } - if (token.kind == kind) { - jj_gen++; - return token; - } - jj_nt = token; - token = oldToken; - jj_kind = kind; - throw generateParseException(); - } - - -/** Get the next Token. */ - final public Token getNextToken() { - if ((token = jj_nt).next != null) { - jj_nt = jj_nt.next; - } else { - jj_nt = jj_nt.next = token_source.getNextToken(); - } - jj_gen++; - return token; - } - -/** Get the specific Token. */ - final public Token getToken(int index) { - Token t = token; - for (int i = 0; i < index; i++) { - if (t.next != null) { - t = t.next; - } else { - t = t.next = token_source.getNextToken(); - } - } - return t; - } - - private java.util.List jj_expentries = new java.util.ArrayList(); - private int[] jj_expentry; - private int jj_kind = -1; - - /** Generate ParseException. */ - public ParseException generateParseException() { - jj_expentries.clear(); - boolean[] la1tokens = new boolean[29]; - if (jj_kind >= 0) { - la1tokens[jj_kind] = true; - jj_kind = -1; - } - for (int i = 0; i < 13; i++) { - if (jj_la1[i] == jj_gen) { - for (int j = 0; j < 32; j++) { - if ((jj_la1_0[i] & (1< jj_expentries = new java.util.ArrayList(); + private int[] jj_expentry; + private int jj_kind = -1; + + /** Generate ParseException. */ + public ParseException generateParseException() { + jj_expentries.clear(); + boolean[] la1tokens = new boolean[29]; + if (jj_kind >= 0) { + la1tokens[jj_kind] = true; + jj_kind = -1; + } + for (int i = 0; i < 13; i++) { + if (jj_la1[i] == jj_gen) { + for (int j = 0; j < 32; j++) { + if ((jj_la1_0[i] & (1 << j)) != 0) { + la1tokens[j] = true; + } + } + } + } + for (int i = 0; i < 29; i++) { + if (la1tokens[i]) { + jj_expentry = new int[1]; + jj_expentry[0] = i; + jj_expentries.add(jj_expentry); + } + } + int[][] exptokseq = new int[jj_expentries.size()][]; + for (int i = 0; i < jj_expentries.size(); i++) { + exptokseq[i] = jj_expentries.get(i); + } + return new ParseException(token, exptokseq, tokenImage); + } + + private int trace_indent = 0; + private boolean trace_enabled; + + /** Trace enabled. */ + final public boolean trace_enabled() { + return trace_enabled; + } + + /** Enable tracing. */ + final public void enable_tracing() { + } + + /** Disable tracing. */ + final public void disable_tracing() { + } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/json/JSONParserConstants.java tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONParserConstants.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/json/JSONParserConstants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONParserConstants.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,104 +19,77 @@ /** - * Token literal values and constants. - * Generated by org.javacc.parser.OtherFilesGen#start() + * Token literal values and constants. Generated by org.javacc.parser.OtherFilesGen#start() */ public interface JSONParserConstants { - /** End of File. */ - int EOF = 0; - /** RegularExpression Id. */ - int C_SINGLE_COMMENT = 1; - /** RegularExpression Id. */ - int C_MULTILINE_COMMENT = 2; - /** RegularExpression Id. */ - int SH_SINGLE_COMMENT = 3; - /** RegularExpression Id. */ - int WHITESPACE = 4; - /** RegularExpression Id. */ - int EOL = 5; - /** RegularExpression Id. */ - int COMMA = 6; - /** RegularExpression Id. */ - int BRACE_OPEN = 7; - /** RegularExpression Id. */ - int BRACE_CLOSE = 8; - /** RegularExpression Id. */ - int COLON = 9; - /** RegularExpression Id. */ - int BRACKET_OPEN = 10; - /** RegularExpression Id. */ - int BRACKET_CLOSE = 11; - /** RegularExpression Id. */ - int ZERO = 12; - /** RegularExpression Id. */ - int DIGIT_NONZERO = 13; - /** RegularExpression Id. */ - int DIGIT = 14; - /** RegularExpression Id. */ - int NUMBER_INTEGER = 15; - /** RegularExpression Id. */ - int NUMBER_DECIMAL = 16; - /** RegularExpression Id. */ - int TRUE = 17; - /** RegularExpression Id. */ - int FALSE = 18; - /** RegularExpression Id. */ - int NULL = 19; - /** RegularExpression Id. */ - int QUOTE_DOUBLE = 20; - /** RegularExpression Id. */ - int QUOTE_SINGLE = 21; - /** RegularExpression Id. */ - int STRING_SINGLE_EMPTY = 22; - /** RegularExpression Id. */ - int STRING_DOUBLE_EMPTY = 23; - /** RegularExpression Id. */ - int STRING_SINGLE_BODY = 24; - /** RegularExpression Id. */ - int STRING_DOUBLE_BODY = 25; - /** RegularExpression Id. */ - int STRING_SINGLE_NONEMPTY = 26; - /** RegularExpression Id. */ - int STRING_DOUBLE_NONEMPTY = 27; - /** RegularExpression Id. */ - int SYMBOL = 28; - - /** Lexical state. */ - int DEFAULT = 0; - - /** Literal token values. */ - String[] tokenImage = { - "", - "", - "", - "", - "", - "", - "\",\"", - "\"{\"", - "\"}\"", - "\":\"", - "\"[\"", - "\"]\"", - "\"0\"", - "", - "", - "", - "", - "\"true\"", - "\"false\"", - "\"null\"", - "\"\\\"\"", - "\"\\\'\"", - "\"\\\'\\\'\"", - "\"\\\"\\\"\"", - "", - "", - "", - "", - "", - }; + /** End of File. */ + int EOF = 0; + /** RegularExpression Id. */ + int C_SINGLE_COMMENT = 1; + /** RegularExpression Id. */ + int C_MULTILINE_COMMENT = 2; + /** RegularExpression Id. */ + int SH_SINGLE_COMMENT = 3; + /** RegularExpression Id. */ + int WHITESPACE = 4; + /** RegularExpression Id. */ + int EOL = 5; + /** RegularExpression Id. */ + int COMMA = 6; + /** RegularExpression Id. */ + int BRACE_OPEN = 7; + /** RegularExpression Id. */ + int BRACE_CLOSE = 8; + /** RegularExpression Id. */ + int COLON = 9; + /** RegularExpression Id. */ + int BRACKET_OPEN = 10; + /** RegularExpression Id. */ + int BRACKET_CLOSE = 11; + /** RegularExpression Id. */ + int ZERO = 12; + /** RegularExpression Id. */ + int DIGIT_NONZERO = 13; + /** RegularExpression Id. */ + int DIGIT = 14; + /** RegularExpression Id. */ + int NUMBER_INTEGER = 15; + /** RegularExpression Id. */ + int NUMBER_DECIMAL = 16; + /** RegularExpression Id. */ + int TRUE = 17; + /** RegularExpression Id. */ + int FALSE = 18; + /** RegularExpression Id. */ + int NULL = 19; + /** RegularExpression Id. */ + int QUOTE_DOUBLE = 20; + /** RegularExpression Id. */ + int QUOTE_SINGLE = 21; + /** RegularExpression Id. */ + int STRING_SINGLE_EMPTY = 22; + /** RegularExpression Id. */ + int STRING_DOUBLE_EMPTY = 23; + /** RegularExpression Id. */ + int STRING_SINGLE_BODY = 24; + /** RegularExpression Id. */ + int STRING_DOUBLE_BODY = 25; + /** RegularExpression Id. */ + int STRING_SINGLE_NONEMPTY = 26; + /** RegularExpression Id. */ + int STRING_DOUBLE_NONEMPTY = 27; + /** RegularExpression Id. */ + int SYMBOL = 28; + + /** Lexical state. */ + int DEFAULT = 0; + + /** Literal token values. */ + String[] tokenImage = { "", "", "", "", + "", "", "\",\"", "\"{\"", "\"}\"", "\":\"", "\"[\"", "\"]\"", "\"0\"", "", + "", "", "", "\"true\"", "\"false\"", "\"null\"", "\"\\\"\"", + "\"\\\'\"", "\"\\\'\\\'\"", "\"\\\"\\\"\"", "", "", + "", "", "", }; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/json/JSONParserTokenManager.java tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONParserTokenManager.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/json/JSONParserTokenManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/json/JSONParserTokenManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,842 +22,850 @@ @SuppressWarnings("all") // Ignore warnings in generated code public class JSONParserTokenManager implements JSONParserConstants { - /** Debug output. */ - public java.io.PrintStream debugStream = System.out; - /** Set debug output. */ - public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; } -private final int jjStopStringLiteralDfa_0(int pos, long active0){ - switch (pos) - { - case 0: - if ((active0 & 0xe0000L) != 0L) - { - jjmatchedKind = 28; - return 15; - } - if ((active0 & 0x400000L) != 0L) { - return 38; - } - if ((active0 & 0x800000L) != 0L) { - return 39; - } - return -1; - case 1: - if ((active0 & 0xe0000L) != 0L) - { - jjmatchedKind = 28; - jjmatchedPos = 1; - return 15; - } - return -1; - case 2: - if ((active0 & 0xe0000L) != 0L) - { - jjmatchedKind = 28; - jjmatchedPos = 2; - return 15; - } - return -1; - case 3: - if ((active0 & 0xa0000L) != 0L) { - return 15; - } - if ((active0 & 0x40000L) != 0L) - { - jjmatchedKind = 28; - jjmatchedPos = 3; - return 15; - } - return -1; - default : - return -1; - } -} -private final int jjStartNfa_0(int pos, long active0){ - return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1); -} -private int jjStopAtPos(int pos, int kind) -{ - jjmatchedKind = kind; - jjmatchedPos = pos; - return pos + 1; -} -private int jjMoveStringLiteralDfa0_0(){ - switch(curChar) - { - case 34: - return jjMoveStringLiteralDfa1_0(0x800000L); - case 39: - return jjMoveStringLiteralDfa1_0(0x400000L); - case 44: - return jjStopAtPos(0, 6); - case 58: - return jjStopAtPos(0, 9); - case 91: - return jjStopAtPos(0, 10); - case 93: - return jjStopAtPos(0, 11); - case 70: - case 102: - return jjMoveStringLiteralDfa1_0(0x40000L); - case 78: - case 110: - return jjMoveStringLiteralDfa1_0(0x80000L); - case 84: - case 116: - return jjMoveStringLiteralDfa1_0(0x20000L); - case 123: - return jjStopAtPos(0, 7); - case 125: - return jjStopAtPos(0, 8); - default : - return jjMoveNfa_0(0, 0); - } -} -private int jjMoveStringLiteralDfa1_0(long active0){ - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { - jjStopStringLiteralDfa_0(0, active0); - return 1; - } - switch(curChar) - { - case 34: - if ((active0 & 0x800000L) != 0L) { - return jjStopAtPos(1, 23); - } - break; - case 39: - if ((active0 & 0x400000L) != 0L) { - return jjStopAtPos(1, 22); - } - break; - case 65: - case 97: - return jjMoveStringLiteralDfa2_0(active0, 0x40000L); - case 82: - case 114: - return jjMoveStringLiteralDfa2_0(active0, 0x20000L); - case 85: - case 117: - return jjMoveStringLiteralDfa2_0(active0, 0x80000L); - default : - break; - } - return jjStartNfa_0(0, active0); -} -private int jjMoveStringLiteralDfa2_0(long old0, long active0){ - if (((active0 &= old0)) == 0L) { - return jjStartNfa_0(0, old0); - } - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { - jjStopStringLiteralDfa_0(1, active0); - return 2; - } - switch(curChar) - { - case 76: - case 108: - return jjMoveStringLiteralDfa3_0(active0, 0xc0000L); - case 85: - case 117: - return jjMoveStringLiteralDfa3_0(active0, 0x20000L); - default : - break; - } - return jjStartNfa_0(1, active0); -} -private int jjMoveStringLiteralDfa3_0(long old0, long active0){ - if (((active0 &= old0)) == 0L) { - return jjStartNfa_0(1, old0); - } - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { - jjStopStringLiteralDfa_0(2, active0); - return 3; - } - switch(curChar) - { - case 69: - case 101: - if ((active0 & 0x20000L) != 0L) { - return jjStartNfaWithStates_0(3, 17, 15); - } - break; - case 76: - case 108: - if ((active0 & 0x80000L) != 0L) { - return jjStartNfaWithStates_0(3, 19, 15); - } - break; - case 83: - case 115: - return jjMoveStringLiteralDfa4_0(active0, 0x40000L); - default : - break; - } - return jjStartNfa_0(2, active0); -} -private int jjMoveStringLiteralDfa4_0(long old0, long active0){ - if (((active0 &= old0)) == 0L) { - return jjStartNfa_0(2, old0); - } - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { - jjStopStringLiteralDfa_0(3, active0); - return 4; - } - switch(curChar) - { - case 69: - case 101: - if ((active0 & 0x40000L) != 0L) { - return jjStartNfaWithStates_0(4, 18, 15); - } - break; - default : - break; - } - return jjStartNfa_0(3, active0); -} -private int jjStartNfaWithStates_0(int pos, int kind, int state) -{ - jjmatchedKind = kind; - jjmatchedPos = pos; - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return pos + 1; } - return jjMoveNfa_0(state, pos + 1); -} -static final long[] jjbitVec0 = { - 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL -}; -static final long[] jjbitVec2 = { - 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL -}; -private int jjMoveNfa_0(int startState, int curPos) -{ - int startsAt = 0; - jjnewStateCnt = 38; - int i = 1; - jjstateSet[0] = startState; - int kind = 0x7fffffff; - for (;;) - { - if (++jjround == 0x7fffffff) { - ReInitRounds(); - } - if (curChar < 64) - { - long l = 1L << curChar; - do - { - switch(jjstateSet[--i]) - { - case 0: - if ((0x3ff000000000000L & l) != 0L) - { - if (kind > 28) { - kind = 28; - } - { jjCheckNAdd(15); } - } - else if ((0x3400L & l) != 0L) - { - if (kind > 5) { - kind = 5; - } - } - else if ((0x100000200L & l) != 0L) - { - if (kind > 4) { - kind = 4; + /** Debug output. */ + public java.io.PrintStream debugStream = System.out; + + /** Set debug output. */ + public void setDebugStream(java.io.PrintStream ds) { + debugStream = ds; + } + + private final int jjStopStringLiteralDfa_0(int pos, long active0) { + switch (pos) { + case 0: + if ((active0 & 0xe0000L) != 0L) { + jjmatchedKind = 28; + return 15; + } + if ((active0 & 0x400000L) != 0L) { + return 38; + } + if ((active0 & 0x800000L) != 0L) { + return 39; + } + return -1; + case 1: + if ((active0 & 0xe0000L) != 0L) { + jjmatchedKind = 28; + jjmatchedPos = 1; + return 15; + } + return -1; + case 2: + if ((active0 & 0xe0000L) != 0L) { + jjmatchedKind = 28; + jjmatchedPos = 2; + return 15; + } + return -1; + case 3: + if ((active0 & 0xa0000L) != 0L) { + return 15; + } + if ((active0 & 0x40000L) != 0L) { + jjmatchedKind = 28; + jjmatchedPos = 3; + return 15; + } + return -1; + default: + return -1; + } + } + + private final int jjStartNfa_0(int pos, long active0) { + return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1); + } + + private int jjStopAtPos(int pos, int kind) { + jjmatchedKind = kind; + jjmatchedPos = pos; + return pos + 1; + } + + private int jjMoveStringLiteralDfa0_0() { + switch (curChar) { + case 34: + return jjMoveStringLiteralDfa1_0(0x800000L); + case 39: + return jjMoveStringLiteralDfa1_0(0x400000L); + case 44: + return jjStopAtPos(0, 6); + case 58: + return jjStopAtPos(0, 9); + case 91: + return jjStopAtPos(0, 10); + case 93: + return jjStopAtPos(0, 11); + case 70: + case 102: + return jjMoveStringLiteralDfa1_0(0x40000L); + case 78: + case 110: + return jjMoveStringLiteralDfa1_0(0x80000L); + case 84: + case 116: + return jjMoveStringLiteralDfa1_0(0x20000L); + case 123: + return jjStopAtPos(0, 7); + case 125: + return jjStopAtPos(0, 8); + default: + return jjMoveNfa_0(0, 0); + } + } + + private int jjMoveStringLiteralDfa1_0(long active0) { + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_0(0, active0); + return 1; + } + switch (curChar) { + case 34: + if ((active0 & 0x800000L) != 0L) { + return jjStopAtPos(1, 23); + } + break; + case 39: + if ((active0 & 0x400000L) != 0L) { + return jjStopAtPos(1, 22); + } + break; + case 65: + case 97: + return jjMoveStringLiteralDfa2_0(active0, 0x40000L); + case 82: + case 114: + return jjMoveStringLiteralDfa2_0(active0, 0x20000L); + case 85: + case 117: + return jjMoveStringLiteralDfa2_0(active0, 0x80000L); + default: + break; + } + return jjStartNfa_0(0, active0); + } + + private int jjMoveStringLiteralDfa2_0(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_0(0, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_0(1, active0); + return 2; + } + switch (curChar) { + case 76: + case 108: + return jjMoveStringLiteralDfa3_0(active0, 0xc0000L); + case 85: + case 117: + return jjMoveStringLiteralDfa3_0(active0, 0x20000L); + default: + break; + } + return jjStartNfa_0(1, active0); + } + + private int jjMoveStringLiteralDfa3_0(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_0(1, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_0(2, active0); + return 3; + } + switch (curChar) { + case 69: + case 101: + if ((active0 & 0x20000L) != 0L) { + return jjStartNfaWithStates_0(3, 17, 15); + } + break; + case 76: + case 108: + if ((active0 & 0x80000L) != 0L) { + return jjStartNfaWithStates_0(3, 19, 15); + } + break; + case 83: + case 115: + return jjMoveStringLiteralDfa4_0(active0, 0x40000L); + default: + break; + } + return jjStartNfa_0(2, active0); + } + + private int jjMoveStringLiteralDfa4_0(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_0(2, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_0(3, active0); + return 4; + } + switch (curChar) { + case 69: + case 101: + if ((active0 & 0x40000L) != 0L) { + return jjStartNfaWithStates_0(4, 18, 15); + } + break; + default: + break; + } + return jjStartNfa_0(3, active0); + } + + private int jjStartNfaWithStates_0(int pos, int kind, int state) { + jjmatchedKind = kind; + jjmatchedPos = pos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return pos + 1; + } + return jjMoveNfa_0(state, pos + 1); + } + + static final long[] jjbitVec0 = + { 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL }; + static final long[] jjbitVec2 = { 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL }; + + private int jjMoveNfa_0(int startState, int curPos) { + int startsAt = 0; + jjnewStateCnt = 38; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) { + if (++jjround == 0x7fffffff) { + ReInitRounds(); + } + if (curChar < 64) { + long l = 1L << curChar; + do { + switch (jjstateSet[--i]) { + case 0: + if ((0x3ff000000000000L & l) != 0L) { + if (kind > 28) { + kind = 28; + } + { + jjCheckNAdd(15); + } + } else if ((0x3400L & l) != 0L) { + if (kind > 5) { + kind = 5; + } + } else if ((0x100000200L & l) != 0L) { + if (kind > 4) { + kind = 4; + } + } else if (curChar == 45) { + jjCheckNAddStates(0, 3); + } else if (curChar == 47) { + jjAddStates(4, 5); + } else if (curChar == 34) { + jjCheckNAddTwoStates(11, 12); + } else if (curChar == 39) { + jjCheckNAddTwoStates(6, 7); + } else if (curChar == 35) { + jjCheckNAddTwoStates(1, 2); + } + if ((0x3fe000000000000L & l) != 0L) { + if (kind > 15) { + kind = 15; + } + { + jjCheckNAddStates(6, 8); + } + } else if (curChar == 48) { + if (kind > 15) { + kind = 15; + } + { + jjCheckNAddStates(9, 11); + } + } + break; + case 38: + case 6: + if ((0xffffff7fffffc9ffL & l) != 0L) { + jjCheckNAddStates(12, 14); + } + break; + case 39: + case 11: + if ((0xfffffffbffffc9ffL & l) != 0L) { + jjCheckNAddStates(15, 17); + } + break; + case 1: + if ((0xffffffffffffcbffL & l) != 0L) { + jjCheckNAddTwoStates(1, 2); + } + break; + case 2: + if ((0x3400L & l) != 0L && kind > 3) { + kind = 3; + } + break; + case 3: + if ((0x100000200L & l) != 0L && kind > 4) { + kind = 4; + } + break; + case 4: + if ((0x3400L & l) != 0L && kind > 5) { + kind = 5; + } + break; + case 5: + if (curChar == 39) { + jjCheckNAddTwoStates(6, 7); + } + break; + case 8: + if ((0x808000000000L & l) != 0L) { + jjCheckNAddStates(12, 14); + } + break; + case 9: + if (curChar == 39 && kind > 26) { + kind = 26; + } + break; + case 10: + if (curChar == 34) { + jjCheckNAddTwoStates(11, 12); + } + break; + case 13: + if ((0x800400000000L & l) != 0L) { + jjCheckNAddStates(15, 17); + } + break; + case 14: + if (curChar == 34 && kind > 27) { + kind = 27; + } + break; + case 15: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 28) { + kind = 28; + } { + jjCheckNAdd(15); + } + break; + case 16: + if (curChar == 47) { + jjAddStates(4, 5); + } + break; + case 17: + if (curChar == 47) { + jjCheckNAddTwoStates(18, 19); + } + break; + case 18: + if ((0xffffffffffffcbffL & l) != 0L) { + jjCheckNAddTwoStates(18, 19); + } + break; + case 19: + if ((0x3400L & l) != 0L && kind > 1) { + kind = 1; + } + break; + case 20: + if (curChar == 42) { + jjCheckNAddTwoStates(21, 23); + } + break; + case 21: { + jjCheckNAddTwoStates(21, 23); + } + break; + case 22: + if (curChar == 47 && kind > 2) { + kind = 2; + } + break; + case 23: + if (curChar == 42) { + jjstateSet[jjnewStateCnt++] = 22; + } + break; + case 24: + if (curChar == 45) { + jjCheckNAddStates(0, 3); + } + break; + case 25: + if (curChar != 48) { + break; + } + if (kind > 15) { + kind = 15; + } { + jjCheckNAdd(25); + } + break; + case 26: + if ((0x3fe000000000000L & l) == 0L) { + break; + } + if (kind > 15) { + kind = 15; + } { + jjCheckNAdd(27); + } + break; + case 27: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 15) { + kind = 15; + } { + jjCheckNAdd(27); + } + break; + case 28: + if (curChar == 48) { + jjCheckNAddTwoStates(28, 29); + } + break; + case 29: + if (curChar == 46) { + jjCheckNAdd(30); + } + break; + case 30: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 16) { + kind = 16; + } { + jjCheckNAddTwoStates(30, 31); + } + break; + case 32: + if ((0x280000000000L & l) != 0L) { + jjCheckNAdd(33); + } + break; + case 33: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 16) { + kind = 16; + } { + jjCheckNAdd(33); + } + break; + case 34: + if ((0x3fe000000000000L & l) != 0L) { + jjCheckNAddTwoStates(35, 29); + } + break; + case 35: + if ((0x3ff000000000000L & l) != 0L) { + jjCheckNAddTwoStates(35, 29); + } + break; + case 36: + if (curChar != 48) { + break; + } + if (kind > 15) { + kind = 15; + } { + jjCheckNAddStates(9, 11); + } + break; + case 37: + if ((0x3fe000000000000L & l) == 0L) { + break; + } + if (kind > 15) { + kind = 15; + } { + jjCheckNAddStates(6, 8); + } + break; + default: + break; } - } - else if (curChar == 45) - { jjCheckNAddStates(0, 3); } - else if (curChar == 47) - { jjAddStates(4, 5); } - else if (curChar == 34) - { jjCheckNAddTwoStates(11, 12); } - else if (curChar == 39) - { jjCheckNAddTwoStates(6, 7); } - else if (curChar == 35) - { jjCheckNAddTwoStates(1, 2); } - if ((0x3fe000000000000L & l) != 0L) - { - if (kind > 15) { - kind = 15; + } while (i != startsAt); + } else if (curChar < 128) { + long l = 1L << (curChar & 077); + do { + switch (jjstateSet[--i]) { + case 0: + case 15: + if ((0x7fffffe07fffffeL & l) == 0L) { + break; + } + if (kind > 28) { + kind = 28; + } { + jjCheckNAdd(15); + } + break; + case 38: + if ((0xffffffffefffffffL & l) != 0L) { + jjCheckNAddStates(12, 14); + } else if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 8; + } + break; + case 39: + if ((0xffffffffefffffffL & l) != 0L) { + jjCheckNAddStates(15, 17); + } else if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 13; + } + break; + case 1: { + jjAddStates(18, 19); + } + break; + case 6: + if ((0xffffffffefffffffL & l) != 0L) { + jjCheckNAddStates(12, 14); + } + break; + case 7: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 8; + } + break; + case 8: + if ((0x14404410144044L & l) != 0L) { + jjCheckNAddStates(12, 14); + } + break; + case 11: + if ((0xffffffffefffffffL & l) != 0L) { + jjCheckNAddStates(15, 17); + } + break; + case 12: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 13; + } + break; + case 13: + if ((0x14404410144044L & l) != 0L) { + jjCheckNAddStates(15, 17); + } + break; + case 18: { + jjAddStates(20, 21); + } + break; + case 21: { + jjAddStates(22, 23); + } + break; + case 31: + if ((0x2000000020L & l) != 0L) { + jjAddStates(24, 25); + } + break; + default: + break; } - { jjCheckNAddStates(6, 8); } - } - else if (curChar == 48) - { - if (kind > 15) { - kind = 15; + } while (i != startsAt); + } else { + int hiByte = (curChar >> 8); + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do { + switch (jjstateSet[--i]) { + case 38: + case 6: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjCheckNAddStates(12, 14); + } + break; + case 39: + case 11: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjCheckNAddStates(15, 17); + } + break; + case 1: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(18, 19); + } + break; + case 18: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(20, 21); + } + break; + case 21: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(22, 23); + } + break; + default: + if (i1 == 0 || l1 == 0 || i2 == 0 || l2 == 0) { + break; + } else { + break; + } } - { jjCheckNAddStates(9, 11); } - } - break; - case 38: - case 6: - if ((0xffffff7fffffc9ffL & l) != 0L) - { jjCheckNAddStates(12, 14); } - break; - case 39: - case 11: - if ((0xfffffffbffffc9ffL & l) != 0L) - { jjCheckNAddStates(15, 17); } - break; - case 1: - if ((0xffffffffffffcbffL & l) != 0L) - { jjCheckNAddTwoStates(1, 2); } - break; - case 2: - if ((0x3400L & l) != 0L && kind > 3) { - kind = 3; - } - break; - case 3: - if ((0x100000200L & l) != 0L && kind > 4) { - kind = 4; - } - break; - case 4: - if ((0x3400L & l) != 0L && kind > 5) { - kind = 5; - } - break; - case 5: - if (curChar == 39) - { jjCheckNAddTwoStates(6, 7); } - break; - case 8: - if ((0x808000000000L & l) != 0L) - { jjCheckNAddStates(12, 14); } - break; - case 9: - if (curChar == 39 && kind > 26) { - kind = 26; - } - break; - case 10: - if (curChar == 34) - { jjCheckNAddTwoStates(11, 12); } - break; - case 13: - if ((0x800400000000L & l) != 0L) - { jjCheckNAddStates(15, 17); } - break; - case 14: - if (curChar == 34 && kind > 27) { - kind = 27; - } - break; - case 15: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 28) { - kind = 28; - } - { jjCheckNAdd(15); } - break; - case 16: - if (curChar == 47) - { jjAddStates(4, 5); } - break; - case 17: - if (curChar == 47) - { jjCheckNAddTwoStates(18, 19); } - break; - case 18: - if ((0xffffffffffffcbffL & l) != 0L) - { jjCheckNAddTwoStates(18, 19); } - break; - case 19: - if ((0x3400L & l) != 0L && kind > 1) { - kind = 1; - } - break; - case 20: - if (curChar == 42) - { jjCheckNAddTwoStates(21, 23); } - break; - case 21: - { jjCheckNAddTwoStates(21, 23); } - break; - case 22: - if (curChar == 47 && kind > 2) { - kind = 2; - } - break; - case 23: - if (curChar == 42) { - jjstateSet[jjnewStateCnt++] = 22; - } - break; - case 24: - if (curChar == 45) - { jjCheckNAddStates(0, 3); } - break; - case 25: - if (curChar != 48) { - break; - } - if (kind > 15) { - kind = 15; - } - { jjCheckNAdd(25); } - break; - case 26: - if ((0x3fe000000000000L & l) == 0L) { - break; - } - if (kind > 15) { - kind = 15; - } - { jjCheckNAdd(27); } - break; - case 27: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 15) { - kind = 15; - } - { jjCheckNAdd(27); } - break; - case 28: - if (curChar == 48) - { jjCheckNAddTwoStates(28, 29); } - break; - case 29: - if (curChar == 46) - { jjCheckNAdd(30); } - break; - case 30: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 16) { - kind = 16; - } - { jjCheckNAddTwoStates(30, 31); } - break; - case 32: - if ((0x280000000000L & l) != 0L) - { jjCheckNAdd(33); } - break; - case 33: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 16) { - kind = 16; - } - { jjCheckNAdd(33); } - break; - case 34: - if ((0x3fe000000000000L & l) != 0L) - { jjCheckNAddTwoStates(35, 29); } - break; - case 35: - if ((0x3ff000000000000L & l) != 0L) - { jjCheckNAddTwoStates(35, 29); } - break; - case 36: - if (curChar != 48) { - break; - } - if (kind > 15) { - kind = 15; - } - { jjCheckNAddStates(9, 11); } - break; - case 37: - if ((0x3fe000000000000L & l) == 0L) { - break; - } - if (kind > 15) { - kind = 15; - } - { jjCheckNAddStates(6, 8); } - break; - default : break; + } while (i != startsAt); } - } while(i != startsAt); - } - else if (curChar < 128) - { - long l = 1L << (curChar & 077); - do - { - switch(jjstateSet[--i]) - { - case 0: - case 15: - if ((0x7fffffe07fffffeL & l) == 0L) { - break; - } - if (kind > 28) { - kind = 28; - } - { jjCheckNAdd(15); } - break; - case 38: - if ((0xffffffffefffffffL & l) != 0L) - { jjCheckNAddStates(12, 14); } - else if (curChar == 92) { - jjstateSet[jjnewStateCnt++] = 8; - } - break; - case 39: - if ((0xffffffffefffffffL & l) != 0L) - { jjCheckNAddStates(15, 17); } - else if (curChar == 92) { - jjstateSet[jjnewStateCnt++] = 13; - } - break; - case 1: - { jjAddStates(18, 19); } - break; - case 6: - if ((0xffffffffefffffffL & l) != 0L) - { jjCheckNAddStates(12, 14); } - break; - case 7: - if (curChar == 92) { - jjstateSet[jjnewStateCnt++] = 8; - } - break; - case 8: - if ((0x14404410144044L & l) != 0L) - { jjCheckNAddStates(12, 14); } - break; - case 11: - if ((0xffffffffefffffffL & l) != 0L) - { jjCheckNAddStates(15, 17); } - break; - case 12: - if (curChar == 92) { - jjstateSet[jjnewStateCnt++] = 13; - } - break; - case 13: - if ((0x14404410144044L & l) != 0L) - { jjCheckNAddStates(15, 17); } - break; - case 18: - { jjAddStates(20, 21); } - break; - case 21: - { jjAddStates(22, 23); } - break; - case 31: - if ((0x2000000020L & l) != 0L) - { jjAddStates(24, 25); } - break; - default : break; + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; } - } while(i != startsAt); - } else { - int hiByte = (curChar >> 8); - int i1 = hiByte >> 6; - long l1 = 1L << (hiByte & 077); - int i2 = (curChar & 0xff) >> 6; - long l2 = 1L << (curChar & 077); - do - { - switch(jjstateSet[--i]) - { - case 38: - case 6: - if (jjCanMove_0(hiByte, i1, i2, l1, l2)) - { jjCheckNAddStates(12, 14); } - break; - case 39: - case 11: - if (jjCanMove_0(hiByte, i1, i2, l1, l2)) - { jjCheckNAddStates(15, 17); } - break; - case 1: - if (jjCanMove_0(hiByte, i1, i2, l1, l2)) - { jjAddStates(18, 19); } - break; - case 18: - if (jjCanMove_0(hiByte, i1, i2, l1, l2)) - { jjAddStates(20, 21); } - break; - case 21: - if (jjCanMove_0(hiByte, i1, i2, l1, l2)) - { jjAddStates(22, 23); } - break; - default : if (i1 == 0 || l1 == 0 || i2 == 0 || l2 == 0) { + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 38 - (jjnewStateCnt = startsAt))) { + return curPos; + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; + } + } + } + + /** Token literal values. */ + public static final String[] jjstrLiteralImages = + { "", null, null, null, null, null, "\54", "\173", "\175", "\72", "\133", "\135", null, null, null, null, + null, null, null, null, null, null, "\47\47", "\42\42", null, null, null, null, null, }; + + protected Token jjFillToken() { + final Token t; + final String curTokenImage; + final int beginLine; + final int endLine; + final int beginColumn; + final int endColumn; + String im = jjstrLiteralImages[jjmatchedKind]; + curTokenImage = (im == null) ? input_stream.GetImage() : im; + beginLine = input_stream.getBeginLine(); + beginColumn = input_stream.getBeginColumn(); + endLine = input_stream.getEndLine(); + endColumn = input_stream.getEndColumn(); + t = Token.newToken(jjmatchedKind, curTokenImage); + + t.beginLine = beginLine; + t.endLine = endLine; + t.beginColumn = beginColumn; + t.endColumn = endColumn; + + return t; + } + + static final int[] jjnextStates = + { 25, 26, 28, 34, 17, 20, 27, 35, 29, 25, 28, 29, 6, 7, 9, 11, 12, 14, 1, 2, 18, 19, 21, 23, 32, 33, }; + + private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) { + switch (hiByte) { + case 0: + return ((jjbitVec2[i2] & l2) != 0L); + default: + if ((jjbitVec0[i1] & l1) != 0L) { + return true; + } + return false; + } + } + + int curLexState = 0; + int defaultLexState = 0; + int jjnewStateCnt; + int jjround; + int jjmatchedPos; + int jjmatchedKind; + + /** Get the next Token. */ + public Token getNextToken() { + Token matchedToken; + int curPos = 0; + + EOFLoop: + for (;;) { + try { + curChar = input_stream.BeginToken(); + } catch (Exception e) { + jjmatchedKind = 0; + jjmatchedPos = -1; + matchedToken = jjFillToken(); + return matchedToken; + } + + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_0(); + if (jjmatchedKind != 0x7fffffff) { + if (jjmatchedPos + 1 < curPos) { + input_stream.backup(curPos - jjmatchedPos - 1); + } + if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) { + matchedToken = jjFillToken(); + return matchedToken; + } else { + continue EOFLoop; + } + } + int error_line = input_stream.getEndLine(); + int error_column = input_stream.getEndColumn(); + String error_after = null; + boolean EOFSeen = false; + try { + input_stream.readChar(); + input_stream.backup(1); + } catch (java.io.IOException e1) { + EOFSeen = true; + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + if (curChar == '\n' || curChar == '\r') { + error_line++; + error_column = 0; + } else { + error_column++; + } + } + if (!EOFSeen) { + input_stream.backup(1); + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + } + throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, + TokenMgrError.LEXICAL_ERROR); + } + } + + void SkipLexicalActions(Token matchedToken) { + switch (jjmatchedKind) { + default: break; - } else { + } + } + + void MoreLexicalActions() { + jjimageLen += (lengthOfMatch = jjmatchedPos + 1); + switch (jjmatchedKind) { + default: break; - } - } - } while(i != startsAt); - } - if (kind != 0x7fffffff) - { - jjmatchedKind = kind; - jjmatchedPos = curPos; - kind = 0x7fffffff; - } - ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 38 - (jjnewStateCnt = startsAt))) { - return curPos; - } - try { curChar = input_stream.readChar(); } - catch(java.io.IOException e) { return curPos; } - } -} - -/** Token literal values. */ -public static final String[] jjstrLiteralImages = { -"", null, null, null, null, null, "\54", "\173", "\175", "\72", "\133", -"\135", null, null, null, null, null, null, null, null, null, null, "\47\47", -"\42\42", null, null, null, null, null, }; -protected Token jjFillToken() -{ - final Token t; - final String curTokenImage; - final int beginLine; - final int endLine; - final int beginColumn; - final int endColumn; - String im = jjstrLiteralImages[jjmatchedKind]; - curTokenImage = (im == null) ? input_stream.GetImage() : im; - beginLine = input_stream.getBeginLine(); - beginColumn = input_stream.getBeginColumn(); - endLine = input_stream.getEndLine(); - endColumn = input_stream.getEndColumn(); - t = Token.newToken(jjmatchedKind, curTokenImage); - - t.beginLine = beginLine; - t.endLine = endLine; - t.beginColumn = beginColumn; - t.endColumn = endColumn; - - return t; -} -static final int[] jjnextStates = { - 25, 26, 28, 34, 17, 20, 27, 35, 29, 25, 28, 29, 6, 7, 9, 11, - 12, 14, 1, 2, 18, 19, 21, 23, 32, 33, -}; -private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) -{ - switch(hiByte) - { - case 0: - return ((jjbitVec2[i2] & l2) != 0L); - default : - if ((jjbitVec0[i1] & l1) != 0L) { - return true; - } - return false; - } -} - -int curLexState = 0; -int defaultLexState = 0; -int jjnewStateCnt; -int jjround; -int jjmatchedPos; -int jjmatchedKind; - -/** Get the next Token. */ -public Token getNextToken() -{ - Token matchedToken; - int curPos = 0; - - EOFLoop : - for (;;) - { - try - { - curChar = input_stream.BeginToken(); - } - catch(Exception e) - { - jjmatchedKind = 0; - jjmatchedPos = -1; - matchedToken = jjFillToken(); - return matchedToken; - } - - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_0(); - if (jjmatchedKind != 0x7fffffff) - { - if (jjmatchedPos + 1 < curPos) { - input_stream.backup(curPos - jjmatchedPos - 1); - } - if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) - { - matchedToken = jjFillToken(); - return matchedToken; - } else { - continue EOFLoop; - } - } - int error_line = input_stream.getEndLine(); - int error_column = input_stream.getEndColumn(); - String error_after = null; - boolean EOFSeen = false; - try { input_stream.readChar(); input_stream.backup(1); } - catch (java.io.IOException e1) { - EOFSeen = true; - error_after = curPos <= 1 ? "" : input_stream.GetImage(); - if (curChar == '\n' || curChar == '\r') { - error_line++; - error_column = 0; - } else { - error_column++; - } - } - if (!EOFSeen) { - input_stream.backup(1); - error_after = curPos <= 1 ? "" : input_stream.GetImage(); - } - throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR); - } -} - -void SkipLexicalActions(Token matchedToken) -{ - switch(jjmatchedKind) - { - default : - break; - } -} -void MoreLexicalActions() -{ - jjimageLen += (lengthOfMatch = jjmatchedPos + 1); - switch(jjmatchedKind) - { - default : - break; - } -} -void TokenLexicalActions(Token matchedToken) -{ - switch(jjmatchedKind) - { - default : - break; - } -} -private void jjCheckNAdd(int state) -{ - if (jjrounds[state] != jjround) - { - jjstateSet[jjnewStateCnt++] = state; - jjrounds[state] = jjround; - } -} -private void jjAddStates(int start, int end) -{ - do { - jjstateSet[jjnewStateCnt++] = jjnextStates[start]; - } while (start++ != end); -} -private void jjCheckNAddTwoStates(int state1, int state2) -{ - jjCheckNAdd(state1); - jjCheckNAdd(state2); -} - -private void jjCheckNAddStates(int start, int end) -{ - do { - jjCheckNAdd(jjnextStates[start]); - } while (start++ != end); -} + } + } + + void TokenLexicalActions(Token matchedToken) { + switch (jjmatchedKind) { + default: + break; + } + } + + private void jjCheckNAdd(int state) { + if (jjrounds[state] != jjround) { + jjstateSet[jjnewStateCnt++] = state; + jjrounds[state] = jjround; + } + } + + private void jjAddStates(int start, int end) { + do { + jjstateSet[jjnewStateCnt++] = jjnextStates[start]; + } while (start++ != end); + } + + private void jjCheckNAddTwoStates(int state1, int state2) { + jjCheckNAdd(state1); + jjCheckNAdd(state2); + } + + private void jjCheckNAddStates(int start, int end) { + do { + jjCheckNAdd(jjnextStates[start]); + } while (start++ != end); + } /** Constructor. */ - public JSONParserTokenManager(JavaCharStream stream){ + public JSONParserTokenManager(JavaCharStream stream) { + + if (JavaCharStream.staticFlag) { + throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer."); + } + + input_stream = stream; + } + + /** Constructor. */ + public JSONParserTokenManager(JavaCharStream stream, int lexState) { + ReInit(stream); + SwitchTo(lexState); + } + + /** Reinitialise parser. */ + public void ReInit(JavaCharStream stream) { + jjmatchedPos = jjnewStateCnt = 0; + curLexState = defaultLexState; + input_stream = stream; + ReInitRounds(); + } + + private void ReInitRounds() { + int i; + jjround = 0x80000001; + for (i = 38; i-- > 0;) { + jjrounds[i] = 0x80000000; + } + } + + /** Reinitialise parser. */ + public void ReInit(JavaCharStream stream, int lexState) { + ReInit(stream); + SwitchTo(lexState); + } + + /** Switch to specified lex state. */ + public void SwitchTo(int lexState) { + if (lexState >= 1 || lexState < 0) { + throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", + TokenMgrError.INVALID_LEXICAL_STATE); + } else { + curLexState = lexState; + } + } + + + /** Lexer state names. */ + public static final String[] lexStateNames = { "DEFAULT", }; - if (JavaCharStream.staticFlag) { - throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer."); - } - - input_stream = stream; - } - - /** Constructor. */ - public JSONParserTokenManager (JavaCharStream stream, int lexState){ - ReInit(stream); - SwitchTo(lexState); - } - - /** Reinitialise parser. */ - public void ReInit(JavaCharStream stream) - { - jjmatchedPos = - jjnewStateCnt = - 0; - curLexState = defaultLexState; - input_stream = stream; - ReInitRounds(); - } - - private void ReInitRounds() - { - int i; - jjround = 0x80000001; - for (i = 38; i-- > 0;) { - jjrounds[i] = 0x80000000; - } - } - - /** Reinitialise parser. */ - public void ReInit(JavaCharStream stream, int lexState) - { - ReInit(stream); - SwitchTo(lexState); - } - - /** Switch to specified lex state. */ - public void SwitchTo(int lexState) - { - if (lexState >= 1 || lexState < 0) { - throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE); - } else { - curLexState = lexState; - } - } - - -/** Lexer state names. */ -public static final String[] lexStateNames = { - "DEFAULT", -}; - -/** Lex State array. */ -public static final int[] jjnewLexState = { - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -}; -static final long[] jjtoToken = { - 0x1ccf8fc1L, -}; -static final long[] jjtoSkip = { - 0x3eL, -}; -static final long[] jjtoSpecial = { - 0x0L, -}; -static final long[] jjtoMore = { - 0x0L, -}; - protected JavaCharStream input_stream; + /** Lex State array. */ + public static final int[] jjnewLexState = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; + static final long[] jjtoToken = { 0x1ccf8fc1L, }; + static final long[] jjtoSkip = { 0x3eL, }; + static final long[] jjtoSpecial = { 0x0L, }; + static final long[] jjtoMore = { 0x0L, }; + protected JavaCharStream input_stream; private final int[] jjrounds = new int[38]; private final int[] jjstateSet = new int[2 * 38]; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/json/JavaCharStream.java tomcat10-10.1.52/java/org/apache/tomcat/util/json/JavaCharStream.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/json/JavaCharStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/json/JavaCharStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,623 +19,570 @@ package org.apache.tomcat.util.json; /** - * An implementation of interface CharStream, where the stream is assumed to - * contain only ASCII characters (with java-like unicode escape processing). + * An implementation of interface CharStream, where the stream is assumed to contain only ASCII characters (with + * java-like unicode escape processing). */ @SuppressWarnings("all") // Ignore warnings in generated code -public -class JavaCharStream -{ - /** Whether parser is static. */ - public static final boolean staticFlag = false; - - static final int hexval(char c) throws java.io.IOException { - switch(c) - { - case '0' : - return 0; - case '1' : - return 1; - case '2' : - return 2; - case '3' : - return 3; - case '4' : - return 4; - case '5' : - return 5; - case '6' : - return 6; - case '7' : - return 7; - case '8' : - return 8; - case '9' : - return 9; - - case 'a' : - case 'A' : - return 10; - case 'b' : - case 'B' : - return 11; - case 'c' : - case 'C' : - return 12; - case 'd' : - case 'D' : - return 13; - case 'e' : - case 'E' : - return 14; - case 'f' : - case 'F' : - return 15; - } - - throw new java.io.IOException(); // Should never come here - } - -/** Position in buffer. */ - public int bufpos = -1; - int bufsize; - int available; - int tokenBegin; - protected int bufline[]; - protected int bufcolumn[]; - - protected int column = 0; - protected int line = 1; - - protected boolean prevCharIsCR = false; - protected boolean prevCharIsLF = false; - - protected java.io.Reader inputStream; - - protected char[] nextCharBuf; - protected char[] buffer; - protected int maxNextCharInd = 0; - protected int nextCharInd = -1; - protected int inBuf = 0; - protected int tabSize = 1; - protected boolean trackLineColumn = true; - - public void setTabSize(int i) { tabSize = i; } - public int getTabSize() { return tabSize; } - - protected void ExpandBuff(boolean wrapAround) - { - char[] newbuffer = new char[bufsize + 2048]; - int newbufline[] = new int[bufsize + 2048]; - int newbufcolumn[] = new int[bufsize + 2048]; - - try - { - if (wrapAround) - { - System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); - System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); - buffer = newbuffer; - - System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); - System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); - bufline = newbufline; - - System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); - System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); - bufcolumn = newbufcolumn; - - bufpos += (bufsize - tokenBegin); - } else { - System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); - buffer = newbuffer; - - System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); - bufline = newbufline; - - System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); - bufcolumn = newbufcolumn; - - bufpos -= tokenBegin; - } - } - catch (Throwable t) - { - throw new Error(t.getMessage()); - } - - available = (bufsize += 2048); - tokenBegin = 0; - } - - protected void FillBuff() throws java.io.IOException - { - int i; - if (maxNextCharInd == 4096) { - maxNextCharInd = nextCharInd = 0; - } - - try { - if ((i = inputStream.read(nextCharBuf, maxNextCharInd, - 4096 - maxNextCharInd)) == -1) - { - inputStream.close(); - throw new java.io.IOException(); - } else { - maxNextCharInd += i; - } - return; - } - catch(java.io.IOException e) { - if (bufpos != 0) - { - --bufpos; - backup(0); - } else { +public class JavaCharStream { + /** Whether parser is static. */ + public static final boolean staticFlag = false; + + static final int hexval(char c) throws java.io.IOException { + switch (c) { + case '0': + return 0; + case '1': + return 1; + case '2': + return 2; + case '3': + return 3; + case '4': + return 4; + case '5': + return 5; + case '6': + return 6; + case '7': + return 7; + case '8': + return 8; + case '9': + return 9; + + case 'a': + case 'A': + return 10; + case 'b': + case 'B': + return 11; + case 'c': + case 'C': + return 12; + case 'd': + case 'D': + return 13; + case 'e': + case 'E': + return 14; + case 'f': + case 'F': + return 15; + } + + throw new java.io.IOException(); // Should never come here + } + + /** Position in buffer. */ + public int bufpos = -1; + int bufsize; + int available; + int tokenBegin; + protected int bufline[]; + protected int bufcolumn[]; + + protected int column = 0; + protected int line = 1; + + protected boolean prevCharIsCR = false; + protected boolean prevCharIsLF = false; + + protected java.io.Reader inputStream; + + protected char[] nextCharBuf; + protected char[] buffer; + protected int maxNextCharInd = 0; + protected int nextCharInd = -1; + protected int inBuf = 0; + protected int tabSize = 1; + protected boolean trackLineColumn = true; + + public void setTabSize(int i) { + tabSize = i; + } + + public int getTabSize() { + return tabSize; + } + + protected void ExpandBuff(boolean wrapAround) { + char[] newbuffer = new char[bufsize + 2048]; + int newbufline[] = new int[bufsize + 2048]; + int newbufcolumn[] = new int[bufsize + 2048]; + + try { + if (wrapAround) { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); + bufcolumn = newbufcolumn; + + bufpos += (bufsize - tokenBegin); + } else { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + bufcolumn = newbufcolumn; + + bufpos -= tokenBegin; + } + } catch (Throwable t) { + throw new Error(t.getMessage()); + } + + available = (bufsize += 2048); + tokenBegin = 0; + } + + protected void FillBuff() throws java.io.IOException { + int i; + if (maxNextCharInd == 4096) { + maxNextCharInd = nextCharInd = 0; + } + + try { + if ((i = inputStream.read(nextCharBuf, maxNextCharInd, 4096 - maxNextCharInd)) == -1) { + inputStream.close(); + throw new java.io.IOException(); + } else { + maxNextCharInd += i; + } + return; + } catch (java.io.IOException e) { + if (bufpos != 0) { + --bufpos; + backup(0); + } else { + bufline[bufpos] = line; + bufcolumn[bufpos] = column; + } + throw e; + } + } + + protected char ReadByte() throws java.io.IOException { + if (++nextCharInd >= maxNextCharInd) { + FillBuff(); + } + + return nextCharBuf[nextCharInd]; + } + + /** @return starting character for token. */ + public char BeginToken() throws java.io.IOException { + if (inBuf > 0) { + --inBuf; + + if (++bufpos == bufsize) { + bufpos = 0; + } + + tokenBegin = bufpos; + return buffer[bufpos]; + } + + tokenBegin = 0; + bufpos = -1; + + return readChar(); + } + + protected void AdjustBuffSize() { + if (available == bufsize) { + if (tokenBegin > 2048) { + bufpos = 0; + available = tokenBegin; + } else { + ExpandBuff(false); + } + } else if (available > tokenBegin) { + available = bufsize; + } else if ((tokenBegin - available) < 2048) { + ExpandBuff(true); + } else { + available = tokenBegin; + } + } + + protected void UpdateLineColumn(char c) { + column++; + + if (prevCharIsLF) { + prevCharIsLF = false; + line += (column = 1); + } else if (prevCharIsCR) { + prevCharIsCR = false; + if (c == '\n') { + prevCharIsLF = true; + } else { + line += (column = 1); + } + } + + switch (c) { + case '\r': + prevCharIsCR = true; + break; + case '\n': + prevCharIsLF = true; + break; + case '\t': + column--; + column += (tabSize - (column % tabSize)); + break; + default: + break; + } + bufline[bufpos] = line; bufcolumn[bufpos] = column; - } - throw e; } - } - protected char ReadByte() throws java.io.IOException - { - if (++nextCharInd >= maxNextCharInd) { - FillBuff(); - } - - return nextCharBuf[nextCharInd]; - } - -/** @return starting character for token. */ - public char BeginToken() throws java.io.IOException - { - if (inBuf > 0) - { - --inBuf; - - if (++bufpos == bufsize) { - bufpos = 0; - } - - tokenBegin = bufpos; - return buffer[bufpos]; - } - - tokenBegin = 0; - bufpos = -1; - - return readChar(); - } - - protected void AdjustBuffSize() - { - if (available == bufsize) - { - if (tokenBegin > 2048) - { - bufpos = 0; - available = tokenBegin; - } else { - ExpandBuff(false); - } - } - else if (available > tokenBegin) { - available = bufsize; - } else if ((tokenBegin - available) < 2048) { - ExpandBuff(true); - } else { - available = tokenBegin; - } - } - - protected void UpdateLineColumn(char c) - { - column++; - - if (prevCharIsLF) - { - prevCharIsLF = false; - line += (column = 1); - } - else if (prevCharIsCR) - { - prevCharIsCR = false; - if (c == '\n') - { - prevCharIsLF = true; - } else { - line += (column = 1); - } - } - - switch (c) - { - case '\r' : - prevCharIsCR = true; - break; - case '\n' : - prevCharIsLF = true; - break; - case '\t' : - column--; - column += (tabSize - (column % tabSize)); - break; - default : - break; - } - - bufline[bufpos] = line; - bufcolumn[bufpos] = column; - } - -/** Read a character. */ - public char readChar() throws java.io.IOException - { - if (inBuf > 0) - { - --inBuf; - - if (++bufpos == bufsize) { - bufpos = 0; - } - - return buffer[bufpos]; - } - - char c; - - if (++bufpos == available) { - AdjustBuffSize(); - } - - if ((buffer[bufpos] = c = ReadByte()) == '\\') - { - if (trackLineColumn) { UpdateLineColumn(c); } + /** Read a character. */ + public char readChar() throws java.io.IOException { + if (inBuf > 0) { + --inBuf; - int backSlashCnt = 1; + if (++bufpos == bufsize) { + bufpos = 0; + } + + return buffer[bufpos]; + } + + char c; - for (;;) // Read all the backslashes - { if (++bufpos == available) { - AdjustBuffSize(); + AdjustBuffSize(); } - try - { - if ((buffer[bufpos] = c = ReadByte()) != '\\') - { - if (trackLineColumn) { UpdateLineColumn(c); } - // found a non-backslash char. - if ((c == 'u') && ((backSlashCnt & 1) == 1)) + if ((buffer[bufpos] = c = ReadByte()) == '\\') { + if (trackLineColumn) { + UpdateLineColumn(c); + } + + int backSlashCnt = 1; + + for (;;) // Read all the backslashes { - if (--bufpos < 0) { - bufpos = bufsize - 1; - } - - break; - } - - backup(backSlashCnt); - return '\\'; - } - } - catch(java.io.IOException e) - { - // We are returning one backslash so we should only backup (count-1) - if (backSlashCnt > 1) { - backup(backSlashCnt-1); - } - - return '\\'; - } - - if (trackLineColumn) { UpdateLineColumn(c); } - backSlashCnt++; - } - - // Here, we have seen an odd number of backslash's followed by a 'u' - try - { - while ((c = ReadByte()) == 'u') { - ++column; - } - - buffer[bufpos] = c = (char)(hexval(c) << 12 | - hexval(ReadByte()) << 8 | - hexval(ReadByte()) << 4 | - hexval(ReadByte())); - - column += 4; - } - catch(java.io.IOException e) - { - throw new Error("Invalid escape character at line " + line + - " column " + column + "."); - } - - if (backSlashCnt == 1) { - return c; - } else - { - backup(backSlashCnt - 1); - return '\\'; - } - } else { - UpdateLineColumn(c); - return c; - } - } - - @Deprecated - /** - * @deprecated - * @see #getEndColumn - */ - public int getColumn() { - return bufcolumn[bufpos]; - } - - @Deprecated - /** - * @deprecated - * @see #getEndLine - */ - public int getLine() { - return bufline[bufpos]; - } - -/** Get end column. */ - public int getEndColumn() { - return bufcolumn[bufpos]; - } - -/** Get end line. */ - public int getEndLine() { - return bufline[bufpos]; - } - -/** @return column of token start */ - public int getBeginColumn() { - return bufcolumn[tokenBegin]; - } - -/** @return line number of token start */ - public int getBeginLine() { - return bufline[tokenBegin]; - } - -/** Retreat. */ - public void backup(int amount) { - - inBuf += amount; - if ((bufpos -= amount) < 0) { - bufpos += bufsize; - } - } - -/** Constructor. */ - public JavaCharStream(java.io.Reader dstream, - int startline, int startcolumn, int buffersize) - { - inputStream = dstream; - line = startline; - column = startcolumn - 1; - - available = bufsize = buffersize; - buffer = new char[buffersize]; - bufline = new int[buffersize]; - bufcolumn = new int[buffersize]; - nextCharBuf = new char[4096]; - } - -/** Constructor. */ - public JavaCharStream(java.io.Reader dstream, - int startline, int startcolumn) - { - this(dstream, startline, startcolumn, 4096); - } - -/** Constructor. */ - public JavaCharStream(java.io.Reader dstream) - { - this(dstream, 1, 1, 4096); - } -/** Reinitialise. */ - public void ReInit(java.io.Reader dstream, - int startline, int startcolumn, int buffersize) - { - inputStream = dstream; - line = startline; - column = startcolumn - 1; - - if (buffer == null || buffersize != buffer.length) - { - available = bufsize = buffersize; - buffer = new char[buffersize]; - bufline = new int[buffersize]; - bufcolumn = new int[buffersize]; - nextCharBuf = new char[4096]; - } - prevCharIsLF = prevCharIsCR = false; - tokenBegin = inBuf = maxNextCharInd = 0; - nextCharInd = bufpos = -1; - } - -/** Reinitialise. */ - public void ReInit(java.io.Reader dstream, - int startline, int startcolumn) - { - ReInit(dstream, startline, startcolumn, 4096); - } - -/** Reinitialise. */ - public void ReInit(java.io.Reader dstream) - { - ReInit(dstream, 1, 1, 4096); - } -/** Constructor. */ - public JavaCharStream(java.io.InputStream dstream, String encoding, int startline, - int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException - { - this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); - } - -/** Constructor. */ - public JavaCharStream(java.io.InputStream dstream, int startline, - int startcolumn, int buffersize) - { - this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); - } - -/** Constructor. */ - public JavaCharStream(java.io.InputStream dstream, String encoding, int startline, - int startcolumn) throws java.io.UnsupportedEncodingException - { - this(dstream, encoding, startline, startcolumn, 4096); - } - -/** Constructor. */ - public JavaCharStream(java.io.InputStream dstream, int startline, - int startcolumn) - { - this(dstream, startline, startcolumn, 4096); - } - -/** Constructor. */ - public JavaCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException - { - this(dstream, encoding, 1, 1, 4096); - } - -/** Constructor. */ - public JavaCharStream(java.io.InputStream dstream) - { - this(dstream, 1, 1, 4096); - } - -/** Reinitialise. */ - public void ReInit(java.io.InputStream dstream, String encoding, int startline, - int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException - { - ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); - } - -/** Reinitialise. */ - public void ReInit(java.io.InputStream dstream, int startline, - int startcolumn, int buffersize) - { - ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); - } -/** Reinitialise. */ - public void ReInit(java.io.InputStream dstream, String encoding, int startline, - int startcolumn) throws java.io.UnsupportedEncodingException - { - ReInit(dstream, encoding, startline, startcolumn, 4096); - } -/** Reinitialise. */ - public void ReInit(java.io.InputStream dstream, int startline, - int startcolumn) - { - ReInit(dstream, startline, startcolumn, 4096); - } -/** Reinitialise. */ - public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException - { - ReInit(dstream, encoding, 1, 1, 4096); - } - -/** Reinitialise. */ - public void ReInit(java.io.InputStream dstream) - { - ReInit(dstream, 1, 1, 4096); - } - - /** @return token image as String */ - public String GetImage() - { - if (bufpos >= tokenBegin) { - return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); - } else { - return new String(buffer, tokenBegin, bufsize - tokenBegin) + - new String(buffer, 0, bufpos + 1); - } - } - - /** @return suffix */ - public char[] GetSuffix(int len) - { - char[] ret = new char[len]; - - if ((bufpos + 1) >= len) { - System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); - } else - { - System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, - len - bufpos - 1); - System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); - } - - return ret; - } - - /** Set buffers back to null when finished. */ - public void Done() - { - nextCharBuf = null; - buffer = null; - bufline = null; - bufcolumn = null; - } - - /** - * Method to adjust line and column numbers for the start of a token. - */ - public void adjustBeginLineColumn(int newLine, int newCol) - { - int start = tokenBegin; - int len; - - if (bufpos >= tokenBegin) - { - len = bufpos - tokenBegin + inBuf + 1; - } else { - len = bufsize - tokenBegin + bufpos + 1 + inBuf; - } - - int i = 0, j = 0, k = 0; - int nextColDiff = 0, columnDiff = 0; - - while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) - { - bufline[j] = newLine; - nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; - bufcolumn[j] = newCol + columnDiff; - columnDiff = nextColDiff; - i++; - } - - if (i < len) - { - bufline[j] = newLine++; - bufcolumn[j] = newCol + columnDiff; - - while (i++ < len) - { - if (bufline[j = start % bufsize] != bufline[++start % bufsize]) { - bufline[j] = newLine++; + if (++bufpos == available) { + AdjustBuffSize(); + } + + try { + if ((buffer[bufpos] = c = ReadByte()) != '\\') { + if (trackLineColumn) { + UpdateLineColumn(c); + } + // found a non-backslash char. + if ((c == 'u') && ((backSlashCnt & 1) == 1)) { + if (--bufpos < 0) { + bufpos = bufsize - 1; + } + + break; + } + + backup(backSlashCnt); + return '\\'; + } + } catch (java.io.IOException e) { + // We are returning one backslash so we should only backup (count-1) + if (backSlashCnt > 1) { + backup(backSlashCnt - 1); + } + + return '\\'; + } + + if (trackLineColumn) { + UpdateLineColumn(c); + } + backSlashCnt++; + } + + // Here, we have seen an odd number of backslash's followed by a 'u' + try { + while ((c = ReadByte()) == 'u') { + ++column; + } + + buffer[bufpos] = c = (char) (hexval(c) << 12 | hexval(ReadByte()) << 8 | hexval(ReadByte()) << 4 | + hexval(ReadByte())); + + column += 4; + } catch (java.io.IOException e) { + throw new Error("Invalid escape character at line " + line + " column " + column + "."); + } + + if (backSlashCnt == 1) { + return c; + } else { + backup(backSlashCnt - 1); + return '\\'; + } } else { - bufline[j] = newLine; + UpdateLineColumn(c); + return c; } - } } - line = bufline[j]; - column = bufcolumn[j]; - } - boolean getTrackLineColumn() { return trackLineColumn; } - void setTrackLineColumn(boolean tlc) { trackLineColumn = tlc; } + @Deprecated + /** + * @deprecated + * + * @see #getEndColumn + */ + public int getColumn() { + return bufcolumn[bufpos]; + } + + @Deprecated + /** + * @deprecated + * + * @see #getEndLine + */ + public int getLine() { + return bufline[bufpos]; + } + + /** Get end column. */ + public int getEndColumn() { + return bufcolumn[bufpos]; + } + + /** Get end line. */ + public int getEndLine() { + return bufline[bufpos]; + } + + /** @return column of token start */ + public int getBeginColumn() { + return bufcolumn[tokenBegin]; + } + + /** @return line number of token start */ + public int getBeginLine() { + return bufline[tokenBegin]; + } + + /** Retreat. */ + public void backup(int amount) { + + inBuf += amount; + if ((bufpos -= amount) < 0) { + bufpos += bufsize; + } + } + + /** Constructor. */ + public JavaCharStream(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + nextCharBuf = new char[4096]; + } + + /** Constructor. */ + public JavaCharStream(java.io.Reader dstream, int startline, int startcolumn) { + this(dstream, startline, startcolumn, 4096); + } + + /** Constructor. */ + public JavaCharStream(java.io.Reader dstream) { + this(dstream, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + if (buffer == null || buffersize != buffer.length) { + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + nextCharBuf = new char[4096]; + } + prevCharIsLF = prevCharIsCR = false; + tokenBegin = inBuf = maxNextCharInd = 0; + nextCharInd = bufpos = -1; + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader dstream, int startline, int startcolumn) { + ReInit(dstream, startline, startcolumn, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader dstream) { + ReInit(dstream, 1, 1, 4096); + } + + /** Constructor. */ + public JavaCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) + throws java.io.UnsupportedEncodingException { + this(encoding == null ? new java.io.InputStreamReader(dstream) : + new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); + } + + /** Constructor. */ + public JavaCharStream(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { + this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + } + + /** Constructor. */ + public JavaCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn) + throws java.io.UnsupportedEncodingException { + this(dstream, encoding, startline, startcolumn, 4096); + } + + /** Constructor. */ + public JavaCharStream(java.io.InputStream dstream, int startline, int startcolumn) { + this(dstream, startline, startcolumn, 4096); + } + + /** Constructor. */ + public JavaCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException { + this(dstream, encoding, 1, 1, 4096); + } + + /** Constructor. */ + public JavaCharStream(java.io.InputStream dstream) { + this(dstream, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) + throws java.io.UnsupportedEncodingException { + ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : + new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { + ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn) + throws java.io.UnsupportedEncodingException { + ReInit(dstream, encoding, startline, startcolumn, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, int startline, int startcolumn) { + ReInit(dstream, startline, startcolumn, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException { + ReInit(dstream, encoding, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream) { + ReInit(dstream, 1, 1, 4096); + } + + /** @return token image as String */ + public String GetImage() { + if (bufpos >= tokenBegin) { + return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); + } else { + return new String(buffer, tokenBegin, bufsize - tokenBegin) + new String(buffer, 0, bufpos + 1); + } + } + + /** @return suffix */ + public char[] GetSuffix(int len) { + char[] ret = new char[len]; + + if ((bufpos + 1) >= len) { + System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + } else { + System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, len - bufpos - 1); + System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); + } + + return ret; + } + + /** Set buffers back to null when finished. */ + public void Done() { + nextCharBuf = null; + buffer = null; + bufline = null; + bufcolumn = null; + } + + /** + * Method to adjust line and column numbers for the start of a token. + */ + public void adjustBeginLineColumn(int newLine, int newCol) { + int start = tokenBegin; + int len; + + if (bufpos >= tokenBegin) { + len = bufpos - tokenBegin + inBuf + 1; + } else { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0, j = 0, k = 0; + int nextColDiff = 0, columnDiff = 0; + + while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) { + bufline[j] = newLine; + nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; + bufcolumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + + if (i < len) { + bufline[j] = newLine++; + bufcolumn[j] = newCol + columnDiff; + + while (i++ < len) { + if (bufline[j = start % bufsize] != bufline[++start % bufsize]) { + bufline[j] = newLine++; + } else { + bufline[j] = newLine; + } + } + } + + line = bufline[j]; + column = bufcolumn[j]; + } + + boolean getTrackLineColumn() { + return trackLineColumn; + } + + void setTrackLineColumn(boolean tlc) { + trackLineColumn = tlc; + } } /* JavaCC - OriginalChecksum=58f718af3466d6064ba0132bca4fbce2 (do not edit this line) */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/json/ParseException.java tomcat10-10.1.52/java/org/apache/tomcat/util/json/ParseException.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/json/ParseException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/json/ParseException.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,194 +19,172 @@ package org.apache.tomcat.util.json; /** - * This exception is thrown when parse errors are encountered. - * You can explicitly create objects of this exception type by - * calling the method generateParseException in the generated - * parser. - * - * You can modify this class to customize your error reporting - * mechanisms so long as you retain the public fields. + * This exception is thrown when parse errors are encountered. You can explicitly create objects of this exception type + * by calling the method generateParseException in the generated parser. You can modify this class to customize your + * error reporting mechanisms so long as you retain the public fields. */ @SuppressWarnings("all") // Ignore warnings in generated code public class ParseException extends Exception { - /** - * The version identifier for this Serializable class. - * Increment only if the serialized form of the - * class changes. - */ - private static final long serialVersionUID = 1L; - - /** - * The end of line string for this machine. - */ - protected static String EOL = System.getProperty("line.separator", "\n"); - - /** - * This constructor is used by the method "generateParseException" - * in the generated parser. Calling this constructor generates - * a new object of this type with the fields "currentToken", - * "expectedTokenSequences", and "tokenImage" set. - */ - public ParseException(Token currentTokenVal, - int[][] expectedTokenSequencesVal, - String[] tokenImageVal - ) - { - super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal)); - currentToken = currentTokenVal; - expectedTokenSequences = expectedTokenSequencesVal; - tokenImage = tokenImageVal; - } - - /** - * The following constructors are for use by you for whatever - * purpose you can think of. Constructing the exception in this - * manner makes the exception behave in the normal way - i.e., as - * documented in the class "Throwable". The fields "errorToken", - * "expectedTokenSequences", and "tokenImage" do not contain - * relevant information. The JavaCC generated code does not use - * these constructors. - */ - - public ParseException() { - super(); - } - - /** Constructor with message. */ - public ParseException(String message) { - super(message); - } - - - /** - * This is the last token that has been consumed successfully. If - * this object has been created due to a parse error, the token - * following this token will (therefore) be the first error token. - */ - public Token currentToken; - - /** - * Each entry in this array is an array of integers. Each array - * of integers represents a sequence of tokens (by their ordinal - * values) that is expected at this point of the parse. - */ - public int[][] expectedTokenSequences; - - /** - * This is a reference to the "tokenImage" array of the generated - * parser within which the parse error occurred. This array is - * defined in the generated ...Constants interface. - */ - public String[] tokenImage; - - /** - * It uses "currentToken" and "expectedTokenSequences" to generate a parse - * error message and returns it. If this object has been created - * due to a parse error, and you do not catch it (it gets thrown - * from the parser) the correct error message - * gets displayed. - */ - private static String initialise(Token currentToken, - int[][] expectedTokenSequences, - String[] tokenImage) { - - StringBuffer expected = new StringBuffer(); - int maxSize = 0; - for (int i = 0; i < expectedTokenSequences.length; i++) { - if (maxSize < expectedTokenSequences[i].length) { - maxSize = expectedTokenSequences[i].length; - } - for (int j = 0; j < expectedTokenSequences[i].length; j++) { - expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' '); - } - if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) { - expected.append("..."); - } - expected.append(EOL).append(" "); + /** + * The version identifier for this Serializable class. Increment only if the serialized form of the class + * changes. + */ + private static final long serialVersionUID = 1L; + + /** + * The end of line string for this machine. + */ + protected static String EOL = System.getProperty("line.separator", "\n"); + + /** + * This constructor is used by the method "generateParseException" in the generated parser. Calling this constructor + * generates a new object of this type with the fields "currentToken", "expectedTokenSequences", and "tokenImage" + * set. + */ + public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal) { + super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal)); + currentToken = currentTokenVal; + expectedTokenSequences = expectedTokenSequencesVal; + tokenImage = tokenImageVal; } - String retval = "Encountered \""; - Token tok = currentToken.next; - for (int i = 0; i < maxSize; i++) { - if (i != 0) { - retval += " "; - } - if (tok.kind == 0) { - retval += tokenImage[0]; - break; - } - retval += " " + tokenImage[tok.kind]; - retval += " \""; - retval += add_escapes(tok.image); - retval += " \""; - tok = tok.next; - } - retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; - retval += "." + EOL; + /** + * The following constructors are for use by you for whatever purpose you can think of. Constructing the exception + * in this manner makes the exception behave in the normal way - i.e., as documented in the class "Throwable". The + * fields "errorToken", "expectedTokenSequences", and "tokenImage" do not contain relevant information. The JavaCC + * generated code does not use these constructors. + */ + + public ParseException() { + super(); + } - if (expectedTokenSequences.length == 0) { - // Nothing to add here - } else { - if (expectedTokenSequences.length == 1) { - retval += "Was expecting:" + EOL + " "; - } else { - retval += "Was expecting one of:" + EOL + " "; - } - retval += expected.toString(); + /** Constructor with message. */ + public ParseException(String message) { + super(message); } - return retval; - } + + /** + * This is the last token that has been consumed successfully. If this object has been created due to a parse error, + * the token following this token will (therefore) be the first error token. + */ + public Token currentToken; + + /** + * Each entry in this array is an array of integers. Each array of integers represents a sequence of tokens (by + * their ordinal values) that is expected at this point of the parse. + */ + public int[][] expectedTokenSequences; + + /** + * This is a reference to the "tokenImage" array of the generated parser within which the parse error occurred. This + * array is defined in the generated ...Constants interface. + */ + public String[] tokenImage; + + /** + * It uses "currentToken" and "expectedTokenSequences" to generate a parse error message and returns it. If this + * object has been created due to a parse error, and you do not catch it (it gets thrown from the parser) the + * correct error message gets displayed. + */ + private static String initialise(Token currentToken, int[][] expectedTokenSequences, String[] tokenImage) { + + StringBuffer expected = new StringBuffer(); + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + for (int j = 0; j < expectedTokenSequences[i].length; j++) { + expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' '); + } + if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) { + expected.append("..."); + } + expected.append(EOL).append(" "); + } + String retval = "Encountered \""; + Token tok = currentToken.next; + for (int i = 0; i < maxSize; i++) { + if (i != 0) { + retval += " "; + } + if (tok.kind == 0) { + retval += tokenImage[0]; + break; + } + retval += " " + tokenImage[tok.kind]; + retval += " \""; + retval += add_escapes(tok.image); + retval += " \""; + tok = tok.next; + } + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; + retval += "." + EOL; + + + if (expectedTokenSequences.length == 0) { + // Nothing to add here + } else { + if (expectedTokenSequences.length == 1) { + retval += "Was expecting:" + EOL + " "; + } else { + retval += "Was expecting one of:" + EOL + " "; + } + retval += expected.toString(); + } + + return retval; + } - /** - * Used to convert raw characters to their escaped version - * when these raw version cannot be used as part of an ASCII - * string literal. - */ - static String add_escapes(String str) { - StringBuffer retval = new StringBuffer(); - char ch; - for (int i = 0; i < str.length(); i++) { - switch (str.charAt(i)) - { - case '\b': - retval.append("\\b"); - continue; - case '\t': - retval.append("\\t"); - continue; - case '\n': - retval.append("\\n"); - continue; - case '\f': - retval.append("\\f"); - continue; - case '\r': - retval.append("\\r"); - continue; - case '\"': - retval.append("\\\""); - continue; - case '\'': - retval.append("\\\'"); - continue; - case '\\': - retval.append("\\\\"); - continue; - default: - if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { - String s = "0000" + Integer.toString(ch, 16); - retval.append("\\u" + s.substring(s.length() - 4, s.length())); - } else { - retval.append(ch); - } - continue; + /** + * Used to convert raw characters to their escaped version when these raw version cannot be used as part of an ASCII + * string literal. + */ + static String add_escapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) { + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } } - } - return retval.toString(); - } + return retval.toString(); + } } /* JavaCC - OriginalChecksum=1ccce09e6dc04d17e33b6d15b9c1f0c3 (do not edit this line) */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/json/Token.java tomcat10-10.1.52/java/org/apache/tomcat/util/json/Token.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/json/Token.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/json/Token.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,124 +24,105 @@ @SuppressWarnings("all") // Ignore warnings in generated code public class Token implements java.io.Serializable { - /** - * The version identifier for this Serializable class. - * Increment only if the serialized form of the - * class changes. - */ - private static final long serialVersionUID = 1L; - - /** - * An integer that describes the kind of this token. This numbering - * system is determined by JavaCCParser, and a table of these numbers is - * stored in the file ...Constants.java. - */ - public int kind; - - /** The line number of the first character of this Token. */ - public int beginLine; - /** The column number of the first character of this Token. */ - public int beginColumn; - /** The line number of the last character of this Token. */ - public int endLine; - /** The column number of the last character of this Token. */ - public int endColumn; - - /** - * The string image of the token. - */ - public String image; - - /** - * A reference to the next regular (non-special) token from the input - * stream. If this is the last token from the input stream, or if the - * token manager has not read tokens beyond this one, this field is - * set to null. This is true only if this token is also a regular - * token. Otherwise, see below for a description of the contents of - * this field. - */ - public Token next; - - /** - * This field is used to access special tokens that occur prior to this - * token, but after the immediately preceding regular (non-special) token. - * If there are no such special tokens, this field is set to null. - * When there are more than one such special token, this field refers - * to the last of these special tokens, which in turn refers to the next - * previous special token through its specialToken field, and so on - * until the first special token (whose specialToken field is null). - * The next fields of special tokens refer to other special tokens that - * immediately follow it (without an intervening regular token). If there - * is no such token, this field is null. - */ - public Token specialToken; - - /** - * An optional attribute value of the Token. - * Tokens which are not used as syntactic sugar will often contain - * meaningful values that will be used later on by the compiler or - * interpreter. This attribute value is often different from the image. - * Any subclass of Token that actually wants to return a non-null value can - * override this method as appropriate. - */ - public Object getValue() { - return null; - } - - /** - * No-argument constructor - */ - public Token() {} - - /** - * Constructs a new token for the specified Image. - */ - public Token(int kind) - { - this(kind, null); - } - - /** - * Constructs a new token for the specified Image and Kind. - */ - public Token(int kind, String image) - { - this.kind = kind; - this.image = image; - } - - /** - * Returns the image. - */ - public String toString() - { - return image; - } - - /** - * Returns a new Token object, by default. However, if you want, you - * can create and return subclass objects based on the value of ofKind. - * Simply add the cases to the switch for all those special cases. - * For example, if you have a subclass of Token called IDToken that - * you want to create if ofKind is ID, simply add something like : - * - * case MyParserConstants.ID : return new IDToken(ofKind, image); - * - * to the following switch statement. Then you can cast matchedToken - * variable to the appropriate type and use sit in your lexical actions. - */ - public static Token newToken(int ofKind, String image) - { - switch(ofKind) - { - default : return new Token(ofKind, image); - } - } - - public static Token newToken(int ofKind) - { - return newToken(ofKind, null); - } + /** + * The version identifier for this Serializable class. Increment only if the serialized form of the class + * changes. + */ + private static final long serialVersionUID = 1L; + + /** + * An integer that describes the kind of this token. This numbering system is determined by JavaCCParser, and a + * table of these numbers is stored in the file ...Constants.java. + */ + public int kind; + + /** The line number of the first character of this Token. */ + public int beginLine; + /** The column number of the first character of this Token. */ + public int beginColumn; + /** The line number of the last character of this Token. */ + public int endLine; + /** The column number of the last character of this Token. */ + public int endColumn; + + /** + * The string image of the token. + */ + public String image; + + /** + * A reference to the next regular (non-special) token from the input stream. If this is the last token from the + * input stream, or if the token manager has not read tokens beyond this one, this field is set to null. This is + * true only if this token is also a regular token. Otherwise, see below for a description of the contents of this + * field. + */ + public Token next; + + /** + * This field is used to access special tokens that occur prior to this token, but after the immediately preceding + * regular (non-special) token. If there are no such special tokens, this field is set to null. When there are more + * than one such special token, this field refers to the last of these special tokens, which in turn refers to the + * next previous special token through its specialToken field, and so on until the first special token (whose + * specialToken field is null). The next fields of special tokens refer to other special tokens that immediately + * follow it (without an intervening regular token). If there is no such token, this field is null. + */ + public Token specialToken; + + /** + * An optional attribute value of the Token. Tokens which are not used as syntactic sugar will often contain + * meaningful values that will be used later on by the compiler or interpreter. This attribute value is often + * different from the image. Any subclass of Token that actually wants to return a non-null value can override this + * method as appropriate. + */ + public Object getValue() { + return null; + } + + /** + * No-argument constructor + */ + public Token() { + } + + /** + * Constructs a new token for the specified Image. + */ + public Token(int kind) { + this(kind, null); + } + + /** + * Constructs a new token for the specified Image and Kind. + */ + public Token(int kind, String image) { + this.kind = kind; + this.image = image; + } + + /** + * Returns the image. + */ + public String toString() { + return image; + } + + /** + * Returns a new Token object, by default. However, if you want, you can create and return subclass objects based on + * the value of ofKind. Simply add the cases to the switch for all those special cases. For example, if you have a + * subclass of Token called IDToken that you want to create if ofKind is ID, simply add something like : case + * MyParserConstants.ID : return new IDToken(ofKind, image); to the following switch statement. Then you can cast + * matchedToken variable to the appropriate type and use sit in your lexical actions. + */ + public static Token newToken(int ofKind, String image) { + switch (ofKind) { + default: + return new Token(ofKind, image); + } + } + + public static Token newToken(int ofKind) { + return newToken(ofKind, null); + } } /* JavaCC - OriginalChecksum=3fa555852689f4df3b05452671ed7031 (do not edit this line) */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/json/TokenMgrError.java tomcat10-10.1.52/java/org/apache/tomcat/util/json/TokenMgrError.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/json/TokenMgrError.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/json/TokenMgrError.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,144 +20,132 @@ /** Token Manager Error. */ @SuppressWarnings("all") // Ignore warnings in generated code -public class TokenMgrError extends Error -{ +public class TokenMgrError extends Error { - /** - * The version identifier for this Serializable class. - * Increment only if the serialized form of the - * class changes. - */ - private static final long serialVersionUID = 1L; - - /* - * Ordinals for various reasons why an Error of this type can be thrown. - */ - - /** - * Lexical error occurred. - */ - public static final int LEXICAL_ERROR = 0; - - /** - * An attempt was made to create a second instance of a static token manager. - */ - public static final int STATIC_LEXER_ERROR = 1; - - /** - * Tried to change to an invalid lexical state. - */ - public static final int INVALID_LEXICAL_STATE = 2; - - /** - * Detected (and bailed out of) an infinite loop in the token manager. - */ - public static final int LOOP_DETECTED = 3; - - /** - * Indicates the reason why the exception is thrown. It will have - * one of the above 4 values. - */ - int errorCode; - - /** - * Replaces unprintable characters by their escaped (or unicode escaped) - * equivalents in the given string - */ - protected static final String addEscapes(String str) { - StringBuffer retval = new StringBuffer(); - char ch; - for (int i = 0; i < str.length(); i++) { - switch (str.charAt(i)) - { - case '\b': - retval.append("\\b"); - continue; - case '\t': - retval.append("\\t"); - continue; - case '\n': - retval.append("\\n"); - continue; - case '\f': - retval.append("\\f"); - continue; - case '\r': - retval.append("\\r"); - continue; - case '\"': - retval.append("\\\""); - continue; - case '\'': - retval.append("\\\'"); - continue; - case '\\': - retval.append("\\\\"); - continue; - default: - if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { - String s = "0000" + Integer.toString(ch, 16); - retval.append("\\u" + s.substring(s.length() - 4, s.length())); - } else { - retval.append(ch); - } - continue; - } - } - return retval.toString(); - } - - /** - * Returns a detailed message for the Error when it is thrown by the - * token manager to indicate a lexical error. - * Parameters : - * EOFSeen : indicates if EOF caused the lexical error - * curLexState : lexical state in which this error occurred - * errorLine : line number when the error occurred - * errorColumn : column number when the error occurred - * errorAfter : prefix that was seen before this error occurred - * curchar : the offending character - * Note: You can customize the lexical error message by modifying this method. - */ - protected static String LexicalErr(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar) { - char curChar1 = (char)curChar; - return("Lexical error at line " + - errorLine + ", column " + - errorColumn + ". Encountered: " + - (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar1)) + "\"") + " (" + (int)curChar + "), ") + - "after : \"" + addEscapes(errorAfter) + "\""); - } - - /** - * You can also modify the body of this method to customize your error messages. - * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not - * of end-users concern, so you can return something like : - * - * "Internal Error : Please file a bug report .... " - * - * from this method for such cases in the release version of your parser. - */ - public String getMessage() { - return super.getMessage(); - } - - /* - * Constructors of various flavors follow. - */ - - /** No arg constructor. */ - public TokenMgrError() { - } - - /** Constructor with message and reason. */ - public TokenMgrError(String message, int reason) { - super(message); - errorCode = reason; - } - - /** Full Constructor. */ - public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar, int reason) { - this(LexicalErr(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); - } + /** + * The version identifier for this Serializable class. Increment only if the serialized form of the class + * changes. + */ + private static final long serialVersionUID = 1L; + + /* + * Ordinals for various reasons why an Error of this type can be thrown. + */ + + /** + * Lexical error occurred. + */ + public static final int LEXICAL_ERROR = 0; + + /** + * An attempt was made to create a second instance of a static token manager. + */ + public static final int STATIC_LEXER_ERROR = 1; + + /** + * Tried to change to an invalid lexical state. + */ + public static final int INVALID_LEXICAL_STATE = 2; + + /** + * Detected (and bailed out of) an infinite loop in the token manager. + */ + public static final int LOOP_DETECTED = 3; + + /** + * Indicates the reason why the exception is thrown. It will have one of the above 4 values. + */ + int errorCode; + + /** + * Replaces unprintable characters by their escaped (or unicode escaped) equivalents in the given string + */ + protected static final String addEscapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) { + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + + /** + * Returns a detailed message for the Error when it is thrown by the token manager to indicate a lexical error. + * Parameters : EOFSeen : indicates if EOF caused the lexical error curLexState : lexical state in which this error + * occurred errorLine : line number when the error occurred errorColumn : column number when the error occurred + * errorAfter : prefix that was seen before this error occurred curchar : the offending character Note: You can + * customize the lexical error message by modifying this method. + */ + protected static String LexicalErr(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, + int curChar) { + char curChar1 = (char) curChar; + return ("Lexical error at line " + errorLine + ", column " + errorColumn + ". Encountered: " + + (EOFSeen ? " " : + ("\"" + addEscapes(String.valueOf(curChar1)) + "\"") + " (" + (int) curChar + "), ") + + "after : \"" + addEscapes(errorAfter) + "\""); + } + + /** + * You can also modify the body of this method to customize your error messages. For example, cases like + * LOOP_DETECTED and INVALID_LEXICAL_STATE are not of end-users concern, so you can return something like : + * "Internal Error : Please file a bug report .... " from this method for such cases in the release version of your + * parser. + */ + public String getMessage() { + return super.getMessage(); + } + + /* + * Constructors of various flavors follow. + */ + + /** No arg constructor. */ + public TokenMgrError() { + } + + /** Constructor with message and reason. */ + public TokenMgrError(String message, int reason) { + super(message); + errorCode = reason; + } + + /** Full Constructor. */ + public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar, + int reason) { + this(LexicalErr(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); + } } /* JavaCC - OriginalChecksum=7da4e668c47dee63e10c9b008699a2ca (do not edit this line) */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/log/CaptureLog.java tomcat10-10.1.52/java/org/apache/tomcat/util/log/CaptureLog.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/log/CaptureLog.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/log/CaptureLog.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,6 @@ /** * Per Thread System.err and System.out log capture data. - * - * @author Glenn L. Nielsen */ class CaptureLog { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/log/SystemLogHandler.java tomcat10-10.1.52/java/org/apache/tomcat/util/log/SystemLogHandler.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/log/SystemLogHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/log/SystemLogHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,14 +25,8 @@ import java.util.concurrent.ConcurrentLinkedQueue; /** - * This helper class may be used to do sophisticated redirection of - * System.out and System.err on a per Thread basis. - * - * A stack is implemented per Thread so that nested startCapture - * and stopCapture can be used. - * - * @author Remy Maucherat - * @author Glenn L. Nielsen + * This helper class may be used to do sophisticated redirection of System.out and System.err on a per Thread basis. A + * stack is implemented per Thread so that nested startCapture and stopCapture can be used. */ public class SystemLogHandler extends PrintStream { @@ -79,7 +73,7 @@ * Start capturing thread's output. */ public static void startCapture() { - CaptureLog log = null; + CaptureLog log; if (!reuse.isEmpty()) { try { log = reuse.remove(); @@ -124,6 +118,7 @@ /** * Find PrintStream to which the output must be written to. + * * @return the print stream */ protected PrintStream findStream() { @@ -161,7 +156,7 @@ @Override protected void setError() { - //findStream().setError(); + // findStream().setError(); } @Override @@ -170,8 +165,7 @@ } @Override - public void write(byte[] b) - throws IOException { + public void write(byte[] b) throws IOException { findStream().write(b); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/log/UserDataHelper.java tomcat10-10.1.52/java/org/apache/tomcat/util/log/UserDataHelper.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/log/UserDataHelper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/log/UserDataHelper.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,22 +19,20 @@ import org.apache.juli.logging.Log; /** - * This helper class assists with the logging associated with invalid input - * data. A developer may want all instances of invalid input data logged to - * assist with debugging whereas in production it is likely to be desirable not - * to log anything for invalid data. The following settings may be used: + * This helper class assists with the logging associated with invalid input data. A developer may want all instances of + * invalid input data logged to assist with debugging whereas in production it is likely to be desirable not to log + * anything for invalid data. The following settings may be used: *
      *
    • NOTHING: Log nothing.
    • *
    • DEBUG_ALL: Log all problems at DEBUG log level.
    • - *
    • INFO_THEN_DEBUG: Log first problem at INFO log level and any further - * issues in the following TBD (configurable) seconds at DEBUG level
    • + *
    • INFO_THEN_DEBUG: Log first problem at INFO log level and any further issues in the following TBD (configurable) + * seconds at DEBUG level
    • *
    • INFO_ALL: Log all problems at INFO log level.
    • *
    * By default, INFO_THEN_DEBUG is used with a suppression time of 24 hours. - * - * NOTE: This class is not completely thread-safe. When using INFO_THEN_DEBUG it - * is possible that several INFO messages will be logged before dropping to - * DEBUG. + *

    + * NOTE: This class is not completely thread-safe. When using INFO_THEN_DEBUG it is possible that several INFO messages + * will be logged before dropping to DEBUG. */ public class UserDataHelper { @@ -54,8 +52,7 @@ this.log = log; Config tempConfig; - String configString = System.getProperty( - "org.apache.juli.logging.UserDataHelper.CONFIG"); + String configString = System.getProperty("org.apache.juli.logging.UserDataHelper.CONFIG"); if (configString == null) { tempConfig = Config.INFO_THEN_DEBUG; } else { @@ -68,9 +65,9 @@ } // Default suppression time of 1 day. - suppressionTime = Integer.getInteger( - "org.apache.juli.logging.UserDataHelper.SUPPRESSION_TIME", - 60 * 60 * 24).intValue() * 1000L; + suppressionTime = + Integer.getInteger("org.apache.juli.logging.UserDataHelper.SUPPRESSION_TIME", 60 * 60 * 24).intValue() * + 1000L; if (suppressionTime == 0) { tempConfig = Config.INFO_ALL; @@ -81,12 +78,10 @@ /** - * Returns log mode for the next log message, or null if the - * message should not be logged. - * + * Returns log mode for the next log message, or null if the message should not be logged. *

    - * If INFO_THEN_DEBUG configuration option is enabled, this - * method might change internal state of this object. + * If INFO_THEN_DEBUG configuration option is enabled, this method might change internal state of this + * object. * * @return Log mode, or null */ @@ -110,9 +105,8 @@ /* - * Not completely thread-safe but good enough for this use case. I couldn't - * see a simple enough way to make it completely thread-safe that was not - * likely to compromise performance. + * Not completely thread-safe but good enough for this use case. I couldn't see a simple enough way to make it + * completely thread-safe that was not likely to compromise performance. */ private boolean logAtInfo() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/AttributeInfo.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/AttributeInfo.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/AttributeInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/AttributeInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,12 +18,10 @@ import javax.management.MBeanAttributeInfo; - /** - *

    Internal configuration information for an Attribute - * descriptor.

    - * - * @author Craig R. McClanahan + *

    + * Internal configuration information for an Attribute descriptor. + *

    */ public class AttributeInfo extends FeatureInfo { private static final long serialVersionUID = -2511626862303972143L; @@ -55,7 +53,7 @@ * @return the name of the property getter method, if non-standard. */ public String getGetMethod() { - if(getMethod == null) { + if (getMethod == null) { getMethod = getMethodName(getName(), true, isIs()); } return this.getMethod; @@ -67,8 +65,8 @@ /** * Is this a boolean attribute with an "is" getter? - * @return true if this is a boolean attribute - * with an "is" getter + * + * @return true if this is a boolean attribute with an "is" getter */ public boolean isIs() { return this.is; @@ -81,6 +79,7 @@ /** * Is this attribute readable by management applications? + * * @return true if readable */ public boolean isReadable() { @@ -96,7 +95,7 @@ * @return the name of the property setter method, if non-standard. */ public String getSetMethod() { - if( setMethod == null ) { + if (setMethod == null) { setMethod = getMethodName(getName(), false, false); } return this.setMethod; @@ -108,6 +107,7 @@ /** * Is this attribute writable by management applications? + * * @return true if writable */ public boolean isWriteable() { @@ -122,29 +122,29 @@ /** - * Create and return a ModelMBeanAttributeInfo object that - * corresponds to the attribute described by this instance. + * Create and return a ModelMBeanAttributeInfo object that corresponds to the attribute described by + * this instance. + * * @return the attribute info */ MBeanAttributeInfo createAttributeInfo() { // Return our cached information (if any) if (info == null) { - info = new MBeanAttributeInfo(getName(), getType(), getDescription(), - isReadable(), isWriteable(), false); + info = new MBeanAttributeInfo(getName(), getType(), getDescription(), isReadable(), isWriteable(), false); } - return (MBeanAttributeInfo)info; + return (MBeanAttributeInfo) info; } // -------------------------------------------------------- Private Methods /** - * Create and return the name of a default property getter or setter - * method, according to the specified values. + * Create and return the name of a default property getter or setter method, according to the specified values. * - * @param name Name of the property itself + * @param name Name of the property itself * @param getter Do we want a get method (versus a set method)? - * @param is If returning a getter, do we want the "is" form? + * @param is If returning a getter, do we want the "is" form? + * * @return the method name */ private String getMethodName(String name, boolean getter, boolean is) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/BaseAttributeFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,12 +26,11 @@ /** - *

    Implementation of NotificationFilter for attribute change - * notifications. This class is used by BaseModelMBean to - * construct attribute change notification event filters when a filter is not - * supplied by the application.

    - * - * @author Craig R. McClanahan + *

    + * Implementation of NotificationFilter for attribute change notifications. This class is used by + * BaseModelMBean to construct attribute change notification event filters when a filter is not supplied by + * the application. + *

    */ public class BaseAttributeFilter implements NotificationFilter { @@ -40,11 +39,10 @@ // ----------------------------------------------------------- Constructors /** - * Construct a new filter that accepts only the specified attribute - * name. + * Construct a new filter that accepts only the specified attribute name. * - * @param name Name of the attribute to be accepted by this filter, or - * null to accept all attribute names + * @param name Name of the attribute to be accepted by this filter, or null to accept all attribute + * names */ public BaseAttributeFilter(String name) { @@ -60,10 +58,10 @@ /** - * The set of attribute names that are accepted by this filter. If this - * list is empty, all attribute names are accepted. + * The set of attribute names that are accepted by this filter. If this list is empty, all attribute names are + * accepted. */ - private Set names = new HashSet<>(); + private final Set names = new HashSet<>(); // --------------------------------------------------------- Public Methods @@ -84,8 +82,7 @@ /** - * Clear all accepted names from this filter, so that it will accept - * all attribute names. + * Clear all accepted names from this filter, so that it will accept all attribute names. */ public void clear() { @@ -97,9 +94,9 @@ /** - * Return the set of names that are accepted by this filter. If this - * filter accepts all attribute names, a zero length array will be - * returned. + * Return the set of names that are accepted by this filter. If this filter accepts all attribute names, a zero + * length array will be returned. + * * @return the array of names */ public String[] getNames() { @@ -112,13 +109,13 @@ /** - *

    Test whether notification enabled for this event. - * Return true if:

    + *

    + * Test whether notification enabled for this event. Return true if: + *

    *
      *
    • This is an attribute change notification
    • - *
    • Either the set of accepted names is empty (implying that all - * attribute names are of interest) or the set of accepted names - * includes the name of the attribute in this notification
    • + *
    • Either the set of accepted names is empty (implying that all attribute names are of interest) or the set of + * accepted names includes the name of the attribute in this notification
    • *
    */ @Override @@ -130,13 +127,12 @@ if (!(notification instanceof AttributeChangeNotification)) { return false; } - AttributeChangeNotification acn = - (AttributeChangeNotification) notification; + AttributeChangeNotification acn = (AttributeChangeNotification) notification; if (!AttributeChangeNotification.ATTRIBUTE_CHANGE.equals(acn.getType())) { return false; } synchronized (names) { - if (names.size() < 1) { + if (names.isEmpty()) { return true; } else { return names.contains(acn.getAttributeName()); @@ -147,8 +143,7 @@ /** - * Remove an attribute name from the set of names accepted by this - * filter. + * Remove an attribute name from the set of names accepted by this filter. * * @param name Name of the attribute to be removed */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/BaseModelMBean.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/BaseModelMBean.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/BaseModelMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/BaseModelMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -70,43 +70,37 @@ */ /** - *

    Basic implementation of the DynamicMBean interface, which - * supports the minimal requirements of the interface contract.

    - * - *

    This can be used directly to wrap an existing java bean, or inside - * an mlet or anywhere an MBean would be used. - * + *

    + * Basic implementation of the DynamicMBean interface, which supports the minimal requirements of the + * interface contract. + *

    + *

    + * This can be used directly to wrap an existing java bean, or inside a mlet or anywhere an MBean would be used. + *

    * Limitations: *

      - *
    • Only managed resources of type objectReference are - * supported.
    • - *
    • Caching of attribute values and operation results is not supported. - * All calls to invoke() are immediately executed.
    • + *
    • Only managed resources of type objectReference are supported.
    • + *
    • Caching of attribute values and operation results is not supported. All calls to invoke() are + * immediately executed.
    • *
    • Persistence of MBean attributes and operations is not supported.
    • - *
    • All classes referenced as attribute types, operation parameters, or - * operation return values must be one of the following: - *
        - *
      • One of the Java primitive types (boolean, byte, char, double, - * float, integer, long, short). Corresponding value will be wrapped - * in the appropriate wrapper class automatically.
      • - *
      • Operations that return no value should declare a return type of - * void.
      • - *
      + *
    • All classes referenced as attribute types, operation parameters, or operation return values must be one of the + * following: + *
        + *
      • One of the Java primitive types (boolean, byte, char, double, float, integer, long, short). Corresponding value + * will be wrapped in the appropriate wrapper class automatically.
      • + *
      • Operations that return no value should declare a return type of void.
      • + *
      *
    • Attribute caching is not supported
    • *
    - * - * @author Craig R. McClanahan - * @author Costin Manolache */ -public class BaseModelMBean implements DynamicMBean, MBeanRegistration, - ModelMBeanNotificationBroadcaster { +public class BaseModelMBean implements DynamicMBean, MBeanRegistration, ModelMBeanNotificationBroadcaster { private static final Log log = LogFactory.getLog(BaseModelMBean.class); private static final StringManager sm = StringManager.getManager(BaseModelMBean.class); // ----------------------------------------------------- Instance Variables - protected ObjectName oname=null; + protected ObjectName oname = null; /** * Notification broadcaster for attribute changes. @@ -135,34 +129,31 @@ protected String resourceType = null; // key: operation val: invoke method - //private Hashtable invokeAttMap=new Hashtable(); + // private Hashtable invokeAttMap=new Hashtable(); @Override - public Object getAttribute(String name) - throws AttributeNotFoundException, MBeanException, - ReflectionException { + public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException { // Validate the input parameters if (name == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullAttributeName")), - sm.getString("baseModelMBean.nullAttributeName")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullAttributeName")), + sm.getString("baseModelMBean.nullAttributeName")); } - if( (resource instanceof DynamicMBean) && - ! ( resource instanceof BaseModelMBean )) { - return ((DynamicMBean)resource).getAttribute(name); + if ((resource instanceof DynamicMBean) && !(resource instanceof BaseModelMBean)) { + return ((DynamicMBean) resource).getAttribute(name); } - Method m=managedBean.getGetter(name, this, resource); - Object result = null; + Method m = managedBean.getGetter(name, this, resource); + Object result; try { Class declaring = m.getDeclaringClass(); // workaround for catalina weird mbeans - the declaring class is BaseModelMBean. // but this is the catalina class. - if( declaring.isAssignableFrom(this.getClass()) ) { - result = m.invoke(this, NO_ARGS_PARAM ); + if (declaring.isAssignableFrom(this.getClass())) { + result = m.invoke(this, NO_ARGS_PARAM); } else { - result = m.invoke(resource, NO_ARGS_PARAM ); + result = m.invoke(resource, NO_ARGS_PARAM); } } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); @@ -170,18 +161,15 @@ t = e; } if (t instanceof RuntimeException) { - throw new RuntimeOperationsException - ((RuntimeException) t, sm.getString("baseModelMBean.invokeError", name)); + throw new RuntimeOperationsException((RuntimeException) t, + sm.getString("baseModelMBean.invokeError", name)); } else if (t instanceof Error) { - throw new RuntimeErrorException - ((Error) t, sm.getString("baseModelMBean.invokeError", name)); + throw new RuntimeErrorException((Error) t, sm.getString("baseModelMBean.invokeError", name)); } else { - throw new MBeanException - (e, sm.getString("baseModelMBean.invokeError", name)); + throw new MBeanException(e, sm.getString("baseModelMBean.invokeError", name)); } } catch (Exception e) { - throw new MBeanException - (e, sm.getString("baseModelMBean.invokeError", name)); + throw new MBeanException(e, sm.getString("baseModelMBean.invokeError", name)); } // Return the results of this method invocation @@ -190,13 +178,13 @@ @Override - public AttributeList getAttributes(String names[]) { + public AttributeList getAttributes(String[] names) { // Validate the input parameters if (names == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullAttributeNameList")), - sm.getString("baseModelMBean.nullAttributeNameList")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullAttributeNameList")), + sm.getString("baseModelMBean.nullAttributeNameList")); } // Prepare our response, eating all exceptions @@ -228,61 +216,55 @@ /** * {@inheritDoc} - *

    IMPLEMENTATION NOTE - This implementation will - * attempt to invoke this method on the MBean itself, or (if not - * available) on the managed resource object associated with this - * MBean.

    + *

    + * IMPLEMENTATION NOTE - This implementation will attempt to invoke this method on the MBean + * itself, or (if not available) on the managed resource object associated with this MBean. + *

    */ @Override - public Object invoke(String name, Object params[], String signature[]) - throws MBeanException, ReflectionException - { - if( (resource instanceof DynamicMBean) && - ! ( resource instanceof BaseModelMBean )) { - return ((DynamicMBean)resource).invoke(name, params, signature); + public Object invoke(String name, Object[] params, String[] signature) throws MBeanException, ReflectionException { + if ((resource instanceof DynamicMBean) && !(resource instanceof BaseModelMBean)) { + return ((DynamicMBean) resource).invoke(name, params, signature); } // Validate the input parameters if (name == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullMethodName")), - sm.getString("baseModelMBean.nullMethodName")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullMethodName")), + sm.getString("baseModelMBean.nullMethodName")); } - if( log.isTraceEnabled()) { + if (log.isTraceEnabled()) { log.trace("Invoke " + name); } - Method method= managedBean.getInvoke(name, params, signature, this, resource); + Method method = managedBean.getInvoke(name, params, signature, this, resource); // Invoke the selected method on the appropriate object - Object result = null; + Object result; try { - if( method.getDeclaringClass().isAssignableFrom( this.getClass()) ) { - result = method.invoke(this, params ); + if (method.getDeclaringClass().isAssignableFrom(this.getClass())) { + result = method.invoke(this, params); } else { result = method.invoke(resource, params); } } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); - log.error(sm.getString("baseModelMBean.invokeError", name), t ); + log.error(sm.getString("baseModelMBean.invokeError", name), t); if (t == null) { t = e; } if (t instanceof RuntimeException) { - throw new RuntimeOperationsException - ((RuntimeException) t, sm.getString("baseModelMBean.invokeError", name)); + throw new RuntimeOperationsException((RuntimeException) t, + sm.getString("baseModelMBean.invokeError", name)); } else if (t instanceof Error) { - throw new RuntimeErrorException - ((Error) t, sm.getString("baseModelMBean.invokeError", name)); + throw new RuntimeErrorException((Error) t, sm.getString("baseModelMBean.invokeError", name)); } else { - throw new MBeanException - ((Exception)t, sm.getString("baseModelMBean.invokeError", name)); + throw new MBeanException((Exception) t, sm.getString("baseModelMBean.invokeError", name)); } } catch (Exception e) { - log.error(sm.getString("baseModelMBean.invokeError", name), e ); - throw new MBeanException - (e, sm.getString("baseModelMBean.invokeError", name)); + log.error(sm.getString("baseModelMBean.invokeError", name), e); + throw new MBeanException(e, sm.getString("baseModelMBean.invokeError", name)); } // Return the results of this method invocation @@ -290,9 +272,7 @@ } - static Class getAttributeClass(String signature) - throws ReflectionException - { + static Class getAttributeClass(String signature) throws ReflectionException { if (signature.equals(Boolean.TYPE.getName())) { return Boolean.TYPE; } else if (signature.equals(Byte.TYPE.getName())) { @@ -311,11 +291,12 @@ return Short.TYPE; } else { try { - ClassLoader cl=Thread.currentThread().getContextClassLoader(); - if( cl!=null ) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl != null) { return cl.loadClass(signature); } - } catch( ClassNotFoundException e ) { + } catch (ClassNotFoundException ignore) { + // Ignore } try { return Class.forName(signature); @@ -327,17 +308,14 @@ @Override public void setAttribute(Attribute attribute) - throws AttributeNotFoundException, MBeanException, - ReflectionException - { - if( log.isTraceEnabled() ) { - log.trace("Setting attribute " + this + " " + attribute ); + throws AttributeNotFoundException, MBeanException, ReflectionException { + if (log.isTraceEnabled()) { + log.trace("Setting attribute " + this + " " + attribute); } - if( (resource instanceof DynamicMBean) && - ! ( resource instanceof BaseModelMBean )) { + if ((resource instanceof DynamicMBean) && !(resource instanceof BaseModelMBean)) { try { - ((DynamicMBean)resource).setAttribute(attribute); + ((DynamicMBean) resource).setAttribute(attribute); } catch (InvalidAttributeValueException e) { throw new MBeanException(e); } @@ -346,31 +324,31 @@ // Validate the input parameters if (attribute == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullAttribute")), - sm.getString("baseModelMBean.nullAttribute")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullAttribute")), + sm.getString("baseModelMBean.nullAttribute")); } String name = attribute.getName(); Object value = attribute.getValue(); if (name == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullAttributeName")), - sm.getString("baseModelMBean.nullAttributeName")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullAttributeName")), + sm.getString("baseModelMBean.nullAttributeName")); } - Object oldValue=null; - //if( getAttMap.get(name) != null ) - // oldValue=getAttribute( name ); + Object oldValue = null; + // if( getAttMap.get(name) != null ) + // oldValue=getAttribute( name ); - Method m=managedBean.getSetter(name,this,resource); + Method m = managedBean.getSetter(name, this, resource); try { - if( m.getDeclaringClass().isAssignableFrom( this.getClass()) ) { - m.invoke(this, new Object[] { value }); + if (m.getDeclaringClass().isAssignableFrom(this.getClass())) { + m.invoke(this, value); } else { - m.invoke(resource, new Object[] { value }); + m.invoke(resource, value); } } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); @@ -378,36 +356,32 @@ t = e; } if (t instanceof RuntimeException) { - throw new RuntimeOperationsException - ((RuntimeException) t, sm.getString("baseModelMBean.invokeError", name)); + throw new RuntimeOperationsException((RuntimeException) t, + sm.getString("baseModelMBean.invokeError", name)); } else if (t instanceof Error) { - throw new RuntimeErrorException - ((Error) t, sm.getString("baseModelMBean.invokeError", name)); + throw new RuntimeErrorException((Error) t, sm.getString("baseModelMBean.invokeError", name)); } else { - throw new MBeanException - (e, sm.getString("baseModelMBean.invokeError", name)); + throw new MBeanException(e, sm.getString("baseModelMBean.invokeError", name)); } } catch (Exception e) { - log.error(sm.getString("baseModelMBean.invokeError", name) , e ); - throw new MBeanException - (e, sm.getString("baseModelMBean.invokeError", name)); + log.error(sm.getString("baseModelMBean.invokeError", name), e); + throw new MBeanException(e, sm.getString("baseModelMBean.invokeError", name)); } try { - sendAttributeChangeNotification(new Attribute( name, oldValue), - attribute); - } catch(Exception ex) { - log.error(sm.getString("baseModelMBean.notificationError", name), ex); - } - //attributes.put( name, value ); -// if( source != null ) { -// // this mbean is associated with a source - maybe we want to persist -// source.updateField(oname, name, value); -// } + sendAttributeChangeNotification(new Attribute(name, oldValue), attribute); + } catch (Exception e) { + log.error(sm.getString("baseModelMBean.notificationError", name), e); + } + // attributes.put( name, value ); + // if( source != null ) { + // // this mbean is associated with a source - maybe we want to persist + // source.updateField(oname, name, value); + // } } @Override public String toString() { - if( resource==null ) { + if (resource == null) { return "BaseModelMbean[" + resourceType + "]"; } return resource.toString(); @@ -423,14 +397,14 @@ } // Prepare and return our response, eating all exceptions - String names[] = new String[attributes.size()]; + String[] names = new String[attributes.size()]; int n = 0; for (Object attribute : attributes) { Attribute item = (Attribute) attribute; names[n++] = item.getName(); try { setAttribute(item); - } catch (Exception e) { + } catch (Exception ignore) { // Ignore all exceptions } } @@ -444,27 +418,24 @@ /** - * Get the instance handle of the object against which we execute - * all methods in this ModelMBean management interface. + * Get the instance handle of the object against which we execute all methods in this ModelMBean management + * interface. * * @return the backend managed object - * @exception InstanceNotFoundException if the managed resource object - * cannot be found - * @exception InvalidTargetObjectTypeException if the managed resource - * object is of the wrong type - * @exception MBeanException if the initializer of the object throws - * an exception - * @exception RuntimeOperationsException if the managed resource or the - * resource type is null or invalid + * + * @exception InstanceNotFoundException if the managed resource object cannot be found + * @exception InvalidTargetObjectTypeException if the managed resource object is of the wrong type + * @exception MBeanException if the initializer of the object throws an exception + * @exception RuntimeOperationsException if the managed resource or the resource type is null or + * invalid */ - public Object getManagedResource() - throws InstanceNotFoundException, InvalidTargetObjectTypeException, - MBeanException, RuntimeOperationsException { + public Object getManagedResource() throws InstanceNotFoundException, InvalidTargetObjectTypeException, + MBeanException, RuntimeOperationsException { if (resource == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullResource")), - sm.getString("baseModelMBean.nullResource")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullResource")), + sm.getString("baseModelMBean.nullResource")); } return resource; @@ -473,62 +444,36 @@ /** - * Set the instance handle of the object against which we will execute - * all methods in this ModelMBean management interface. - * - * The caller can provide the mbean instance or the object name to - * the resource, if needed. + * Set the instance handle of the object against which we will execute all methods in this ModelMBean management + * interface. The caller can provide the mbean instance or the object name to the resource, if needed. * * @param resource The resource object to be managed - * @param type The type of reference for the managed resource - * ("ObjectReference", "Handle", "IOR", "EJBHandle", or - * "RMIReference") + * @param type The type of reference for the managed resource ("ObjectReference", "Handle", "IOR", "EJBHandle", + * or "RMIReference") * - * @exception InstanceNotFoundException if the managed resource object - * cannot be found - * @exception MBeanException if the initializer of the object throws - * an exception - * @exception RuntimeOperationsException if the managed resource or the - * resource type is null or invalid + * @exception InstanceNotFoundException if the managed resource object cannot be found + * @exception MBeanException if the initializer of the object throws an exception + * @exception RuntimeOperationsException if the managed resource or the resource type is null or + * invalid */ public void setManagedResource(Object resource, String type) - throws InstanceNotFoundException, - MBeanException, RuntimeOperationsException - { + throws InstanceNotFoundException, MBeanException, RuntimeOperationsException { if (resource == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullResource")), - sm.getString("baseModelMBean.nullResource")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullResource")), + sm.getString("baseModelMBean.nullResource")); } -// if (!"objectreference".equalsIgnoreCase(type)) -// throw new InvalidTargetObjectTypeException(type); - this.resource = resource; this.resourceType = resource.getClass().getName(); - -// // Make the resource aware of the model mbean. -// try { -// Method m=resource.getClass().getMethod("setModelMBean", -// new Class[] {ModelMBean.class}); -// if( m!= null ) { -// m.invoke(resource, new Object[] {this}); -// } -// } catch( NoSuchMethodException t ) { -// // ignore -// } catch( Throwable t ) { -// log.error( "Can't set model mbean ", t ); -// } } // ------------------------------ ModelMBeanNotificationBroadcaster Methods - @Override - public void addAttributeChangeNotificationListener - (NotificationListener listener, String name, Object handback) - throws IllegalArgumentException { + public void addAttributeChangeNotificationListener(NotificationListener listener, String name, Object handback) + throws IllegalArgumentException { if (listener == null) { throw new IllegalArgumentException(sm.getString("baseModelMBean.nullListener")); @@ -537,21 +482,19 @@ attributeBroadcaster = new BaseNotificationBroadcaster(); } - if( log.isTraceEnabled() ) { + if (log.isTraceEnabled()) { log.trace("addAttributeNotificationListener " + listener); } BaseAttributeFilter filter = new BaseAttributeFilter(name); - attributeBroadcaster.addNotificationListener - (listener, filter, handback); + attributeBroadcaster.addNotificationListener(listener, filter, handback); } @Override - public void removeAttributeChangeNotificationListener - (NotificationListener listener, String name) - throws ListenerNotFoundException { + public void removeAttributeChangeNotificationListener(NotificationListener listener, String name) + throws ListenerNotFoundException { if (listener == null) { throw new IllegalArgumentException(sm.getString("baseModelMBean.nullListener")); @@ -566,21 +509,19 @@ @Override - public void sendAttributeChangeNotification - (AttributeChangeNotification notification) - throws MBeanException, RuntimeOperationsException { + public void sendAttributeChangeNotification(AttributeChangeNotification notification) + throws MBeanException, RuntimeOperationsException { if (notification == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullNotification")), - sm.getString("baseModelMBean.nullNotification")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullNotification")), + sm.getString("baseModelMBean.nullNotification")); } - if (attributeBroadcaster == null) - { + if (attributeBroadcaster == null) { return; // This means there are no registered listeners } - if( log.isTraceEnabled() ) { - log.trace( "AttributeChangeNotification " + notification ); + if (log.isTraceEnabled()) { + log.trace("AttributeChangeNotification " + notification); } attributeBroadcaster.sendNotification(notification); @@ -588,43 +529,35 @@ @Override - public void sendAttributeChangeNotification - (Attribute oldValue, Attribute newValue) - throws MBeanException, RuntimeOperationsException { + public void sendAttributeChangeNotification(Attribute oldValue, Attribute newValue) + throws MBeanException, RuntimeOperationsException { // Calculate the class name for the change notification - String type = null; + String type; if (newValue.getValue() != null) { type = newValue.getValue().getClass().getName(); } else if (oldValue.getValue() != null) { type = oldValue.getValue().getClass().getName(); - } - else { - return; // Old and new are both null == no change + } else { + return; // Old and new are both null == no change } - AttributeChangeNotification notification = - new AttributeChangeNotification - (this, 1, System.currentTimeMillis(), - "Attribute value has changed", - oldValue.getName(), type, - oldValue.getValue(), newValue.getValue()); + AttributeChangeNotification notification = new AttributeChangeNotification(this, 1, System.currentTimeMillis(), + "Attribute value has changed", oldValue.getName(), type, oldValue.getValue(), newValue.getValue()); sendAttributeChangeNotification(notification); } @Override - public void sendNotification(Notification notification) - throws MBeanException, RuntimeOperationsException { + public void sendNotification(Notification notification) throws MBeanException, RuntimeOperationsException { if (notification == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullNotification")), - sm.getString("baseModelMBean.nullNotification")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullNotification")), + sm.getString("baseModelMBean.nullNotification")); } - if (generalBroadcaster == null) - { + if (generalBroadcaster == null) { return; // This means there are no registered listeners } generalBroadcaster.sendNotification(notification); @@ -633,16 +566,14 @@ @Override - public void sendNotification(String message) - throws MBeanException, RuntimeOperationsException { + public void sendNotification(String message) throws MBeanException, RuntimeOperationsException { if (message == null) { - throw new RuntimeOperationsException - (new IllegalArgumentException(sm.getString("baseModelMBean.nullMessage")), - sm.getString("baseModelMBean.nullMessage")); + throw new RuntimeOperationsException( + new IllegalArgumentException(sm.getString("baseModelMBean.nullMessage")), + sm.getString("baseModelMBean.nullMessage")); } - Notification notification = new Notification - ("jmx.modelmbean.generic", this, 1, message); + Notification notification = new Notification("jmx.modelmbean.generic", this, 1, message); sendNotification(notification); } @@ -652,24 +583,21 @@ @Override - public void addNotificationListener(NotificationListener listener, - NotificationFilter filter, - Object handback) - throws IllegalArgumentException { + public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) + throws IllegalArgumentException { if (listener == null) { throw new IllegalArgumentException(sm.getString("baseModelMBean.nullListener")); } - if( log.isTraceEnabled() ) { + if (log.isTraceEnabled()) { log.trace("addNotificationListener " + listener); } if (generalBroadcaster == null) { generalBroadcaster = new BaseNotificationBroadcaster(); } - generalBroadcaster.addNotificationListener - (listener, filter, handback); + generalBroadcaster.addNotificationListener(listener, filter, handback); // We'll send the attribute change notifications to all listeners ( who care ) // The normal filtering can be used. @@ -679,12 +607,11 @@ attributeBroadcaster = new BaseNotificationBroadcaster(); } - if( log.isTraceEnabled() ) { + if (log.isTraceEnabled()) { log.trace("addAttributeNotificationListener " + listener); } - attributeBroadcaster.addNotificationListener - (listener, filter, handback); + attributeBroadcaster.addNotificationListener(listener, filter, handback); } @@ -692,36 +619,31 @@ public MBeanNotificationInfo[] getNotificationInfo() { // Acquire the set of application notifications - MBeanNotificationInfo current[] = getMBeanInfo().getNotifications(); - MBeanNotificationInfo response[] = - new MBeanNotificationInfo[current.length + 2]; - // Descriptor descriptor = null; + MBeanNotificationInfo[] current = getMBeanInfo().getNotifications(); + MBeanNotificationInfo[] response = new MBeanNotificationInfo[current.length + 2]; + // Descriptor descriptor = null; // Fill in entry for general notifications -// descriptor = new DescriptorSupport -// (new String[] { "name=GENERIC", -// "descriptorType=notification", -// "log=T", -// "severity=5", -// "displayName=jmx.modelmbean.generic" }); - response[0] = new MBeanNotificationInfo - (new String[] { "jmx.modelmbean.generic" }, - "GENERIC", - "Text message notification from the managed resource"); - //descriptor); + // descriptor = new DescriptorSupport + // (new String[] { "name=GENERIC", + // "descriptorType=notification", + // "log=T", + // "severity=5", + // "displayName=jmx.modelmbean.generic" }); + response[0] = new MBeanNotificationInfo(new String[] { "jmx.modelmbean.generic" }, "GENERIC", + "Text message notification from the managed resource"); + // descriptor); // Fill in entry for attribute change notifications -// descriptor = new DescriptorSupport -// (new String[] { "name=ATTRIBUTE_CHANGE", -// "descriptorType=notification", -// "log=T", -// "severity=5", -// "displayName=jmx.attribute.change" }); - response[1] = new MBeanNotificationInfo - (new String[] { "jmx.attribute.change" }, - "ATTRIBUTE_CHANGE", - "Observed MBean attribute value has changed"); - //descriptor); + // descriptor = new DescriptorSupport + // (new String[] { "name=ATTRIBUTE_CHANGE", + // "descriptorType=notification", + // "log=T", + // "severity=5", + // "displayName=jmx.attribute.change" }); + response[1] = new MBeanNotificationInfo(new String[] { "jmx.attribute.change" }, "ATTRIBUTE_CHANGE", + "Observed MBean attribute value has changed"); + // descriptor); // Copy remaining notifications as reported by the application System.arraycopy(current, 0, response, 2, current.length); @@ -731,8 +653,7 @@ @Override - public void removeNotificationListener(NotificationListener listener) - throws ListenerNotFoundException { + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { if (listener == null) { throw new IllegalArgumentException(sm.getString("baseModelMBean.nullListener")); @@ -745,7 +666,7 @@ if (attributeBroadcaster != null) { attributeBroadcaster.removeNotificationListener(listener); } - } + } public String getModelerType() { @@ -772,43 +693,40 @@ } - // -------------------- Registration -------------------- + // -------------------- Registration -------------------- // XXX We can add some method patterns here- like setName() and // setDomain() for code that doesn't implement the Registration @Override - public ObjectName preRegister(MBeanServer server, - ObjectName name) - throws Exception - { - if( log.isTraceEnabled()) { - log.trace("preRegister " + resource + " " + name ); - } - oname=name; - if( resource instanceof MBeanRegistration ) { - oname = ((MBeanRegistration)resource).preRegister(server, name ); + public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { + if (log.isTraceEnabled()) { + log.trace("preRegister " + resource + " " + name); + } + oname = name; + if (resource instanceof MBeanRegistration) { + oname = ((MBeanRegistration) resource).preRegister(server, name); } return oname; } @Override public void postRegister(Boolean registrationDone) { - if( resource instanceof MBeanRegistration ) { - ((MBeanRegistration)resource).postRegister(registrationDone); + if (resource instanceof MBeanRegistration) { + ((MBeanRegistration) resource).postRegister(registrationDone); } } @Override public void preDeregister() throws Exception { - if( resource instanceof MBeanRegistration ) { - ((MBeanRegistration)resource).preDeregister(); + if (resource instanceof MBeanRegistration) { + ((MBeanRegistration) resource).preDeregister(); } } @Override public void postDeregister() { - if( resource instanceof MBeanRegistration ) { - ((MBeanRegistration)resource).postDeregister(); + if (resource instanceof MBeanRegistration) { + ((MBeanRegistration) resource).postDeregister(); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/BaseNotificationBroadcaster.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/BaseNotificationBroadcaster.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/BaseNotificationBroadcaster.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/BaseNotificationBroadcaster.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,12 +28,8 @@ /** - * Implementation of NotificationBroadcaster for attribute - * change notifications. This class is used by BaseModelMBean to - * handle notifications of attribute change events to interested listeners. - * - * @author Craig R. McClanahan - * @author Costin Manolache + * Implementation of NotificationBroadcaster for attribute change notifications. This class is used by + * BaseModelMBean to handle notifications of attribute change events to interested listeners. */ public class BaseNotificationBroadcaster implements NotificationBroadcaster { @@ -46,11 +42,9 @@ /** - * The set of registered BaseNotificationBroadcasterEntry - * entries. + * The set of registered BaseNotificationBroadcasterEntry entries. */ - protected ArrayList entries = - new ArrayList<>(); + final ArrayList entries = new ArrayList<>(); // --------------------------------------------------------- Public Methods @@ -60,18 +54,15 @@ * Add a notification event listener to this MBean. * * @param listener Listener that will receive event notifications - * @param filter Filter object used to filter event notifications - * actually delivered, or null for no filtering - * @param handback Handback object to be sent along with event - * notifications + * @param filter Filter object used to filter event notifications actually delivered, or null for no + * filtering + * @param handback object to be sent along with event notifications * * @exception IllegalArgumentException if the listener parameter is null */ @Override - public void addNotificationListener(NotificationListener listener, - NotificationFilter filter, - Object handback) - throws IllegalArgumentException { + public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) + throws IllegalArgumentException { synchronized (entries) { @@ -79,14 +70,11 @@ if (filter instanceof BaseAttributeFilter) { BaseAttributeFilter newFilter = (BaseAttributeFilter) filter; for (BaseNotificationBroadcasterEntry item : entries) { - if ((item.listener == listener) && - (item.filter != null) && - (item.filter instanceof BaseAttributeFilter) && - (item.handback == handback)) { - BaseAttributeFilter oldFilter = - (BaseAttributeFilter) item.filter; - String newNames[] = newFilter.getNames(); - String oldNames[] = oldFilter.getNames(); + if ((item.listener == listener) && (item.filter instanceof BaseAttributeFilter) && + (item.handback == handback)) { + BaseAttributeFilter oldFilter = (BaseAttributeFilter) item.filter; + String[] newNames = newFilter.getNames(); + String[] oldNames = oldFilter.getNames(); if (newNames.length == 0) { oldFilter.clear(); } else { @@ -102,16 +90,14 @@ } // General purpose addition of a new entry - entries.add(new BaseNotificationBroadcasterEntry - (listener, filter, handback)); + entries.add(new BaseNotificationBroadcasterEntry(listener, filter, handback)); } } /** - * Return an MBeanNotificationInfo object describing the - * notifications sent by this MBean. + * Return an MBeanNotificationInfo object describing the notifications sent by this MBean. */ @Override public MBeanNotificationInfo[] getNotificationInfo() { @@ -122,15 +108,12 @@ /** * Remove a notification event listener from this MBean. * - * @param listener The listener to be removed (any and all registrations - * for this listener will be eliminated) + * @param listener The listener to be removed (any and all registrations for this listener will be eliminated) * - * @exception ListenerNotFoundException if this listener is not - * registered in the MBean + * @exception ListenerNotFoundException if this listener is not registered in the MBean */ @Override - public void removeNotificationListener(NotificationListener listener) - throws ListenerNotFoundException { + public void removeNotificationListener(NotificationListener listener) throws ListenerNotFoundException { synchronized (entries) { entries.removeIf(item -> item.listener == listener); @@ -148,8 +131,7 @@ synchronized (entries) { for (BaseNotificationBroadcasterEntry item : entries) { - if ((item.filter != null) && - (!item.filter.isNotificationEnabled(notification))) { + if ((item.filter != null) && (!item.filter.isNotificationEnabled(notification))) { continue; } item.listener.handleNotification(notification, item.handback); @@ -167,18 +149,16 @@ class BaseNotificationBroadcasterEntry { - BaseNotificationBroadcasterEntry(NotificationListener listener, - NotificationFilter filter, - Object handback) { + BaseNotificationBroadcasterEntry(NotificationListener listener, NotificationFilter filter, Object handback) { this.listener = listener; this.filter = filter; this.handback = handback; } - public NotificationFilter filter = null; + public NotificationFilter filter; - public Object handback = null; + public Object handback; - public NotificationListener listener = null; + public NotificationListener listener; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/FeatureInfo.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/FeatureInfo.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/FeatureInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/FeatureInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,11 +23,10 @@ /** - *

    Convenience base class for AttributeInfo and - * OperationInfo classes that will be used to collect configuration - * information for the ModelMBean beans exposed for management.

    - * - * @author Craig R. McClanahan + *

    + * Convenience base class for AttributeInfo and OperationInfo classes that will be used to + * collect configuration information for the ModelMBean beans exposed for management. + *

    */ public class FeatureInfo implements Serializable { private static final long serialVersionUID = -911529176124712296L; @@ -55,8 +54,7 @@ /** - * @return the name of this feature, which must be unique among features - * in the same collection. + * @return the name of this feature, which must be unique among features in the same collection. */ public String getName() { return this.name; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -40,6 +40,7 @@ modules.digesterParseError=Error parsing registry data modules.readDescriptorsError=Error reading descriptors +registry.cannotAccessRegistry=Guard object [{0}] does not allow access to the registry registry.createdServer=Created MBeanServer registry.existingServer=Using existing MBeanServer registry.initError=Error initializing [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -40,6 +40,7 @@ modules.digesterParseError=Erreur lors de l'analyse des données du registre modules.readDescriptorsError=Erreur lors de la lecture des descripteurs +registry.cannotAccessRegistry=L''objet garde [{0}] ne permet pas l''accès au registre registry.createdServer=Le MBeanServer a été crée registry.existingServer=Le MBeanServer existant est utilisé registry.initError=Erreur lors de l''initialisation [{0}] diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -40,6 +40,7 @@ modules.digesterParseError=レジストリデータ解析中のエラー modules.readDescriptorsError=記述子読み取り中のエラー +registry.cannotAccessRegistry=ガードオブジェクト [{0}] はレジストリへのアクセスを許可していません registry.createdServer=作成された MBeanServer registry.existingServer=既存の MBeanServer を使用する registry.initError=[{0}]初期化中のエラー diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/ManagedBean.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/ManagedBean.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/ManagedBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/ManagedBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,10 +41,9 @@ /** - *

    Internal configuration information for a managed bean (MBean) - * descriptor.

    - * - * @author Craig R. McClanahan + *

    + * Internal configuration information for a managed bean (MBean) descriptor. + *

    */ public class ManagedBean implements java.io.Serializable { @@ -58,14 +57,13 @@ private final ReadWriteLock mBeanInfoLock = new ReentrantReadWriteLock(); /** - * The ModelMBeanInfo object that corresponds - * to this ManagedBean instance. + * The ModelMBeanInfo object that corresponds to this ManagedBean instance. */ private transient volatile MBeanInfo info = null; - private Map attributes = new HashMap<>(); + private final Map attributes = new HashMap<>(); - private Map operations = new HashMap<>(); + private final Map operations = new HashMap<>(); protected String className = BASE_MBEAN; protected String description = null; @@ -73,14 +71,14 @@ protected String group = null; protected String name = null; - private NotificationInfo notifications[] = new NotificationInfo[0]; + private NotificationInfo[] notifications = new NotificationInfo[0]; protected String type = null; /** * Constructor. Will add default attributes. */ public ManagedBean() { - AttributeInfo ai=new AttributeInfo(); + AttributeInfo ai = new AttributeInfo(); ai.setName("modelerType"); ai.setDescription("Type of the modeled resource. Can be set only once"); ai.setType("java.lang.String"); @@ -100,10 +98,9 @@ /** - * The fully qualified name of the Java class of the MBean - * described by this descriptor. If not specified, the standard JMX - * class (javax.management.modelmbean.RequiredModeLMBean) - * will be utilized. + * The fully qualified name of the Java class of the MBean described by this descriptor. If not specified, the + * standard JMX class (javax.management.modelmbean.RequiredModeLMBean) will be utilized. + * * @return the class name */ public String getClassName() { @@ -140,8 +137,8 @@ /** - * @return the (optional) ObjectName domain in which - * this MBean should be registered in the MBeanServer. + * @return the (optional) ObjectName domain in which this MBean should be registered in the + * MBeanServer. */ public String getDomain() { return this.domain; @@ -165,8 +162,8 @@ /** - * @return the name of this managed bean, which must be unique - * among all MBeans managed by a particular MBeans server. + * @return the name of this managed bean, which must be unique among all MBeans managed by a particular MBeans + * server. */ public String getName() { return this.name; @@ -200,9 +197,8 @@ /** - * @return the fully qualified name of the Java class of the resource - * implementation class described by the managed bean described - * by this descriptor. + * @return the fully qualified name of the Java class of the resource implementation class described by the managed + * bean described by this descriptor. */ public String getType() { return this.type; @@ -240,10 +236,8 @@ public void addNotification(NotificationInfo notification) { mBeanInfoLock.writeLock().lock(); try { - NotificationInfo results[] = - new NotificationInfo[notifications.length + 1]; - System.arraycopy(notifications, 0, results, 0, - notifications.length); + NotificationInfo[] results = new NotificationInfo[notifications.length + 1]; + System.arraycopy(notifications, 0, results, 0, notifications.length); results[notifications.length] = notification; notifications = results; this.info = null; @@ -264,30 +258,25 @@ /** - * Create and return a ModelMBean that has been - * preconfigured with the ModelMBeanInfo information - * for this managed bean, and is associated with the specified - * managed object instance. The returned ModelMBean - * will NOT have been registered with our - * MBeanServer. + * Create and return a ModelMBean that has been preconfigured with the ModelMBeanInfo + * information for this managed bean, and is associated with the specified managed object instance. The returned + * ModelMBean will NOT have been registered with our MBeanServer. + * + * @param instance Instanced of the managed object, or null for no associated instance * - * @param instance Instanced of the managed object, or null - * for no associated instance * @return the MBean - * @exception InstanceNotFoundException if the managed resource - * object cannot be found - * @exception MBeanException if a problem occurs instantiating the - * ModelMBean instance + * + * @exception InstanceNotFoundException if the managed resource object cannot be found + * @exception MBeanException if a problem occurs instantiating the ModelMBean instance * @exception RuntimeOperationsException if a JMX runtime error occurs */ public DynamicMBean createMBean(Object instance) - throws InstanceNotFoundException, - MBeanException, RuntimeOperationsException { + throws InstanceNotFoundException, MBeanException, RuntimeOperationsException { - BaseModelMBean mbean = null; + BaseModelMBean mbean; // Load the ModelMBean implementation class - if(getClassName().equals(BASE_MBEAN)) { + if (getClassName().equals(BASE_MBEAN)) { // Skip introspection mbean = new BaseModelMBean(); } else { @@ -296,22 +285,22 @@ try { clazz = Class.forName(getClassName()); } catch (Exception e) { + // Ignore } - if( clazz==null ) { + if (clazz == null) { try { - ClassLoader cl= Thread.currentThread().getContextClassLoader(); - if ( cl != null) { - clazz= cl.loadClass(getClassName()); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl != null) { + clazz = cl.loadClass(getClassName()); } } catch (Exception e) { - ex=e; + ex = e; } } - if( clazz==null) { - throw new MBeanException - (ex, sm.getString("managedMBean.cannotLoadClass", getClassName())); + if (clazz == null) { + throw new MBeanException(ex, sm.getString("managedMBean.cannotLoadClass", getClassName())); } try { // Stupid - this will set the default minfo first.... @@ -319,20 +308,15 @@ } catch (RuntimeOperationsException e) { throw e; } catch (Exception e) { - throw new MBeanException - (e, sm.getString("managedMBean.cannotInstantiateClass", getClassName())); + throw new MBeanException(e, sm.getString("managedMBean.cannotInstantiateClass", getClassName())); } } mbean.setManagedBean(this); // Set the managed resource (if any) - try { - if (instance != null) { - mbean.setManagedResource(instance, "ObjectReference"); - } - } catch (InstanceNotFoundException e) { - throw e; + if (instance != null) { + mbean.setManagedResource(instance, "ObjectReference"); } return mbean; @@ -340,8 +324,8 @@ /** - * Create and return a ModelMBeanInfo object that - * describes this entire managed bean. + * Create and return a ModelMBeanInfo object that describes this entire managed bean. + * * @return the MBean info */ MBeanInfo getMBeanInfo() { @@ -360,36 +344,29 @@ try { if (info == null) { // Create subordinate information descriptors as required - AttributeInfo attrs[] = getAttributes(); - MBeanAttributeInfo attributes[] = - new MBeanAttributeInfo[attrs.length]; + AttributeInfo[] attrs = getAttributes(); + MBeanAttributeInfo[] attributes = new MBeanAttributeInfo[attrs.length]; for (int i = 0; i < attrs.length; i++) { attributes[i] = attrs[i].createAttributeInfo(); } - OperationInfo opers[] = getOperations(); - MBeanOperationInfo operations[] = - new MBeanOperationInfo[opers.length]; + OperationInfo[] opers = getOperations(); + MBeanOperationInfo[] operations = new MBeanOperationInfo[opers.length]; for (int i = 0; i < opers.length; i++) { operations[i] = opers[i].createOperationInfo(); } - NotificationInfo notifs[] = getNotifications(); - MBeanNotificationInfo notifications[] = - new MBeanNotificationInfo[notifs.length]; + NotificationInfo[] notifs = getNotifications(); + MBeanNotificationInfo[] notifications = new MBeanNotificationInfo[notifs.length]; for (int i = 0; i < notifs.length; i++) { notifications[i] = notifs[i].createNotificationInfo(); } // Construct and return a new ModelMBeanInfo object - info = new MBeanInfo(getClassName(), - getDescription(), - attributes, - new MBeanConstructorInfo[] {}, - operations, - notifications); + info = new MBeanInfo(getClassName(), getDescription(), attributes, new MBeanConstructorInfo[] {}, + operations, notifications); } return info; @@ -436,7 +413,7 @@ String getMethod = attrInfo.getGetMethod(); - Object object = null; + Object object; NoSuchMethodException exception = null; try { object = mbean; @@ -448,7 +425,7 @@ try { object = resource; m = object.getClass().getMethod(getMethod, NO_ARGS_PARAM_SIG); - exception=null; + exception = null; } catch (NoSuchMethodException e) { exception = e; } @@ -472,11 +449,11 @@ // Look up the actual operation to be used String setMethod = attrInfo.getSetMethod(); - String argType=attrInfo.getType(); + String argType = attrInfo.getType(); - Class signature[] = new Class[] { BaseModelMBean.getAttributeClass( argType ) }; + Class[] signature = new Class[] { BaseModelMBean.getAttributeClass(argType) }; - Object object = null; + Object object; NoSuchMethodException exception = null; try { object = bean; @@ -488,7 +465,7 @@ try { object = resource; m = object.getClass().getMethod(setMethod, signature); - exception=null; + exception = null; } catch (NoSuchMethodException e) { exception = e; } @@ -519,8 +496,7 @@ // Acquire the ModelMBeanOperationInfo information for // the requested operation - OperationInfo opInfo = - operations.get(createOperationKey(aname, signature)); + OperationInfo opInfo = operations.get(createOperationKey(aname, signature)); if (opInfo == null) { throw new MBeanException(new ServiceNotFoundException(sm.getString("managedMBean.noOperation", aname)), sm.getString("managedMBean.noOperation", aname)); @@ -528,7 +504,7 @@ // Prepare the signature required by Java reflection APIs // FIXME - should we use the signature from opInfo? - Class types[] = new Class[signature.length]; + Class[] types = new Class[signature.length]; for (int i = 0; i < signature.length; i++) { types[i] = BaseModelMBean.getAttributeClass(signature[i]); } @@ -536,7 +512,7 @@ // Locate the method to be invoked, either in this MBean itself // or in the corresponding managed resource // FIXME - Accessible methods in superinterfaces? - Object object = null; + Object object; Exception exception = null; try { object = bean; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/NoDescriptorRegistry.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/NoDescriptorRegistry.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/NoDescriptorRegistry.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/NoDescriptorRegistry.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,9 +45,8 @@ import javax.management.loading.ClassLoaderRepository; /** - * An implementation of the MBean registry that effectively disables MBean - * registration. This is typically used when low memory footprint is a primary - * concern. + * An implementation of the MBean registry that effectively disables MBean registration. This is typically used when low + * memory footprint is a primary concern. */ public class NoDescriptorRegistry extends Registry { @@ -56,8 +55,7 @@ @Override - public void registerComponent(final Object bean, final String oname, final String type) - throws Exception { + public void registerComponent(final Object bean, final String oname, final String type) throws Exception { // no-op } @@ -69,8 +67,8 @@ @Override - public void invoke(final List mbeans, final String operation, - final boolean failFirst) throws Exception { + public void invoke(final List mbeans, final String operation, final boolean failFirst) + throws Exception { // no-op } @@ -107,15 +105,14 @@ @Override - public ManagedBean findManagedBean(final Object bean, final Class beanClass, - final String type) throws Exception { + public ManagedBean findManagedBean(final Object bean, final Class beanClass, final String type) + throws Exception { return null; } @Override - public List load(final String sourceType, final Object source, final String param) - throws Exception { + public List load(final String sourceType, final Object source, final String param) throws Exception { return Collections.emptyList(); } @@ -127,8 +124,7 @@ @Override - public void registerComponent(final Object bean, final ObjectName oname, final String type) - throws Exception { + public void registerComponent(final Object bean, final ObjectName oname, final String type) throws Exception { // no-op } @@ -147,50 +143,45 @@ private static class NoJmxMBeanServer implements MBeanServer { @Override - public ObjectInstance createMBean(String className, ObjectName name) - throws ReflectionException, InstanceAlreadyExistsException, - MBeanRegistrationException, NotCompliantMBeanException, MBeanRegistrationException { + public ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException, + InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { return null; } @Override public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) - throws ReflectionException, InstanceAlreadyExistsException, - MBeanRegistrationException, NotCompliantMBeanException, InstanceNotFoundException, - MBeanRegistrationException { + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, + NotCompliantMBeanException, InstanceNotFoundException { return null; } @Override - public ObjectInstance createMBean(String className, ObjectName name, Object[] params, - String[] signature) throws ReflectionException, InstanceAlreadyExistsException, - MBeanRegistrationException, NotCompliantMBeanException, MBeanRegistrationException { + public ObjectInstance createMBean(String className, ObjectName name, Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, + NotCompliantMBeanException { return null; } @Override - public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, - Object[] params, String[] signature) throws ReflectionException, - InstanceAlreadyExistsException, MBeanRegistrationException, - NotCompliantMBeanException, InstanceNotFoundException, MBeanRegistrationException { + public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object[] params, + String[] signature) throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, NotCompliantMBeanException, InstanceNotFoundException { return null; } @Override public ObjectInstance registerMBean(Object object, ObjectName name) - throws InstanceAlreadyExistsException, MBeanRegistrationException, - NotCompliantMBeanException { + throws InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { return null; } @Override - public void unregisterMBean(ObjectName name) - throws InstanceNotFoundException, MBeanRegistrationException { + public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException { } @@ -226,8 +217,8 @@ @Override - public Object getAttribute(ObjectName name, String attribute) throws MBeanException, - AttributeNotFoundException, InstanceNotFoundException, ReflectionException { + public Object getAttribute(ObjectName name, String attribute) + throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException { return null; } @@ -240,9 +231,8 @@ @Override - public void setAttribute(ObjectName name, Attribute attribute) - throws InstanceNotFoundException, AttributeNotFoundException, - InvalidAttributeValueException, MBeanException, ReflectionException { + public void setAttribute(ObjectName name, Attribute attribute) throws InstanceNotFoundException, + AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException { } @@ -255,8 +245,7 @@ @Override - public Object invoke(ObjectName name, String operationName, Object[] params, - String[] signature) + public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) throws InstanceNotFoundException, MBeanException, ReflectionException { return null; } @@ -275,15 +264,15 @@ @Override - public void addNotificationListener(ObjectName name, NotificationListener listener, - NotificationFilter filter, Object handback) throws InstanceNotFoundException { + public void addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, + Object handback) throws InstanceNotFoundException { } @Override - public void addNotificationListener(ObjectName name, ObjectName listener, - NotificationFilter filter, Object handback) throws InstanceNotFoundException { + public void addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, + Object handback) throws InstanceNotFoundException { } @@ -296,9 +285,8 @@ @Override - public void removeNotificationListener(ObjectName name, ObjectName listener, - NotificationFilter filter, Object handback) - throws InstanceNotFoundException, ListenerNotFoundException { + public void removeNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, + Object handback) throws InstanceNotFoundException, ListenerNotFoundException { } @@ -326,8 +314,7 @@ @Override - public boolean isInstanceOf(ObjectName name, String className) - throws InstanceNotFoundException { + public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException { return false; } @@ -353,8 +340,7 @@ @Override - public Object instantiate(String className, ObjectName loaderName, Object[] params, - String[] signature) + public Object instantiate(String className, ObjectName loaderName, Object[] params, String[] signature) throws ReflectionException, MBeanException, InstanceNotFoundException { return null; } @@ -382,8 +368,7 @@ @Override - public ClassLoader getClassLoaderFor(ObjectName mbeanName) - throws InstanceNotFoundException { + public ClassLoader getClassLoaderFor(ObjectName mbeanName) throws InstanceNotFoundException { return null; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/NotificationInfo.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/NotificationInfo.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/NotificationInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/NotificationInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,10 +23,9 @@ import javax.management.MBeanNotificationInfo; /** - *

    Internal configuration information for a Notification - * descriptor.

    - * - * @author Craig R. McClanahan + *

    + * Internal configuration information for a Notification descriptor. + *

    */ public class NotificationInfo extends FeatureInfo { @@ -36,11 +35,11 @@ /** - * The ModelMBeanNotificationInfo object that corresponds - * to this NotificationInfo instance. + * The ModelMBeanNotificationInfo object that corresponds to this NotificationInfo + * instance. */ transient MBeanNotificationInfo info = null; - protected String notifTypes[] = new String[0]; + protected String[] notifTypes = new String[0]; protected final ReadWriteLock notifTypesLock = new ReentrantReadWriteLock(); // ------------------------------------------------------------- Properties @@ -71,7 +70,7 @@ /** - * @return the set of notification types for this MBean. + * @return the array of notification types for this MBean. */ public String[] getNotifTypes() { Lock readLock = notifTypesLock.readLock(); @@ -98,7 +97,7 @@ writeLock.lock(); try { - String results[] = new String[notifTypes.length + 1]; + String[] results = new String[notifTypes.length + 1]; System.arraycopy(notifTypes, 0, results, 0, notifTypes.length); results[notifTypes.length] = notifType; notifTypes = results; @@ -110,8 +109,9 @@ /** - * Create and return a ModelMBeanNotificationInfo object that - * corresponds to the attribute described by this instance. + * Create and return a ModelMBeanNotificationInfo object that corresponds to the attribute described by + * this instance. + * * @return the notification info */ public MBeanNotificationInfo createNotificationInfo() { @@ -122,11 +122,10 @@ } // Create and return a new information object - info = new MBeanNotificationInfo - (getNotifTypes(), getName(), getDescription()); - //Descriptor descriptor = info.getDescriptor(); - //addFields(descriptor); - //info.setDescriptor(descriptor); + info = new MBeanNotificationInfo(getNotifTypes(), getName(), getDescription()); + // Descriptor descriptor = info.getDescriptor(); + // addFields(descriptor); + // info.setDescriptor(descriptor); return info; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/OperationInfo.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/OperationInfo.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/OperationInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/OperationInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,10 +25,9 @@ import javax.management.MBeanParameterInfo; /** - *

    Internal configuration information for an Operation - * descriptor.

    - * - * @author Craig R. McClanahan + *

    + * Internal configuration information for an Operation descriptor. + *

    */ public class OperationInfo extends FeatureInfo { @@ -49,15 +48,14 @@ protected String impact = "UNKNOWN"; protected String role = "operation"; protected final ReadWriteLock parametersLock = new ReentrantReadWriteLock(); - protected ParameterInfo parameters[] = new ParameterInfo[0]; + protected ParameterInfo[] parameters = new ParameterInfo[0]; // ------------------------------------------------------------- Properties /** - * @return the "impact" of this operation, which should be - * a (case-insensitive) string value "ACTION", "ACTION_INFO", - * "INFO", or "UNKNOWN". + * @return the "impact" of this operation, which should be a (case-insensitive) string value "ACTION", + * "ACTION_INFO", "INFO", or "UNKNOWN". */ public String getImpact() { return this.impact; @@ -73,8 +71,7 @@ /** - * @return the role of this operation ("getter", "setter", "operation", or - * "constructor"). + * @return the role of this operation ("getter", "setter", "operation", or "constructor"). */ public String getRole() { return this.role; @@ -86,11 +83,10 @@ /** - * @return the fully qualified Java class name of the return type for this - * operation. + * @return the fully qualified Java class name of the return type for this operation. */ public String getReturnType() { - if(type == null) { + if (type == null) { type = "void"; } return type; @@ -101,7 +97,7 @@ } /** - * @return the set of parameters for this operation. + * @return the array of parameters for this operation. */ public ParameterInfo[] getSignature() { Lock readLock = parametersLock.readLock(); @@ -126,7 +122,7 @@ Lock writeLock = parametersLock.writeLock(); writeLock.lock(); try { - ParameterInfo results[] = new ParameterInfo[parameters.length + 1]; + ParameterInfo[] results = new ParameterInfo[parameters.length + 1]; System.arraycopy(parameters, 0, results, 0, parameters.length); results[parameters.length] = parameter; parameters = results; @@ -138,8 +134,9 @@ /** - * Create and return a ModelMBeanOperationInfo object that - * corresponds to the attribute described by this instance. + * Create and return a ModelMBeanOperationInfo object that corresponds to the attribute described by + * this instance. + * * @return the operation info */ MBeanOperationInfo createOperationInfo() { @@ -156,17 +153,15 @@ impact = MBeanOperationInfo.INFO; } - info = new MBeanOperationInfo(getName(), getDescription(), - getMBeanParameterInfo(), - getReturnType(), impact); + info = new MBeanOperationInfo(getName(), getDescription(), getMBeanParameterInfo(), getReturnType(), + impact); } - return (MBeanOperationInfo)info; + return (MBeanOperationInfo) info; } protected MBeanParameterInfo[] getMBeanParameterInfo() { - ParameterInfo params[] = getSignature(); - MBeanParameterInfo parameters[] = - new MBeanParameterInfo[params.length]; + ParameterInfo[] params = getSignature(); + MBeanParameterInfo[] parameters = new MBeanParameterInfo[params.length]; for (int i = 0; i < params.length; i++) { parameters[i] = params[i].createParameterInfo(); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/ParameterInfo.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/ParameterInfo.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/ParameterInfo.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/ParameterInfo.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,12 +19,10 @@ import javax.management.MBeanParameterInfo; - /** - *

    Internal configuration information for a Parameter - * descriptor.

    - * - * @author Craig R. McClanahan + *

    + * Internal configuration information for a Parameter descriptor. + *

    */ public class ParameterInfo extends FeatureInfo { private static final long serialVersionUID = 2222796006787664020L; @@ -39,17 +37,17 @@ } /** - * Create and return a MBeanParameterInfo object that - * corresponds to the parameter described by this instance. + * Create and return a MBeanParameterInfo object that corresponds to the parameter described by this + * instance. + * * @return a parameter info */ public MBeanParameterInfo createParameterInfo() { // Return our cached information (if any) if (info == null) { - info = new MBeanParameterInfo - (getName(), getType(), getDescription()); + info = new MBeanParameterInfo(getName(), getType(), getDescription()); } - return (MBeanParameterInfo)info; + return (MBeanParameterInfo) info; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/Registry.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/Registry.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/Registry.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/Registry.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,15 +43,8 @@ import org.apache.tomcat.util.res.StringManager; /** - * Registry for modeler MBeans. - * - * This is the main entry point into modeler. It provides methods to create and - * manipulate model mbeans and simplify their use. - * - * This class is itself an mbean. - * - * @author Craig R. McClanahan - * @author Costin Manolache + * Registry for modeler MBeans. This is the main entry point into modeler. It provides methods to create and manipulate + * model mbeans and simplify their use. This class is itself a mbean. */ public class Registry implements RegistryMBean, MBeanRegistration { @@ -64,40 +57,37 @@ // Support for the factory methods /** - * The registry instance created by our factory method the first time it is - * called. + * The registry instance created by our factory method the first time it is called. */ private static Registry registry = null; // Per registry fields /** - * The MBeanServer instance that we will use to register - * management beans. + * The MBeanServer instance that we will use to register management beans. */ private volatile MBeanServer server = null; private final Object serverLock = new Object(); /** - * The set of ManagedBean instances for the beans this registry knows about, - * keyed by name. + * The set of ManagedBean instances for the beans this registry knows about, keyed by name. */ - private Map descriptors = new HashMap<>(); + private Map descriptors = new HashMap<>(); /** * List of managed beans, keyed by class name */ - private Map descriptorsByClass = new HashMap<>(); + private Map descriptorsByClass = new HashMap<>(); // map to avoid duplicated searching or loading descriptors - private Map searchedPaths = new HashMap<>(); + private Map searchedPaths = new HashMap<>(); private Object guard; // Id - small ints to use array access. No reset on stop() // Used for notifications - private final Hashtable> idDomains = new Hashtable<>(); - private final Hashtable ids = new Hashtable<>(); + private final Hashtable> idDomains = new Hashtable<>(); + private final Hashtable ids = new Hashtable<>(); // ----------------------------------------------------------- Constructors @@ -111,13 +101,13 @@ // Factories /** - * Factory method to create (if necessary) and return our - * Registry instance. + * Factory method to create (if necessary) and return our Registry instance. * - * @param key Unused + * @param key Unused * @param guard Prevent access to the registry by untrusted components * * @return the registry + * * @since 1.1 */ public static synchronized Registry getRegistry(Object key, Object guard) { @@ -132,6 +122,27 @@ } + /** + * Factory method to create (if necessary) and return our Registry instance. + * + * @param guard Prevent access to the registry by untrusted components + * + * @return the registry + * + * @throws IllegalArgumentException if the guard object does not allow access + */ + public static synchronized Registry getRegistry(Object guard) { + if (registry == null) { + registry = new Registry(); + registry.guard = guard; + } + if (registry.guard != null && registry.guard != guard) { + throw new IllegalArgumentException(sm.getString("registry.cannotAccessRegistry", guard)); + } + return registry; + } + + public static synchronized void disableRegistry() { if (registry == null) { registry = new NoDescriptorRegistry(); @@ -144,8 +155,7 @@ // -------------------- Generic methods -------------------- /** - * Lifecycle method - clean up the registry metadata. Called from - * resetMetadata(). + * Lifecycle method - clean up the registry metadata. Called from resetMetadata(). * * @since 1.1 */ @@ -158,34 +168,21 @@ /** - * Register a bean by creating a modeler mbean and adding it to the - * MBeanServer. - * - * If metadata is not loaded, we'll look up and read a file named - * "mbeans-descriptors.ser" or "mbeans-descriptors.xml" in the same package - * or parent. - * - * If the bean is an instance of DynamicMBean. it's metadata will be - * converted to a model mbean and we'll wrap it - so modeler services will - * be supported - * - * If the metadata is still not found, introspection will be used to extract - * it automatically. - * - * If an mbean is already registered under this name, it'll be first - * unregistered. - * - * If the component implements MBeanRegistration, the methods will be - * called. If the method has a method "setRegistry" that takes a + * Register a bean by creating a modeler mbean and adding it to the MBeanServer. If metadata is not loaded, we'll + * look up and read a file named "mbeans-descriptors.ser" or "mbeans-descriptors.xml" in the same package or parent. + * If the bean is an instance of DynamicMBean. its metadata will be converted to a model mbean, and we'll wrap it so + * modeler services are supported. If the metadata is still not found, introspection will be used to extract it + * automatically. If a mbean is already registered under this name, it'll be first unregistered. If the component + * implements MBeanRegistration, the methods will be called. If the method has a method "setRegistry" that takes a * RegistryMBean as parameter, it'll be called with the current registry. * - * - * @param bean Object to be registered + * @param bean Object to be registered * @param oname Name used for registration - * @param type The type of the mbean, as declared in mbeans-descriptors. If - * null, the name of the class will be used. This can be used as - * a hint or by subclasses. + * @param type The type of the mbean, as declared in mbeans-descriptors. If null, the name of the class will be + * used. This can be used as a hint or by subclasses. + * * @throws Exception if a registration error occurred + * * @since 1.1 */ @Override @@ -195,8 +192,7 @@ /** - * Unregister a component. We'll first check if it is registered, and mask - * all errors. This is mostly a helper. + * Unregister a component. We'll first check if it is registered, and mask all errors. This is mostly a helper. * * @param oname Name used for unregistration * @@ -213,18 +209,18 @@ /** - * Invoke a operation on a list of mbeans. Can be used to implement - * lifecycle operations. + * Invoke an operation on a list of mbeans. Can be used to implement lifecycle operations. + * + * @param mbeans list of ObjectName on which we'll invoke the operations + * @param operation Name of the operation ( init, start, stop, etc) + * @param failFirst If false, exceptions will be ignored * - * @param mbeans list of ObjectName on which we'll invoke the operations - * @param operation Name of the operation ( init, start, stop, etc) - * @param failFirst If false, exceptions will be ignored * @throws Exception Error invoking operation + * * @since 1.1 */ @Override - public void invoke(List mbeans, String operation, boolean failFirst) - throws Exception { + public void invoke(List mbeans, String operation, boolean failFirst) throws Exception { if (mbeans == null) { return; @@ -239,11 +235,11 @@ } getMBeanServer().invoke(current, operation, new Object[] {}, new String[] {}); - } catch (Exception t) { + } catch (Exception e) { if (failFirst) { - throw t; + throw e; } - log.info(sm.getString("registry.initError"), t); + log.info(sm.getString("registry.initError"), e); } } } @@ -251,12 +247,13 @@ // -------------------- ID registry -------------------- /** - * Return an int ID for faster access. Will be used for notifications and - * for other operations we want to optimize. + * Return an int ID for faster access. Will be used for notifications and for other operations we want to optimize. * * @param domain Namespace - * @param name Type of the notification + * @param name Type of the notification + * * @return A unique id for the domain:name combination + * * @since 1.1 */ @Override @@ -264,7 +261,7 @@ if (domain == null) { domain = ""; } - Hashtable domainTable = idDomains.computeIfAbsent(domain, k -> new Hashtable<>()); + Hashtable domainTable = idDomains.computeIfAbsent(domain, k -> new Hashtable<>()); if (name == null) { name = ""; } @@ -285,10 +282,10 @@ // methods from 1.0 /** - * Add a new bean metadata to the set of beans known to this registry. This - * is used by internal components. + * Add a new bean metadata to the set of beans known to this registry. This is used by internal components. * * @param bean The managed bean to be added + * * @since 1.0 */ public void addManagedBean(ManagedBean bean) { @@ -301,12 +298,14 @@ /** - * Find and return the managed bean definition for the specified bean name, - * if any; otherwise return null. + * Find and return the managed bean definition for the specified bean name, if any; otherwise return + * null. + * + * @param name Name of the managed bean to be returned. Since 1.1, both short names or the full name of the class + * can be used. * - * @param name Name of the managed bean to be returned. Since 1.1, both - * short names or the full name of the class can be used. * @return the managed bean + * * @since 1.0 */ public ManagedBean findManagedBean(String name) { @@ -324,22 +323,27 @@ /** * Get the type of an attribute of the object, from the metadata. * - * @param oname The bean name + * @param oname The bean name * @param attName The attribute name + * * @return null if metadata about the attribute is not found + * * @since 1.1 */ public String getType(ObjectName oname, String attName) { - String type = null; - MBeanInfo info = null; + String type; + MBeanInfo info; try { info = getMBeanServer().getMBeanInfo(oname); - } catch (Exception e) { + } catch (InstanceNotFoundException e) { log.info(sm.getString("registry.noMetadata", oname)); return null; + } catch (Exception e) { + log.warn(sm.getString("registry.noMetadata", oname), e); + return null; } - MBeanAttributeInfo attInfo[] = info.getAttributes(); + MBeanAttributeInfo[] attInfo = info.getAttributes(); for (MBeanAttributeInfo mBeanAttributeInfo : attInfo) { if (attName.equals(mBeanAttributeInfo.getName())) { type = mBeanAttributeInfo.getType(); @@ -353,19 +357,23 @@ /** * Find the operation info for a method * - * @param oname The bean name + * @param oname The bean name * @param opName The operation name + * * @return the operation info for the specified operation */ public MBeanOperationInfo getMethodInfo(ObjectName oname, String opName) { - MBeanInfo info = null; + MBeanInfo info; try { info = getMBeanServer().getMBeanInfo(oname); - } catch (Exception e) { + } catch (InstanceNotFoundException e) { log.info(sm.getString("registry.noMetadata", oname)); return null; + } catch (Exception e) { + log.warn(sm.getString("registry.noMetadata", oname), e); + return null; } - MBeanOperationInfo attInfo[] = info.getOperations(); + MBeanOperationInfo[] attInfo = info.getOperations(); for (MBeanOperationInfo mBeanOperationInfo : attInfo) { if (opName.equals(mBeanOperationInfo.getName())) { return mBeanOperationInfo; @@ -377,16 +385,17 @@ /** * Find the operation info for a method. * - * @param oname The bean name - * @param opName The operation name + * @param oname The bean name + * @param opName The operation name * @param argCount The number of arguments to the method + * * @return the operation info for the specified operation + * * @throws InstanceNotFoundException If the object name is not bound to an MBean */ public MBeanOperationInfo getMethodInfo(ObjectName oname, String opName, int argCount) - throws InstanceNotFoundException - { - MBeanInfo info = null; + throws InstanceNotFoundException { + MBeanInfo info; try { info = getMBeanServer().getMBeanInfo(oname); } catch (InstanceNotFoundException infe) { @@ -395,10 +404,9 @@ log.warn(sm.getString("registry.noMetadata", oname), e); return null; } - MBeanOperationInfo attInfo[] = info.getOperations(); + MBeanOperationInfo[] attInfo = info.getOperations(); for (MBeanOperationInfo mBeanOperationInfo : attInfo) { - if (opName.equals(mBeanOperationInfo.getName()) - && argCount == mBeanOperationInfo.getSignature().length) { + if (opName.equals(mBeanOperationInfo.getName()) && argCount == mBeanOperationInfo.getSignature().length) { return mBeanOperationInfo; } } @@ -406,8 +414,8 @@ } /** - * Unregister a component. This is just a helper that avoids exceptions by - * checking if the mbean is already registered + * Unregister a component. This is just a helper that avoids exceptions by checking if the mbean is already + * registered * * @param oname The bean name */ @@ -423,8 +431,7 @@ /** - * Factory method to create (if necessary) and return our - * MBeanServer instance. + * Factory method to create (if necessary) and return our MBeanServer instance. * * @return the MBean server */ @@ -432,7 +439,7 @@ if (server == null) { synchronized (serverLock) { if (server == null) { - if (MBeanServerFactory.findMBeanServer(null).size() > 0) { + if (!MBeanServerFactory.findMBeanServer(null).isEmpty()) { server = MBeanServerFactory.findMBeanServer(null).get(0); if (log.isDebugEnabled()) { log.debug(sm.getString("registry.existingServer")); @@ -453,14 +460,15 @@ /** * Find or load metadata. * - * @param bean The bean + * @param bean The bean * @param beanClass The bean class - * @param type The registry type + * @param type The registry type + * * @return the managed bean + * * @throws Exception An error occurred */ - public ManagedBean findManagedBean(Object bean, Class beanClass, String type) - throws Exception { + public ManagedBean findManagedBean(Object bean, Class beanClass, String type) throws Exception { if (bean != null && beanClass == null) { beanClass = bean.getClass(); @@ -506,12 +514,12 @@ /** - * Convert a string to object, based on type. Used by several - * components. We could provide some pluggability. It is here to keep things - * consistent and avoid duplication in other tasks + * Convert a string to object, based on type. Used by several components. We could provide some pluggability. It is + * here to keep things consistent and avoid duplication in other tasks * - * @param type Fully qualified class name of the resulting value + * @param type Fully qualified class name of the resulting value * @param value String value to be converted + * * @return Converted value */ public Object convertValue(String type, String value) { @@ -519,7 +527,6 @@ if (type == null || "java.lang.String".equals(type)) { // string is default - objValue = value; } else if ("javax.management.ObjectName".equals(type) || "ObjectName".equals(type)) { try { objValue = new ObjectName(value); @@ -541,18 +548,20 @@ * Load descriptors. * * @param sourceType The source type - * @param source The bean - * @param param A type to load + * @param source The bean + * @param param A type to load + * * @return List of descriptors + * * @throws Exception Error loading descriptors */ public List load(String sourceType, Object source, String param) throws Exception { if (log.isTraceEnabled()) { log.trace("load " + source); } - String location = null; - String type = null; - Object inputsource = null; + String location; + String type; + Object inputsource; if (source instanceof URL) { URL url = (URL) source; @@ -587,18 +596,18 @@ sourceType = "MbeansDescriptorsDigesterSource"; } ModelerSource ds = getModelerSource(sourceType); - List mbeans = ds.loadDescriptors(this, type, inputsource); - return mbeans; + return ds.loadDescriptors(this, type, inputsource); } /** * Register a component * - * @param bean The bean + * @param bean The bean * @param oname The object name - * @param type The registry type + * @param type The registry type + * * @throws Exception Error registering component */ public void registerComponent(Object bean, ObjectName oname, String type) throws Exception { @@ -629,16 +638,15 @@ } getMBeanServer().registerMBean(mbean, oname); - } catch (Exception ex) { - log.error(sm.getString("registry.registerError", oname), ex); - throw ex; + } catch (Exception e) { + log.error(sm.getString("registry.registerError", oname), e); + throw e; } } /** - * Lookup the component descriptor in the package and in the parent - * packages. + * Lookup the component descriptor in the package and in the parent packages. * * @param packageName The package name * @param classLoader The class loader @@ -667,15 +675,14 @@ searchedPaths.put(packageName, dURL); try { load("MbeansDescriptorsDigesterSource", dURL, null); - } catch (Exception ex) { + } catch (Exception e) { log.error(sm.getString("registry.loadError", dURL)); } } /** - * Lookup the component descriptor in the package and in the parent - * packages. + * Lookup the component descriptor in the package and in the parent packages. */ private void findDescriptor(Class beanClass, String type) { if (type == null) { @@ -692,8 +699,7 @@ classLoader = this.getClass().getClassLoader(); } - String className = type; - String pkg = className; + String pkg = type; while (pkg.indexOf('.') > 0) { int lastComp = pkg.lastIndexOf('.'); if (lastComp <= 0) { @@ -717,8 +723,7 @@ } Class c = Class.forName(type); - ModelerSource ds = (ModelerSource) c.getConstructor().newInstance(); - return ds; + return (ModelerSource) c.getConstructor().newInstance(); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/RegistryMBean.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/RegistryMBean.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/RegistryMBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/RegistryMBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,71 +22,45 @@ import javax.management.ObjectName; /** - * Interface for modeler MBeans. - * - * This is the main entry point into modeler. It provides methods to create - * and manipulate model mbeans and simplify their use. - * - * Starting with version 1.1, this is no longer a singleton and the static - * methods are strongly deprecated. In a container environment we can expect - * different applications to use different registries. - * - * @author Craig R. McClanahan - * @author Costin Manolache - * - * @since 1.1 + * Interface for modeler MBeans. This is the main entry point into modeler. It provides methods to create and manipulate + * model mbeans and simplify their use. Starting with version 1.1, this is no longer a singleton and the static methods + * are strongly deprecated. In a container environment we can expect different applications to use different registries. */ public interface RegistryMBean { /** * Invoke an operation on a set of mbeans. * - * @param mbeans List of ObjectNames + * @param mbeans List of ObjectNames * @param operation Operation to perform. Typically "init" "start" "stop" or "destroy" - * @param failFirst Behavior in case of exceptions - if false we'll ignore - * errors + * @param failFirst Behavior in case of exceptions - if false we'll ignore errors + * * @throws Exception Error invoking operation */ - void invoke(List mbeans, String operation, boolean failFirst) - throws Exception; + void invoke(List mbeans, String operation, boolean failFirst) throws Exception; /** - * Register a bean by creating a modeler mbean and adding it to the - * MBeanServer. - * - * If metadata is not loaded, we'll look up and read a file named - * "mbeans-descriptors.ser" or "mbeans-descriptors.xml" in the same package - * or parent. - * - * If the bean is an instance of DynamicMBean. it's metadata will be converted - * to a model mbean and we'll wrap it - so modeler services will be supported - * - * If the metadata is still not found, introspection will be used to extract - * it automatically. - * - * If an mbean is already registered under this name, it'll be first - * unregistered. - * - * If the component implements MBeanRegistration, the methods will be called. - * If the method has a method "setRegistry" that takes a RegistryMBean as - * parameter, it'll be called with the current registry. - * + * Register a bean by creating a modeler mbean and adding it to the MBeanServer. If metadata is not loaded, we'll + * look up and read a file named "mbeans-descriptors.ser" or "mbeans-descriptors.xml" in the same package or parent. + * If the bean is an instance of DynamicMBean. it's metadata will be converted to a model mbean, and we'll wrap it - + * so modeler services will be supported If the metadata is still not found, introspection will be used to extract + * it automatically. If a mbean is already registered under this name, it'll be first unregistered. If the component + * implements MBeanRegistration, the methods will be called. If the method has a method "setRegistry" that takes a + * RegistryMBean as parameter, it'll be called with the current registry. * - * @param bean Object to be registered + * @param bean Object to be registered * @param oname Name used for registration - * @param type The type of the mbean, as declared in mbeans-descriptors. If - * null, the name of the class will be used. This can be used as a hint or - * by subclasses. + * @param type The type of the mbean, as declared in mbeans-descriptors. If null, the name of the class will be + * used. This can be used as a hint or by subclasses. + * * @throws Exception Error registering MBean * * @since 1.1 */ - void registerComponent(Object bean, String oname, String type) - throws Exception; + void registerComponent(Object bean, String oname, String type) throws Exception; /** - * Unregister a component. We'll first check if it is registered, - * and mask all errors. This is mostly a helper. + * Unregister a component. We'll first check if it is registered, and mask all errors. This is mostly a helper. * * @param oname The name used by the bean * @@ -95,23 +69,23 @@ void unregisterComponent(String oname); - /** - * Return an int ID for faster access. Will be used for notifications - * and for other operations we want to optimize. - * - * @param domain Namespace - * @param name Type of the notification - * @return A unique id for the domain:name combination - * @since 1.1 - */ + /** + * Return an int ID for faster access. Will be used for notifications and for other operations we want to optimize. + * + * @param domain Namespace + * @param name Type of the notification + * + * @return A unique id for the domain:name combination + * + * @since 1.1 + */ int getId(String domain, String name); /** - * Reset all metadata cached by this registry. Should be called - * to support reloading. Existing mbeans will not be affected or modified. + * Reset all metadata cached by this registry. Should be called to support reloading. Existing mbeans will not be + * affected or modified. It will be called automatically if the Registry is unregistered. * - * It will be called automatically if the Registry is unregistered. * @since 1.1 */ void stop(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsDigesterSource.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsDigesterSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsDigesterSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsDigesterSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,10 +29,8 @@ import org.apache.tomcat.util.modeler.ManagedBean; import org.apache.tomcat.util.modeler.Registry; -public class MbeansDescriptorsDigesterSource extends ModelerSource -{ - private static final Log log = - LogFactory.getLog(MbeansDescriptorsDigesterSource.class); +public class MbeansDescriptorsDigesterSource extends ModelerSource { + private static final Log log = LogFactory.getLog(MbeansDescriptorsDigesterSource.class); private static final Object dLock = new Object(); private Registry registry; @@ -44,105 +42,68 @@ Digester digester = new Digester(); digester.setNamespaceAware(false); digester.setValidating(false); - URL url = Registry.getRegistry(null, null).getClass().getResource - ("/org/apache/tomcat/util/modeler/mbeans-descriptors.dtd"); + URL url = Registry.getRegistry(null).getClass() + .getResource("/org/apache/tomcat/util/modeler/mbeans-descriptors.dtd"); if (url != null) { - digester.register - ("-//Apache Software Foundation//DTD Model MBeans Configuration File", - url.toString()); + digester.register("-//Apache Software Foundation//DTD Model MBeans Configuration File", url.toString()); } // Configure the parsing rules - digester.addObjectCreate - ("mbeans-descriptors/mbean", - "org.apache.tomcat.util.modeler.ManagedBean"); - digester.addSetProperties - ("mbeans-descriptors/mbean"); - digester.addSetNext - ("mbeans-descriptors/mbean", - "add", - "java.lang.Object"); - - digester.addObjectCreate - ("mbeans-descriptors/mbean/attribute", - "org.apache.tomcat.util.modeler.AttributeInfo"); - digester.addSetProperties - ("mbeans-descriptors/mbean/attribute"); - digester.addSetNext - ("mbeans-descriptors/mbean/attribute", - "addAttribute", - "org.apache.tomcat.util.modeler.AttributeInfo"); - - digester.addObjectCreate - ("mbeans-descriptors/mbean/notification", - "org.apache.tomcat.util.modeler.NotificationInfo"); - digester.addSetProperties - ("mbeans-descriptors/mbean/notification"); - digester.addSetNext - ("mbeans-descriptors/mbean/notification", - "addNotification", - "org.apache.tomcat.util.modeler.NotificationInfo"); - - digester.addObjectCreate - ("mbeans-descriptors/mbean/notification/descriptor/field", - "org.apache.tomcat.util.modeler.FieldInfo"); - digester.addSetProperties - ("mbeans-descriptors/mbean/notification/descriptor/field"); - digester.addSetNext - ("mbeans-descriptors/mbean/notification/descriptor/field", - "addField", - "org.apache.tomcat.util.modeler.FieldInfo"); - - digester.addCallMethod - ("mbeans-descriptors/mbean/notification/notification-type", - "addNotifType", 0); - - digester.addObjectCreate - ("mbeans-descriptors/mbean/operation", - "org.apache.tomcat.util.modeler.OperationInfo"); - digester.addSetProperties - ("mbeans-descriptors/mbean/operation"); - digester.addSetNext - ("mbeans-descriptors/mbean/operation", - "addOperation", - "org.apache.tomcat.util.modeler.OperationInfo"); - - digester.addObjectCreate - ("mbeans-descriptors/mbean/operation/descriptor/field", - "org.apache.tomcat.util.modeler.FieldInfo"); - digester.addSetProperties - ("mbeans-descriptors/mbean/operation/descriptor/field"); - digester.addSetNext - ("mbeans-descriptors/mbean/operation/descriptor/field", - "addField", - "org.apache.tomcat.util.modeler.FieldInfo"); - - digester.addObjectCreate - ("mbeans-descriptors/mbean/operation/parameter", - "org.apache.tomcat.util.modeler.ParameterInfo"); - digester.addSetProperties - ("mbeans-descriptors/mbean/operation/parameter"); - digester.addSetNext - ("mbeans-descriptors/mbean/operation/parameter", - "addParameter", - "org.apache.tomcat.util.modeler.ParameterInfo"); + digester.addObjectCreate("mbeans-descriptors/mbean", "org.apache.tomcat.util.modeler.ManagedBean"); + digester.addSetProperties("mbeans-descriptors/mbean"); + digester.addSetNext("mbeans-descriptors/mbean", "add", "java.lang.Object"); + + digester.addObjectCreate("mbeans-descriptors/mbean/attribute", "org.apache.tomcat.util.modeler.AttributeInfo"); + digester.addSetProperties("mbeans-descriptors/mbean/attribute"); + digester.addSetNext("mbeans-descriptors/mbean/attribute", "addAttribute", + "org.apache.tomcat.util.modeler.AttributeInfo"); + + digester.addObjectCreate("mbeans-descriptors/mbean/notification", + "org.apache.tomcat.util.modeler.NotificationInfo"); + digester.addSetProperties("mbeans-descriptors/mbean/notification"); + digester.addSetNext("mbeans-descriptors/mbean/notification", "addNotification", + "org.apache.tomcat.util.modeler.NotificationInfo"); + + digester.addObjectCreate("mbeans-descriptors/mbean/notification/descriptor/field", + "org.apache.tomcat.util.modeler.FieldInfo"); + digester.addSetProperties("mbeans-descriptors/mbean/notification/descriptor/field"); + digester.addSetNext("mbeans-descriptors/mbean/notification/descriptor/field", "addField", + "org.apache.tomcat.util.modeler.FieldInfo"); + + digester.addCallMethod("mbeans-descriptors/mbean/notification/notification-type", "addNotifType", 0); + + digester.addObjectCreate("mbeans-descriptors/mbean/operation", "org.apache.tomcat.util.modeler.OperationInfo"); + digester.addSetProperties("mbeans-descriptors/mbean/operation"); + digester.addSetNext("mbeans-descriptors/mbean/operation", "addOperation", + "org.apache.tomcat.util.modeler.OperationInfo"); + + digester.addObjectCreate("mbeans-descriptors/mbean/operation/descriptor/field", + "org.apache.tomcat.util.modeler.FieldInfo"); + digester.addSetProperties("mbeans-descriptors/mbean/operation/descriptor/field"); + digester.addSetNext("mbeans-descriptors/mbean/operation/descriptor/field", "addField", + "org.apache.tomcat.util.modeler.FieldInfo"); + + digester.addObjectCreate("mbeans-descriptors/mbean/operation/parameter", + "org.apache.tomcat.util.modeler.ParameterInfo"); + digester.addSetProperties("mbeans-descriptors/mbean/operation/parameter"); + digester.addSetNext("mbeans-descriptors/mbean/operation/parameter", "addParameter", + "org.apache.tomcat.util.modeler.ParameterInfo"); return digester; } public void setRegistry(Registry reg) { - this.registry=reg; + this.registry = reg; } - public void setSource( Object source ) { - this.source=source; + public void setSource(Object source) { + this.source = source; } @Override - public List loadDescriptors( Registry registry, String type, - Object source) throws Exception { + public List loadDescriptors(Registry registry, String type, Object source) throws Exception { setRegistry(registry); setSource(source); execute(); @@ -151,13 +112,13 @@ public void execute() throws Exception { if (registry == null) { - registry = Registry.getRegistry(null, null); + registry = Registry.getRegistry(null); } InputStream stream = (InputStream) source; List loadedMbeans = new ArrayList<>(); - synchronized(dLock) { + synchronized (dLock) { if (digester == null) { digester = createDigester(); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/modules/MbeansDescriptorsIntrospectionSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,8 +38,7 @@ import org.apache.tomcat.util.modeler.Registry; import org.apache.tomcat.util.res.StringManager; -public class MbeansDescriptorsIntrospectionSource extends ModelerSource -{ +public class MbeansDescriptorsIntrospectionSource extends ModelerSource { private static final Log log = LogFactory.getLog(MbeansDescriptorsIntrospectionSource.class); private static final StringManager sm = StringManager.getManager(MbeansDescriptorsIntrospectionSource.class); @@ -48,7 +47,7 @@ private final List mbeans = new ArrayList<>(); public void setRegistry(Registry reg) { - this.registry=reg; + this.registry = reg; } /** @@ -56,17 +55,16 @@ * * @param type The type */ - public void setType( String type ) { - this.type=type; + public void setType(String type) { + this.type = type; } - public void setSource( Object source ) { - this.source=source; + public void setSource(Object source) { + this.source = source; } @Override - public List loadDescriptors(Registry registry, String type, - Object source) throws Exception { + public List loadDescriptors(Registry registry, String type, Object source) throws Exception { setRegistry(registry); setType(type); setSource(source); @@ -75,65 +73,43 @@ } public void execute() throws Exception { - if( registry==null ) { - registry=Registry.getRegistry(null, null); + if (registry == null) { + registry = Registry.getRegistry(null); } try { - ManagedBean managed = createManagedBean(registry, null, - (Class)source, type); - if( managed==null ) { + ManagedBean managed = createManagedBean(registry, null, (Class) source, type); + if (managed == null) { return; } - managed.setName( type ); + managed.setName(type); registry.addManagedBean(managed); - } catch( Exception ex ) { - log.error(sm.getString("modules.readDescriptorsError"), ex); + } catch (Exception e) { + log.error(sm.getString("modules.readDescriptorsError"), e); } } - // ------------ Implementation for non-declared introspection classes private static final Map specialMethods = new HashMap<>(); static { - specialMethods.put( "preDeregister", ""); - specialMethods.put( "postDeregister", ""); + specialMethods.put("preDeregister", ""); + specialMethods.put("postDeregister", ""); } - private static final Class[] supportedTypes = new Class[] { - Boolean.class, - Boolean.TYPE, - Byte.class, - Byte.TYPE, - Character.class, - Character.TYPE, - Short.class, - Short.TYPE, - Integer.class, - Integer.TYPE, - Long.class, - Long.TYPE, - Float.class, - Float.TYPE, - Double.class, - Double.TYPE, - String.class, - String[].class, - BigDecimal.class, - BigInteger.class, - ObjectName.class, - Object[].class, - java.io.File.class, - }; + private static final Class[] supportedTypes = new Class[] { Boolean.class, Boolean.TYPE, Byte.class, Byte.TYPE, + Character.class, Character.TYPE, Short.class, Short.TYPE, Integer.class, Integer.TYPE, Long.class, + Long.TYPE, Float.class, Float.TYPE, Double.class, Double.TYPE, String.class, String[].class, + BigDecimal.class, BigInteger.class, ObjectName.class, Object[].class, java.io.File.class, }; /** - * Check if this class is one of the supported types. - * If the class is supported, returns true. Otherwise, - * returns false. + * Check if this class is one of the supported types. If the class is supported, returns true. Otherwise, returns + * false. + * * @param ret The class to check + * * @return boolean True if class is supported */ private boolean supportedType(Class ret) { @@ -142,17 +118,14 @@ return true; } } - if (isBeanCompatible(ret)) { - return true; - } - return false; + return isBeanCompatible(ret); } /** - * Check if this class conforms to JavaBeans specifications. - * If the class is conformant, returns true. + * Check if this class conforms to JavaBeans specifications. If the class is conformant, returns true. * * @param javaType The class to check + * * @return boolean True if the class is compatible. */ private boolean isBeanCompatible(Class javaType) { @@ -163,26 +136,21 @@ // Anything in the java or javax package that // does not have a defined mapping is excluded. - if (javaType.getName().startsWith("java.") || - javaType.getName().startsWith("javax.")) { + if (javaType.getName().startsWith("java.") || javaType.getName().startsWith("javax.")) { return false; } try { - javaType.getConstructor(new Class[]{}); + javaType.getConstructor(); } catch (NoSuchMethodException e) { return false; } // Make sure superclass is compatible Class superClass = javaType.getSuperclass(); - if (superClass != null && - superClass != Object.class && - superClass != Exception.class && - superClass != Throwable.class) { - if (!isBeanCompatible(superClass)) { - return false; - } + if (superClass != null && superClass != Object.class && superClass != Exception.class && + superClass != Throwable.class) { + return isBeanCompatible(superClass); } return true; } @@ -190,15 +158,14 @@ /** * Process the methods and extract 'attributes', methods, etc. * - * @param realClass The class to process - * @param attNames The attribute name (complete) - * @param getAttMap The readable attributes map - * @param setAttMap The settable attributes map + * @param realClass The class to process + * @param attNames The attribute name (complete) + * @param getAttMap The readable attributes map + * @param setAttMap The settable attributes map * @param invokeAttList The invokable attributes list */ - private void initMethods(Class realClass, Set attNames, - Map getAttMap, Map setAttMap, - List invokeAttList) { + private void initMethods(Class realClass, Set attNames, Map getAttMap, + Map setAttMap, List invokeAttList) { Method[] methods = realClass.getMethods(); for (Method method : methods) { @@ -276,29 +243,25 @@ } /** - * XXX Find if the 'className' is the name of the MBean or - * the real class ( I suppose first ) - * XXX Read (optional) descriptions from a .properties, generated - * from source - * XXX Deal with constructors + * XXX Find if the 'className' is the name of the MBean or the real class ( I suppose first ) XXX Read (optional) + * descriptions from a .properties, generated from source XXX Deal with constructors * - * @param registry The Bean registry (not used) - * @param domain The bean domain (not used) + * @param registry The Bean registry (not used) + * @param domain The bean domain (not used) * @param realClass The class to analyze - * @param type The bean type + * @param type The bean type + * * @return ManagedBean The create MBean */ - public ManagedBean createManagedBean(Registry registry, String domain, - Class realClass, String type) - { + public ManagedBean createManagedBean(Registry registry, String domain, Class realClass, String type) { ManagedBean mbean = new ManagedBean(); Set attrNames = new HashSet<>(); // key: attribute val: getter method - Map getAttMap = new HashMap<>(); + Map getAttMap = new HashMap<>(); // key: attribute val: setter method - Map setAttMap = new HashMap<>(); + Map setAttMap = new HashMap<>(); // key: operation val: invoke method List invokeAttList = new ArrayList<>(); @@ -312,16 +275,12 @@ if (gm != null) { ai.setGetMethod(gm.getName()); Class t = gm.getReturnType(); - if (t != null) { - ai.setType(t.getName()); - } + ai.setType(t.getName()); } Method sm = setAttMap.get(name); if (sm != null) { Class t = sm.getParameterTypes()[0]; - if (t != null) { - ai.setType(t.getName()); - } + ai.setType(t.getName()); ai.setSetMethod(sm.getName()); } ai.setDescription("Introspected attribute " + name); @@ -362,8 +321,8 @@ mbean.setName(type); return mbean; - } catch (Exception ex) { - log.error(sm.getString("source.introspectionError", realClass.getName()), ex); + } catch (Exception e) { + log.error(sm.getString("source.introspectionError", realClass.getName()), e); return null; } } @@ -371,17 +330,17 @@ // -------------------- Utils -------------------- /** - * Converts the first character of the given - * String into lower-case. + * Converts the first character of the given String into lower-case. * * @param name The string to convert + * * @return String */ private static String unCapitalize(String name) { - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { return name; } - char chars[] = name.toCharArray(); + char[] chars = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/modules/ModelerSource.java tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/modules/ModelerSource.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/modeler/modules/ModelerSource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/modeler/modules/ModelerSource.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,11 +34,12 @@ * Load data, returns a list of items. * * @param registry The registry - * @param type The bean registry type - * @param source Introspected object or some other source + * @param type The bean registry type + * @param source Introspected object or some other source + * * @return a list of object names + * * @throws Exception Error loading descriptors */ - public abstract List loadDescriptors(Registry registry, - String type, Object source) throws Exception; + public abstract List loadDescriptors(Registry registry, String type, Object source) throws Exception; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/AbstractEndpoint.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/AbstractEndpoint.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/AbstractEndpoint.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/AbstractEndpoint.java 2026-01-23 19:33:36.000000000 +0000 @@ -61,15 +61,10 @@ import org.apache.tomcat.util.threads.VirtualThreadExecutor; /** - * @param The type used by the socket wrapper associated with this endpoint. - * May be the same as U. - * @param The type of the underlying socket used by this endpoint. May be - * the same as S. - * - * @author Mladen Turk - * @author Remy Maucherat + * @param The type used by the socket wrapper associated with this endpoint. Might be the same as U. + * @param The type of the underlying socket used by this endpoint. Might be the same as S. */ -public abstract class AbstractEndpoint { +public abstract class AbstractEndpoint { // -------------------------------------------------------------- Constants @@ -82,8 +77,16 @@ */ enum SocketState { // TODO Add a new state to the AsyncStateMachine and remove - // ASYNC_END (if possible) - OPEN, CLOSED, LONG, ASYNC_END, SENDFILE, UPGRADING, UPGRADED, ASYNC_IO, SUSPENDED + // ASYNC_END (if possible) + OPEN, + CLOSED, + LONG, + ASYNC_END, + SENDFILE, + UPGRADING, + UPGRADED, + ASYNC_IO, + SUSPENDED } @@ -95,8 +98,7 @@ * * @return The state of the socket after processing */ - SocketState process(SocketWrapperBase socket, - SocketEvent status); + SocketState process(SocketWrapperBase socket, SocketEvent status); /** @@ -116,10 +118,9 @@ /** - * Inform the handler that the endpoint has stopped accepting any new - * connections. Typically, the endpoint will be stopped shortly - * afterwards but it is possible that the endpoint will be resumed so - * the handler should not assume that a stop will follow. + * Inform the handler that the endpoint has stopped accepting any new connections. Typically, the endpoint will + * be stopped shortly afterwards but it is possible that the endpoint will be resumed so the handler should not + * assume that a stop will follow. */ void pause(); @@ -187,6 +188,7 @@ * Socket properties */ protected final SocketProperties socketProperties = new SocketProperties(); + public SocketProperties getSocketProperties() { return socketProperties; } @@ -206,10 +208,11 @@ /** * Map holding all current connections keyed with the sockets. */ - protected Map> connections = new ConcurrentHashMap<>(); + protected Map> connections = new ConcurrentHashMap<>(); /** * Get a set with the current open connections. + * * @return A set with the open socket wrappers */ public Set> getConnections() { @@ -218,53 +221,61 @@ // ----------------------------------------------------------------- Properties + private boolean strictSni = true; + + public boolean getStrictSni() { + return strictSni; + } + + public void setStrictSni(boolean strictSni) { + this.strictSni = strictSni; + } + + private String defaultSSLHostConfigName = SSLHostConfig.DEFAULT_SSL_HOST_NAME; + /** - * @return The host name for the default SSL configuration for this endpoint - * - always in lower case. + * @return The host name for the default SSL configuration for this endpoint - always in lower case. */ public String getDefaultSSLHostConfigName() { return defaultSSLHostConfigName; } + public void setDefaultSSLHostConfigName(String defaultSSLHostConfigName) { this.defaultSSLHostConfigName = defaultSSLHostConfigName.toLowerCase(Locale.ENGLISH); } protected ConcurrentMap sslHostConfigs = new ConcurrentHashMap<>(); + /** * Add the given SSL Host configuration. * * @param sslHostConfig The configuration to add * - * @throws IllegalArgumentException If the host name is not valid or if a - * configuration has already been provided - * for that host + * @throws IllegalArgumentException If the host name is not valid or if a configuration has already been provided + * for that host */ public void addSslHostConfig(SSLHostConfig sslHostConfig) throws IllegalArgumentException { addSslHostConfig(sslHostConfig, false); } + /** - * Add the given SSL Host configuration, optionally replacing the existing - * configuration for the given host. + * Add the given SSL Host configuration, optionally replacing the existing configuration for the given host. * * @param sslHostConfig The configuration to add - * @param replace If {@code true} replacement of an existing - * configuration is permitted, otherwise any such - * attempted replacement will trigger an exception - * - * @throws IllegalArgumentException If the host name is not valid or if a - * configuration has already been provided - * for that host and replacement is not - * allowed + * @param replace If {@code true} replacement of an existing configuration is permitted, otherwise any such + * attempted replacement will trigger an exception + * + * @throws IllegalArgumentException If the host name is not valid or if a configuration has already been provided + * for that host and replacement is not allowed */ public void addSslHostConfig(SSLHostConfig sslHostConfig, boolean replace) throws IllegalArgumentException { String key = sslHostConfig.getHostName(); - if (key == null || key.length() == 0) { + if (key == null || key.isEmpty()) { throw new IllegalArgumentException(sm.getString("endpoint.noSslHostName")); } - if (bindState != BindState.UNBOUND && bindState != BindState.SOCKET_CLOSED_ON_STOP && - isSSLEnabled()) { + if (bindState != BindState.UNBOUND && bindState != BindState.SOCKET_CLOSED_ON_STOP && isSSLEnabled()) { try { createSSLContext(sslHostConfig); } catch (IllegalArgumentException e) { @@ -283,7 +294,7 @@ // Do not release any SSLContexts associated with a replaced // SSLHostConfig. They may still be in used by existing connections // and releasing them would break the connection at best. Let GC - // handle the clean up. + // handle the cleanup. } else { SSLHostConfig duplicate = sslHostConfigs.putIfAbsent(key, sslHostConfig); if (duplicate != null) { @@ -293,63 +304,60 @@ registerJmx(sslHostConfig); } } + /** - * Removes the SSL host configuration for the given host name, if such a - * configuration exists. + * Removes the SSL host configuration for the given host name, if such a configuration exists. * - * @param hostName The host name associated with the SSL host configuration - * to remove + * @param hostName The host name associated with the SSL host configuration to remove * - * @return The SSL host configuration that was removed, if any + * @return The SSL host configuration that was removed, if any */ public SSLHostConfig removeSslHostConfig(String hostName) { if (hostName == null) { return null; } - // Host names are case insensitive but stored/processed in lower case + // Host names are case-insensitive but stored/processed in lower case // internally because they are used as keys in a ConcurrentMap where - // keys are compared in a case sensitive manner. + // keys are compared in a case-sensitive manner. String hostNameLower = hostName.toLowerCase(Locale.ENGLISH); if (hostNameLower.equals(getDefaultSSLHostConfigName())) { - throw new IllegalArgumentException( - sm.getString("endpoint.removeDefaultSslHostConfig", hostName)); + throw new IllegalArgumentException(sm.getString("endpoint.removeDefaultSslHostConfig", hostName)); } SSLHostConfig sslHostConfig = sslHostConfigs.remove(hostNameLower); unregisterJmx(sslHostConfig); return sslHostConfig; } + /** - * Re-read the configuration files for the SSL host and replace the existing - * SSL configuration with the updated settings. Note this replacement will - * happen even if the settings remain unchanged. + * Re-read the configuration files for the SSL host and replace the existing SSL configuration with the updated + * settings. Note this replacement will happen even if the settings remain unchanged. * - * @param hostName The SSL host for which the configuration should be - * reloaded. This must match a current SSL host + * @param hostName The SSL host for which the configuration should be reloaded. This must match a current SSL host */ public void reloadSslHostConfig(String hostName) { - // Host names are case insensitive but stored/processed in lower case + // Host names are case-insensitive but stored/processed in lower case // internally because they are used as keys in a ConcurrentMap where - // keys are compared in a case sensitive manner. + // keys are compared in a case-sensitive manner. // This method can be called via various paths so convert the supplied // host name to lower case here to ensure the conversion occurs whatever // the call path. SSLHostConfig sslHostConfig = sslHostConfigs.get(hostName.toLowerCase(Locale.ENGLISH)); if (sslHostConfig == null) { - throw new IllegalArgumentException( - sm.getString("endpoint.unknownSslHostName", hostName)); + throw new IllegalArgumentException(sm.getString("endpoint.unknownSslHostName", hostName)); } addSslHostConfig(sslHostConfig, true); } + /** - * Re-read the configuration files for all SSL hosts and replace the - * existing SSL configuration with the updated settings. Note this - * replacement will happen even if the settings remain unchanged. + * Re-read the configuration files for all SSL hosts and replace the existing SSL configuration with the updated + * settings. Note this replacement will happen even if the settings remain unchanged. */ public void reloadSslHostConfigs() { for (String hostName : sslHostConfigs.keySet()) { reloadSslHostConfig(hostName); } } + public SSLHostConfig[] findSslHostConfigs() { return sslHostConfigs.values().toArray(new SSLHostConfig[0]); } @@ -357,10 +365,9 @@ /** * Create the SSLContext for the given SSLHostConfig. * - * @param sslHostConfig The SSLHostConfig for which the SSLContext should be - * created - * @throws Exception If the SSLContext cannot be created for the given - * SSLHostConfig + * @param sslHostConfig The SSLHostConfig for which the SSLContext should be created + * + * @throws Exception If the SSLContext cannot be created for the given SSLHostConfig */ protected abstract void createSSLContext(SSLHostConfig sslHostConfig) throws Exception; @@ -380,8 +387,14 @@ if (keyAlias == null) { keyAlias = SSLUtilBase.DEFAULT_KEY_ALIAS; } - certificateInfo = - sm.getString("endpoint.tls.info.cert.keystore", certificate.getCertificateKeystoreFile(), keyAlias); + String keystoreFile; + if (certificate.getCertificateKeystoreInternal() != null) { + // Keystore was set directly. Original location is unknown. + keystoreFile = sm.getString("endpoint.tls.info.cert.keystore.direct"); + } else { + keystoreFile = certificate.getCertificateKeystoreFile(); + } + certificateInfo = sm.getString("endpoint.tls.info.cert.keystore", keystoreFile, keyAlias); } String trustStoreSource = sslHostConfig.getTruststoreFile(); @@ -451,8 +464,7 @@ /** * Release the SSLContext, if any, associated with the SSLHostConfig. * - * @param sslHostConfig The SSLHostConfig for which the SSLContext should be - * released + * @param sslHostConfig The SSLHostConfig for which the SSLContext should be released */ protected void releaseSSLContext(SSLHostConfig sslHostConfig) { for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates()) { @@ -475,7 +487,7 @@ *
  • default SSLHostConfig
  • * * - * @param sniHostName Host name - must be in lower case + * @param sniHostName Host name - must be in lower case * * @return The SSLHostConfig for the given host name. */ @@ -508,20 +520,38 @@ /** + * Check if two host names share the same SSLHostConfig. + * + * @param sniHostName the host name from SNI, null if SNI is not in use + * @param protocolHostName the host name from the protocol + * @return true if SNI is not checked, if the SNI host name matches the protocol host name, + * if both host names use the same SSLHostConfig configuration, if there is no SNI and the + * protocol host name uses the default SSLHostConfig configuration, and false otherwise + */ + public boolean checkSni(String sniHostName, String protocolHostName) { + return (!strictSni || !isSSLEnabled() + || (sniHostName != null && sniHostName.equalsIgnoreCase(protocolHostName)) + || getSSLHostConfig(sniHostName) == getSSLHostConfig(protocolHostName)); + } + + + /** * Has the user requested that send file be used where possible? */ private boolean useSendfile = true; + public boolean getUseSendfile() { return useSendfile; } + public void setUseSendfile(boolean useSendfile) { this.useSendfile = useSendfile; } /** - * Time to wait for the internal executor (if used) to terminate when the - * endpoint is stopped in milliseconds. Defaults to 5000 (5 seconds). + * Time to wait for the internal executor (if used) to terminate when the endpoint is stopped in milliseconds. + * Defaults to 5000 (5 seconds). */ private long executorTerminationTimeoutMillis = 5000; @@ -529,8 +559,7 @@ return executorTerminationTimeoutMillis; } - public void setExecutorTerminationTimeoutMillis( - long executorTerminationTimeoutMillis) { + public void setExecutorTerminationTimeoutMillis(long executorTerminationTimeoutMillis) { this.executorTerminationTimeoutMillis = executorTerminationTimeoutMillis; } @@ -539,13 +568,18 @@ * Priority of the acceptor threads. */ protected int acceptorThreadPriority = Thread.NORM_PRIORITY; + public void setAcceptorThreadPriority(int acceptorThreadPriority) { this.acceptorThreadPriority = acceptorThreadPriority; } - public int getAcceptorThreadPriority() { return acceptorThreadPriority; } + + public int getAcceptorThreadPriority() { + return acceptorThreadPriority; + } - private int maxConnections = 8*1024; + private int maxConnections = 8 * 1024; + public void setMaxConnections(int maxCon) { this.maxConnections = maxCon; LimitLatch latch = this.connectionLimitLatch; @@ -560,19 +594,19 @@ initializeConnectionLatch(); } } - public int getMaxConnections() { return this.maxConnections; } + + public int getMaxConnections() { + return this.maxConnections; + } /** - * Return the current count of connections handled by this endpoint, if the - * connections are counted (which happens when the maximum count of - * connections is limited), or -1 if they are not. This - * property is added here so that this value can be inspected through JMX. - * It is visible on "ThreadPool" MBean. - * - *

    The count is incremented by the Acceptor before it tries to accept a - * new connection. Until the limit is reached and thus the count cannot be - * incremented, this value is more by 1 (the count of acceptors) than the - * actual count of connections that are being served. + * Return the current count of connections handled by this endpoint, if the connections are counted (which happens + * when the maximum count of connections is limited), or -1 if they are not. This property is added + * here so that this value can be inspected through JMX. It is visible on "ThreadPool" MBean. + *

    + * The count is incremented by the Acceptor before it tries to accept a new connection. Until the limit is reached + * and thus the count cannot be incremented, this value is more by 1 (the count of acceptors) than the actual count + * of connections that are being served. * * @return The count */ @@ -588,17 +622,23 @@ * External Executor based thread pool. */ private Executor executor = null; + public void setExecutor(Executor executor) { this.executor = executor; this.internalExecutor = (executor == null); } - public Executor getExecutor() { return executor; } + + public Executor getExecutor() { + return executor; + } private boolean useVirtualThreads = false; + public void setUseVirtualThreads(boolean useVirtualThreads) { this.useVirtualThreads = useVirtualThreads; } + public boolean getUseVirtualThreads() { return useVirtualThreads; } @@ -608,9 +648,11 @@ * External Executor based thread pool for utility tasks. */ private ScheduledExecutorService utilityExecutor = null; + public void setUtilityExecutor(ScheduledExecutorService utilityExecutor) { this.utilityExecutor = utilityExecutor; } + public ScheduledExecutorService getUtilityExecutor() { if (utilityExecutor == null) { getLog().warn(sm.getString("endpoint.warn.noUtilityExecutor")); @@ -624,13 +666,23 @@ * Server socket port. */ private int port = -1; - public int getPort() { return port; } - public void setPort(int port ) { this.port=port; } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } private int portOffset = 0; - public int getPortOffset() { return portOffset; } - public void setPortOffset(int portOffset ) { + + public int getPortOffset() { + return portOffset; + } + + public void setPortOffset(int portOffset) { if (portOffset < 0) { throw new IllegalArgumentException( sm.getString("endpoint.portOffset.invalid", Integer.valueOf(portOffset))); @@ -666,46 +718,62 @@ * Address for the server socket. */ private InetAddress address; - public InetAddress getAddress() { return address; } - public void setAddress(InetAddress address) { this.address = address; } + + public InetAddress getAddress() { + return address; + } + + public void setAddress(InetAddress address) { + this.address = address; + } /** - * Obtain the network address the server socket is bound to. This primarily - * exists to enable the correct address to be used when unlocking the server - * socket since it removes the guess-work involved if no address is - * specifically set. + * Obtain the network address the server socket is bound to. This primarily exists to enable the correct address to + * be used when unlocking the server socket since it removes the guess-work involved if no address is specifically + * set. * - * @return The network address that the server socket is listening on or - * null if the server socket is not currently bound. + * @return The network address that the server socket is listening on or null if the server socket is not currently + * bound. * - * @throws IOException If there is a problem determining the currently bound - * socket + * @throws IOException If there is a problem determining the currently bound socket */ protected abstract InetSocketAddress getLocalAddress() throws IOException; /** - * Allows the server developer to specify the acceptCount (backlog) that - * should be used for server sockets. By default, this value - * is 100. + * Allows the server developer to specify the acceptCount (backlog) that should be used for server sockets. By + * default, this value is 100. */ private int acceptCount = 100; - public void setAcceptCount(int acceptCount) { if (acceptCount > 0) { - this.acceptCount = acceptCount; - } } - public int getAcceptCount() { return acceptCount; } + + public void setAcceptCount(int acceptCount) { + if (acceptCount > 0) { + this.acceptCount = acceptCount; + } + } + + public int getAcceptCount() { + return acceptCount; + } /** - * Controls when the Endpoint binds the port. true, the default - * binds the port on {@link #init()} and unbinds it on {@link #destroy()}. - * If set to false the port is bound on {@link #start()} and - * unbound on {@link #stop()}. + * Controls when the Endpoint binds the port. true, the default binds the port on {@link #init()} and + * unbinds it on {@link #destroy()}. If set to false the port is bound on {@link #start()} and unbound + * on {@link #stop()}. */ private boolean bindOnInit = true; - public boolean getBindOnInit() { return bindOnInit; } - public void setBindOnInit(boolean b) { this.bindOnInit = b; } + + public boolean getBindOnInit() { + return bindOnInit; + } + + public void setBindOnInit(boolean b) { + this.bindOnInit = b; + } + private volatile BindState bindState = BindState.UNBOUND; + protected BindState getBindState() { return bindState; } @@ -714,6 +782,7 @@ * Keepalive timeout, if not set the soTimeout is used. */ private Integer keepAliveTimeout = null; + public int getKeepAliveTimeout() { if (keepAliveTimeout == null) { return getConnectionTimeout(); @@ -721,6 +790,7 @@ return keepAliveTimeout.intValue(); } } + public void setKeepAliveTimeout(int keepAliveTimeout) { this.keepAliveTimeout = Integer.valueOf(keepAliveTimeout); } @@ -729,23 +799,29 @@ /** * Socket TCP no delay. * - * @return The current TCP no delay setting for sockets created by this - * endpoint + * @return The current TCP no delay setting for sockets created by this endpoint */ - public boolean getTcpNoDelay() { return socketProperties.getTcpNoDelay();} - public void setTcpNoDelay(boolean tcpNoDelay) { socketProperties.setTcpNoDelay(tcpNoDelay); } + public boolean getTcpNoDelay() { + return socketProperties.getTcpNoDelay(); + } + + public void setTcpNoDelay(boolean tcpNoDelay) { + socketProperties.setTcpNoDelay(tcpNoDelay); + } /** * Socket linger. * - * @return The current socket linger time for sockets created by this - * endpoint + * @return The current socket linger time for sockets created by this endpoint */ - public int getConnectionLinger() { return socketProperties.getSoLingerTime(); } + public int getConnectionLinger() { + return socketProperties.getSoLingerTime(); + } + public void setConnectionLinger(int connectionLinger) { socketProperties.setSoLingerTime(connectionLinger); - socketProperties.setSoLingerOn(connectionLinger>=0); + socketProperties.setSoLingerOn(connectionLinger >= 0); } @@ -754,17 +830,29 @@ * * @return The current socket timeout for sockets created by this endpoint */ - public int getConnectionTimeout() { return socketProperties.getSoTimeout(); } - public void setConnectionTimeout(int soTimeout) { socketProperties.setSoTimeout(soTimeout); } + public int getConnectionTimeout() { + return socketProperties.getSoTimeout(); + } + + public void setConnectionTimeout(int soTimeout) { + socketProperties.setSoTimeout(soTimeout); + } /** * SSL engine. */ private boolean SSLEnabled = false; - public boolean isSSLEnabled() { return SSLEnabled; } - public void setSSLEnabled(boolean SSLEnabled) { this.SSLEnabled = SSLEnabled; } + + public boolean isSSLEnabled() { + return SSLEnabled; + } + + public void setSSLEnabled(boolean SSLEnabled) { + this.SSLEnabled = SSLEnabled; + } private int minSpareThreads = 10; + public void setMinSpareThreads(int minSpareThreads) { this.minSpareThreads = minSpareThreads; Executor executor = this.executor; @@ -776,9 +864,11 @@ ((ThreadPoolExecutor) executor).setCorePoolSize(minSpareThreads); } } + public int getMinSpareThreads() { return Math.min(getMinSpareThreadsInternal(), getMaxThreads()); } + private int getMinSpareThreadsInternal() { if (internalExecutor) { return minSpareThreads; @@ -792,6 +882,7 @@ * Maximum amount of worker threads. */ private int maxThreads = 200; + public void setMaxThreads(int maxThreads) { this.maxThreads = maxThreads; Executor executor = this.executor; @@ -803,6 +894,7 @@ ((ThreadPoolExecutor) executor).setMaximumPoolSize(maxThreads); } } + public int getMaxThreads() { if (internalExecutor) { return maxThreads; @@ -816,9 +908,11 @@ * Task queue capacity for the thread pool. */ private int maxQueueSize = Integer.MAX_VALUE; + public void setMaxQueueSize(int maxQueueSize) { this.maxQueueSize = maxQueueSize; } + public int getMaxQueueSize() { if (internalExecutor) { return maxQueueSize; @@ -829,10 +923,11 @@ /** - * Amount of time in milliseconds before the internal thread pool stops any idle threads - * if the amount of thread is greater than the minimum amount of spare threads. + * Amount of time in milliseconds before the internal thread pool stops any idle threads if the amount of thread is + * greater than the minimum amount of spare threads. */ private int threadsMaxIdleTime = 60000; + public void setThreadsMaxIdleTime(int threadsMaxIdleTime) { this.threadsMaxIdleTime = threadsMaxIdleTime; Executor executor = this.executor; @@ -844,6 +939,7 @@ ((ThreadPoolExecutor) executor).setKeepAliveTime(threadsMaxIdleTime, TimeUnit.MILLISECONDS); } } + public int getThreadsMaxIdleTime() { if (internalExecutor) { return threadsMaxIdleTime; @@ -856,10 +952,12 @@ * Priority of the worker threads. */ protected int threadPriority = Thread.NORM_PRIORITY; + public void setThreadPriority(int threadPriority) { // Can't change this once the executor has started this.threadPriority = threadPriority; } + public int getThreadPriority() { if (internalExecutor) { return threadPriority; @@ -872,7 +970,8 @@ /** * Max keep alive requests */ - private int maxKeepAliveRequests=100; // as in Apache HTTPD server + private int maxKeepAliveRequests = 100; // as in Apache HTTPD server + public int getMaxKeepAliveRequests() { // Disable keep-alive if the server socket is not bound if (bindState.isBound()) { @@ -881,6 +980,7 @@ return 1; } } + public void setMaxKeepAliveRequests(int maxKeepAliveRequests) { this.maxKeepAliveRequests = maxKeepAliveRequests; } @@ -890,34 +990,57 @@ * Name of the thread pool, which will be used for naming child threads. */ private String name = "TP"; - public void setName(String name) { this.name = name; } - public String getName() { return name; } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } /** * Name of domain to use for JMX registration. */ private String domain; - public void setDomain(String domain) { this.domain = domain; } - public String getDomain() { return domain; } + + public void setDomain(String domain) { + this.domain = domain; + } + + public String getDomain() { + return domain; + } /** - * The default is true - the created threads will be - * in daemon mode. If set to false, the control thread - * will not be daemon - and will keep the process alive. + * The default is true - the created threads will be in daemon mode. If set to false, the control thread will not be + * daemon - and will keep the process alive. */ private boolean daemon = true; - public void setDaemon(boolean b) { daemon = b; } - public boolean getDaemon() { return daemon; } + + public void setDaemon(boolean b) { + daemon = b; + } + + public boolean getDaemon() { + return daemon; + } /** * Expose asynchronous IO capability. */ private boolean useAsyncIO = true; - public void setUseAsyncIO(boolean useAsyncIO) { this.useAsyncIO = useAsyncIO; } - public boolean getUseAsyncIO() { return useAsyncIO; } + + public void setUseAsyncIO(boolean useAsyncIO) { + this.useAsyncIO = useAsyncIO; + } + + public boolean getUseAsyncIO() { + return useAsyncIO; + } /** @@ -928,15 +1051,15 @@ * @deprecated This code will be removed in Tomcat 11 onwards */ @Deprecated - protected boolean getDeferAccept() { + public boolean getDeferAccept() { return false; } /** - * The default behavior is to identify connectors uniquely with address - * and port. However, certain connectors are not using that and need - * some other identifier, which then can be used as a replacement. + * The default behavior is to identify connectors uniquely with address and port. However, certain connectors are + * not using that and need some other identifier, which then can be used as a replacement. + * * @return the id */ public String getId() { @@ -945,11 +1068,13 @@ protected final List negotiableProtocols = new ArrayList<>(); + public void addNegotiatedProtocol(String negotiableProtocol) { negotiableProtocols.add(negotiableProtocol); } + public boolean hasNegotiableProtocols() { - return (negotiableProtocols.size() > 0); + return (!negotiableProtocols.isEmpty()); } @@ -957,23 +1082,26 @@ * Handling of accepted sockets. */ private Handler handler = null; - public void setHandler(Handler handler ) { this.handler = handler; } - public Handler getHandler() { return handler; } + + public void setHandler(Handler handler) { + this.handler = handler; + } + + public Handler getHandler() { + return handler; + } /** - * Attributes provide a way for configuration to be passed to sub-components - * without the {@link org.apache.coyote.ProtocolHandler} being aware of the - * properties available on those sub-components. + * Attributes provide a way for configuration to be passed to subcomponents without the + * {@link org.apache.coyote.ProtocolHandler} being aware of the properties available on those subcomponents. */ - protected HashMap attributes = new HashMap<>(); + protected HashMap attributes = new HashMap<>(); /** - * Generic property setter called when a property for which a specific - * setter already exists within the - * {@link org.apache.coyote.ProtocolHandler} needs to be made available to - * sub-components. The specific setter will call this method to populate the - * attributes. + * Generic property setter called when a property for which a specific setter already exists within the + * {@link org.apache.coyote.ProtocolHandler} needs to be made available to subcomponents. The specific setter will + * call this method to populate the attributes. * * @param name Name of property to set * @param value The value to set the property to @@ -984,11 +1112,11 @@ } attributes.put(name, value); } + /** - * Used by sub-components to retrieve configuration information. + * Used by subcomponents to retrieve configuration information. * - * @param key The name of the property for which the value should be - * retrieved + * @param key The name of the property for which the value should be retrieved * * @return The value of the specified property */ @@ -1001,7 +1129,6 @@ } - public boolean setProperty(String name, String value) { setAttribute(name, value); final String socketName = "socket."; @@ -1009,13 +1136,14 @@ if (name.startsWith(socketName)) { return IntrospectionUtils.setProperty(socketProperties, name.substring(socketName.length()), value); } else { - return IntrospectionUtils.setProperty(this,name,value,false); + return IntrospectionUtils.setProperty(this, name, value, false); } - }catch ( Exception x ) { - getLog().error(sm.getString("endpoint.setAttributeError", name, value), x); + } catch (Exception e) { + getLog().error(sm.getString("endpoint.setAttributeError", name, value), e); return false; } } + public String getProperty(String name) { String value = (String) getAttribute(name); final String socketName = "socket."; @@ -1100,7 +1228,7 @@ if (executor != null && internalExecutor) { this.executor = null; if (executor instanceof ThreadPoolExecutor) { - //this is our internal one, so we need to shut it down + // this is our internal one, so we need to shut it down ThreadPoolExecutor tpe = (ThreadPoolExecutor) executor; tpe.shutdownNow(); long timeout = getExecutorTerminationTimeoutMillis(); @@ -1129,12 +1257,14 @@ return; } - InetSocketAddress unlockAddress = null; + InetSocketAddress unlockAddress; InetSocketAddress localAddress = null; try { localAddress = getLocalAddress(); } catch (IOException ioe) { - getLog().debug(sm.getString("endpoint.debug.unlock.localFail", getName()), ioe); + if (getLog().isDebugEnabled()) { + getLog().debug(sm.getString("endpoint.debug.unlock.localFail", getName()), ioe); + } } if (localAddress == null) { getLog().warn(sm.getString("endpoint.debug.unlock.localNone", getName())); @@ -1147,7 +1277,7 @@ try (java.net.Socket s = new java.net.Socket()) { // Never going to read from this socket so the timeout doesn't matter. Use the unlock timeout. s.setSoTimeout(getSocketProperties().getUnlockTimeout()); - // Newer MacOS versions (e.g. Ventura 13.2) appear to linger for ~1s on close when linger is disabled. + // Newer macOS versions (e.g. Ventura 13.2) appear to linger for ~1s on close when linger is disabled. // That causes delays when running the unit tests. Explicitly enabling linger but with a timeout of // zero seconds seems to fix the issue. s.setSoLinger(true, 0); @@ -1171,11 +1301,10 @@ Thread.sleep(1); } } - } catch(Throwable t) { + } catch (Throwable t) { ExceptionUtils.handleThrowable(t); if (getLog().isDebugEnabled()) { - getLog().debug(sm.getString( - "endpoint.debug.unlock.fail", String.valueOf(getPortWithOffset())), t); + getLog().debug(sm.getString("endpoint.debug.unlock.fail", String.valueOf(getPortWithOffset())), t); } } } @@ -1233,19 +1362,16 @@ // ---------------------------------------------- Request processing methods /** - * Process the given SocketWrapper with the given status. Used to trigger - * processing as if the Poller (for those endpoints that have one) - * selected the socket. + * Process the given SocketWrapper with the given status. Used to trigger processing as if the Poller (for those + * endpoints that have one) selected the socket. * * @param socketWrapper The socket wrapper to process * @param event The socket event to be processed - * @param dispatch Should the processing be performed on a new - * container thread + * @param dispatch Should the processing be performed on a new container thread * * @return if processing was triggered successfully */ - public boolean processSocket(SocketWrapperBase socketWrapper, - SocketEvent event, boolean dispatch) { + public boolean processSocket(SocketWrapperBase socketWrapper, SocketEvent event, boolean dispatch) { try { if (socketWrapper == null) { return false; @@ -1266,7 +1392,7 @@ sc.run(); } } catch (RejectedExecutionException ree) { - getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree); + getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper), ree); return false; } catch (Throwable t) { ExceptionUtils.handleThrowable(t); @@ -1279,23 +1405,24 @@ } - protected abstract SocketProcessorBase createSocketProcessor( - SocketWrapperBase socketWrapper, SocketEvent event); + protected abstract SocketProcessorBase createSocketProcessor(SocketWrapperBase socketWrapper, + SocketEvent event); // ------------------------------------------------------- Lifecycle methods /* - * NOTE: There is no maintenance of state or checking for valid transitions - * within this class other than ensuring that bind/unbind are called in the - * right place. It is expected that the calling code will maintain state and + * NOTE: There is no maintenance of state or checking for valid transitions within this class other than ensuring + * that bind/unbind are called in the right place. It is expected that the calling code will maintain state and * prevent invalid state transitions. */ public abstract void bind() throws Exception; + public abstract void unbind() throws Exception; public abstract void startInternal() throws Exception; + public abstract void stopInternal() throws Exception; @@ -1320,12 +1447,12 @@ if (this.domain != null) { // Register endpoint (as ThreadPool - historical name) oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\""); - Registry.getRegistry(null, null).registerComponent(this, oname, null); + Registry.getRegistry(null).registerComponent(this, oname, null); - ObjectName socketPropertiesOname = new ObjectName(domain + - ":type=SocketProperties,name=\"" + getName() + "\""); + ObjectName socketPropertiesOname = + new ObjectName(domain + ":type=SocketProperties,name=\"" + getName() + "\""); socketProperties.setObjectName(socketPropertiesOname); - Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null); + Registry.getRegistry(null).registerComponent(socketProperties, socketPropertiesOname, null); for (SSLHostConfig sslHostConfig : findSslHostConfigs()) { registerJmx(sslHostConfig); @@ -1339,45 +1466,42 @@ // Before init the domain is null return; } - ObjectName sslOname = null; + ObjectName sslOname; try { - sslOname = new ObjectName(domain + ":type=SSLHostConfig,ThreadPool=\"" + - getName() + "\",name=" + ObjectName.quote(sslHostConfig.getHostName())); + sslOname = new ObjectName(domain + ":type=SSLHostConfig,ThreadPool=\"" + getName() + "\",name=" + + ObjectName.quote(sslHostConfig.getHostName())); sslHostConfig.setObjectName(sslOname); try { - Registry.getRegistry(null, null).registerComponent(sslHostConfig, sslOname, null); + Registry.getRegistry(null).registerComponent(sslHostConfig, sslOname, null); } catch (Exception e) { getLog().warn(sm.getString("endpoint.jmxRegistrationFailed", sslOname), e); } } catch (MalformedObjectNameException e) { - getLog().warn(sm.getString("endpoint.invalidJmxNameSslHost", - sslHostConfig.getHostName()), e); + getLog().warn(sm.getString("endpoint.invalidJmxNameSslHost", sslHostConfig.getHostName()), e); } for (SSLHostConfigCertificate sslHostConfigCert : sslHostConfig.getCertificates()) { - ObjectName sslCertOname = null; + ObjectName sslCertOname; try { - sslCertOname = new ObjectName(domain + - ":type=SSLHostConfigCertificate,ThreadPool=\"" + getName() + - "\",Host=" + ObjectName.quote(sslHostConfig.getHostName()) + - ",name=" + sslHostConfigCert.getType()); + sslCertOname = new ObjectName( + domain + ":type=SSLHostConfigCertificate,ThreadPool=\"" + getName() + "\",Host=" + + ObjectName.quote(sslHostConfig.getHostName()) + ",name=" + sslHostConfigCert.getType()); sslHostConfigCert.setObjectName(sslCertOname); try { - Registry.getRegistry(null, null).registerComponent( - sslHostConfigCert, sslCertOname, null); + Registry.getRegistry(null).registerComponent(sslHostConfigCert, sslCertOname, null); } catch (Exception e) { getLog().warn(sm.getString("endpoint.jmxRegistrationFailed", sslCertOname), e); } } catch (MalformedObjectNameException e) { - getLog().warn(sm.getString("endpoint.invalidJmxNameSslHostCert", - sslHostConfig.getHostName(), sslHostConfigCert.getType()), e); + getLog().warn(sm.getString("endpoint.invalidJmxNameSslHostCert", sslHostConfig.getHostName(), + sslHostConfigCert.getType()), e); } } } private void unregisterJmx(SSLHostConfig sslHostConfig) { - Registry registry = Registry.getRegistry(null, null); + Registry registry = Registry.getRegistry(null); registry.unregisterComponent(sslHostConfig.getObjectName()); for (SSLHostConfigCertificate sslHostConfigCert : sslHostConfig.getCertificates()) { registry.unregisterComponent(sslHostConfigCert.getObjectName()); @@ -1406,8 +1530,7 @@ /** - * Pause the endpoint, which will stop it accepting new connections and - * unlock the acceptor. + * Pause the endpoint, which will stop it accepting new connections and unlock the acceptor. */ public void pause() { if (running && !paused) { @@ -1419,8 +1542,7 @@ } /** - * Resume the endpoint, which will make it start accepting new connections - * again. + * Resume the endpoint, which will make it start accepting new connections again. */ public void resume() { if (running) { @@ -1441,7 +1563,7 @@ unbind(); bindState = BindState.UNBOUND; } - Registry registry = Registry.getRegistry(null, null); + Registry registry = Registry.getRegistry(null); registry.unregisterComponent(oname); registry.unregisterComponent(socketProperties.getObjectName()); for (SSLHostConfig sslHostConfig : findSslHostConfigs()) { @@ -1457,10 +1579,10 @@ } protected LimitLatch initializeConnectionLatch() { - if (maxConnections==-1) { + if (maxConnections == -1) { return null; } - if (connectionLimitLatch==null) { + if (connectionLimitLatch == null) { connectionLimitLatch = new LimitLatch(getMaxConnections()); } return connectionLimitLatch; @@ -1468,30 +1590,30 @@ private void releaseConnectionLatch() { LimitLatch latch = connectionLimitLatch; - if (latch!=null) { + if (latch != null) { latch.releaseAll(); } connectionLimitLatch = null; } protected void countUpOrAwaitConnection() throws InterruptedException { - if (maxConnections==-1) { + if (maxConnections == -1) { return; } LimitLatch latch = connectionLimitLatch; - if (latch!=null) { + if (latch != null) { latch.countUpOrAwait(); } } protected long countDownConnection() { - if (maxConnections==-1) { + if (maxConnections == -1) { return -1; } LimitLatch latch = connectionLimitLatch; - if (latch!=null) { + if (latch != null) { long result = latch.countDown(); - if (result<0) { + if (result < 0) { getLog().warn(sm.getString("endpoint.warn.incorrectConnectionCount")); } return result; @@ -1502,9 +1624,8 @@ /** - * Close the server socket (to prevent further connections) if the server - * socket was originally bound on {@link #start()} (rather than on - * {@link #init()}). + * Close the server socket (to prevent further connections) if the server socket was originally bound on + * {@link #start()} (rather than on {@link #init()}). * * @see #getBindOnInit() */ @@ -1518,7 +1639,7 @@ // Signal to any multiplexed protocols (HTTP/2) that they may wish // to stop accepting new streams getHandler().pause(); - // Update the bindState. This has the side-effect of disabling + // Update the bindState. This has the side effect of disabling // keep-alive for any in-progress connections bindState = BindState.SOCKET_CLOSED_ON_STOP; try { @@ -1531,12 +1652,10 @@ /** - * Wait for the client connections to the server to close gracefully. The - * method will return when all of the client connections have closed or the - * method has been waiting for {@code waitTimeMillis}. + * Wait for the client connections to the server to close gracefully. The method will return when all of the client + * connections have closed or the method has been waiting for {@code waitTimeMillis}. * - * @param waitMillis The maximum time to wait in milliseconds for the - * client connections to close. + * @param waitMillis The maximum time to wait in milliseconds for the client connections to close. * * @return The wait time, if any remaining when the method returned */ @@ -1566,10 +1685,10 @@ protected abstract boolean setSocketOptions(U socket); /** - * Close the socket when the connection has to be immediately closed when - * an error occurs while configuring the accepted socket or trying to - * dispatch it for processing. The wrapper associated with the socket will - * be used for the close. + * Close the socket when the connection has to be immediately closed when an error occurs while configuring the + * accepted socket or trying to dispatch it for processing. The wrapper associated with the socket will be used for + * the close. + * * @param socket The newly accepted socket */ protected void closeSocket(U socket) { @@ -1580,9 +1699,9 @@ } /** - * Close the socket. This is used when the connector is not in a state - * which allows processing the socket, or if there was an error which - * prevented the allocation of the socket wrapper. + * Close the socket. This is used when the connector is not in a state which allows processing the socket, or if + * there was an error which prevented the allocation of the socket wrapper. + * * @param socket The newly accepted socket */ protected abstract void destroySocket(U socket); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/AbstractJsseEndpoint.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,9 +28,17 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; +import org.apache.tomcat.util.compat.JreCompat; import org.apache.tomcat.util.net.openssl.ciphers.Cipher; +import org.apache.tomcat.util.net.openssl.ciphers.Group; +import org.apache.tomcat.util.net.openssl.ciphers.SignatureScheme; -public abstract class AbstractJsseEndpoint extends AbstractEndpoint { +public abstract class AbstractJsseEndpoint extends AbstractEndpoint { + + // Thread local use to pass additional arguments to createSSLEngine without changing the protected method signature + static final ThreadLocal> clientRequestedProtocolsThreadLocal = new ThreadLocal<>(); + static final ThreadLocal> clientSupportedGroupsThreadLocal = new ThreadLocal<>(); + static final ThreadLocal> clientSignatureSchemesThreadLocal = new ThreadLocal<>(); private String sslImplementationName = null; private int sniParseLimit = 64 * 1024; @@ -72,8 +80,8 @@ // Validate default SSLHostConfigName if (sslHostConfigs.get(getDefaultSSLHostConfigName()) == null) { - throw new IllegalArgumentException(sm.getString("endpoint.noSslHostConfig", - getDefaultSSLHostConfigName(), getName())); + throw new IllegalArgumentException( + sm.getString("endpoint.noSslHostConfig", getDefaultSSLHostConfigName(), getName())); } } @@ -83,13 +91,6 @@ @Override protected void createSSLContext(SSLHostConfig sslHostConfig) throws IllegalArgumentException { - // HTTP/2 does not permit optional certificate authentication with any - // version of TLS. - if (sslHostConfig.getCertificateVerification().isOptional() && - negotiableProtocols.contains("h2")) { - getLog().warn(sm.getString("sslHostConfig.certificateVerificationWithHttp2", sslHostConfig.getHostName())); - } - boolean firstCertificate = true; for (SSLHostConfigCertificate certificate : sslHostConfig.getCertificates(true)) { SSLUtil sslUtil = sslImplementation.getSSLUtil(certificate); @@ -103,15 +104,15 @@ SSLContext sslContextGenerated = certificate.getSslContextGenerated(); // Generate the SSLContext from configuration unless (e.g. embedded) an SSLContext has been provided. // Need to handle both initial configuration and reload. - // Initial, SSLContext provided - sslContext will be non-null and sslContextGenerated will be null + // Initial, SSLContext provided - sslContext will be non-null and sslContextGenerated will be null // Initial, SSLContext not provided - sslContext null and sslContextGenerated will be null - // Reload, SSLContext provided - sslContext will be non-null and sslContextGenerated will be null - // Reload, SSLContext not provided - sslContext non-null and equal to sslContextGenerated + // Reload, SSLContext provided - sslContext will be non-null and sslContextGenerated will be null + // Reload, SSLContext not provided - sslContext non-null and equal to sslContextGenerated if (sslContext == null || sslContext == sslContextGenerated) { try { sslContext = sslUtil.createSSLContext(negotiableProtocols); } catch (Exception e) { - throw new IllegalArgumentException(e.getMessage(), e); + throw new IllegalArgumentException(sm.getString("endpoint.errorCreatingSSLContext"), e); } certificate.setSslContextGenerated(sslContext); @@ -119,19 +120,33 @@ logCertificate(certificate); } + } protected SSLEngine createSSLEngine(String sniHostName, List clientRequestedCiphers, List clientRequestedApplicationProtocols) { + List clientRequestedProtocols = clientRequestedProtocolsThreadLocal.get(); + if (clientRequestedProtocols == null) { + clientRequestedProtocols = new ArrayList(); + } + List clientSupportedGroups = clientSupportedGroupsThreadLocal.get(); + if (clientSupportedGroups == null) { + clientSupportedGroups = new ArrayList(); + } + List clientSignatureSchemes = clientSignatureSchemesThreadLocal.get(); + if (clientSignatureSchemes == null) { + clientSignatureSchemes = new ArrayList(); + } + SSLHostConfig sslHostConfig = getSSLHostConfig(sniHostName); - SSLHostConfigCertificate certificate = selectCertificate(sslHostConfig, clientRequestedCiphers); + SSLHostConfigCertificate certificate = selectCertificate(sslHostConfig, clientRequestedCiphers, + clientRequestedProtocols, clientSignatureSchemes); SSLContext sslContext = certificate.getSslContext(); if (sslContext == null) { - throw new IllegalStateException( - sm.getString("endpoint.jsse.noSslContext", sniHostName)); + throw new IllegalStateException(sm.getString("endpoint.jsse.noSslContext", sniHostName)); } SSLEngine engine = sslContext.createSSLEngine(); @@ -141,32 +156,54 @@ SSLParameters sslParameters = engine.getSSLParameters(); sslParameters.setUseCipherSuitesOrder(sslHostConfig.getHonorCipherOrder()); - if (clientRequestedApplicationProtocols != null - && clientRequestedApplicationProtocols.size() > 0 - && negotiableProtocols.size() > 0) { + if (clientRequestedApplicationProtocols != null && !clientRequestedApplicationProtocols.isEmpty() && + !negotiableProtocols.isEmpty()) { // Only try to negotiate if both client and server have at least // one protocol in common // Note: Tomcat does not explicitly negotiate http/1.1 - // TODO: Is this correct? Should it change? List commonProtocols = new ArrayList<>(negotiableProtocols); commonProtocols.retainAll(clientRequestedApplicationProtocols); - if (commonProtocols.size() > 0) { + if (!commonProtocols.isEmpty()) { String[] commonProtocolsArray = commonProtocols.toArray(new String[0]); sslParameters.setApplicationProtocols(commonProtocolsArray); } } + // Merge server groups with the client groups + if (JreCompat.isJre20Available()) { + List supportedGroups = new ArrayList<>(); + LinkedHashSet serverSupportedGroups = sslHostConfig.getGroupList(); + if (serverSupportedGroups != null) { + if (!clientSupportedGroups.isEmpty()) { + for (Group group : clientSupportedGroups) { + if (serverSupportedGroups.contains(group)) { + supportedGroups.add(group.toString()); + } + } + } else { + for (Group group : serverSupportedGroups) { + supportedGroups.add(group.toString()); + } + } + JreCompat.getInstance().setNamedGroupsMethod(sslParameters, supportedGroups.toArray(new String[0])); + } else if (!clientSupportedGroups.isEmpty()) { + for (Group group : clientSupportedGroups) { + supportedGroups.add(group.toString()); + } + JreCompat.getInstance().setNamedGroupsMethod(sslParameters, supportedGroups.toArray(new String[0])); + } + } switch (sslHostConfig.getCertificateVerification()) { - case NONE: - sslParameters.setNeedClientAuth(false); - sslParameters.setWantClientAuth(false); - break; - case OPTIONAL: - case OPTIONAL_NO_CA: - sslParameters.setWantClientAuth(true); - break; - case REQUIRED: - sslParameters.setNeedClientAuth(true); - break; + case NONE: + sslParameters.setNeedClientAuth(false); + sslParameters.setWantClientAuth(false); + break; + case OPTIONAL: + case OPTIONAL_NO_CA: + sslParameters.setWantClientAuth(true); + break; + case REQUIRED: + sslParameters.setNeedClientAuth(true); + break; } // The getter (at least in OpenJDK and derivatives) returns a defensive copy engine.setSSLParameters(sslParameters); @@ -175,14 +212,26 @@ } - private SSLHostConfigCertificate selectCertificate( - SSLHostConfig sslHostConfig, List clientCiphers) { + private SSLHostConfigCertificate selectCertificate(SSLHostConfig sslHostConfig, List clientCiphers, + List clientRequestedProtocols, List clientSignatureSchemes) { Set certificates = sslHostConfig.getCertificates(true); if (certificates.size() == 1) { return certificates.iterator().next(); } + // Use signature algorithm for cipher matching with TLS 1.3 + if ((clientRequestedProtocols.contains(Constants.SSL_PROTO_TLSv1_3)) && + sslHostConfig.getProtocols().contains(Constants.SSL_PROTO_TLSv1_3)) { + for (SignatureScheme signatureScheme : clientSignatureSchemes) { + for (SSLHostConfigCertificate certificate : certificates) { + if (certificate.getType().isCompatibleWith(signatureScheme)) { + return certificate; + } + } + } + } + LinkedHashSet serverCiphers = sslHostConfig.getCipherList(); List candidateCiphers = new ArrayList<>(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/Acceptor.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/Acceptor.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/Acceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/Acceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,9 +35,8 @@ private final AbstractEndpoint endpoint; private String threadName; /* - * Tracked separately rather than using endpoint.isRunning() as calls to - * endpoint.stop() and endpoint.start() in quick succession can cause the - * acceptor to continue running when it should terminate. + * Tracked separately rather than using endpoint.isRunning() as calls to endpoint.stop() and endpoint.start() in + * quick succession can cause the acceptor to continue running when it should terminate. */ private volatile boolean stopCalled = false; private final CountDownLatch stopLatch = new CountDownLatch(1); @@ -83,9 +82,9 @@ // excessive CPU usage. // Therefore, we start with a tight loop but if there isn't a // rapid transition to stop then sleeps are introduced. - // < 1ms - tight loop + // < 1ms - tight loop // 1ms to 10ms - 1ms sleep - // > 10ms - 10ms sleep + // > 10ms - 10ms sleep while (endpoint.isPaused() && !stopCalled) { if (state != AcceptorState.PAUSED) { pauseStart = System.nanoTime(); @@ -112,7 +111,7 @@ state = AcceptorState.RUNNING; try { - //if we have reached max connections, wait + // if we have reached max connections, wait endpoint.countUpOrAwaitConnection(); // Endpoint might have been paused while waiting for latch @@ -121,19 +120,19 @@ continue; } - U socket = null; + U socket; try { // Accept the next incoming connection from the server // socket socket = endpoint.serverSocketAccept(); - } catch (Exception ioe) { + } catch (Exception e) { // We didn't get a socket endpoint.countDownConnection(); if (endpoint.isRunning()) { // Introduce delay if necessary errorDelay = handleExceptionWithDelay(errorDelay); // re-throw - throw ioe; + throw e; } else { break; } @@ -153,8 +152,7 @@ } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); - String msg = sm.getString("endpoint.accept.fail"); - log.error(msg, t); + log.error(sm.getString("endpoint.accept.fail"), t); } } } finally { @@ -165,12 +163,10 @@ /** - * Signals the Acceptor to stop, optionally waiting for that stop process - * to complete before returning. If a wait is requested and the stop does - * not complete in that time a warning will be logged. + * Signals the Acceptor to stop, optionally waiting for that stop process to complete before returning. If a wait is + * requested and the stop does not complete in that time a warning will be logged. * - * @param waitSeconds The time to wait in seconds. Use a value less than - * zero for no wait. + * @param waitSeconds The time to wait in seconds. Use a value less than zero for no wait. * * @deprecated Unused. Will be remove in Tomcat 11 onwards. */ @@ -185,7 +181,7 @@ if (waitMilliseconds > 0) { try { if (!stopLatch.await(waitMilliseconds, TimeUnit.MILLISECONDS)) { - log.warn(sm.getString("acceptor.stop.fail", getThreadName())); + log.warn(sm.getString("acceptor.stop.fail", getThreadName())); } } catch (InterruptedException e) { log.warn(sm.getString("acceptor.stop.interrupted", getThreadName()), e); @@ -195,13 +191,13 @@ /** - * Handles exceptions where a delay is required to prevent a Thread from - * entering a tight loop which will consume CPU and may also trigger large - * amounts of logging. For example, this can happen if the ulimit for open - * files is reached. + * Handles exceptions where a delay is required to prevent a Thread from entering a tight loop which will consume + * CPU and may also trigger large amounts of logging. For example, this can happen if the ulimit for open files is + * reached. * * @param currentErrorDelay The current delay being applied on failure - * @return The delay to apply on the next failure + * + * @return The delay to apply on the next failure */ protected int handleExceptionWithDelay(int currentErrorDelay) { // Don't delay on first exception @@ -226,6 +222,9 @@ public enum AcceptorState { - NEW, RUNNING, PAUSED, ENDED + NEW, + RUNNING, + PAUSED, + ENDED } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/ApplicationBufferHandler.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/ApplicationBufferHandler.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/ApplicationBufferHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/ApplicationBufferHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,8 +19,7 @@ import java.nio.ByteBuffer; /** - * Callback interface to be able to expand buffers when buffer overflow - * exceptions happen or to replace buffers + * Callback interface to be able to expand buffers when buffer overflow exceptions happen or to replace buffers */ public interface ApplicationBufferHandler { @@ -30,9 +29,11 @@ @Override public void expand(int newSize) { } + @Override public void setByteBuffer(ByteBuffer buffer) { } + @Override public ByteBuffer getByteBuffer() { return EMPTY_BUFFER; @@ -41,6 +42,7 @@ /** * Set the byte buffer. + * * @param buffer the byte buffer */ void setByteBuffer(ByteBuffer buffer); @@ -52,6 +54,7 @@ /** * Expand the byte buffer to at least the given size. Some implementations may not implement this. + * * @param size the desired size */ void expand(int size); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/Constants.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/Constants.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,24 +19,23 @@ public class Constants { /** - * Name of the system property containing - * the tomcat instance installation path + * Name of the system property containing the tomcat instance installation path */ public static final String CATALINA_BASE_PROP = "catalina.base"; /** * JSSE and OpenSSL protocol names */ - public static final String SSL_PROTO_ALL = "all"; - public static final String SSL_PROTO_TLS = "TLS"; - public static final String SSL_PROTO_TLSv1_3 = "TLSv1.3"; - public static final String SSL_PROTO_TLSv1_2 = "TLSv1.2"; - public static final String SSL_PROTO_TLSv1_1 = "TLSv1.1"; + public static final String SSL_PROTO_ALL = "all"; + public static final String SSL_PROTO_TLS = "TLS"; + public static final String SSL_PROTO_TLSv1_3 = "TLSv1.3"; + public static final String SSL_PROTO_TLSv1_2 = "TLSv1.2"; + public static final String SSL_PROTO_TLSv1_1 = "TLSv1.1"; // Two different forms for TLS 1.0 - public static final String SSL_PROTO_TLSv1_0 = "TLSv1.0"; - public static final String SSL_PROTO_TLSv1 = "TLSv1"; - public static final String SSL_PROTO_SSLv3 = "SSLv3"; - public static final String SSL_PROTO_SSLv2 = "SSLv2"; + public static final String SSL_PROTO_TLSv1_0 = "TLSv1.0"; + public static final String SSL_PROTO_TLSv1 = "TLSv1"; + public static final String SSL_PROTO_SSLv3 = "SSLv3"; + public static final String SSL_PROTO_SSLv2 = "SSLv2"; public static final String SSL_PROTO_SSLv2Hello = "SSLv2Hello"; public static final boolean IS_SECURITY_ENABLED = (System.getSecurityManager() != null); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/DispatchType.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/DispatchType.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/DispatchType.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/DispatchType.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,9 +17,8 @@ package org.apache.tomcat.util.net; /** - * This enumeration lists the different types of dispatches that request - * processing can trigger. In this instance, dispatch means re-process this - * request using the given socket status. + * This enumeration lists the different types of dispatches that request processing can trigger. In this instance, + * dispatch means re-process this request using the given socket status. */ public enum DispatchType { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/IPv6Utils.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/IPv6Utils.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/IPv6Utils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/IPv6Utils.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,9 +17,10 @@ package org.apache.tomcat.util.net; /** - *

    IPv6 utilities. - *

    For the moment, it only contains function to canonicalize IPv6 address - * into RFC 5952 form. + *

    + * IPv6 utilities. + *

    + * For the moment, it only contains function to canonicalize IPv6 address into RFC 5952 form. */ public class IPv6Utils { @@ -27,21 +28,25 @@ private static final int MAX_GROUP_LENGTH = 4; /** - *

    Convert IPv6 address into RFC 5952 form. - * E.g. 2001:db8:0:1:0:0:0:1 -> 2001:db8:0:1::1

    - * - *

    Method is null safe, and if IPv4 address or host name is passed to the - * method it is returned without any processing.

    - * - *

    Method also supports IPv4 in IPv6 (e.g. 0:0:0:0:0:ffff:192.0.2.1 -> - * ::ffff:192.0.2.1), and zone ID (e.g. fe80:0:0:0:f0f0:c0c0:1919:1234%4 - * -> fe80::f0f0:c0c0:1919:1234%4).

    - * - *

    The behaviour of this method is undefined if an invalid IPv6 address - * is passed in as input.

    + *

    + * Convert IPv6 address into RFC 5952 form. E.g. 2001:db8:0:1:0:0:0:1 -> 2001:db8:0:1::1 + *

    + *

    + * Method is null safe, and if IPv4 address or host name is passed to the method it is returned without any + * processing. + *

    + *

    + * Method also supports IPv4 in IPv6 (e.g. 0:0:0:0:0:ffff:192.0.2.1 -> ::ffff:192.0.2.1), and zone ID (e.g. + * fe80:0:0:0:f0f0:c0c0:1919:1234%4 -> fe80::f0f0:c0c0:1919:1234%4). + *

    + *

    + * The behaviour of this method is undefined if an invalid IPv6 address is passed in as input. + *

    * * @param ipv6Address String representing valid IPv6 address. + * * @return String representing IPv6 in canonical form. + * * @throws IllegalArgumentException if IPv6 format is unacceptable. */ public static String canonize(String ipv6Address) throws IllegalArgumentException { @@ -64,16 +69,12 @@ int lastColonsPos = ipv6Address.lastIndexOf("::"); if (lastColonsPos >= 0 && lastColonPos == lastColonsPos + 1) { /* - * IPv6 part ends with two consecutive colons, - * last colon is part of IPv6 format. - * e.g. ::127.0.0.1 + * IPv6 part ends with two consecutive colons, last colon is part of IPv6 format. e.g. ::127.0.0.1 */ ipv6AddressLength = lastColonPos + 1; } else { /* - * IPv6 part ends with only one colon, - * last colon is not part of IPv6 format. - * e.g. ::FFFF:127.0.0.1 + * IPv6 part ends with only one colon, last colon is not part of IPv6 format. e.g. ::FFFF:127.0.0.1 */ ipv6AddressLength = lastColonPos; } @@ -84,7 +85,7 @@ } StringBuilder result = new StringBuilder(); - char [][] groups = new char[MAX_NUMBER_OF_GROUPS][MAX_GROUP_LENGTH]; + char[][] groups = new char[MAX_NUMBER_OF_GROUPS][MAX_GROUP_LENGTH]; int groupCounter = 0; int charInGroupCounter = 0; @@ -92,7 +93,7 @@ int zeroGroupIndex = -1; int zeroGroupLength = 0; - // maximum length zero group, if there is more then one, then first one + // maximum length zero group, if there is more than one, then first one int maxZeroGroupIndex = -1; int maxZeroGroupLength = 0; @@ -100,8 +101,7 @@ boolean groupStart = true; /* - * Two consecutive colons, initial expansion. - * e.g. 2001:db8:0:0:1::1 -> 2001:db8:0:0:1:0:0:1 + * Two consecutive colons, initial expansion. e.g. 2001:db8:0:0:1::1 -> 2001:db8:0:0:1:0:0:1 */ StringBuilder expanded = new StringBuilder(ipv6Address); @@ -183,16 +183,15 @@ // Output results for (groupCounter = 0; groupCounter < numberOfGroups; groupCounter++) { - if (maxZeroGroupLength <= 1 || groupCounter < maxZeroGroupIndex - || groupCounter >= maxZeroGroupIndex + maxZeroGroupLength) { + if (maxZeroGroupLength <= 1 || groupCounter < maxZeroGroupIndex || + groupCounter >= maxZeroGroupIndex + maxZeroGroupLength) { for (int j = 0; j < MAX_GROUP_LENGTH; j++) { if (groups[groupCounter][j] != 0) { result.append(groups[groupCounter][j]); } } - if (groupCounter < (numberOfGroups - 1) - && (groupCounter != maxZeroGroupIndex - 1 - || maxZeroGroupLength <= 1)) { + if (groupCounter < (numberOfGroups - 1) && + (groupCounter != maxZeroGroupIndex - 1 || maxZeroGroupLength <= 1)) { result.append(':'); } } else if (groupCounter == maxZeroGroupIndex) { @@ -203,8 +202,8 @@ // Solve problem with three colons in IPv4 in IPv6 format // e.g. 0:0:0:0:0:0:127.0.0.1 -> :::127.0.0.1 -> ::127.0.0.1 int resultLength = result.length(); - if (result.charAt(resultLength - 1) == ':' && ipv6AddressLength < ipv6Address.length() - && ipv6Address.charAt(ipv6AddressLength) == ':') { + if (result.charAt(resultLength - 1) == ':' && ipv6AddressLength < ipv6Address.length() && + ipv6Address.charAt(ipv6AddressLength) == ':') { result.delete(resultLength - 1, resultLength); } @@ -222,6 +221,7 @@ * Heuristic check if string might be an IPv6 address. * * @param input Any string or null + * * @return true, if input string contains only hex digits and at least two colons, before '.' or '%' character */ static boolean mayBeIPv6Address(String input) { @@ -237,16 +237,12 @@ // IPv4 in IPv6 or Zone ID detected, end of checking. break; } - if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') - || (c >= 'A' && c <= 'F') || c == ':')) { + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == ':')) { return false; } else if (c == ':') { colonsCounter++; } } - if (colonsCounter < 2) { - return false; - } - return true; + return colonsCounter >= 2; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -73,6 +73,7 @@ endpoint.err.duplicateAccept=Duplicate socket accept detected. This is a known Linux kernel bug. The original connection has been processed normally and the duplicate has been ignored. The client should be unaffected. Updating the OS to a version that uses kernel 5.10 or later should fix the duplicate accept bug. endpoint.err.handshake=Handshake failed for client connection from IP address [{0}] and port [{1}] endpoint.err.unexpected=Unexpected error processing socket +endpoint.errorCreatingSSLContext=Error creating SSLContext endpoint.executor.fail=Executor rejected socket [{0}] for processing endpoint.getAttribute=[{0}] is [{1}] endpoint.init.bind=Socket bind failed: [{0}] [{1}] @@ -124,6 +125,7 @@ endpoint.tls.cert.noCerts=Certificate details not available as the certificate chain returned from the SSLContext was empty endpoint.tls.info=Connector [{0}], TLS virtual host [{1}], certificate type [{2}] configured from {3} with trust store [{4}] endpoint.tls.info.cert.keystore=keystore [{0}] using alias [{1}] +endpoint.tls.info.cert.keystore.direct=Set directly - location unknown endpoint.tls.info.cert.pem=key [{0}], certificate [{1}] and certificate chain [{2}] endpoint.unknownSslHostName=The SSL host name [{0}] is not recognised for this endpoint endpoint.warn.executorShutdown=The executor associated with thread pool [{0}] has not fully shutdown. Some application threads may still be running. @@ -151,10 +153,12 @@ sslHostConfig.certificate.notype=Multiple certificates were specified and at least one is missing the required attribute type sslHostConfig.certificateVerificationInvalid=The certificate verification value [{0}] is not recognised -sslHostConfig.certificateVerificationWithHttp2=The TLS virtual host [{0}] is configured for optional certificate verification and the enclosing connector is configured to support upgrade to h2. HTTP/2 over TLS does not permit optional certificate verification. sslHostConfig.fileNotFound=Configured file [{0}] does not exist +sslHostConfig.ignoreNonTls13Ciphersuite=The non-TLS 1.3 cipher suite [{0}] included in the TLS 1.3 cipher suite list will be ignored +sslHostConfig.ignoreTls13Ciphersuite=The TLS 1.3 cipher suite [{0}] included in the TLS 1.2 and below ciphers list will be ignored sslHostConfig.invalid_truststore_password=The provided trust store password could not be used to unlock and/or validate the trust store. Retrying to access the trust store with a null password which will skip validation. sslHostConfig.mismatch=The property [{0}] was set on the SSLHostConfig named [{1}] and is for the [{2}] configuration syntax but the SSLHostConfig is being used with the [{3}] configuration syntax +sslHostConfig.mismatch.trust=The trust configuration property [{0}] was set on the SSLHostConfig named [{1}] and is for the [{2}] configuration syntax but the SSLHostConfig is being used with the [{3}] trust configuration syntax sslHostConfig.opensslconf.alreadyset=Attempt to set another OpenSSLConf ignored sslHostConfig.opensslconf.null=Attempt to set null OpenSSLConf ignored sslHostConfig.prefix_missing=The protocol [{0}] was added to the list of protocols on the SSLHostConfig named [{1}]. Check if a +/- prefix is missing. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -73,6 +73,7 @@ endpoint.err.duplicateAccept=Le socket a été accpeté deux fois. Ceci est un bug connu du kernel Linux. La connection originelle a été traitée normalement et le doublon a été ignoré. Le client ne devrait pas être affecté. Mettre à jour l'OS vers une version qui utilise un kernel 5.10 ou plus récent devrait corriger le problème. endpoint.err.handshake=Echec de négociation endpoint.err.unexpected=Erreur inattendue lors du traitement du socket +endpoint.errorCreatingSSLContext=Erreur lors de la création du SSLContext endpoint.executor.fail=L''exécuteur a rejeté le traitement du socket [{0}] endpoint.getAttribute=[{0}] est [{1}] endpoint.init.bind=L''association du socket a échoué : [{0}] [{1}] @@ -151,7 +152,6 @@ sslHostConfig.certificate.notype=Plusieurs certificats ont été spécifiés et au moins un n'a pas d'attribut type sslHostConfig.certificateVerificationInvalid=La valeur de vérification de certificat [{0}] n''est pas reconnue -sslHostConfig.certificateVerificationWithHttp2=L''hôte virtuel TLS [{0}] est configuré avec la validation optionelle du certificat, et le connecteur qui l''utilise est configuré pour supporter l''upgrade vers h2. HTTP/2 sur TLS ne permet pas la validation optionelle du certificat. sslHostConfig.fileNotFound=Le fichier [{0}] configuré n''existe pas. sslHostConfig.invalid_truststore_password=Le mot de passe de la base de confiance n'a pas pu être utilisé pour déverrouiller et ou valider celle ci, nouvel essai en utilisant un mot de passe null pour passer la validation sslHostConfig.mismatch=La propriété [{0}] a été fixée sur le SSLHostConfig nommé [{1}] et est pour la syntaxe de configuration [{2}] mais le SSLHostConfig est utilisé avec la syntaxe de configuration [{3}] diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -73,10 +73,11 @@ endpoint.err.duplicateAccept=重複したソケット受け付けが検出されました。 これはLinuxカーネルの既知のバグです。 最初のコネクションは正常に処理され、重複受け付けは無視されました。 クライアントは影響を受けないはずです。Linuxカーネルをバージョン5.10以降に更新すると、重複受け付けのバグが修正されます。 endpoint.err.handshake=ハンドシェイク失敗 endpoint.err.unexpected=ソケット処理中の予期せぬエラー +endpoint.errorCreatingSSLContext=SSLContextの作成中にエラーが発生しました endpoint.executor.fail=エグゼキュータは処理するソケット [{0}] を拒否しました endpoint.getAttribute=[{0}] は [{1}] です endpoint.init.bind=ソケットバインドに失敗しました:[{0}] [{1}] -endpoint.init.bind.inherited=コネクタが1つを使用するように構成されている間、継承されたチャネルはありません。 +endpoint.init.bind.inherited=コネクタは継承されたチャネルを使用するように設定されていますが、継承すべきチャネルが存在しません endpoint.init.listen=ソケットの待ち受けを開始できません: [{0}] [{1}] endpoint.init.unixnotavail=Unixドメインソケットのサポートは利用できません endpoint.invalidJmxNameSslHost=ホスト [{0}] に関連付けられた SSLHostConfig に有効な JMX オブジェクト名を生成できません。 @@ -124,6 +125,7 @@ endpoint.tls.cert.noCerts=SSLContext から返された証明書チェーンが空だったため、証明書の詳細を利用できません endpoint.tls.info=トラストストア [{4}] を使用して {3} から構成されたコネクタ [{0}]、TLS 仮想ホスト [{1}]、および証明書タイプ [{2}] endpoint.tls.info.cert.keystore=エイリアス [{1}] を使用したキーストア [{0}] +endpoint.tls.info.cert.keystore.direct=直接設定 - 場所が不明 endpoint.tls.info.cert.pem=キー [{0}]、証明書 [{1}]、および証明書チェーン [{2}] endpoint.unknownSslHostName=SSL ホスト名 [{0}] はこのエンドポイントから認識されていません。 endpoint.warn.executorShutdown=スレッドプール [{0}] と関連付けられたエグゼキュータは完全に停止できませんでした。いくつかのアプリケーションスレッドはまだ動作し続けている可能性があります。 @@ -151,7 +153,6 @@ sslHostConfig.certificate.notype=指定された複数の証明書の中に、少なくとも1つは必須要素の存在しない証明書が含まれています。 sslHostConfig.certificateVerificationInvalid=証明書検証値[{0}]が認識されません -sslHostConfig.certificateVerificationWithHttp2=TLS仮想ホスト[{0}]はオプションの証明書検証用に構成されており、コネクタはh2へのアップグレードをサポートするように構成されています。 HTTP/2 over TLSでは、オプションの証明書検証は許可されていません。 sslHostConfig.fileNotFound=構成ファイル[{0}]は存在しません sslHostConfig.invalid_truststore_password=提供されたトラストストアパスワードは、トラストストアのロック解除および検証に使用できませんでした。 検証をスキップするnullパスワードでトラストストアにアクセスしようとしました。 sslHostConfig.mismatch=[{0}] プロパティは [{1}] という名前のSSLHostConfigで設定され、[{2}] 構成構文用ですが、[{3}] 構成構文でSSLHostConfigが使用されています diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings_ko.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_ko.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings_ko.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_ko.properties 2026-01-23 19:33:36.000000000 +0000 @@ -141,7 +141,6 @@ sslHostConfig.certificate.notype=여러 개의 인증서들이 지정되었는데, 적어도 하나의 인증서에 필수 속성 타입이 없습니다. sslHostConfig.certificateVerificationInvalid=인증서 검증 값 [{0}]은(는) 인식되지 않는 값입니다. -sslHostConfig.certificateVerificationWithHttp2=TLS 가상 호스트 [{0}](은)는 선택적 인증서 확인을 하도록 설정되었고, 내부에 정의된 커넥터는 HTTP/2로 업그레이드를 지원하도록 설정되었습니다. TLS 기반의 HTTP/2는 선택적 인증서 확인을 허용하지 않습니다. sslHostConfig.fileNotFound=설정된 파일 [{0}]이(가) 존재하지 않습니다. sslHostConfig.invalid_truststore_password=Trust 저장소를 잠금을 풀거나 유효한지 확인하는 용도로, 제공된 Trust 저장소 비밀번호를 사용할 수 없었습니다. 널 비밀번호를 사용하여, 해당 Trust 저장소에 대한 접근을 다시 시도합니다. 이는 유효한지 확인하는 작업을 건너뛸 것입니다. sslHostConfig.mismatch=[{1}](이)라는 이름의 SSLHostConfig에 프로퍼티 [{0}]이(가) 설정되었는데, 이 프로퍼티는 [{2}] 설정 문법을 위한 것이나, 해당 SSLHostConfig은 [{3}] 설정 문법으로 사용되고 있습니다. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -143,7 +143,6 @@ sslHostConfig.certificate.notype=指定了多个证书,并且至少有一个证书缺少必需的属性类型 sslHostConfig.certificateVerificationInvalid=证书认证值[{0}]未识别 -sslHostConfig.certificateVerificationWithHttp2=TLS虚拟主机[{0}]被配置为可选的证书验证,包围的连接器被配置为支持升级到h2。HTTP/2 over TLS不允许可选的证书验证。 sslHostConfig.fileNotFound=配置文件 [{0}] 不存在 sslHostConfig.invalid_truststore_password=提供的信任存储密码无法用于解锁和/或验证信任存储。正在重试使用空密码访问信任存储,该密码将跳过验证。 sslHostConfig.mismatch=属性[{0}]是在名为[{1}]的SSLHostConfig 上设置的,用于[{2}]配置语法,但SSLHostConfig 正与[{3}]配置语法一起使用 diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/Nio2Channel.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/Nio2Channel.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/Nio2Channel.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/Nio2Channel.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,9 +28,8 @@ import java.util.concurrent.TimeoutException; /** - * Base class for a SocketChannel wrapper used by the endpoint. - * This way, logic for an SSL socket channel remains the same as for - * a non SSL, making sure we don't need to code for any exception cases. + * Base class for a SocketChannel wrapper used by the endpoint. This way, logic for an SSL socket channel remains the + * same as for a non SSL, making sure we don't need to code for any exception cases. */ public class Nio2Channel implements AsynchronousByteChannel { @@ -47,7 +46,7 @@ /** * Reset the channel. * - * @param channel The new async channel to associate with this NIO2 channel + * @param channel The new async channel to associate with this NIO2 channel * @param socketWrapper The new socket to associate with this NIO2 channel * * @throws IOException If a problem was encountered resetting the channel @@ -123,8 +122,7 @@ } /** - * Performs SSL handshake hence is a no-op for the non-secure - * implementation. + * Performs SSL handshake hence is a no-op for the non-secure implementation. * * @return Always returns zero * @@ -145,20 +143,17 @@ } @Override - public void read(ByteBuffer dst, A attachment, - CompletionHandler handler) { + public void read(ByteBuffer dst, A attachment, CompletionHandler handler) { read(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler); } - public void read(ByteBuffer dst, - long timeout, TimeUnit unit, A attachment, - CompletionHandler handler) { + public void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, + CompletionHandler handler) { sc.read(dst, timeout, unit, attachment, handler); } - public void read(ByteBuffer[] dsts, - int offset, int length, long timeout, TimeUnit unit, - A attachment, CompletionHandler handler) { + public void read(ByteBuffer[] dsts, int offset, int length, long timeout, TimeUnit unit, A attachment, + CompletionHandler handler) { sc.read(dsts, offset, length, timeout, unit, attachment, handler); } @@ -168,18 +163,16 @@ } @Override - public void write(ByteBuffer src, A attachment, - CompletionHandler handler) { + public void write(ByteBuffer src, A attachment, CompletionHandler handler) { write(src, 0L, TimeUnit.MILLISECONDS, attachment, handler); } public void write(ByteBuffer src, long timeout, TimeUnit unit, A attachment, - CompletionHandler handler) { + CompletionHandler handler) { sc.write(src, timeout, unit, attachment, handler); } - public void write(ByteBuffer[] srcs, int offset, int length, - long timeout, TimeUnit unit, A attachment, + public void write(ByteBuffer[] srcs, int offset, int length, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) { sc.write(srcs, offset, length, timeout, unit, attachment, handler); } @@ -189,23 +182,25 @@ public boolean cancel(boolean mayInterruptIfRunning) { return false; } + @Override public boolean isCancelled() { return false; } + @Override public boolean isDone() { return true; } + @Override - public Boolean get() throws InterruptedException, - ExecutionException { + public Boolean get() throws InterruptedException, ExecutionException { return Boolean.TRUE; } + @Override public Boolean get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, - TimeoutException { + throws InterruptedException, ExecutionException, TimeoutException { return Boolean.TRUE; } }; @@ -215,9 +210,11 @@ } private ApplicationBufferHandler appReadBufHandler; + public void setAppReadBufHandler(ApplicationBufferHandler handler) { this.appReadBufHandler = handler; } + protected ApplicationBufferHandler getAppReadBufHandler() { return appReadBufHandler; } @@ -227,23 +224,25 @@ public boolean cancel(boolean mayInterruptIfRunning) { return false; } + @Override public boolean isCancelled() { return false; } + @Override public boolean isDone() { return true; } + @Override - public Integer get() throws InterruptedException, - ExecutionException { + public Integer get() throws InterruptedException, ExecutionException { return Integer.valueOf(-1); } + @Override public Integer get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, - TimeoutException { + throws InterruptedException, ExecutionException, TimeoutException { return Integer.valueOf(-1); } }; @@ -252,54 +251,63 @@ @Override public void close() throws IOException { } + @Override public boolean isOpen() { return false; } + @Override public void reset(AsynchronousSocketChannel channel, SocketWrapperBase socket) throws IOException { } + @Override public void free() { } + @Override protected ApplicationBufferHandler getAppReadBufHandler() { return ApplicationBufferHandler.EMPTY; } + @Override public void setAppReadBufHandler(ApplicationBufferHandler handler) { } + @Override public Future read(ByteBuffer dst) { return DONE_INT; } + @Override - public void read(ByteBuffer dst, - long timeout, TimeUnit unit, A attachment, - CompletionHandler handler) { + public void read(ByteBuffer dst, long timeout, TimeUnit unit, A attachment, + CompletionHandler handler) { handler.failed(new ClosedChannelException(), attachment); } + @Override - public void read(ByteBuffer[] dsts, - int offset, int length, long timeout, TimeUnit unit, - A attachment, CompletionHandler handler) { + public void read(ByteBuffer[] dsts, int offset, int length, long timeout, TimeUnit unit, A attachment, + CompletionHandler handler) { handler.failed(new ClosedChannelException(), attachment); } + @Override public Future write(ByteBuffer src) { return DONE_INT; } + @Override public void write(ByteBuffer src, long timeout, TimeUnit unit, A attachment, - CompletionHandler handler) { + CompletionHandler handler) { handler.failed(new ClosedChannelException(), attachment); } + @Override - public void write(ByteBuffer[] srcs, int offset, int length, - long timeout, TimeUnit unit, A attachment, + public void write(ByteBuffer[] srcs, int offset, int length, long timeout, TimeUnit unit, A attachment, CompletionHandler handler) { handler.failed(new ClosedChannelException(), attachment); } + @Override public String toString() { return "Closed Nio2Channel"; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/Nio2Endpoint.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/Nio2Endpoint.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/Nio2Endpoint.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/Nio2Endpoint.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,6 +31,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.NetworkChannel; import java.nio.file.StandardOpenOption; +import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -73,7 +74,7 @@ /** * Allows detecting if a completion handler completes inline. */ - private static ThreadLocal inlineCompletion = new ThreadLocal<>(); + private static final ThreadLocal inlineCompletion = new ThreadLocal<>(); /** * Thread group associated with the server socket. @@ -149,14 +150,12 @@ paused = false; if (socketProperties.getProcessorCache() != 0) { - processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, - socketProperties.getProcessorCache()); + processorCache = + new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, socketProperties.getProcessorCache()); } - int actualBufferPool = - socketProperties.getActualBufferPool(isSSLEnabled() ? getSniParseLimit() * 2 : 0); + int actualBufferPool = socketProperties.getActualBufferPool(isSSLEnabled() ? getSniParseLimit() * 2 : 0); if (actualBufferPool != 0) { - nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, - actualBufferPool); + nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, actualBufferPool); } // Create worker collection if (getExecutor() == null) { @@ -281,7 +280,7 @@ } threadGroup = null; } - // Mostly to cleanup references + // Mostly to clean up references super.shutdownExecutor(); } @@ -290,10 +289,11 @@ /** * Process the specified connection. + * * @param socket The socket channel - * @return true if the socket was correctly configured - * and processing may continue, false if the socket needs to be - * close immediately + * + * @return true if the socket was correctly configured and processing may continue, false + * if the socket needs to be close immediately */ @Override protected boolean setSocketOptions(AsynchronousSocketChannel socket) { @@ -305,10 +305,8 @@ channel = nioChannels.pop(); } if (channel == null) { - SocketBufferHandler bufhandler = new SocketBufferHandler( - socketProperties.getAppReadBufSize(), - socketProperties.getAppWriteBufSize(), - socketProperties.getDirectBuffer()); + SocketBufferHandler bufhandler = new SocketBufferHandler(socketProperties.getAppReadBufSize(), + socketProperties.getAppWriteBufSize(), socketProperties.getDirectBuffer()); if (isSSLEnabled()) { channel = new SecureNio2Channel(bufhandler, this); } else { @@ -397,18 +395,18 @@ @Override - protected SocketProcessorBase createSocketProcessor( - SocketWrapperBase socketWrapper, SocketEvent event) { + protected SocketProcessorBase createSocketProcessor(SocketWrapperBase socketWrapper, + SocketEvent event) { return new SocketProcessor(socketWrapper, event); } protected class Nio2Acceptor extends Acceptor - implements CompletionHandler { + implements CompletionHandler { protected int errorDelay = 0; - public Nio2Acceptor(AbstractEndpoint endpoint) { + public Nio2Acceptor(AbstractEndpoint endpoint) { super(endpoint); } @@ -416,7 +414,7 @@ public void run() { // The initial accept will be called in a separate utility thread if (!isPaused()) { - //if we have reached max connections, wait + // if we have reached max connections, wait try { countUpOrAwaitConnection(); } catch (InterruptedException e) { @@ -438,7 +436,6 @@ * Signals the Acceptor to stop. * * @param waitMilliseconds Ignored for NIO2. - * */ @Override public void stopMillis(int waitMilliseconds) { @@ -446,8 +443,7 @@ } @Override - public void completed(AsynchronousSocketChannel socket, - Void attachment) { + public void completed(AsynchronousSocketChannel socket, Void attachment) { // Successful accept, reset the error delay errorDelay = 0; // Continue processing the socket on the current thread @@ -511,17 +507,16 @@ private SendfileData sendfileData = null; - private final CompletionHandler readCompletionHandler; + private final CompletionHandler readCompletionHandler; private boolean readInterest = false; // Guarded by readCompletionHandler private boolean readNotify = false; - private final CompletionHandler writeCompletionHandler; - private final CompletionHandler gatheringWriteCompletionHandler; + private final CompletionHandler writeCompletionHandler; + private final CompletionHandler gatheringWriteCompletionHandler; private boolean writeInterest = false; // Guarded by writeCompletionHandler private boolean writeNotify = false; - private CompletionHandler sendfileHandler - = new CompletionHandler<>() { + private final CompletionHandler sendfileHandler = new CompletionHandler<>() { @Override public void completed(Integer nWrite, SendfileData attachment) { @@ -544,27 +539,27 @@ attachment.doneInline = true; } else { switch (attachment.keepAliveState) { - case NONE: { - getEndpoint().processSocket(Nio2SocketWrapper.this, - SocketEvent.DISCONNECT, false); - break; - } - case PIPELINED: { - if (!getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.OPEN_READ, true)) { - close(); + case NONE: { + getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.DISCONNECT, false); + break; + } + case PIPELINED: { + if (!getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.OPEN_READ, + true)) { + close(); + } + break; + } + case OPEN: { + registerReadInterest(); + break; } - break; - } - case OPEN: { - registerReadInterest(); - break; - } } } return; } else { getSocket().getBufHandler().configureWriteBufferForWrite(); - int nRead = -1; + int nRead; try { nRead = attachment.fchannel.read(buffer); } catch (IOException e) { @@ -613,7 +608,7 @@ if (log.isTraceEnabled()) { log.trace("Socket: [" + Nio2SocketWrapper.this + "], Interest: [" + readInterest + "]"); } - boolean notify = false; + boolean notify; synchronized (readCompletionHandler) { readNotify = false; if (nBytes.intValue() < 0) { @@ -634,6 +629,7 @@ getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.OPEN_READ, false); } } + @Override public void failed(Throwable exc, ByteBuffer attachment) { IOException ioe; @@ -666,13 +662,12 @@ } else if (!nonBlockingWriteBuffer.isEmpty()) { // Continue writing data using a gathering write ByteBuffer[] array = nonBlockingWriteBuffer.toArray(attachment); - getSocket().write(array, 0, array.length, - toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS, - array, gatheringWriteCompletionHandler); + getSocket().write(array, 0, array.length, toTimeout(getWriteTimeout()), + TimeUnit.MILLISECONDS, array, gatheringWriteCompletionHandler); } else if (attachment.hasRemaining()) { // Regular write - getSocket().write(attachment, toTimeout(getWriteTimeout()), - TimeUnit.MILLISECONDS, attachment, writeCompletionHandler); + getSocket().write(attachment, toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS, + attachment, writeCompletionHandler); } else { // All data has been written if (writeInterest && !isInline()) { @@ -693,6 +688,7 @@ } } } + @Override public void failed(Throwable exc, ByteBuffer attachment) { IOException ioe; @@ -717,12 +713,12 @@ writeNotify = false; if (nBytes.longValue() < 0) { failed(new EOFException(sm.getString("iob.failedwrite")), attachment); - } else if (!nonBlockingWriteBuffer.isEmpty() || buffersArrayHasRemaining(attachment, 0, attachment.length)) { + } else if (!nonBlockingWriteBuffer.isEmpty() || + buffersArrayHasRemaining(attachment, 0, attachment.length)) { // Continue writing data using a gathering write ByteBuffer[] array = nonBlockingWriteBuffer.toArray(attachment); - getSocket().write(array, 0, array.length, - toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS, - array, gatheringWriteCompletionHandler); + getSocket().write(array, 0, array.length, toTimeout(getWriteTimeout()), + TimeUnit.MILLISECONDS, array, gatheringWriteCompletionHandler); } else { // All data has been written if (writeInterest && !isInline()) { @@ -743,6 +739,7 @@ } } } + @Override public void failed(Throwable exc, ByteBuffer[] attachment) { IOException ioe; @@ -756,14 +753,19 @@ if (!endpoint.processSocket(Nio2SocketWrapper.this, SocketEvent.ERROR, true)) { close(); } - } + } }; } - public void setSendfileData(SendfileData sf) { this.sendfileData = sf; } - public SendfileData getSendfileData() { return this.sendfileData; } + public void setSendfileData(SendfileData sf) { + this.sendfileData = sf; + } + + public SendfileData getSendfileData() { + return this.sendfileData; + } @Override public boolean isReadyForRead() throws IOException { @@ -831,7 +833,7 @@ throw new IOException(sm.getString("socket.closed")); } - boolean notify = false; + boolean notify; synchronized (readCompletionHandler) { notify = readNotify; } @@ -859,10 +861,10 @@ synchronized (readCompletionHandler) { readNotify = false; } - // This may be sufficient to complete the request and we + // This may be sufficient to complete the request, and we // don't want to trigger another read since if there is no // more data to read and this request takes a while to - // process the read will timeout triggering an error. + // process the read will time out triggering an error. readPending.release(); return nRead; } @@ -895,7 +897,7 @@ throw new IOException(sm.getString("socket.closed")); } - boolean notify = false; + boolean notify; synchronized (readCompletionHandler) { notify = readNotify; } @@ -923,10 +925,10 @@ synchronized (readCompletionHandler) { readNotify = false; } - // This may be sufficient to complete the request and we + // This may be sufficient to complete the request, and we // don't want to trigger another read since if there is no // more data to read and this request takes a while to - // process the read will timeout triggering an error. + // process the read will time out triggering an error. readPending.release(); return nRead; } @@ -970,6 +972,7 @@ getSocket().close(true); } if (getEndpoint().running) { + getSocket().reset(null, null); if (nioChannels == null || !nioChannels.push(getSocket())) { getSocket().free(); } @@ -1013,22 +1016,21 @@ } @Override - protected OperationState newOperationState(boolean read, - ByteBuffer[] buffers, int offset, int length, - BlockingMode block, long timeout, TimeUnit unit, A attachment, - CompletionCheck check, CompletionHandler handler, - Semaphore semaphore, VectoredIOCompletionHandler completion) { - return new Nio2OperationState<>(read, buffers, offset, length, block, - timeout, unit, attachment, check, handler, semaphore, completion); + protected OperationState newOperationState(boolean read, ByteBuffer[] buffers, int offset, int length, + BlockingMode block, long timeout, TimeUnit unit, A attachment, CompletionCheck check, + CompletionHandler handler, Semaphore semaphore, + VectoredIOCompletionHandler completion) { + return new Nio2OperationState<>(read, buffers, offset, length, block, timeout, unit, attachment, check, + handler, semaphore, completion); } private class Nio2OperationState extends OperationState { - private Nio2OperationState(boolean read, ByteBuffer[] buffers, int offset, int length, - BlockingMode block, long timeout, TimeUnit unit, A attachment, - CompletionCheck check, CompletionHandler handler, - Semaphore semaphore, VectoredIOCompletionHandler completion) { - super(read, buffers, offset, length, block, - timeout, unit, attachment, check, handler, semaphore, completion); + private Nio2OperationState(boolean read, ByteBuffer[] buffers, int offset, int length, BlockingMode block, + long timeout, TimeUnit unit, A attachment, CompletionCheck check, + CompletionHandler handler, Semaphore semaphore, + VectoredIOCompletionHandler completion) { + super(read, buffers, offset, length, block, timeout, unit, attachment, check, handler, semaphore, + completion); } @Override @@ -1083,20 +1085,22 @@ socketBufferHandler.configureWriteBufferForRead(); ByteBuffer[] array = nonBlockingWriteBuffer.toArray(socketBufferHandler.getWriteBuffer()); if (buffersArrayHasRemaining(array, 0, array.length)) { - getSocket().write(array, 0, array.length, timeout, unit, - array, new CompletionHandler() { + getSocket().write(array, 0, array.length, timeout, unit, array, + new CompletionHandler<>() { @Override public void completed(Long nBytes, ByteBuffer[] buffers) { if (nBytes.longValue() < 0) { failed(new EOFException(), null); } else if (buffersArrayHasRemaining(buffers, 0, buffers.length)) { - getSocket().write(buffers, 0, buffers.length, toTimeout(getWriteTimeout()), - TimeUnit.MILLISECONDS, buffers, this); + getSocket().write(buffers, 0, buffers.length, + toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS, + buffers, this); } else { // Continue until everything is written process(); } } + @Override public void failed(Throwable exc, ByteBuffer[] buffers) { completion.failed(exc, Nio2OperationState.this); @@ -1111,12 +1115,12 @@ } } - /* Callers of this method must: - * - have acquired the readPending semaphore - * - have acquired a lock on readCompletionHandler + /* + * Callers of this method must: - have acquired the readPending semaphore - have acquired a lock on + * readCompletionHandler * - * This method will release (or arrange for the release of) the - * readPending semaphore once the read has completed. + * This method will release (or arrange for the release of) the readPending semaphore once the read has + * completed. */ private int fillReadBuffer(boolean block) throws IOException { socketBufferHandler.configureReadBufferForWrite(); @@ -1153,8 +1157,7 @@ } } else { startInline(); - getSocket().read(to, toTimeout(getReadTimeout()), TimeUnit.MILLISECONDS, to, - readCompletionHandler); + getSocket().read(to, toTimeout(getReadTimeout()), TimeUnit.MILLISECONDS, to, readCompletionHandler); endInline(); if (readPending.availablePermits() == 1) { nRead = to.position(); @@ -1167,17 +1170,16 @@ /** * {@inheritDoc} *

    - * Overridden for NIO2 to enable a gathering write to be used to write - * all of the remaining data in a single additional write should a - * non-blocking write leave data in the buffer. + * Overridden for NIO2 to enable a gathering write to be used to write all of the remaining data in a single + * additional write should a non-blocking write leave data in the buffer. */ @Override protected void writeNonBlocking(byte[] buf, int off, int len) throws IOException { // Note: Possible alternate behavior: - // If there's non blocking abuse (like a test writing 1MB in a single - // "non blocking" write), then block until the previous write is + // If there's non-blocking abuse (like a test writing 1MB in a single + // "non-blocking" write), then block until the previous write is // done rather than continue buffering - // Also allows doing autoblocking + // Also allows doing auto blocking // Could be "smart" with coordination with the main CoyoteOutputStream to // indicate the end of a write // Uses: if (writePending.tryAcquire(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS)) @@ -1205,9 +1207,8 @@ /** * {@inheritDoc} *

    - * Overridden for NIO2 to enable a gathering write to be used to write - * all of the remaining data in a single additional write should a - * non-blocking write leave data in the buffer. + * Overridden for NIO2 to enable a gathering write to be used to write all of the remaining data in a single + * additional write should a non-blocking write leave data in the buffer. */ @Override protected void writeNonBlocking(ByteBuffer from) throws IOException { @@ -1218,17 +1219,16 @@ /** * {@inheritDoc} *

    - * Overridden for NIO2 to enable a gathering write to be used to write - * all of the remaining data in a single additional write should a - * non-blocking write leave data in the buffer. + * Overridden for NIO2 to enable a gathering write to be used to write all of the remaining data in a single + * additional write should a non-blocking write leave data in the buffer. */ @Override protected void writeNonBlockingInternal(ByteBuffer from) throws IOException { // Note: Possible alternate behavior: - // If there's non blocking abuse (like a test writing 1MB in a single - // "non blocking" write), then block until the previous write is + // If there's non-blocking abuse (like a test writing 1MB in a single + // "non-blocking" write), then block until the previous write is // done rather than continue buffering - // Also allows doing autoblocking + // Also allows doing auto blocking // Could be "smart" with coordination with the main CoyoteOutputStream to // indicate the end of a write // Uses: if (writePending.tryAcquire(socketWrapper.getTimeout(), TimeUnit.MILLISECONDS)) @@ -1252,8 +1252,7 @@ /** - * @param block Ignored since this method is only called in the - * blocking case + * @param block Ignored since this method is only called in the blocking case */ @Override protected void doWrite(boolean block, ByteBuffer from) throws IOException { @@ -1291,8 +1290,7 @@ protected void flushBlocking() throws IOException { checkError(); - // Before doing a blocking flush, make sure that any pending non - // blocking write has completed. + // Before doing a blocking flush, make sure that any pending non-blocking write has completed. try { if (writePending.tryAcquire(toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS)) { writePending.release(); @@ -1321,15 +1319,14 @@ if (!nonBlockingWriteBuffer.isEmpty()) { ByteBuffer[] array = nonBlockingWriteBuffer.toArray(socketBufferHandler.getWriteBuffer()); startInline(); - getSocket().write(array, 0, array.length, toTimeout(getWriteTimeout()), - TimeUnit.MILLISECONDS, array, gatheringWriteCompletionHandler); + getSocket().write(array, 0, array.length, toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS, + array, gatheringWriteCompletionHandler); endInline(); } else if (socketBufferHandler.getWriteBuffer().hasRemaining()) { // Regular write startInline(); getSocket().write(socketBufferHandler.getWriteBuffer(), toTimeout(getWriteTimeout()), - TimeUnit.MILLISECONDS, socketBufferHandler.getWriteBuffer(), - writeCompletionHandler); + TimeUnit.MILLISECONDS, socketBufferHandler.getWriteBuffer(), writeCompletionHandler); endInline(); } else { // Nothing was written @@ -1347,8 +1344,7 @@ @Override public boolean hasDataToRead() { synchronized (readCompletionHandler) { - return !socketBufferHandler.isReadBufferEmpty() - || readNotify || getError() != null; + return !socketBufferHandler.isReadBufferEmpty() || readNotify || getError() != null; } } @@ -1356,8 +1352,8 @@ @Override public boolean hasDataToWrite() { synchronized (writeCompletionHandler) { - return !socketBufferHandler.isWriteBufferEmpty() || !nonBlockingWriteBuffer.isEmpty() - || writeNotify || writePending.availablePermits() == 0 || getError() != null; + return !socketBufferHandler.isWriteBufferEmpty() || !nonBlockingWriteBuffer.isEmpty() || writeNotify || + writePending.availablePermits() == 0 || getError() != null; } } @@ -1450,7 +1446,7 @@ } getSocket().getBufHandler().configureWriteBufferForWrite(); ByteBuffer buffer = getSocket().getBufHandler().getWriteBuffer(); - int nRead = -1; + int nRead; try { nRead = data.fchannel.read(buffer); } catch (IOException e1) { @@ -1461,8 +1457,7 @@ data.length -= nRead; getSocket().getBufHandler().configureWriteBufferForRead(); startInline(); - getSocket().write(buffer, toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS, - data, sendfileHandler); + getSocket().write(buffer, toTimeout(getWriteTimeout()), TimeUnit.MILLISECONDS, data, sendfileHandler); endInline(); if (data.doneInline) { if (data.error) { @@ -1633,8 +1628,7 @@ // ---------------------------------------------- SocketProcessor Inner Class /** - * This class is the equivalent of the Worker, but will simply use in an - * external Executor thread pool. + * This class is the equivalent of the Worker, but will simply use in an external Executor thread pool. */ protected class SocketProcessor extends SocketProcessorBase { @@ -1646,7 +1640,7 @@ protected void doRun() { boolean launch = false; try { - int handshake = -1; + int handshake; try { if (socketWrapper.getSocket().isHandshakeComplete()) { @@ -1672,25 +1666,22 @@ } catch (IOException x) { handshake = -1; if (logHandshake.isDebugEnabled()) { - logHandshake.debug(sm.getString("endpoint.err.handshake", - socketWrapper.getRemoteAddr(), Integer.toString(socketWrapper.getRemotePort())), x); + logHandshake.debug(sm.getString("endpoint.err.handshake", socketWrapper.getRemoteAddr(), + Integer.toString(socketWrapper.getRemotePort())), x); } } if (handshake == 0) { - SocketState state = SocketState.OPEN; + SocketState state; // Process the request from this socket - if (event == null) { - state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ); - } else { - state = getHandler().process(socketWrapper, event); - } + state = getHandler().process(socketWrapper, + Objects.requireNonNullElse(event, SocketEvent.OPEN_READ)); if (state == SocketState.CLOSED) { // Close socket and pool socketWrapper.close(); } else if (state == SocketState.UPGRADING) { launch = true; } - } else if (handshake == -1 ) { + } else if (handshake == -1) { getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL); socketWrapper.close(); } @@ -1699,7 +1690,7 @@ } catch (Throwable t) { log.error(sm.getString("endpoint.processing.fail"), t); if (socketWrapper != null) { - ((Nio2SocketWrapper) socketWrapper).close(); + socketWrapper.close(); } } finally { if (launch) { @@ -1707,14 +1698,13 @@ getExecutor().execute(new SocketProcessor(socketWrapper, SocketEvent.OPEN_READ)); } catch (NullPointerException npe) { if (running) { - log.error(sm.getString("endpoint.launch.fail"), - npe); + log.error(sm.getString("endpoint.launch.fail"), npe); } } } socketWrapper = null; event = null; - //return to cache + // return to cache if (running && processorCache != null) { processorCache.push(this); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/NioChannel.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/NioChannel.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/NioChannel.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/NioChannel.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,9 +28,8 @@ import org.apache.tomcat.util.res.StringManager; /** - * Base class for a SocketChannel wrapper used by the endpoint. - * This way, logic for an SSL socket channel remains the same as for - * a non SSL, making sure we don't need to code for any exception cases. + * Base class for a SocketChannel wrapper used by the endpoint. This way, logic for an SSL socket channel remains the + * same as for a non SSL, making sure we don't need to code for any exception cases. */ public class NioChannel implements ByteChannel, ScatteringByteChannel, GatheringByteChannel { @@ -49,12 +48,17 @@ /** * Reset the channel * - * @param channel the socket channel + * @param channel the socket channel * @param socketWrapper the socket wrapper + * * @throws IOException If a problem was encountered resetting the channel */ public void reset(SocketChannel channel, NioSocketWrapper socketWrapper) throws IOException { - this.sc = channel; + // Don't reset socket on null as it can lead to NPEs + if (channel != null) { + this.sc = channel; + } + // Resetting socketWrapper is possible this.socketWrapper = socketWrapper; bufHandler.reset(); } @@ -87,6 +91,7 @@ * Close the connection. * * @param force Should the underlying socket be forcibly closed? + * * @throws IOException If closing the secure channel fails. */ public void close(boolean force) throws IOException { @@ -109,7 +114,9 @@ * Writes a sequence of bytes to this channel from the given buffer. * * @param src The buffer from which bytes are to be retrieved + * * @return The number of bytes written, possibly zero + * * @throws IOException If some other I/O error occurs */ @Override @@ -128,8 +135,7 @@ } @Override - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException { + public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { checkInterruptStatus(); return sc.write(srcs, offset, length); } @@ -138,8 +144,9 @@ * Reads a sequence of bytes from this channel into the given buffer. * * @param dst The buffer into which bytes are to be transferred - * @return The number of bytes read, possibly zero, or -1 if - * the channel has reached end-of-stream + * + * @return The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream + * * @throws IOException If some other I/O error occurs */ @Override @@ -153,8 +160,7 @@ } @Override - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException { + public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { return sc.read(dsts, offset, length); } @@ -175,12 +181,13 @@ } /** - * Performs SSL handshake hence is a no-op for the non-secure - * implementation. + * Performs SSL handshake hence is a no-op for the non-secure implementation. * * @param read Unused in non-secure implementation * @param write Unused in non-secure implementation + * * @return Always returns zero + * * @throws IOException Never for non-secure channel */ public int handshake(boolean read, boolean write) throws IOException { @@ -200,6 +207,7 @@ * Return true if the buffer wrote data. NO-OP for non-secure channel. * * @return Always returns {@code false} for non-secure channel + * * @throws IOException Never for non-secure channel */ public boolean flushOutbound() throws IOException { @@ -207,14 +215,10 @@ } /** - * This method should be used to check the interrupt status before - * attempting a write. - * - * If a thread has been interrupted and the interrupt has not been cleared - * then an attempt to write to the socket will fail. When this happens the - * socket is removed from the poller without the socket being selected. This - * results in a connection limit leak for NIO as the endpoint expects the - * socket to be selected even in error conditions. + * This method should be used to check the interrupt status before attempting a write. If a thread has been + * interrupted and the interrupt has not been cleared then an attempt to write to the socket will fail. When this + * happens the socket is removed from the poller without the socket being selected. This results in a connection + * limit leak for NIO as the endpoint expects the socket to be selected even in error conditions. * * @throws IOException If the current thread was interrupted */ @@ -225,9 +229,11 @@ } private ApplicationBufferHandler appReadBufHandler; + public void setAppReadBufHandler(ApplicationBufferHandler handler) { this.appReadBufHandler = handler; } + protected ApplicationBufferHandler getAppReadBufHandler() { return appReadBufHandler; } @@ -236,42 +242,50 @@ @Override public void close() throws IOException { } + @Override public boolean isOpen() { return false; } + @Override public void reset(SocketChannel channel, NioSocketWrapper socketWrapper) throws IOException { } + @Override public void free() { } + @Override protected ApplicationBufferHandler getAppReadBufHandler() { return ApplicationBufferHandler.EMPTY; } + @Override public void setAppReadBufHandler(ApplicationBufferHandler handler) { } + @Override public int read(ByteBuffer dst) throws IOException { return -1; } + @Override - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException { + public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { return -1L; } + @Override public int write(ByteBuffer src) throws IOException { checkInterruptStatus(); throw new ClosedChannelException(); } + @Override - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException { + public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { throw new ClosedChannelException(); } + @Override public String toString() { return "Closed NioChannel"; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/NioEndpoint.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/NioEndpoint.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/NioEndpoint.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/NioEndpoint.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,6 +44,7 @@ import java.nio.file.attribute.PosixFilePermissions; import java.util.ConcurrentModificationException; import java.util.Iterator; +import java.util.Objects; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; @@ -65,8 +66,6 @@ /** * NIO endpoint. - * - * @author Mladen Turk */ public class NioEndpoint extends AbstractJsseEndpoint { @@ -79,7 +78,7 @@ private static final Log logHandshake = LogFactory.getLog(NioEndpoint.class.getName() + ".handshake"); - public static final int OP_REGISTER = 0x100; //register interest op + public static final int OP_REGISTER = 0x100; // register interest op // ----------------------------------------------------------------- Fields @@ -113,15 +112,25 @@ * Use System.inheritableChannel to obtain channel from stdin/stdout. */ private boolean useInheritedChannel = false; - public void setUseInheritedChannel(boolean useInheritedChannel) { this.useInheritedChannel = useInheritedChannel; } - public boolean getUseInheritedChannel() { return useInheritedChannel; } + + public void setUseInheritedChannel(boolean useInheritedChannel) { + this.useInheritedChannel = useInheritedChannel; + } + + public boolean getUseInheritedChannel() { + return useInheritedChannel; + } /** * Path for the Unix domain socket, used to create the socket address. */ private String unixDomainSocketPath = null; - public String getUnixDomainSocketPath() { return this.unixDomainSocketPath; } + + public String getUnixDomainSocketPath() { + return this.unixDomainSocketPath; + } + public void setUnixDomainSocketPath(String unixDomainSocketPath) { this.unixDomainSocketPath = unixDomainSocketPath; } @@ -131,7 +140,11 @@ * Permissions which will be set on the Unix domain socket if it is created. */ private String unixDomainSocketPathPermissions = null; - public String getUnixDomainSocketPathPermissions() { return this.unixDomainSocketPathPermissions; } + + public String getUnixDomainSocketPathPermissions() { + return this.unixDomainSocketPathPermissions; + } + public void setUnixDomainSocketPathPermissions(String unixDomainSocketPathPermissions) { this.unixDomainSocketPathPermissions = unixDomainSocketPathPermissions; } @@ -141,13 +154,25 @@ * Priority of the poller thread. */ private int pollerThreadPriority = Thread.NORM_PRIORITY; - public void setPollerThreadPriority(int pollerThreadPriority) { this.pollerThreadPriority = pollerThreadPriority; } - public int getPollerThreadPriority() { return pollerThreadPriority; } + + public void setPollerThreadPriority(int pollerThreadPriority) { + this.pollerThreadPriority = pollerThreadPriority; + } + + public int getPollerThreadPriority() { + return pollerThreadPriority; + } private long selectorTimeout = 1000; - public void setSelectorTimeout(long timeout) { this.selectorTimeout = timeout;} - public long getSelectorTimeout() { return this.selectorTimeout; } + + public void setSelectorTimeout(long timeout) { + this.selectorTimeout = timeout; + } + + public long getSelectorTimeout() { + return this.selectorTimeout; + } /** * The socket poller. @@ -160,8 +185,8 @@ /** * Number of keep-alive sockets. * - * @return The number of sockets currently in the keep-alive state waiting - * for the next request to be received on the socket + * @return The number of sockets currently in the keep-alive state waiting for the next request to be received on + * the socket */ public int getKeepAliveCount() { if (poller == null) { @@ -238,7 +263,7 @@ InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset()); serverSock.bind(addr, getAcceptCount()); } - serverSock.configureBlocking(true); //mimic APR behavior + serverSock.configureBlocking(true); // mimic APR behavior } @@ -253,18 +278,15 @@ paused = false; if (socketProperties.getProcessorCache() != 0) { - processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, - socketProperties.getProcessorCache()); + processorCache = + new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, socketProperties.getProcessorCache()); } if (socketProperties.getEventCache() != 0) { - eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, - socketProperties.getEventCache()); + eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, socketProperties.getEventCache()); } - int actualBufferPool = - socketProperties.getActualBufferPool(isSSLEnabled() ? getSniParseLimit() * 2 : 0); + int actualBufferPool = socketProperties.getActualBufferPool(isSSLEnabled() ? getSniParseLimit() * 2 : 0); if (actualBufferPool != 0) { - nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, - actualBufferPool); + nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE, actualBufferPool); } // Create worker collection @@ -339,8 +361,7 @@ @Override public void unbind() throws Exception { if (log.isTraceEnabled()) { - log.trace("Destroy initiated for " + - new InetSocketAddress(getAddress(),getPortWithOffset())); + log.trace("Destroy initiated for " + new InetSocketAddress(getAddress(), getPortWithOffset())); } if (running) { stop(); @@ -352,12 +373,11 @@ } destroySsl(); super.unbind(); - if (getHandler() != null ) { + if (getHandler() != null) { getHandler().recycle(); } if (log.isTraceEnabled()) { - log.trace("Destroy completed for " + - new InetSocketAddress(getAddress(), getPortWithOffset())); + log.trace("Destroy completed for " + new InetSocketAddress(getAddress(), getPortWithOffset())); } } @@ -398,16 +418,14 @@ } // Wait for up to 1000ms acceptor threads to unlock long waitLeft = 1000; - while (waitLeft > 0 && - acceptor.getState() == AcceptorState.RUNNING) { + while (waitLeft > 0 && acceptor.getState() == AcceptorState.RUNNING) { Thread.sleep(5); waitLeft -= 5; } - } catch(Throwable t) { + } catch (Throwable t) { ExceptionUtils.handleThrowable(t); if (getLog().isDebugEnabled()) { - getLog().debug(sm.getString( - "endpoint.debug.unlock.fail", String.valueOf(getPortWithOffset())), t); + getLog().debug(sm.getString("endpoint.debug.unlock.fail", String.valueOf(getPortWithOffset())), t); } } } @@ -436,10 +454,11 @@ /** * Process the specified connection. + * * @param socket The socket channel - * @return true if the socket was correctly configured - * and processing may continue, false if the socket needs to be - * close immediately + * + * @return true if the socket was correctly configured and processing may continue, false + * if the socket needs to be close immediately */ @Override protected boolean setSocketOptions(SocketChannel socket) { @@ -451,10 +470,8 @@ channel = nioChannels.pop(); } if (channel == null) { - SocketBufferHandler bufhandler = new SocketBufferHandler( - socketProperties.getAppReadBufSize(), - socketProperties.getAppWriteBufSize(), - socketProperties.getDirectBuffer()); + SocketBufferHandler bufhandler = new SocketBufferHandler(socketProperties.getAppReadBufSize(), + socketProperties.getAppWriteBufSize(), socketProperties.getDirectBuffer()); if (isSSLEnabled()) { channel = new SecureNioChannel(bufhandler, this); } else { @@ -546,8 +563,8 @@ @Override - protected SocketProcessorBase createSocketProcessor( - SocketWrapperBase socketWrapper, SocketEvent event) { + protected SocketProcessorBase createSocketProcessor(SocketWrapperBase socketWrapper, + SocketEvent event) { return new SocketProcessor(socketWrapper, event); } @@ -594,15 +611,14 @@ */ public class Poller implements Runnable { - private Selector selector; - private final SynchronizedQueue events = - new SynchronizedQueue<>(); + private final Selector selector; + private final SynchronizedQueue events = new SynchronizedQueue<>(); private volatile boolean close = false; // Optimize expiration handling private long nextExpiration = 0; - private AtomicLong wakeupCounter = new AtomicLong(0); + private final AtomicLong wakeupCounter = new AtomicLong(0); private volatile int keyCount = 0; @@ -610,9 +626,13 @@ this.selector = Selector.open(); } - public int getKeyCount() { return keyCount; } + public int getKeyCount() { + return selector.keys().size(); + } - public Selector getSelector() { return selector; } + public Selector getSelector() { + return selector; + } /** * Destroy the poller. @@ -646,14 +666,12 @@ } /** - * Add specified socket and associated pool to the poller. The socket will - * be added to a temporary array, and polled first after a maximum amount - * of time equal to pollTime (in most cases, latency will be much lower, + * Add specified socket and associated pool to the poller. The socket will be added to a temporary array, and + * polled first after a maximum amount of time equal to pollTime (in most cases, latency will be much lower, * however). * * @param socketWrapper to add to the poller - * @param interestOps Operations for which to register this socket with - * the Poller + * @param interestOps Operations for which to register this socket with the Poller */ public void add(NioSocketWrapper socketWrapper, int interestOps) { PollerEvent pollerEvent = createPollerEvent(socketWrapper, interestOps); @@ -666,26 +684,27 @@ /** * Processes events in the event queue of the Poller. * - * @return true if some events were processed, - * false if queue was empty + * @return true if some events were processed, false if queue was empty */ public boolean events() { boolean result = false; - PollerEvent pe = null; - for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) { + PollerEvent pe; + for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++) { result = true; NioSocketWrapper socketWrapper = pe.getSocketWrapper(); SocketChannel sc = socketWrapper.getSocket().getIOChannel(); int interestOps = pe.getInterestOps(); if (sc == null) { - log.warn(sm.getString("endpoint.nio.nullSocketChannel")); + if (log.isDebugEnabled()) { + log.debug(sm.getString("endpoint.nio.nullSocketChannel")); + } socketWrapper.close(); } else if (interestOps == OP_REGISTER) { try { sc.register(getSelector(), SelectionKey.OP_READ, socketWrapper); - } catch (Exception x) { - log.error(sm.getString("endpoint.nio.registerFail"), x); + } catch (Exception e) { + log.error(sm.getString("endpoint.nio.registerFail"), e); } } else { final SelectionKey key = sc.keyFor(getSelector()); @@ -727,15 +746,14 @@ * @param socketWrapper The socket wrapper */ public void register(final NioSocketWrapper socketWrapper) { - socketWrapper.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into. + socketWrapper.interestOps(SelectionKey.OP_READ);// this is what OP_REGISTER turns into. PollerEvent pollerEvent = createPollerEvent(socketWrapper, OP_REGISTER); addEvent(pollerEvent); } /** - * The background thread that adds sockets to the Poller, checks the - * poller for triggered events and hands the associated socket off to an - * appropriate processor as events occur. + * The background thread that adds sockets to the Poller, checks the poller for triggered events and hands the + * associated socket off to an appropriate processor as events occur. */ @Override public void run() { @@ -749,7 +767,7 @@ hasEvents = events(); if (wakeupCounter.getAndSet(-1) > 0) { // If we are here, means we have other stuff to do - // Do a non blocking select + // Do a non-blocking select keyCount = selector.selectNow(); } else { keyCount = selector.select(selectorTimeout); @@ -776,8 +794,7 @@ continue; } - Iterator iterator = - keyCount > 0 ? selector.selectedKeys().iterator() : null; + Iterator iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null; // Walk through the collection of ready keys and dispatch // any active event. while (iterator != null && iterator.hasNext()) { @@ -792,7 +809,7 @@ } // Process timeouts - timeout(keyCount,hasEvents); + timeout(keyCount, hasEvents); } getStopLatch().countDown(); @@ -867,7 +884,7 @@ } if (sd.fchannel == null) { - // Setup the file channel + // Set up the file channel File f = new File(sd.fileName); @SuppressWarnings("resource") // Closed when channel is closed FileInputStream fis = new FileInputStream(f); @@ -898,7 +915,7 @@ } } } - if (sd.length <= 0 && sc.getOutboundRemaining()<=0) { + if (sd.length <= 0 && sc.getOutboundRemaining() <= 0) { if (log.isTraceEnabled()) { log.trace("Send file complete for: " + sd.fileName); } @@ -906,35 +923,36 @@ try { sd.fchannel.close(); } catch (Exception ignore) { + // Ignore } // For calls from outside the Poller, the caller is // responsible for registering the socket for the // appropriate event(s) if sendfile completes. if (!calledByProcessor) { switch (sd.keepAliveState) { - case NONE: { - if (log.isTraceEnabled()) { - log.trace("Send file connection is being closed"); - } - socketWrapper.close(); - break; - } - case PIPELINED: { - if (log.isTraceEnabled()) { - log.trace("Connection is keep alive, processing pipe-lined data"); - } - if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) { + case NONE: { + if (log.isTraceEnabled()) { + log.trace("Send file connection is being closed"); + } socketWrapper.close(); + break; } - break; - } - case OPEN: { - if (log.isTraceEnabled()) { - log.trace("Connection is keep alive, registering back for OP_READ"); + case PIPELINED: { + if (log.isTraceEnabled()) { + log.trace("Connection is keep alive, processing pipe-lined data"); + } + if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) { + socketWrapper.close(); + } + break; + } + case OPEN: { + if (log.isTraceEnabled()) { + log.trace("Connection is keep alive, registering back for OP_READ"); + } + reg(sk, socketWrapper, SelectionKey.OP_READ); + break; } - reg(sk, socketWrapper, SelectionKey.OP_READ); - break; - } } } return SendfileState.DONE; @@ -949,9 +967,9 @@ } return SendfileState.PENDING; } - } catch (IOException e) { + } catch (IOException ioe) { if (log.isDebugEnabled()) { - log.debug(sm.getString("endpoint.sendfile.error"), e); + log.debug(sm.getString("endpoint.sendfile.error"), ioe); } if (!calledByProcessor && sc != null) { socketWrapper.close(); @@ -1005,7 +1023,7 @@ socketWrapper.interestOps(0); socketWrapper.close(); } else if (socketWrapper.interestOpsHas(SelectionKey.OP_READ) || - socketWrapper.interestOpsHas(SelectionKey.OP_WRITE)) { + socketWrapper.interestOpsHas(SelectionKey.OP_WRITE)) { boolean readTimeout = false; boolean writeTimeout = false; // Check for read timeout @@ -1054,13 +1072,11 @@ } // For logging purposes only long prevExp = nextExpiration; - nextExpiration = System.currentTimeMillis() + - socketProperties.getTimeoutInterval(); + nextExpiration = System.currentTimeMillis() + socketProperties.getTimeoutInterval(); if (log.isTraceEnabled()) { - log.trace("timeout completed: keys processed=" + keycount + - "; now=" + now + "; nextExpiration=" + prevExp + - "; keyCount=" + keyCount + "; hasEvents=" + hasEvents + - "; eval=" + ((now < prevExp) && (keyCount>0 || hasEvents) && (!close) )); + log.trace("timeout completed: keys processed=" + keycount + "; now=" + now + "; nextExpiration=" + + prevExp + "; keyCount=" + keyCount + "; hasEvents=" + hasEvents + "; eval=" + + ((now < prevExp) && (keyCount > 0 || hasEvents) && (!close))); } } @@ -1101,20 +1117,46 @@ writeLock = (writePending == null) ? new Object() : writePending; } - public Poller getPoller() { return poller; } - public int interestOps() { return interestOps; } - public int interestOps(int ops) { this.interestOps = ops; return ops; } + public Poller getPoller() { + return poller; + } + + public int interestOps() { + return interestOps; + } + + public int interestOps(int ops) { + this.interestOps = ops; + return ops; + } + public boolean interestOpsHas(int targetOp) { return (this.interestOps() & targetOp) == targetOp; } - public void setSendfileData(SendfileData sf) { this.sendfileData = sf;} - public SendfileData getSendfileData() { return this.sendfileData; } + public void setSendfileData(SendfileData sf) { + this.sendfileData = sf; + } - public void updateLastWrite() { lastWrite = System.currentTimeMillis(); } - public long getLastWrite() { return lastWrite; } - public void updateLastRead() { lastRead = System.currentTimeMillis(); } - public long getLastRead() { return lastRead; } + public SendfileData getSendfileData() { + return this.sendfileData; + } + + public void updateLastWrite() { + lastWrite = System.currentTimeMillis(); + } + + public long getLastWrite() { + return lastWrite; + } + + public void updateLastRead() { + lastRead = System.currentTimeMillis(); + } + + public long getLastRead() { + return lastRead; + } @Override public boolean isReadyForRead() throws IOException { @@ -1126,8 +1168,7 @@ fillReadBuffer(false); - boolean isReady = socketBufferHandler.getReadBuffer().position() > 0; - return isReady; + return socketBufferHandler.getReadBuffer().position() > 0; } @@ -1137,11 +1178,9 @@ if (nRead > 0) { return nRead; /* - * Since more bytes may have arrived since the buffer was last - * filled, it is an option at this point to perform a - * non-blocking read. However correctly handling the case if - * that read returns end of stream adds complexity. Therefore, - * at the moment, the preference is for simplicity. + * Since more bytes may have arrived since the buffer was last filled, it is an option at this point to + * perform a non-blocking read. However, correctly handling the case if that read returns end of stream + * adds complexity. Therefore, at the moment, the preference is for simplicity. */ } @@ -1166,11 +1205,9 @@ if (nRead > 0) { return nRead; /* - * Since more bytes may have arrived since the buffer was last - * filled, it is an option at this point to perform a - * non-blocking read. However correctly handling the case if - * that read returns end of stream adds complexity. Therefore, - * at the moment, the preference is for simplicity. + * Since more bytes may have arrived since the buffer was last filled, it is an option at this point to + * perform a non-blocking read. However, correctly handling the case if that read returns end of stream + * adds complexity. Therefore, at the moment, the preference is for simplicity. */ } @@ -1212,14 +1249,15 @@ getSocket().close(true); } if (getEndpoint().running) { + getSocket().reset(null, null); if (nioChannels == null || !nioChannels.push(getSocket())) { getSocket().free(); } } - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); if (log.isDebugEnabled()) { - log.error(sm.getString("endpoint.debug.channelCloseFail"), e); + log.error(sm.getString("endpoint.debug.channelCloseFail"), t); } } finally { socketBufferHandler = SocketBufferHandler.EMPTY; @@ -1231,10 +1269,10 @@ if (data != null && data.fchannel != null && data.fchannel.isOpen()) { data.fchannel.close(); } - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); if (log.isDebugEnabled()) { - log.error(sm.getString("endpoint.sendfile.closeError"), e); + log.error(sm.getString("endpoint.sendfile.closeError"), t); } } } @@ -1246,7 +1284,7 @@ private int fillReadBuffer(boolean block, ByteBuffer buffer) throws IOException { - int n = 0; + int n; if (getSocket() == NioChannel.CLOSED_NIO_CHANNEL) { throw new ClosedChannelException(); } @@ -1281,8 +1319,11 @@ } else { readLock.wait(); } - } catch (InterruptedException e) { - // Continue + } catch (InterruptedException ignore) { + /* + * Most likely the Poller signalling there is data to read but could be spurious. Exit + * the wait, check status and proceed accordingly. + */ } } } @@ -1323,13 +1364,11 @@ /* * https://bz.apache.org/bugzilla/show_bug.cgi?id=66076 * - * When using TLS an additional buffer is used for the encrypted data - * before it is written to the network. It is possible for this network - * output buffer to contain data while the socket write buffer is empty. + * When using TLS an additional buffer is used for the encrypted data before it is written to the network. It is + * possible for this network output buffer to contain data while the socket write buffer is empty. * - * For NIO with non-blocking I/O, this case is handling by ensuring that - * flush only returns false (i.e. no data left to flush) if all buffers - * are empty. + * For NIO with non-blocking I/O, this case is handling by ensuring that flush only returns false (i.e. no data + * left to flush) if all buffers are empty. */ private boolean socketOrNetworkBufferHasDataLeft() { return !socketBufferHandler.isWriteBufferEmpty() || getSocket().getOutboundRemaining() > 0; @@ -1338,7 +1377,7 @@ @Override protected void doWrite(boolean block, ByteBuffer buffer) throws IOException { - int n = 0; + int n; if (getSocket() == NioChannel.CLOSED_NIO_CHANNEL) { throw new ClosedChannelException(); } @@ -1347,16 +1386,13 @@ /* * Socket has previously timed out. * - * Blocking writes assume that buffer is always fully - * written so there is no code checking for incomplete - * writes, retaining the unwritten data and attempting to - * write it as part of a subsequent write call. + * Blocking writes assume that buffer is always fully written so there is no code checking for + * incomplete writes, retaining the unwritten data and attempting to write it as part of a + * subsequent write call. * - * Because of the above, when a timeout is triggered we need - * to skip subsequent attempts to write as otherwise it will - * appear to the client as if some data was dropped just - * before the connection is lost. It is better if the client - * just sees the dropped connection. + * Because of the above, when a timeout is triggered we need to skip subsequent attempts to write as + * otherwise it will appear to the client as if some data was dropped just before the connection is + * lost. It is better if the client just sees the dropped connection. */ throw new IOException(previousIOException); } @@ -1376,7 +1412,7 @@ } synchronized (writeLock) { n = getSocket().write(buffer); - // n == 0 could be an incomplete write but it could also + // n == 0 could be an incomplete write, but it could also // indicate that a previous incomplete write of the // outbound buffer (for TLS) has now completed. Only // block if there is still data to write. @@ -1393,8 +1429,11 @@ } else { writeLock.wait(); } - } catch (InterruptedException e) { - // Continue + } catch (InterruptedException ignore) { + /* + * Most likely the Poller signalling that data can be written but could be spurious. + * Exit the wait, check status and proceed accordingly. + */ } } else if (startNanos > 0) { // If something was written, reset timeout @@ -1551,23 +1590,23 @@ } @Override - protected OperationState newOperationState(boolean read, - ByteBuffer[] buffers, int offset, int length, - BlockingMode block, long timeout, TimeUnit unit, A attachment, - CompletionCheck check, CompletionHandler handler, - Semaphore semaphore, VectoredIOCompletionHandler completion) { - return new NioOperationState<>(read, buffers, offset, length, block, - timeout, unit, attachment, check, handler, semaphore, completion); + protected OperationState newOperationState(boolean read, ByteBuffer[] buffers, int offset, int length, + BlockingMode block, long timeout, TimeUnit unit, A attachment, CompletionCheck check, + CompletionHandler handler, Semaphore semaphore, + VectoredIOCompletionHandler completion) { + return new NioOperationState<>(read, buffers, offset, length, block, timeout, unit, attachment, check, + handler, semaphore, completion); } private class NioOperationState extends OperationState { private volatile boolean inline = true; - private NioOperationState(boolean read, ByteBuffer[] buffers, int offset, int length, - BlockingMode block, long timeout, TimeUnit unit, A attachment, CompletionCheck check, - CompletionHandler handler, Semaphore semaphore, + + private NioOperationState(boolean read, ByteBuffer[] buffers, int offset, int length, BlockingMode block, + long timeout, TimeUnit unit, A attachment, CompletionCheck check, + CompletionHandler handler, Semaphore semaphore, VectoredIOCompletionHandler completion) { - super(read, buffers, offset, length, block, - timeout, unit, attachment, check, handler, semaphore, completion); + super(read, buffers, offset, length, block, timeout, unit, attachment, check, handler, semaphore, + completion); } @Override @@ -1627,7 +1666,7 @@ } } if (doWrite) { - long n = 0; + long n; do { n = getSocket().write(buffers, offset, length); if (n == -1) { @@ -1644,8 +1683,8 @@ completionDone = false; } } - } catch (IOException e) { - setError(e); + } catch (IOException ioe) { + setError(ioe); } } if (nBytes > 0 || (nBytes == 0 && !buffersArrayHasRemaining(buffers, offset, length) && @@ -1675,8 +1714,7 @@ // ---------------------------------------------- SocketProcessor Inner Class /** - * This class is the equivalent of the Worker, but will simply use in an - * external Executor thread pool. + * This class is the equivalent of the Worker, but will simply use in an external Executor thread pool. */ protected class SocketProcessor extends SocketProcessorBase { @@ -1687,12 +1725,10 @@ @Override protected void doRun() { /* - * Do not cache and re-use the value of socketWrapper.getSocket() in - * this method. If the socket closes the value will be updated to - * CLOSED_NIO_CHANNEL and the previous value potentially re-used for - * a new connection. That can result in a stale cached value which - * in turn can result in unintentionally closing currently active - * connections. + * Do not cache and re-use the value of socketWrapper.getSocket() in this method. If the socket closes the + * value will be updated to CLOSED_NIO_CHANNEL and the previous value potentially re-used for a new + * connection. That can result in a stale cached value which in turn can result in unintentionally closing + * currently active connections. */ Poller poller = NioEndpoint.this.poller; if (poller == null) { @@ -1701,7 +1737,7 @@ } try { - int handshake = -1; + int handshake; try { if (socketWrapper.getSocket().isHandshakeComplete()) { // No TLS handshaking required. Let the handler @@ -1713,7 +1749,8 @@ // if the handshake failed. handshake = -1; } else { - handshake = socketWrapper.getSocket().handshake(event == SocketEvent.OPEN_READ, event == SocketEvent.OPEN_WRITE); + handshake = socketWrapper.getSocket().handshake(event == SocketEvent.OPEN_READ, + event == SocketEvent.OPEN_WRITE); // The handshake process reads/writes from/to the // socket. status may therefore be OPEN_WRITE once // the handshake completes. However, the handshake @@ -1723,32 +1760,29 @@ // the handshake completes. event = SocketEvent.OPEN_READ; } - } catch (IOException x) { + } catch (IOException ioe) { handshake = -1; if (logHandshake.isDebugEnabled()) { - logHandshake.debug(sm.getString("endpoint.err.handshake", - socketWrapper.getRemoteAddr(), Integer.toString(socketWrapper.getRemotePort())), x); + logHandshake.debug(sm.getString("endpoint.err.handshake", socketWrapper.getRemoteAddr(), + Integer.toString(socketWrapper.getRemotePort())), ioe); } } catch (CancelledKeyException ckx) { handshake = -1; } if (handshake == 0) { - SocketState state = SocketState.OPEN; + SocketState state; // Process the request from this socket - if (event == null) { - state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ); - } else { - state = getHandler().process(socketWrapper, event); - } + state = getHandler().process(socketWrapper, + Objects.requireNonNullElse(event, SocketEvent.OPEN_READ)); if (state == SocketState.CLOSED) { socketWrapper.close(); } - } else if (handshake == -1 ) { + } else if (handshake == -1) { getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL); socketWrapper.close(); - } else if (handshake == SelectionKey.OP_READ){ + } else if (handshake == SelectionKey.OP_READ) { socketWrapper.registerReadInterest(); - } else if (handshake == SelectionKey.OP_WRITE){ + } else if (handshake == SelectionKey.OP_WRITE) { socketWrapper.registerWriteInterest(); } } catch (CancelledKeyException cx) { @@ -1761,7 +1795,7 @@ } finally { socketWrapper = null; event = null; - //return to cache + // return to cache if (running && processorCache != null) { processorCache.push(this); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLContext.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLContext.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,14 +28,12 @@ import javax.net.ssl.TrustManager; /** - * This interface is needed to override the default SSLContext class - * to allow SSL implementation pluggability without having to use JCE. With - * regular JSSE it will do nothing but delegate to the SSLContext. + * This interface is needed to override the default SSLContext class to allow SSL implementation pluggability without + * having to use JCE. With regular JSSE it will do nothing but delegate to the SSLContext. */ public interface SSLContext { - void init(KeyManager[] kms, TrustManager[] tms, - SecureRandom sr) throws KeyManagementException; + void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) throws KeyManagementException; void destroy(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLContextWrapper.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLContextWrapper.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLContextWrapper.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLContextWrapper.java 2026-01-23 19:33:36.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.net; + +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.Objects; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSessionContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; + +/** + * Wrapper class to simplify using a pre-configured {@code javax.net.ssl.SSLContext} instance with an + * {@code SSLHostConfigCertificate}. + */ +class SSLContextWrapper implements SSLContext { + + private final javax.net.ssl.SSLContext sslContext; + private final X509KeyManager keyManager; + private final X509TrustManager trustManager; + + SSLContextWrapper(javax.net.ssl.SSLContext sslContext, X509KeyManager keyManager, X509TrustManager trustManager) { + this.sslContext = Objects.requireNonNull(sslContext); + this.keyManager = Objects.requireNonNull(keyManager); + this.trustManager = Objects.requireNonNull(trustManager); + } + + @Override + public void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) { + // NO-OP as it is already initialized + } + + @Override + public void destroy() { + + } + + @Override + public SSLSessionContext getServerSessionContext() { + return sslContext.getServerSessionContext(); + } + + @Override + public SSLEngine createSSLEngine() { + return sslContext.createSSLEngine(); + } + + @Override + public SSLServerSocketFactory getServerSocketFactory() { + return sslContext.getServerSocketFactory(); + } + + @Override + public SSLParameters getSupportedSSLParameters() { + return sslContext.getSupportedSSLParameters(); + } + + @Override + public X509Certificate[] getCertificateChain(String alias) { + return keyManager.getCertificateChain(alias); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return trustManager.getAcceptedIssuers(); + } +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLHostConfig.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLHostConfig.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLHostConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLHostConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,6 +38,7 @@ import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.net.openssl.OpenSSLConf; import org.apache.tomcat.util.net.openssl.ciphers.Cipher; +import org.apache.tomcat.util.net.openssl.ciphers.Group; import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser; import org.apache.tomcat.util.res.StringManager; @@ -51,16 +52,23 @@ private static final Log log = LogFactory.getLog(SSLHostConfig.class); private static final StringManager sm = StringManager.getManager(SSLHostConfig.class); - // Must be lower case. SSL host names are always stored using lower case as - // they are case insensitive but are used by case sensitive code such as + // Must be lowercase. SSL host names are always stored using lower case as + // they are case-insensitive but are used by case-sensitive code such as // keys in Maps. protected static final String DEFAULT_SSL_HOST_NAME = "_default_"; protected static final Set SSL_PROTO_ALL_SET = new HashSet<>(); - public static final String DEFAULT_TLS_CIPHERS = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA"; + public static final String DEFAULT_TLS_CIPHERS_12 = "HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!kRSA"; + public static final String DEFAULT_TLS_CIPHERS_13 = "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256"; + /** + * Default cipher list for TLS 1.2 and below. + * @deprecated Replaced by {@link #DEFAULT_TLS_CIPHERS_12} + */ + @Deprecated + public static final String DEFAULT_TLS_CIPHERS = DEFAULT_TLS_CIPHERS_12; static { - /* Default used if protocols are not configured, also used if - * protocols="All" + /* + * Default used if protocols are not configured, also used if protocols="All" */ SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_SSLv2Hello); SSL_PROTO_ALL_SET.add(Constants.SSL_PROTO_TLSv1); @@ -70,6 +78,7 @@ } private Type configType = null; + private Type trustConfigType = null; private String hostName = DEFAULT_SSL_HOST_NAME; @@ -90,21 +99,27 @@ // Need to know if TLS 1.3 has been explicitly requested as a warning needs // to generated if it is explicitly requested for a JVM that does not // support it. Uses a set so it is extensible for TLS 1.4 etc. - private Set explicitlyRequestedProtocols = new HashSet<>(); + private final Set explicitlyRequestedProtocols = new HashSet<>(); // Nested private SSLHostConfigCertificate defaultCertificate = null; - private Set certificates = new LinkedHashSet<>(4); + private final Set certificates = new LinkedHashSet<>(4); // Common private String certificateRevocationListFile; private CertificateVerification certificateVerification = CertificateVerification.NONE; private int certificateVerificationDepth = 10; // Used to track if certificateVerificationDepth has been explicitly set private boolean certificateVerificationDepthConfigured = false; - private String ciphers = DEFAULT_TLS_CIPHERS; + private String ciphers = DEFAULT_TLS_CIPHERS_12; + private String cipherSuites = DEFAULT_TLS_CIPHERS_13; private LinkedHashSet cipherList = null; + private LinkedHashSet cipherSuiteList = null; private List jsseCipherNames = null; private boolean honorCipherOrder = false; - private Set protocols = new HashSet<>(); + private boolean ocspEnabled = false; + private boolean ocspSoftFail = true; + private int ocspTimeout = 15000; + private int ocspVerifyFlags = 0; + private final Set protocols = new HashSet<>(); // Values <0 mean use the implementation default private int sessionCacheSize = -1; private int sessionTimeout = 86400; @@ -119,6 +134,8 @@ private String truststoreProvider = System.getProperty("javax.net.ssl.trustStoreProvider"); private String truststoreType = System.getProperty("javax.net.ssl.trustStoreType"); private transient KeyStore truststore = null; + private String groups = null; + private LinkedHashSet groupList = null; // OpenSSL private String certificateRevocationListPath; private String caCertificateFile; @@ -172,18 +189,40 @@ /** * Set property which belongs to the specified configuration type. - * @param name the property name + * + * @param name the property name * @param configType the configuration type - * @return true if the property belongs to the current configuration, - * and false otherwise + * + * @return true if the property belongs to the current configuration type, and false otherwise */ boolean setProperty(String name, Type configType) { if (this.configType == null) { this.configType = configType; } else { if (configType != this.configType) { - log.warn(sm.getString("sslHostConfig.mismatch", - name, getHostName(), configType, this.configType)); + log.warn(sm.getString("sslHostConfig.mismatch", name, getHostName(), configType, this.configType)); + return false; + } + } + return true; + } + + + /** + * Set property which belongs to the specified trust configuration type. + * + * @param name the property name + * @param trustConfigType the trust configuration type + * + * @return true if the property belongs to the current trust configuration type, and false otherwise + */ + boolean setTrustProperty(String name, Type trustConfigType) { + if (this.trustConfigType == null) { + this.trustConfigType = trustConfigType; + } else { + if (trustConfigType != this.trustConfigType) { + log.warn(sm.getString("sslHostConfig.mismatch.trust", name, getHostName(), trustConfigType, + this.trustConfigType)); return false; } } @@ -237,8 +276,8 @@ private void registerDefaultCertificate() { if (defaultCertificate == null) { - SSLHostConfigCertificate defaultCertificate = new SSLHostConfigCertificate( - this, SSLHostConfigCertificate.Type.UNDEFINED); + SSLHostConfigCertificate defaultCertificate = + new SSLHostConfigCertificate(this, SSLHostConfigCertificate.Type.UNDEFINED); addCertificate(defaultCertificate); this.defaultCertificate = defaultCertificate; } @@ -248,14 +287,14 @@ public void addCertificate(SSLHostConfigCertificate certificate) { // Need to make sure that if there is more than one certificate, none of // them have a type of undefined. - if (certificates.size() == 0) { + if (certificates.isEmpty()) { certificates.add(certificate); return; } if (certificates.size() == 1 && certificates.iterator().next().getType() == SSLHostConfigCertificate.Type.UNDEFINED || - certificate.getType() == SSLHostConfigCertificate.Type.UNDEFINED) { + certificate.getType() == SSLHostConfigCertificate.Type.UNDEFINED) { // Invalid config throw new IllegalArgumentException(sm.getString("sslHostConfig.certificate.notype")); } @@ -275,7 +314,6 @@ } else if (openSslConf != null) { throw new IllegalArgumentException(sm.getString("sslHostConfig.opensslconf.alreadySet")); } - setProperty("", Type.OPENSSL); openSslConf = conf; } @@ -286,7 +324,7 @@ public Set getCertificates(boolean createDefaultIfEmpty) { - if (certificates.size() == 0 && createDefaultIfEmpty) { + if (certificates.isEmpty() && createDefaultIfEmpty) { registerDefaultCertificate(); } return certificates; @@ -307,8 +345,7 @@ public void setCertificateVerification(String certificateVerification) { try { - this.certificateVerification = - CertificateVerification.fromString(certificateVerification); + this.certificateVerification = CertificateVerification.fromString(certificateVerification); } catch (IllegalArgumentException iae) { // If the specified value is not recognised, default to the // strictest possible option. @@ -350,36 +387,59 @@ /** - * Set the new cipher configuration. Note: Regardless of the format used to - * set the configuration, it is always stored in OpenSSL format. + * Set the new cipher (TLSv1.2 and below) configuration. Note: Regardless of the format used to set the + * configuration, it is always stored in OpenSSL format. * * @param ciphersList The new cipher configuration in OpenSSL or JSSE format */ public void setCiphers(String ciphersList) { // Ciphers is stored in OpenSSL format. Convert the provided value if // necessary. - if (ciphersList != null && !ciphersList.contains(":")) { - StringBuilder sb = new StringBuilder(); - // Not obviously in OpenSSL format. May be a single OpenSSL or JSSE - // cipher name. May be a comma separated list of cipher names - String ciphers[] = ciphersList.split(","); - for (String cipher : ciphers) { - String trimmed = cipher.trim(); - if (trimmed.length() > 0) { - String openSSLName = OpenSSLCipherConfigurationParser.jsseToOpenSSL(trimmed); - if (openSSLName == null) { - // Not a JSSE name. Maybe an OpenSSL name or alias - openSSLName = trimmed; + if (ciphersList != null) { + if (ciphersList.contains(":")) { + // OpenSSL format + StringBuilder sb = new StringBuilder(); + String[] components = ciphersList.split(":"); + // Remove any TLS 1.3 cipher suites + for (String component : components) { + String trimmed = component.trim(); + if (OpenSSLCipherConfigurationParser.isTls13Cipher(trimmed)) { + log.warn(sm.getString("sslHostConfig.ignoreTls13Ciphersuite", trimmed)); + } else { + if (sb.length() > 0) { + sb.append(':'); + } + sb.append(trimmed); } - if (sb.length() > 0) { - sb.append(':'); + } + this.ciphers = sb.toString(); + } else { + // Not obviously in OpenSSL format. Might be a single OpenSSL or JSSE + // cipher name. Might be a comma separated list of cipher names + StringBuilder sb = new StringBuilder(); + String[] ciphers = ciphersList.split(","); + for (String cipher : ciphers) { + String trimmed = cipher.trim(); + if (!trimmed.isEmpty()) { + if (OpenSSLCipherConfigurationParser.isTls13Cipher(trimmed)) { + log.warn(sm.getString("sslHostConfig.ignoreTls13Ciphersuite", trimmed)); + continue; + } + String openSSLName = OpenSSLCipherConfigurationParser.jsseToOpenSSL(trimmed); + if (openSSLName == null) { + // Not a JSSE name. Maybe an OpenSSL name or alias + openSSLName = trimmed; + } + if (sb.length() > 0) { + sb.append(':'); + } + sb.append(openSSLName); } - sb.append(openSSLName); } + this.ciphers = sb.toString(); } - this.ciphers = sb.toString(); } else { - this.ciphers = ciphersList; + this.ciphers = null; } this.cipherList = null; this.jsseCipherNames = null; @@ -403,20 +463,83 @@ /** - * Obtain the list of JSSE cipher names for the current configuration. - * Ciphers included in the configuration but not supported by JSSE will be - * excluded from this list. + * Obtain the list of JSSE cipher names for the current configuration. Ciphers included in the configuration but not + * supported by JSSE will be excluded from this list. * * @return A list of the JSSE cipher names */ public List getJsseCipherNames() { if (jsseCipherNames == null) { - jsseCipherNames = OpenSSLCipherConfigurationParser.convertForJSSE(getCipherList()); + Set jsseCiphers = new HashSet<>(); + jsseCiphers.addAll(getCipherSuiteList()); + jsseCiphers.addAll(getCipherList()); + jsseCipherNames = OpenSSLCipherConfigurationParser.convertForJSSE(jsseCiphers); } return jsseCipherNames; } + /** + * Set the cipher suite (TLSv1.3) configuration. + * + * @param cipherSuites The cipher suites to use in a colon-separated, preference order list + */ + public void setCipherSuites(String cipherSuites) { + StringBuilder sb = new StringBuilder(); + String[] values; + if (cipherSuites.contains(":")) { + // OpenSSL format + values = cipherSuites.split(":"); + } else { + // JSSE format or possible a single cipher suite name + values = cipherSuites.split(","); + } + for (String value : values) { + String trimmed = value.trim(); + if (!trimmed.isEmpty()) { + if (!OpenSSLCipherConfigurationParser.isTls13Cipher(trimmed)) { + log.warn(sm.getString("sslHostConfig.ignoreNonTls13Ciphersuite", trimmed)); + continue; + } + /* + * OpenSSL and JSSE names for TLSv1.3 cipher suites are currently (January 2026) the same but handle the + * possible future case where they are not. + */ + String openSSLName = OpenSSLCipherConfigurationParser.jsseToOpenSSL(trimmed); + if (openSSLName == null) { + // Not a JSSE name. Maybe an OpenSSL name or alias + openSSLName = trimmed; + } + if (sb.length() > 0) { + sb.append(':'); + } + sb.append(trimmed); + } + } + this.cipherSuites = sb.toString(); + this.cipherSuiteList = null; + this.jsseCipherNames = null; + } + + + /** + * Obtain the current cipher suite (TLSv1.3) configuration. + * + * @return An OpenSSL cipher suite string for the current configuration. + */ + public String getCipherSuites() { + return cipherSuites; + } + + + private LinkedHashSet getCipherSuiteList() { + if (cipherSuiteList == null) { + cipherSuiteList = OpenSSLCipherConfigurationParser.parse(getCipherSuites()); + } + return cipherSuiteList; + } + + public void setHonorCipherOrder(boolean honorCipherOrder) { this.honorCipherOrder = honorCipherOrder; } @@ -433,14 +556,53 @@ /** - * @return The host name associated with this SSL configuration - always in - * lower case. + * @return The host name associated with this SSL configuration - always in lower case. */ public String getHostName() { return hostName; } + public boolean getOcspEnabled() { + return ocspEnabled; + } + + + public void setOcspEnabled(boolean ocspEnabled) { + this.ocspEnabled = ocspEnabled; + } + + + public boolean getOcspSoftFail() { + return ocspSoftFail; + } + + + public void setOcspSoftFail(boolean ocspSoftFail) { + this.ocspSoftFail = ocspSoftFail; + } + + + public int getOcspTimeout() { + return ocspTimeout; + } + + + public void setOcspTimeout(int ocspTimeout) { + this.ocspTimeout = ocspTimeout; + } + + + public int getOcspVerifyFlags() { + return ocspVerifyFlags; + } + + + public void setOcspVerifyFlags(int ocspVerifyFlags) { + this.ocspVerifyFlags = ocspVerifyFlags; + } + + public void setProtocols(String input) { protocols.clear(); explicitlyRequestedProtocols.clear(); @@ -456,7 +618,7 @@ // Split using a positive lookahead to keep the separator in // the capture so we can check which case it is. - for (String value: input.split("(?=[-+,])")) { + for (String value : input.split("(?=[-+,])")) { String trimmed = value.trim(); // Ignore token which only consists of prefix character if (trimmed.length() > 1) { @@ -481,8 +643,7 @@ trimmed = trimmed.substring(1).trim(); } if (!protocols.isEmpty()) { - log.warn(sm.getString("sslHostConfig.prefix_missing", - trimmed, getHostName())); + log.warn(sm.getString("sslHostConfig.prefix_missing", trimmed, getHostName())); } if (trimmed.equalsIgnoreCase(Constants.SSL_PROTO_ALL)) { protocols.addAll(SSL_PROTO_ALL_SET); @@ -526,8 +687,43 @@ } + /** + * @return the configured named groups + */ + public String getGroups() { + return groups; + } + + + /** + * Set the enabled named groups. + * @param groupsString the case sensitive comma separated list of groups + */ + public void setGroups(String groupsString) { + if (groupsString != null) { + LinkedHashSet groupList = new LinkedHashSet<>(); + String[] groupNames = groupsString.split(","); + for (String groupName : groupNames) { + Group group = Group.valueOf(groupName.trim()); + groupList.add(group); + } + this.groups = groupsString; + this.groupList = groupList; + } + } + + + /** + * @return the groupList + */ + public LinkedHashSet getGroupList() { + return this.groupList; + } + + // ---------------------------------- JSSE specific configuration properties + public void setKeyManagerAlgorithm(String keyManagerAlgorithm) { setProperty("keyManagerAlgorithm", Type.JSSE); this.keyManagerAlgorithm = keyManagerAlgorithm; @@ -562,7 +758,7 @@ public void setTrustManagerClassName(String trustManagerClassName) { - setProperty("trustManagerClassName", Type.JSSE); + setTrustProperty("trustManagerClassName", Type.JSSE); this.trustManagerClassName = trustManagerClassName; } @@ -573,7 +769,7 @@ public void setTruststoreAlgorithm(String truststoreAlgorithm) { - setProperty("truststoreAlgorithm", Type.JSSE); + setTrustProperty("truststoreAlgorithm", Type.JSSE); this.truststoreAlgorithm = truststoreAlgorithm; } @@ -584,7 +780,7 @@ public void setTruststoreFile(String truststoreFile) { - setProperty("truststoreFile", Type.JSSE); + setTrustProperty("truststoreFile", Type.JSSE); this.truststoreFile = truststoreFile; } @@ -595,7 +791,7 @@ public void setTruststorePassword(String truststorePassword) { - setProperty("truststorePassword", Type.JSSE); + setTrustProperty("truststorePassword", Type.JSSE); this.truststorePassword = truststorePassword; } @@ -606,7 +802,7 @@ public void setTruststoreProvider(String truststoreProvider) { - setProperty("truststoreProvider", Type.JSSE); + setTrustProperty("truststoreProvider", Type.JSSE); this.truststoreProvider = truststoreProvider; } @@ -625,7 +821,7 @@ public void setTruststoreType(String truststoreType) { - setProperty("truststoreType", Type.JSSE); + setTrustProperty("truststoreType", Type.JSSE); this.truststoreType = truststoreType; } @@ -649,6 +845,7 @@ public void setTrustStore(KeyStore truststore) { + setTrustProperty("trustStore", Type.JSSE); this.truststore = truststore; } @@ -656,19 +853,18 @@ public KeyStore getTruststore() throws IOException { KeyStore result = truststore; if (result == null) { - if (truststoreFile != null){ + if (truststoreFile != null) { try { - result = SSLUtilBase.getStore(getTruststoreType(), getTruststoreProvider(), - getTruststoreFile(), getTruststorePassword(), null); + result = SSLUtilBase.getStore(getTruststoreType(), getTruststoreProvider(), getTruststoreFile(), + getTruststorePassword(), null); } catch (IOException ioe) { Throwable cause = ioe.getCause(); if (cause instanceof UnrecoverableKeyException) { // Log a warning we had a password issue - log.warn(sm.getString("sslHostConfig.invalid_truststore_password"), - cause); + log.warn(sm.getString("sslHostConfig.invalid_truststore_password"), cause); // Re-try - result = SSLUtilBase.getStore(getTruststoreType(), getTruststoreProvider(), - getTruststoreFile(), null, null); + result = SSLUtilBase.getStore(getTruststoreType(), getTruststoreProvider(), getTruststoreFile(), + null, null); } else { // Something else went wrong - re-throw throw ioe; @@ -694,7 +890,7 @@ public void setCaCertificateFile(String caCertificateFile) { - if (setProperty("caCertificateFile", Type.OPENSSL)) { + if (setTrustProperty("caCertificateFile", Type.OPENSSL)) { // Reset default JSSE trust store if not a JSSE configuration if (truststoreFile != null) { truststoreFile = null; @@ -710,7 +906,7 @@ public void setCaCertificatePath(String caCertificatePath) { - if (setProperty("caCertificatePath", Type.OPENSSL)) { + if (setTrustProperty("caCertificatePath", Type.OPENSSL)) { // Reset default JSSE trust store if not a JSSE configuration if (truststoreFile != null) { truststoreFile = null; @@ -787,12 +983,12 @@ public static String adjustRelativePath(String path) throws FileNotFoundException { // Empty or null path can't point to anything useful. The assumption is // that the value is deliberately empty / null so leave it that way. - if (path == null || path.length() == 0) { + if (path == null || path.isEmpty()) { return path; } String newPath = path; File f = new File(newPath); - if ( !f.isAbsolute()) { + if (!f.isAbsolute()) { newPath = System.getProperty(Constants.CATALINA_BASE_PROP) + File.separator + newPath; f = new File(newPath); } @@ -824,31 +1020,25 @@ } public boolean isOptional() { - return optional; + return optional; } public static CertificateVerification fromString(String value) { - if ("true".equalsIgnoreCase(value) || - "yes".equalsIgnoreCase(value) || - "require".equalsIgnoreCase(value) || + if ("true".equalsIgnoreCase(value) || "yes".equalsIgnoreCase(value) || "require".equalsIgnoreCase(value) || "required".equalsIgnoreCase(value)) { return REQUIRED; - } else if ("optional".equalsIgnoreCase(value) || - "want".equalsIgnoreCase(value)) { + } else if ("optional".equalsIgnoreCase(value) || "want".equalsIgnoreCase(value)) { return OPTIONAL; - } else if ("optionalNoCA".equalsIgnoreCase(value) || - "optional_no_ca".equalsIgnoreCase(value)) { + } else if ("optionalNoCA".equalsIgnoreCase(value) || "optional_no_ca".equalsIgnoreCase(value)) { return OPTIONAL_NO_CA; - } else if ("false".equalsIgnoreCase(value) || - "no".equalsIgnoreCase(value) || + } else if ("false".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value) || "none".equalsIgnoreCase(value)) { return NONE; } else { // Could be a typo. Don't default to NONE since that is not // secure. Force user to fix config. Could default to REQUIRED // instead. - throw new IllegalArgumentException( - sm.getString("sslHostConfig.certificateVerificationInvalid", value)); + throw new IllegalArgumentException(sm.getString("sslHostConfig.certificateVerificationInvalid", value)); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLHostConfigCertificate.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,6 +30,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.net.openssl.ciphers.Authentication; +import org.apache.tomcat.util.net.openssl.ciphers.SignatureScheme; import org.apache.tomcat.util.res.StringManager; public class SSLHostConfigCertificate implements Serializable { @@ -43,17 +44,16 @@ static final String DEFAULT_KEYSTORE_PROVIDER = System.getProperty("javax.net.ssl.keyStoreProvider"); static final String DEFAULT_KEYSTORE_TYPE = System.getProperty("javax.net.ssl.keyStoreType", "JKS"); - private static final String DEFAULT_KEYSTORE_FILE = - System.getProperty("user.home") + File.separator + ".keystore"; + private static final String DEFAULT_KEYSTORE_FILE = System.getProperty("user.home") + File.separator + ".keystore"; private static final String DEFAULT_KEYSTORE_PASSWORD = "changeit"; // Internal private ObjectName oname; /* - * OpenSSL can handle multiple certs in a single config so the reference to the context is at the virtual host - * level. JSSE can't so the reference is held here on the certificate. Typically, the SSLContext is generated from - * the configuration but, particularly in embedded scenarios, it can be provided directly. + * OpenSSL can handle multiple certs in a single config so the reference to the context is at the virtual host + * level. JSSE can't so the reference is held here on the certificate. Typically, the SSLContext is generated from + * the configuration but, particularly in embedded scenarios, it can be provided directly. */ private transient volatile SSLContext sslContextProvided; private transient volatile SSLContext sslContextGenerated; @@ -84,7 +84,7 @@ private StoreType storeType = null; public SSLHostConfigCertificate() { - this(null, Type.UNDEFINED); + this(null, DEFAULT_TYPE); } public SSLHostConfigCertificate(SSLHostConfig sslHostConfig, Type type) { @@ -163,8 +163,7 @@ // JSSE public void setCertificateKeyAlias(String certificateKeyAlias) { - sslHostConfig.setProperty( - "Certificate.certificateKeyAlias", SSLHostConfig.Type.JSSE); + sslHostConfig.setProperty("Certificate.certificateKeyAlias", SSLHostConfig.Type.JSSE); this.certificateKeyAlias = certificateKeyAlias; } @@ -175,8 +174,7 @@ public void setCertificateKeystoreFile(String certificateKeystoreFile) { - sslHostConfig.setProperty( - "Certificate.certificateKeystoreFile", SSLHostConfig.Type.JSSE); + sslHostConfig.setProperty("Certificate.certificateKeystoreFile", SSLHostConfig.Type.JSSE); setStoreType("Certificate.certificateKeystoreFile", StoreType.KEYSTORE); this.certificateKeystoreFile = certificateKeystoreFile; } @@ -188,8 +186,7 @@ public void setCertificateKeystorePassword(String certificateKeystorePassword) { - sslHostConfig.setProperty( - "Certificate.certificateKeystorePassword", SSLHostConfig.Type.JSSE); + sslHostConfig.setProperty("Certificate.certificateKeystorePassword", SSLHostConfig.Type.JSSE); setStoreType("Certificate.certificateKeystorePassword", StoreType.KEYSTORE); this.certificateKeystorePassword = certificateKeystorePassword; } @@ -201,8 +198,7 @@ public void setCertificateKeystorePasswordFile(String certificateKeystorePasswordFile) { - sslHostConfig.setProperty( - "Certificate.certificateKeystorePasswordFile", SSLHostConfig.Type.JSSE); + sslHostConfig.setProperty("Certificate.certificateKeystorePasswordFile", SSLHostConfig.Type.JSSE); setStoreType("Certificate.certificateKeystorePasswordFile", StoreType.KEYSTORE); this.certificateKeystorePasswordFile = certificateKeystorePasswordFile; } @@ -214,8 +210,7 @@ public void setCertificateKeystoreProvider(String certificateKeystoreProvider) { - sslHostConfig.setProperty( - "Certificate.certificateKeystoreProvider", SSLHostConfig.Type.JSSE); + sslHostConfig.setProperty("Certificate.certificateKeystoreProvider", SSLHostConfig.Type.JSSE); setStoreType("Certificate.certificateKeystoreProvider", StoreType.KEYSTORE); this.certificateKeystoreProvider = certificateKeystoreProvider; } @@ -227,8 +222,7 @@ public void setCertificateKeystoreType(String certificateKeystoreType) { - sslHostConfig.setProperty( - "Certificate.certificateKeystoreType", SSLHostConfig.Type.JSSE); + sslHostConfig.setProperty("Certificate.certificateKeystoreType", SSLHostConfig.Type.JSSE); setStoreType("Certificate.certificateKeystoreType", StoreType.KEYSTORE); this.certificateKeystoreType = certificateKeystoreType; } @@ -251,15 +245,20 @@ KeyStore result = certificateKeystore; if (result == null && storeType == StoreType.KEYSTORE) { - result = SSLUtilBase.getStore(getCertificateKeystoreType(), - getCertificateKeystoreProvider(), getCertificateKeystoreFile(), - getCertificateKeystorePassword(), getCertificateKeystorePasswordFile()); + result = SSLUtilBase.getStore(getCertificateKeystoreType(), getCertificateKeystoreProvider(), + getCertificateKeystoreFile(), getCertificateKeystorePassword(), + getCertificateKeystorePasswordFile()); } return result; } + KeyStore getCertificateKeystoreInternal() { + return certificateKeystore; + } + + public void setCertificateKeyManager(X509KeyManager certificateKeyManager) { this.certificateKeyManager = certificateKeyManager; } @@ -309,8 +308,8 @@ if (storeType == null) { storeType = type; } else if (storeType != type) { - log.warn(sm.getString("sslHostConfigCertificate.mismatch", - name, sslHostConfig.getHostName(), type, this.storeType)); + log.warn(sm.getString("sslHostConfigCertificate.mismatch", name, sslHostConfig.getHostName(), type, + this.storeType)); } } @@ -323,12 +322,19 @@ UNDEFINED, RSA(Authentication.RSA), - DSA(Authentication.DSS), - EC(Authentication.ECDH, Authentication.ECDSA); + DSA(Authentication.DSS, Authentication.EdDSA), + EC(Authentication.ECDH, Authentication.ECDSA), + MLDSA("ML-DSA", Authentication.MLDSA); + private final String keyType; private final Set compatibleAuthentications; Type(Authentication... authentications) { + this(null, authentications); + } + + Type(String keyType, Authentication... authentications) { + this.keyType = keyType; compatibleAuthentications = new HashSet<>(); if (authentications != null) { compatibleAuthentications.addAll(Arrays.asList(authentications)); @@ -338,6 +344,18 @@ public boolean isCompatibleWith(Authentication au) { return compatibleAuthentications.contains(au); } + + public boolean isCompatibleWith(SignatureScheme scheme) { + return compatibleAuthentications.contains(scheme.getAuth()); + } + + public String getKeyType() { + if (keyType != null) { + return keyType; + } + return super.toString(); + } + } enum StoreType { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLImplementation.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLImplementation.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLImplementation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLImplementation.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,9 +27,8 @@ import org.apache.tomcat.util.res.StringManager; /** - * Provides a factory and base implementation for the Tomcat specific mechanism - * that allows alternative SSL/TLS implementations to be used without requiring - * the implementation of a full JSSE provider. + * Provides a factory and base implementation for the Tomcat specific mechanism that allows alternative SSL/TLS + * implementations to be used without requiring the implementation of a full JSSE provider. */ public abstract class SSLImplementation { @@ -37,19 +36,16 @@ private static final StringManager sm = StringManager.getManager(SSLImplementation.class); /** - * Obtain an instance (not a singleton) of the implementation with the given - * class name. + * Obtain an instance (not a singleton) of the implementation with the given class name. * - * @param className The class name of the required implementation or null to - * use the default (currently {@link JSSEImplementation}. + * @param className The class name of the required implementation or null to use the default (currently + * {@link JSSEImplementation}). * * @return An instance of the required implementation * - * @throws ClassNotFoundException If an instance of the requested class - * cannot be created + * @throws ClassNotFoundException If an instance of the requested class cannot be created */ - public static SSLImplementation getInstance(String className) - throws ClassNotFoundException { + public static SSLImplementation getInstance(String className) throws ClassNotFoundException { if (className == null) { return new JSSEImplementation(); } @@ -69,12 +65,10 @@ /** * Obtain an instance of SSLSupport. * - * @param session The SSL session - * @param additionalAttributes Additional SSL attributes that are not - * available from the session. + * @param session The SSL session + * @param additionalAttributes Additional SSL attributes that are not available from the session. * - * @return An instance of SSLSupport based on the given session and the - * provided additional attributes + * @return An instance of SSLSupport based on the given session and the provided additional attributes */ public abstract SSLSupport getSSLSupport(SSLSession session, Map> additionalAttributes); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLSessionManager.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLSessionManager.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLSessionManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLSessionManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,8 +17,7 @@ package org.apache.tomcat.util.net; /** - * Defines an interface used to manage SSL sessions. The manager operates on a - * single session. + * Defines an interface used to manage SSL sessions. The manager operates on a single session. */ public interface SSLSessionManager { /** diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLSupport.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLSupport.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLSupport.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLSupport.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,7 @@ /** * The Request attribute key for the cipher suite. */ - String CIPHER_SUITE_KEY = - "jakarta.servlet.request.cipher_suite"; + String CIPHER_SUITE_KEY = "jakarta.servlet.request.cipher_suite"; /** * The Request attribute key for the key size. @@ -37,49 +36,38 @@ /** * The Request attribute key for the client certificate chain. */ - String CERTIFICATE_KEY = - "jakarta.servlet.request.X509Certificate"; + String CERTIFICATE_KEY = "jakarta.servlet.request.X509Certificate"; /** - * The Request attribute key for the session id. - * This one is a Tomcat extension to the Servlet spec. + * The Request attribute key for the session id. This one is a Tomcat extension to the Servlet spec. */ - String SESSION_ID_KEY = - "jakarta.servlet.request.ssl_session_id"; + String SESSION_ID_KEY = "jakarta.servlet.request.ssl_session_id"; /** - * The request attribute key for the session manager. - * This one is a Tomcat extension to the Servlet spec. + * The request attribute key for the session manager. This one is a Tomcat extension to the Servlet spec. */ - String SESSION_MGR = - "jakarta.servlet.request.ssl_session_mgr"; + String SESSION_MGR = "jakarta.servlet.request.ssl_session_mgr"; /** - * The request attribute key under which the String indicating the protocol - * that created the SSL socket is recorded - e.g. TLSv1 or TLSv1.2 etc. + * The request attribute key under which the String indicating the protocol that created the SSL socket is recorded + * - e.g. TLSv1 or TLSv1.2 etc. */ - String PROTOCOL_VERSION_KEY = - "org.apache.tomcat.util.net.secure_protocol_version"; + String PROTOCOL_VERSION_KEY = "org.apache.tomcat.util.net.secure_protocol_version"; /** - * The request attribute key under which the String indicating the ciphers - * requested by the client are recorded. + * The request attribute key under which the String indicating the ciphers requested by the client are recorded. */ - String REQUESTED_CIPHERS_KEY = - "org.apache.tomcat.util.net.secure_requested_ciphers"; + String REQUESTED_CIPHERS_KEY = "org.apache.tomcat.util.net.secure_requested_ciphers"; /** - * The request attribute key under which the String indicating the protocols - * requested by the client are recorded. + * The request attribute key under which the String indicating the protocols requested by the client are recorded. */ - String REQUESTED_PROTOCOL_VERSIONS_KEY = - "org.apache.tomcat.util.net.secure_requested_protocol_versions"; + String REQUESTED_PROTOCOL_VERSIONS_KEY = "org.apache.tomcat.util.net.secure_requested_protocol_versions"; /** * The cipher suite being used on this connection. * - * @return The name of the cipher suite as returned by the SSL/TLS - * implementation + * @return The name of the cipher suite as returned by the SSL/TLS implementation * * @throws IOException If an error occurs trying to obtain the cipher suite */ @@ -88,39 +76,28 @@ /** * The client certificate chain (if any). * - * @return The certificate chain presented by the client with the peer's - * certificate first, followed by those of any certificate - * authorities + * @return The certificate chain presented by the client with the peer's certificate first, followed by those of any + * certificate authorities * - * @throws IOException If an error occurs trying to obtain the certificate - * chain + * @throws IOException If an error occurs trying to obtain the certificate chain */ X509Certificate[] getPeerCertificateChain() throws IOException; /** * The server certificate chain (if any) that were sent to the peer. * - * @return The certificate chain sent with the server - * certificate first, followed by those of any certificate - * authorities + * @return The certificate chain sent with the server certificate first, followed by those of any certificate + * authorities */ default X509Certificate[] getLocalCertificateChain() { return null; } /** - * Get the keysize. - * - * What we're supposed to put here is ill-defined by the - * Servlet spec (S 4.7 again). There are at least 4 potential - * values that might go here: - * - * (a) The size of the encryption key - * (b) The size of the MAC key - * (c) The size of the key-exchange key - * (d) The size of the signature key used by the server - * - * Unfortunately, all of these values are nonsensical. + * Get the key size. What we're supposed to put here is ill-defined by the Servlet spec (S 4.7 again). There are at + * least 4 potential values that might go here: (a) The size of the encryption key (b) The size of the MAC key (c) + * The size of the key-exchange key (d) The size of the signature key used by the server Unfortunately, all of these + * values are nonsensical. * * @return The effective key size for the current cipher suite * @@ -138,27 +115,23 @@ String getSessionId() throws IOException; /** - * @return the protocol String indicating how the SSL socket was created - * e.g. TLSv1 or TLSv1.2 etc. + * @return the protocol String indicating how the SSL socket was created e.g. TLSv1 or TLSv1.2 etc. * - * @throws IOException If an error occurs trying to obtain the protocol - * information from the socket + * @throws IOException If an error occurs trying to obtain the protocol information from the socket */ String getProtocol() throws IOException; /** * @return the list of SSL/TLS protocol versions requested by the client * - * @throws IOException If an error occurs trying to obtain the client - * requested protocol information from the socket + * @throws IOException If an error occurs trying to obtain the client requested protocol information from the socket */ String getRequestedProtocols() throws IOException; /** - * @return the list of SSL/TLS ciphers requested by the client - * - * @throws IOException If an error occurs trying to obtain the client - * request cipher information from the socket - */ - String getRequestedCiphers() throws IOException; + * @return the list of SSL/TLS ciphers requested by the client + * + * @throws IOException If an error occurs trying to obtain the client request cipher information from the socket + */ + String getRequestedCiphers() throws IOException; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,14 +21,31 @@ import javax.net.ssl.KeyManager; import javax.net.ssl.SSLSessionContext; import javax.net.ssl.TrustManager; +import javax.net.ssl.X509KeyManager; +import javax.net.ssl.X509TrustManager; /** - * Provides a common interface for {@link SSLImplementation}s to create the - * necessary JSSE implementation objects for TLS connections created via the - * JSSE API. + * Provides a common interface for {@link SSLImplementation}s to create the necessary JSSE implementation objects for + * TLS connections created via the JSSE API. */ public interface SSLUtil { + /** + * Creates an instance of Tomcat's {@code SSLContext} from the provided inputs. Typically used when the user wants + * to provide a pre-configured {@code javax.net.ssl.SSLContext} instance. There is no need to call + * {@link SSLContext#init(KeyManager[], TrustManager[], java.security.SecureRandom)} on the returned value. + * + * @param sslContext The JSSE SSL context + * @param keyManager The JSSE key manager + * @param trustManager The JSSE trust manager + * + * @return An instance of Tomcat's {@code SSLContext} formed from the provided inputs. + */ + static SSLContext createSSLContext(javax.net.ssl.SSLContext sslContext, X509KeyManager keyManager, + X509TrustManager trustManager) { + return new SSLContextWrapper(sslContext, keyManager, trustManager); + } + SSLContext createSSLContext(List negotiableProtocols) throws Exception; KeyManager[] getKeyManagers() throws Exception; @@ -38,45 +55,40 @@ void configureSessionContext(SSLSessionContext sslSessionContext); /** - * The set of enabled protocols is the intersection of the implemented - * protocols and the configured protocols. If no protocols are explicitly - * configured, then all of the implemented protocols will be included in the - * returned array. + * The set of enabled protocols is the intersection of the implemented protocols and the configured protocols. If no + * protocols are explicitly configured, then all of the implemented protocols will be included in the returned + * array. * - * @return The protocols currently enabled and available for clients to - * select from for the associated connection + * @return The protocols currently enabled and available for clients to select from for the associated connection * - * @throws IllegalArgumentException If there is no intersection between the - * implemented and configured protocols + * @throws IllegalArgumentException If there is no intersection between the implemented and configured protocols */ String[] getEnabledProtocols() throws IllegalArgumentException; /** - * The set of enabled ciphers is the intersection of the implemented ciphers - * and the configured ciphers. If no ciphers are explicitly configured, then - * the default ciphers will be included in the returned array. + * The set of enabled ciphers is the intersection of the implemented ciphers and the configured ciphers. If no + * ciphers are explicitly configured, then the default ciphers will be included in the returned array. *

    - * The ciphers used during the TLS handshake may be further restricted by - * the {@link #getEnabledProtocols()} and the certificates. + * The ciphers used during the TLS handshake may be further restricted by the {@link #getEnabledProtocols()} and the + * certificates. * - * @return The ciphers currently enabled and available for clients to select - * from for the associated connection + * @return The ciphers currently enabled and available for clients to select from for the associated connection * - * @throws IllegalArgumentException If there is no intersection between the - * implemented and configured ciphers + * @throws IllegalArgumentException If there is no intersection between the implemented and configured ciphers */ String[] getEnabledCiphers() throws IllegalArgumentException; /** - * Optional interface that can be implemented by - * {@link javax.net.ssl.SSLEngine}s to indicate that they support ALPN and - * can provided the protocol agreed with the client. + * Optional interface that can be implemented by {@link javax.net.ssl.SSLEngine}s to indicate that they support ALPN + * and can provide the protocol agreed with the client. */ interface ProtocolInfo { /** * ALPN information. + * * @return the protocol selected using ALPN */ String getNegotiatedProtocol(); } + } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLUtilBase.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLUtilBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SSLUtilBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SSLUtilBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,6 +28,7 @@ import java.security.cert.CRL; import java.security.cert.CRLException; import java.security.cert.CertPathParameters; +import java.security.cert.CertPathValidator; import java.security.cert.CertStore; import java.security.cert.CertStoreParameters; import java.security.cert.Certificate; @@ -37,12 +38,14 @@ import java.security.cert.CertificateNotYetValidException; import java.security.cert.CollectionCertStoreParameters; import java.security.cert.PKIXBuilderParameters; +import java.security.cert.PKIXRevocationChecker; import java.security.cert.X509CertSelector; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.EnumSet; import java.util.Enumeration; import java.util.List; import java.util.Locale; @@ -59,6 +62,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.compat.JreCompat; import org.apache.tomcat.util.file.ConfigFileLoader; import org.apache.tomcat.util.net.jsse.JSSEKeyManager; import org.apache.tomcat.util.net.jsse.PEMFile; @@ -116,8 +120,7 @@ this.enabledProtocols = enabledProtocols.toArray(new String[0]); if (enabledProtocols.contains(Constants.SSL_PROTO_TLSv1_3) && - sslHostConfig.getCertificateVerification().isOptional() && - !isTls13RenegAuthAvailable() && warnTls13) { + sslHostConfig.getCertificateVerification().isOptional() && !isTls13RenegAuthAvailable() && warnTls13) { log.warn(sm.getString("sslUtilBase.tls13.auth")); } @@ -125,12 +128,11 @@ sslHostConfig.setTls13RenegotiationAvailable(isTls13RenegAuthAvailable()); // Calculate the enabled ciphers - if (sslHostConfig.getCiphers().startsWith("PROFILE=")) { - // OpenSSL profiles - // TODO: sslHostConfig can query that with Panama, but skip for now + if (!JreCompat.isJre22Available() && sslHostConfig.getCiphers().startsWith("PROFILE=")) { + // OpenSSL profiles cannot be resolved without Java 22 this.enabledCiphers = new String[0]; } else { - boolean warnOnSkip = !sslHostConfig.getCiphers().equals(SSLHostConfig.DEFAULT_TLS_CIPHERS); + boolean warnOnSkip = !sslHostConfig.getCiphers().equals(SSLHostConfig.DEFAULT_TLS_CIPHERS_12); List configuredCiphers = sslHostConfig.getJsseCipherNames(); Set implementedCiphers = getImplementedCiphers(); List enabledCiphers = @@ -145,7 +147,7 @@ List enabled = new ArrayList<>(); - if (implemented.size() == 0) { + if (implemented.isEmpty()) { // Unable to determine the list of available protocols. This will // have been logged previously. // Use the configuredProtocols and hope they work. If not, an error @@ -160,8 +162,7 @@ // Don't use the defaults in this case. They may be less secure // than the configuration the user intended. // Force the failure of the connector - throw new IllegalArgumentException( - sm.getString("sslUtilBase.noneSupported", name, configured)); + throw new IllegalArgumentException(sm.getString("sslUtilBase.noneSupported", name, configured)); } if (log.isDebugEnabled()) { log.debug(sm.getString("sslUtilBase.active", name, enabled)); @@ -187,10 +188,10 @@ /* * Gets the key- or truststore with the specified type, path, password and password file. */ - static KeyStore getStore(String type, String provider, String path, - String pass, String passFile) throws IOException { + static KeyStore getStore(String type, String provider, String path, String pass, String passFile) + throws IOException { - KeyStore ks = null; + KeyStore ks; InputStream istream = null; try { if (provider == null) { @@ -204,9 +205,7 @@ } else { // Some key store types (e.g. hardware) expect the InputStream // to be null - if(!("PKCS11".equalsIgnoreCase(type) || - path.isEmpty() || - "NONE".equalsIgnoreCase(path))) { + if (!("PKCS11".equalsIgnoreCase(type) || path.isEmpty() || "NONE".equalsIgnoreCase(path))) { istream = ConfigFileLoader.getSource().getResource(path).getInputStream(); } @@ -219,22 +218,21 @@ // Therefore: // - generally use null if pass is null or "" // - for JKS or PKCS12 only use null if pass is null - // (because JKS will auto-switch to PKCS12) + // (because JKS will auto-switch to PKCS12) char[] storePass = null; - String passToUse = null; + String passToUse; if (passFile != null) { - try (BufferedReader reader = - new BufferedReader(new InputStreamReader( - ConfigFileLoader.getSource().getResource(passFile).getInputStream(), - StandardCharsets.UTF_8))) { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(ConfigFileLoader.getSource().getResource(passFile).getInputStream(), + StandardCharsets.UTF_8))) { passToUse = reader.readLine(); } } else { passToUse = pass; } - if (passToUse != null && (!"".equals(passToUse) || - "JKS".equalsIgnoreCase(type) || "PKCS12".equalsIgnoreCase(type))) { + if (passToUse != null && + (!passToUse.isEmpty() || "JKS".equalsIgnoreCase(type) || "PKCS12".equalsIgnoreCase(type))) { storePass = passToUse.toCharArray(); } KeyStoreUtil.load(ks, istream, storePass); @@ -243,10 +241,9 @@ // May be expected when working with a trust store // Re-throw. Caller will catch and log as required throw ioe; - } catch(Exception ex) { - String msg = sm.getString("sslUtilBase.keystore_load_failed", type, path, - ex.getMessage()); - log.error(msg, ex); + } catch (Exception e) { + String msg = sm.getString("sslUtilBase.keystore_load_failed", type, path, e.getMessage()); + log.error(msg, e); throw new IOException(msg); } finally { if (istream != null) { @@ -309,23 +306,17 @@ KeyStore ksUsed = ks; /* - * Use an in memory key store where possible. - * For PEM format keys and certificates, it allows them to be imported - * into the expected format. - * For Java key stores with PKCS8 encoded keys (e.g. JKS files), it - * enables Tomcat to handle the case where multiple keys exist in the - * key store, each with a different password. The KeyManagerFactory - * can't handle that so using an in memory key store with just the - * required key works around that. - * Other keys stores (hardware, MS, etc.) will be used as is. + * Use an in memory key store where possible. For PEM format keys and certificates, it allows them to be + * imported into the expected format. For Java key stores with PKCS8 encoded keys (e.g. JKS files), it enables + * Tomcat to handle the case where multiple keys exist in the key store, each with a different password. The + * KeyManagerFactory can't handle that so using an in memory key store with just the required key works around + * that. Other keys stores (hardware, MS, etc.) will be used as is. */ char[] keyPassArray = null; String keyPassToUse = null; if (keyPassFile != null) { - try (BufferedReader reader = - new BufferedReader(new InputStreamReader( - ConfigFileLoader.getSource().getResource(keyPassFile).getInputStream(), - StandardCharsets.UTF_8))) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader( + ConfigFileLoader.getSource().getResource(keyPassFile).getInputStream(), StandardCharsets.UTF_8))) { keyPassToUse = reader.readLine(); } } else { @@ -351,9 +342,9 @@ throw new IOException(sm.getString("sslUtilBase.noCertFile")); } - PEMFile privateKeyFile = new PEMFile( - certificate.getCertificateKeyFile() != null ? certificate.getCertificateKeyFile() : certificate.getCertificateFile(), - keyPass, keyPassFile, null); + PEMFile privateKeyFile = + new PEMFile(certificate.getCertificateKeyFile() != null ? certificate.getCertificateKeyFile() : + certificate.getCertificateFile(), keyPass, keyPassFile, null); PEMFile certificateFile = new PEMFile(certificate.getCertificateFile()); Collection chain = new ArrayList<>(certificateFile.getCertificates()); @@ -368,7 +359,7 @@ // Switch to in-memory key store ksUsed = KeyStore.getInstance("JKS"); - ksUsed.load(null, null); + ksUsed.load(null, null); ksUsed.setKeyEntry(keyAlias, privateKeyFile.getPrivateKey(), keyPassArray, chain.toArray(new Certificate[0])); } else { @@ -398,10 +389,9 @@ if (provider == null) { ksUsed = KeyStore.getInstance(certificate.getCertificateKeystoreType()); } else { - ksUsed = KeyStore.getInstance(certificate.getCertificateKeystoreType(), - provider); + ksUsed = KeyStore.getInstance(certificate.getCertificateKeystoreType(), provider); } - ksUsed.load(null, null); + ksUsed.load(null, null); ksUsed.setKeyEntry(keyAlias, k, keyPassArray, ks.getCertificateChain(keyAlias)); } // Non-PKCS#8 key stores will use the original key store @@ -421,8 +411,8 @@ if ("JKS".equals(certificate.getCertificateKeystoreType())) { alias = alias.toLowerCase(Locale.ENGLISH); } - for(int i = 0; i < kms.length; i++) { - kms[i] = new JSSEKeyManager((X509KeyManager)kms[i], alias); + for (int i = 0; i < kms.length; i++) { + kms[i] = new JSSEKeyManager((X509KeyManager) kms[i], alias); } } @@ -446,16 +436,15 @@ public TrustManager[] getTrustManagers() throws Exception { String className = sslHostConfig.getTrustManagerClassName(); - if(className != null && className.length() > 0) { - ClassLoader classLoader = getClass().getClassLoader(); - Class clazz = classLoader.loadClass(className); - if(!(TrustManager.class.isAssignableFrom(clazz))){ - throw new InstantiationException(sm.getString( - "sslUtilBase.invalidTrustManagerClassName", className)); - } - Object trustManagerObject = clazz.getConstructor().newInstance(); - TrustManager trustManager = (TrustManager) trustManagerObject; - return new TrustManager[]{ trustManager }; + if (className != null && !className.isEmpty()) { + ClassLoader classLoader = getClass().getClassLoader(); + Class clazz = classLoader.loadClass(className); + if (!(TrustManager.class.isAssignableFrom(clazz))) { + throw new InstantiationException(sm.getString("sslUtilBase.invalidTrustManagerClassName", className)); + } + Object trustManagerObject = clazz.getConstructor().newInstance(); + TrustManager trustManager = (TrustManager) trustManagerObject; + return new TrustManager[] { trustManager }; } TrustManager[] tms = null; @@ -467,17 +456,16 @@ String crlf = sslHostConfig.getCertificateRevocationListFile(); boolean revocationEnabled = sslHostConfig.getRevocationEnabled(); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); if ("PKIX".equalsIgnoreCase(algorithm)) { - TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); CertPathParameters params = getParameters(crlf, trustStore, revocationEnabled); ManagerFactoryParameters mfp = new CertPathTrustManagerParameters(params); tmf.init(mfp); tms = tmf.getTrustManagers(); } else { - TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); tmf.init(trustStore); tms = tmf.getTrustManagers(); - if (crlf != null && crlf.length() > 0) { + if (crlf != null && !crlf.isEmpty()) { throw new CRLException(sm.getString("sslUtilBase.noCrlSupport", algorithm)); } // Only warn if the attribute has been explicitly configured @@ -523,64 +511,81 @@ /** - * Return the initialization parameters for the TrustManager. - * Currently, only the default PKIX is supported. + * Return the initialization parameters for the TrustManager. Currently, only the default PKIX is + * supported. + * + * @param crlf The path to the CRL file. + * @param trustStore The configured TrustStore. + * @param revocationEnabled Should the JSSE provider perform revocation checks? Ignored if {@code crlf} is non-null. + * Configuration of revocation checks are expected to be via proprietary JSSE provider + * methods. * - * @param crlf The path to the CRL file. - * @param trustStore The configured TrustStore. - * @param revocationEnabled Should the JSSE provider perform revocation - * checks? Ignored if {@code crlf} is non-null. - * Configuration of revocation checks are expected - * to be via proprietary JSSE provider methods. * @return The parameters including the CRLs and TrustStore. + * * @throws Exception An error occurred */ - protected CertPathParameters getParameters(String crlf, KeyStore trustStore, - boolean revocationEnabled) throws Exception { + protected CertPathParameters getParameters(final String crlf, final KeyStore trustStore, + final boolean revocationEnabled) throws Exception { - PKIXBuilderParameters xparams = - new PKIXBuilderParameters(trustStore, new X509CertSelector()); - if (crlf != null && crlf.length() > 0) { + boolean enableRevocation = revocationEnabled; + + PKIXBuilderParameters xparams = new PKIXBuilderParameters(trustStore, new X509CertSelector()); + if (crlf != null && !crlf.isEmpty()) { Collection crls = getCRLs(crlf); CertStoreParameters csp = new CollectionCertStoreParameters(crls); CertStore store = CertStore.getInstance("Collection", csp); xparams.addCertStore(store); - xparams.setRevocationEnabled(true); - } else { - xparams.setRevocationEnabled(revocationEnabled); + enableRevocation = true; } + + if (sslHostConfig.getOcspEnabled()) { + PKIXRevocationChecker revocationChecker =(PKIXRevocationChecker) CertPathValidator.getInstance("PKIX").getRevocationChecker(); + if (sslHostConfig.getOcspSoftFail()) { + revocationChecker.setOptions(EnumSet.of(PKIXRevocationChecker.Option.SOFT_FAIL)); + } else { + revocationChecker.setOptions(Collections.emptySet()); + } + xparams.addCertPathChecker(revocationChecker); + enableRevocation = true; + } + + xparams.setRevocationEnabled(enableRevocation); + xparams.setMaxPathLength(sslHostConfig.getCertificateVerificationDepth()); + return xparams; } /** * Load the collection of CRLs. + * * @param crlf The path to the CRL file. + * * @return the CRLs collection - * @throws IOException Error reading CRL file - * @throws CRLException CRL error + * + * @throws IOException Error reading CRL file + * @throws CRLException CRL error * @throws CertificateException Error processing certificate */ - protected Collection getCRLs(String crlf) - throws IOException, CRLException, CertificateException { + protected Collection getCRLs(String crlf) throws IOException, CRLException, CertificateException { - Collection crls = null; - try { - CertificateFactory cf = CertificateFactory.getInstance("X.509"); - try (InputStream is = ConfigFileLoader.getSource().getResource(crlf).getInputStream()) { - crls = cf.generateCRLs(is); - } - } catch(IOException | CRLException | CertificateException e) { - throw e; + Collection crls; + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + try (InputStream is = ConfigFileLoader.getSource().getResource(crlf).getInputStream()) { + crls = cf.generateCRLs(is); } return crls; } protected abstract Set getImplementedProtocols(); + protected abstract Set getImplementedCiphers(); + protected abstract Log getLog(); + protected abstract boolean isTls13RenegAuthAvailable(); + protected abstract SSLContext createSSLContextInternal(List negotiableProtocols) throws Exception; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SecureNio2Channel.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SecureNio2Channel.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SecureNio2Channel.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SecureNio2Channel.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,12 +44,14 @@ import org.apache.tomcat.util.buf.ByteBufferUtils; import org.apache.tomcat.util.net.TLSClientHelloExtractor.ExtractorResult; import org.apache.tomcat.util.net.openssl.ciphers.Cipher; +import org.apache.tomcat.util.net.openssl.ciphers.Group; +import org.apache.tomcat.util.net.openssl.ciphers.SignatureScheme; import org.apache.tomcat.util.res.StringManager; /** * Implementation of a secure socket channel for NIO2. */ -public class SecureNio2Channel extends Nio2Channel { +public class SecureNio2Channel extends Nio2Channel { private static final Log log = LogFactory.getLog(SecureNio2Channel.class); private static final StringManager sm = StringManager.getManager(SecureNio2Channel.class); @@ -71,7 +73,7 @@ private volatile boolean handshakeComplete = false; private final AtomicInteger handshakeWrapQueueLength = new AtomicInteger(); - private volatile HandshakeStatus handshakeStatus; //gets set by handshake + private volatile HandshakeStatus handshakeStatus; // gets set by handshake protected boolean closed; protected boolean closing; @@ -79,8 +81,8 @@ private final Map> additionalTlsAttributes = new HashMap<>(); private volatile boolean unwrapBeforeRead; - private final CompletionHandler> handshakeReadCompletionHandler; - private final CompletionHandler> handshakeWriteCompletionHandler; + private final CompletionHandler> handshakeReadCompletionHandler; + private final CompletionHandler> handshakeWriteCompletionHandler; public SecureNio2Channel(SocketBufferHandler bufHandler, Nio2Endpoint endpoint) { super(bufHandler); @@ -96,9 +98,7 @@ handshakeWriteCompletionHandler = new HandshakeWriteCompletionHandler(); } - - private class HandshakeReadCompletionHandler - implements CompletionHandler> { + private class HandshakeReadCompletionHandler implements CompletionHandler> { @Override public void completed(Integer result, SocketWrapperBase attachment) { if (result.intValue() < 0) { @@ -107,6 +107,7 @@ endpoint.processSocket(attachment, SocketEvent.OPEN_READ, false); } } + @Override public void failed(Throwable exc, SocketWrapperBase attachment) { endpoint.processSocket(attachment, SocketEvent.ERROR, false); @@ -114,8 +115,7 @@ } - private class HandshakeWriteCompletionHandler - implements CompletionHandler> { + private class HandshakeWriteCompletionHandler implements CompletionHandler> { @Override public void completed(Integer result, SocketWrapperBase attachment) { if (result.intValue() < 0) { @@ -124,6 +124,7 @@ endpoint.processSocket(attachment, SocketEvent.OPEN_WRITE, false); } } + @Override public void failed(Throwable exc, SocketWrapperBase attachment) { endpoint.processSocket(attachment, SocketEvent.ERROR, false); @@ -132,8 +133,7 @@ @Override - public void reset(AsynchronousSocketChannel channel, SocketWrapperBase socket) - throws IOException { + public void reset(AsynchronousSocketChannel channel, SocketWrapperBase socket) throws IOException { super.reset(channel, socket); sslEngine = null; sniComplete = false; @@ -157,6 +157,7 @@ private class FutureFlush implements Future { private Future integer; private Exception e = null; + protected FutureFlush() { try { integer = sc.write(netOutBuffer); @@ -164,30 +165,33 @@ this.e = e; } } + @Override public boolean cancel(boolean mayInterruptIfRunning) { - return (e != null) ? true : integer.cancel(mayInterruptIfRunning); + return e != null || integer.cancel(mayInterruptIfRunning); } + @Override public boolean isCancelled() { - return (e != null) ? true : integer.isCancelled(); + return e != null || integer.isCancelled(); } + @Override public boolean isDone() { - return (e != null) ? true : integer.isDone(); + return e != null || integer.isDone(); } + @Override - public Boolean get() throws InterruptedException, - ExecutionException { + public Boolean get() throws InterruptedException, ExecutionException { if (e != null) { throw new ExecutionException(e); } return Boolean.valueOf(integer.get().intValue() >= 0); } + @Override public Boolean get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, - TimeoutException { + throws InterruptedException, ExecutionException, TimeoutException { if (e != null) { throw new ExecutionException(e); } @@ -198,8 +202,8 @@ /** * Flush the channel. * - * @return true if the network buffer has been flushed out and - * is empty else false (as a future) + * @return true if the network buffer has been flushed out and is empty else false (as a + * future) */ @Override public Future flush() { @@ -207,17 +211,15 @@ } /** - * Performs SSL handshake, non blocking, but performs NEED_TASK on the same - * thread. Hence, you should never call this method using your Acceptor - * thread, as you would slow down your system significantly. + * Performs SSL handshake, non-blocking, but performs NEED_TASK on the same thread. Hence, you should never call + * this method using your Acceptor thread, as you would slow down your system significantly. *

    - * The return for this operation is 0 if the handshake is complete and a - * positive value if it is not complete. In the event of a positive value - * coming back, the appropriate read/write will already have been called - * with an appropriate CompletionHandler. + * The return for this operation is 0 if the handshake is complete and a positive value if it is not complete. In + * the event of a positive value coming back, the appropriate read/write will already have been called with an + * appropriate CompletionHandler. * - * @return 0 if hand shake is complete, negative if the socket needs to - * close and positive if the handshake is incomplete + * @return 0 if hand shake is complete, negative if the socket needs to close and positive if the handshake is + * incomplete * * @throws IOException if an error occurs during the handshake */ @@ -228,7 +230,7 @@ protected int handshakeInternal(boolean async) throws IOException { if (handshakeComplete) { - return 0; //we have done our initial handshake + return 0; // we have done our initial handshake } if (!sniComplete) { @@ -240,33 +242,33 @@ } } - SSLEngineResult handshake = null; + SSLEngineResult handshake; long timeout = endpoint.getConnectionTimeout(); while (!handshakeComplete) { switch (handshakeStatus) { case NOT_HANDSHAKING: { - //should never happen + // should never happen throw new IOException(sm.getString("channel.nio.ssl.notHandshaking")); } case FINISHED: { if (endpoint.hasNegotiableProtocols()) { if (sslEngine instanceof SSLUtil.ProtocolInfo) { - socketWrapper.setNegotiatedProtocol( - ((SSLUtil.ProtocolInfo) sslEngine).getNegotiatedProtocol()); + socketWrapper + .setNegotiatedProtocol(((SSLUtil.ProtocolInfo) sslEngine).getNegotiatedProtocol()); } else { socketWrapper.setNegotiatedProtocol(sslEngine.getApplicationProtocol()); } } - //we are complete if we have delivered the last package + // we are complete if we have delivered the last package handshakeComplete = !netOutBuffer.hasRemaining(); - //return 0 if we are complete, otherwise we still have data to write + // return 0 if we are complete, otherwise we still have data to write if (handshakeComplete) { return 0; } else { if (async) { - sc.write(netOutBuffer, AbstractEndpoint.toTimeout(timeout), - TimeUnit.MILLISECONDS, socketWrapper, handshakeWriteCompletionHandler); + sc.write(netOutBuffer, AbstractEndpoint.toTimeout(timeout), TimeUnit.MILLISECONDS, + socketWrapper, handshakeWriteCompletionHandler); } else { try { if (timeout > 0) { @@ -282,7 +284,7 @@ } } case NEED_WRAP: { - //perform the wrap function + // perform the wrap function try { handshake = handshakeWrap(); } catch (SSLException e) { @@ -296,14 +298,15 @@ } else if (handshake.getStatus() == Status.CLOSED) { return -1; } else { - //wrap should always work with our buffers - throw new IOException(sm.getString("channel.nio.ssl.unexpectedStatusDuringWrap", handshake.getStatus())); + // wrap should always work with our buffers + throw new IOException( + sm.getString("channel.nio.ssl.unexpectedStatusDuringWrap", handshake.getStatus())); } if (handshakeStatus != HandshakeStatus.NEED_UNWRAP || netOutBuffer.remaining() > 0) { - //should actually return OP_READ if we have NEED_UNWRAP + // should actually return OP_READ if we have NEED_UNWRAP if (async) { - sc.write(netOutBuffer, AbstractEndpoint.toTimeout(timeout), - TimeUnit.MILLISECONDS, socketWrapper, handshakeWriteCompletionHandler); + sc.write(netOutBuffer, AbstractEndpoint.toTimeout(timeout), TimeUnit.MILLISECONDS, + socketWrapper, handshakeWriteCompletionHandler); } else { try { if (timeout > 0) { @@ -317,22 +320,22 @@ } return 1; } - //fall down to NEED_UNWRAP on the same call, will result in a - //BUFFER_UNDERFLOW if it needs data + // fall down to NEED_UNWRAP on the same call, will result in a + // BUFFER_UNDERFLOW if it needs data } //$FALL-THROUGH$ case NEED_UNWRAP: { - //perform the unwrap function + // perform the unwrap function handshake = handshakeUnwrap(); if (handshake.getStatus() == Status.OK) { if (handshakeStatus == HandshakeStatus.NEED_TASK) { handshakeStatus = tasks(); } } else if (handshake.getStatus() == Status.BUFFER_UNDERFLOW) { - //read more data + // read more data if (async) { - sc.read(netInBuffer, AbstractEndpoint.toTimeout(timeout), - TimeUnit.MILLISECONDS, socketWrapper, handshakeReadCompletionHandler); + sc.read(netInBuffer, AbstractEndpoint.toTimeout(timeout), TimeUnit.MILLISECONDS, + socketWrapper, handshakeReadCompletionHandler); } else { try { int read; @@ -350,7 +353,8 @@ } return 1; } else { - throw new IOException(sm.getString("channel.nio.ssl.unexpectedStatusDuringUnwrap", handshake.getStatus())); + throw new IOException( + sm.getString("channel.nio.ssl.unexpectedStatusDuringUnwrap", handshake.getStatus())); } break; } @@ -358,26 +362,26 @@ handshakeStatus = tasks(); break; } - default: throw new IllegalStateException(sm.getString("channel.nio.ssl.invalidStatus", handshakeStatus)); + default: + throw new IllegalStateException(sm.getString("channel.nio.ssl.invalidStatus", handshakeStatus)); } } - //return 0 if we are complete, otherwise recurse to process the task + // return 0 if we are complete, otherwise recurse to process the task return handshakeComplete ? 0 : handshakeInternal(async); } /* - * Peeks at the initial network bytes to determine if the SNI extension is - * present and, if it is, what host name has been requested. Based on the - * provided host name, configure the SSLEngine for this connection. + * Peeks at the initial network bytes to determine if the SNI extension is present and, if it is, what host name has + * been requested. Based on the provided host name, configure the SSLEngine for this connection. */ private int processSNI() throws IOException { // If there is no data to process, trigger a read immediately. This is // an optimisation for the typical case so we don't create an // SNIExtractor only to discover there is no data to process if (netInBuffer.position() == 0) { - sc.read(netInBuffer, AbstractEndpoint.toTimeout(endpoint.getConnectionTimeout()), - TimeUnit.MILLISECONDS, socketWrapper, handshakeReadCompletionHandler); + sc.read(netInBuffer, AbstractEndpoint.toTimeout(endpoint.getConnectionTimeout()), TimeUnit.MILLISECONDS, + socketWrapper, handshakeReadCompletionHandler); return 1; } @@ -388,60 +392,70 @@ // extractor needed more data to process but netInBuffer was full so // expand the buffer and read some more data. int newLimit = Math.min(netInBuffer.capacity() * 2, endpoint.getSniParseLimit()); - log.info(sm.getString("channel.nio.ssl.expandNetInBuffer", - Integer.toString(newLimit))); + log.info(sm.getString("channel.nio.ssl.expandNetInBuffer", Integer.toString(newLimit))); netInBuffer = ByteBufferUtils.expand(netInBuffer, newLimit); - sc.read(netInBuffer, AbstractEndpoint.toTimeout(endpoint.getConnectionTimeout()), - TimeUnit.MILLISECONDS, socketWrapper, handshakeReadCompletionHandler); + sc.read(netInBuffer, AbstractEndpoint.toTimeout(endpoint.getConnectionTimeout()), TimeUnit.MILLISECONDS, + socketWrapper, handshakeReadCompletionHandler); return 1; } String hostName = null; List clientRequestedCiphers = null; List clientRequestedApplicationProtocols = null; + List clientSupportedGroups = null; + List clientSignatureSchemes = null; switch (extractor.getResult()) { - case COMPLETE: - hostName = extractor.getSNIValue(); - clientRequestedApplicationProtocols = - extractor.getClientRequestedApplicationProtocols(); - //$FALL-THROUGH$ to set the client requested ciphers - case NOT_PRESENT: - clientRequestedCiphers = extractor.getClientRequestedCiphers(); - break; - case NEED_READ: - sc.read(netInBuffer, AbstractEndpoint.toTimeout(endpoint.getConnectionTimeout()), - TimeUnit.MILLISECONDS, socketWrapper, handshakeReadCompletionHandler); - return 1; - case UNDERFLOW: - // Unable to buffer enough data to read SNI extension data - if (log.isDebugEnabled()) { - log.debug(sm.getString("channel.nio.ssl.sniDefault")); - } - hostName = endpoint.getDefaultSSLHostConfigName(); - clientRequestedCiphers = Collections.emptyList(); - break; - case NON_SECURE: - netOutBuffer.clear(); - netOutBuffer.put(TLSClientHelloExtractor.USE_TLS_RESPONSE); - netOutBuffer.flip(); - flush(); - throw new IOException(sm.getString("channel.nio.ssl.foundHttp")); + case COMPLETE: + hostName = extractor.getSNIValue(); + socketWrapper.setSniHostName(hostName); + clientRequestedApplicationProtocols = extractor.getClientRequestedApplicationProtocols(); + //$FALL-THROUGH$ to set the client requested ciphers + case NOT_PRESENT: + clientRequestedCiphers = extractor.getClientRequestedCiphers(); + clientSupportedGroups = extractor.getClientSupportedGroups(); + clientSignatureSchemes = extractor.getClientSignatureSchemes(); + break; + case NEED_READ: + sc.read(netInBuffer, AbstractEndpoint.toTimeout(endpoint.getConnectionTimeout()), TimeUnit.MILLISECONDS, + socketWrapper, handshakeReadCompletionHandler); + return 1; + case UNDERFLOW: + // Unable to buffer enough data to read SNI extension data + if (log.isDebugEnabled()) { + log.debug(sm.getString("channel.nio.ssl.sniDefault")); + } + hostName = endpoint.getDefaultSSLHostConfigName(); + clientRequestedCiphers = Collections.emptyList(); + break; + case NON_SECURE: + netOutBuffer.clear(); + netOutBuffer.put(TLSClientHelloExtractor.USE_TLS_RESPONSE); + netOutBuffer.flip(); + flush(); + throw new IOException(sm.getString("channel.nio.ssl.foundHttp")); } if (log.isTraceEnabled()) { log.trace(sm.getString("channel.nio.ssl.sniHostName", sc, hostName)); } - sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers, - clientRequestedApplicationProtocols); + try { + AbstractJsseEndpoint.clientRequestedProtocolsThreadLocal.set(extractor.getClientRequestedProtocols()); + AbstractJsseEndpoint.clientSupportedGroupsThreadLocal.set(clientSupportedGroups); + AbstractJsseEndpoint.clientSignatureSchemesThreadLocal.set(clientSignatureSchemes); + sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers, clientRequestedApplicationProtocols); + } finally { + AbstractJsseEndpoint.clientRequestedProtocolsThreadLocal.set(null); + AbstractJsseEndpoint.clientSupportedGroupsThreadLocal.set(null); + AbstractJsseEndpoint.clientSignatureSchemesThreadLocal.set(null); + } // Populate additional TLS attributes obtained from the handshake that // aren't available from the session additionalTlsAttributes.put(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY, extractor.getClientRequestedProtocols()); - additionalTlsAttributes.put(SSLSupport.REQUESTED_CIPHERS_KEY, - extractor.getClientRequestedCipherNames()); + additionalTlsAttributes.put(SSLSupport.REQUESTED_CIPHERS_KEY, extractor.getClientRequestedCipherNames()); // Ensure the application buffers (which have to be created earlier) are // big enough. @@ -467,14 +481,15 @@ /** - * Force a blocking handshake to take place for this key. - * This requires that both network and application buffers have been emptied out prior to this call taking place, or a - * IOException will be thrown. - * @throws IOException - if an IO exception occurs or if application or network buffers contain data + * Force a blocking handshake to take place for this key. This requires that both network and application buffers + * have been emptied out prior to this call taking place, or a IOException will be thrown. + * + * @throws IOException - if an IO exception occurs or if application or network buffers contain + * data * @throws java.net.SocketTimeoutException - if a socket operation timed out */ public void rehandshake() throws IOException { - //validate the network buffers are empty + // validate the network buffers are empty if (netInBuffer.position() > 0 && netInBuffer.position() < netInBuffer.limit()) { throw new IOException(sm.getString("channel.nio.ssl.netInputNotEmpty")); } @@ -495,7 +510,7 @@ getBufHandler().reset(); handshakeComplete = false; - //initiate handshake + // initiate handshake sslEngine.beginHandshake(); handshakeStatus = sslEngine.getHandshakeStatus(); @@ -504,9 +519,12 @@ while (handshaking) { int hsStatus = handshakeInternal(false); switch (hsStatus) { - case -1 : throw new EOFException(sm.getString("channel.nio.ssl.eofDuringHandshake")); - case 0 : handshaking = false; break; - default : // Some blocking IO occurred, so iterate + case -1: + throw new EOFException(sm.getString("channel.nio.ssl.eofDuringHandshake")); + case 0: + handshaking = false; + break; + default: // Some blocking IO occurred, so iterate } } } catch (IOException x) { @@ -514,19 +532,19 @@ throw x; } catch (Exception cx) { closeSilently(); - IOException x = new IOException(cx); - throw x; + throw new IOException(cx); } } /** * Executes all the tasks needed on the same thread. + * * @return the status */ protected SSLEngineResult.HandshakeStatus tasks() { - Runnable r = null; - while ( (r = sslEngine.getDelegatedTask()) != null) { + Runnable r; + while ((r = sslEngine.getDelegatedTask()) != null) { r.run(); } return sslEngine.getHandshakeStatus(); @@ -534,55 +552,58 @@ /** * Performs the WRAP function + * * @return the result + * * @throws IOException An IO error occurred */ protected SSLEngineResult handshakeWrap() throws IOException { - //this should never be called with a network buffer that contains data - //so we can clear it here. + // this should never be called with a network buffer that contains data + // so we can clear it here. netOutBuffer.clear(); - //perform the wrap + // perform the wrap getBufHandler().configureWriteBufferForRead(); SSLEngineResult result = sslEngine.wrap(getBufHandler().getWriteBuffer(), netOutBuffer); - //prepare the results to be written + // prepare the results to be written netOutBuffer.flip(); - //set the status + // set the status handshakeStatus = result.getHandshakeStatus(); return result; } /** * Perform handshake unwrap + * * @return the result + * * @throws IOException An IO error occurred */ protected SSLEngineResult handshakeUnwrap() throws IOException { SSLEngineResult result; - boolean cont = false; - //loop while we can perform pure SSLEngine data + boolean cont; + // loop while we can perform pure SSLEngine data do { - //prepare the buffer with the incoming data + // prepare the buffer with the incoming data netInBuffer.flip(); - //call unwrap + // call unwrap getBufHandler().configureReadBufferForWrite(); result = sslEngine.unwrap(netInBuffer, getBufHandler().getReadBuffer()); /* * ByteBuffer.compact() is an optional method but netInBuffer is created from either ByteBuffer.allocate() - * or ByteBuffer.allocateDirect() and the ByteBuffers returned by those methods do implement compact(). - * The ByteBuffer must be in 'read from' mode when compact() is called and will be in 'write to' mode + * or ByteBuffer.allocateDirect() and the ByteBuffers returned by those methods do implement compact(). The + * ByteBuffer must be in 'read from' mode when compact() is called and will be in 'write to' mode * afterwards. */ netInBuffer.compact(); - //read in the status + // read in the status handshakeStatus = result.getHandshakeStatus(); if (result.getStatus() == SSLEngineResult.Status.OK && - result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { - //execute tasks if we need to + result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + // execute tasks if we need to handshakeStatus = tasks(); } - //perform another unwrap? - cont = result.getStatus() == SSLEngineResult.Status.OK && - handshakeStatus == HandshakeStatus.NEED_UNWRAP; + // perform another unwrap? + cont = result.getStatus() == SSLEngineResult.Status.OK && handshakeStatus == HandshakeStatus.NEED_UNWRAP; } while (cont); return result; } @@ -598,13 +619,17 @@ /** * Sends an SSL close message, will not physically close the connection here.
    * To close the connection, you could do something like - *

    
    +     *
    +     * 
    +     * 
          *   close();
          *   while (isOpen() && !myTimeoutFunction()) Thread.sleep(25);
          *   if ( isOpen() ) close(true); //forces a close if you timed out
    -     * 
    + *
    + *
    + * * @throws IOException if an I/O error occurs - * @throws IOException if there is data on the outgoing network buffer and we are unable to flush it + * @throws IOException if there is data on the outgoing network buffer, and we are unable to flush it */ @Override public void close() throws IOException { @@ -639,17 +664,17 @@ closeSilently(); throw new IOException(sm.getString("channel.nio.ssl.pendingWriteDuringClose"), e); } - //prep the buffer for the close message + // prep the buffer for the close message netOutBuffer.clear(); - //perform the close, since we called sslEngine.closeOutbound + // perform the close, since we called sslEngine.closeOutbound SSLEngineResult handshake = sslEngine.wrap(getEmptyBuf(), netOutBuffer); - //we should be in a close state + // we should be in a close state if (handshake.getStatus() != SSLEngineResult.Status.CLOSED) { throw new IOException(sm.getString("channel.nio.ssl.invalidCloseState")); } - //prepare the buffer for writing + // prepare the buffer for writing netOutBuffer.flip(); - //if there is data to be written + // if there is data to be written try { if (timeout > 0) { if (!flush().get(timeout, TimeUnit.MILLISECONDS).booleanValue()) { @@ -670,7 +695,7 @@ throw new IOException(sm.getString("channel.nio.ssl.pendingWriteDuringClose"), e); } - //is the channel closed? + // is the channel closed? closed = (!netOutBuffer.hasRemaining() && (handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP)); } @@ -702,6 +727,7 @@ private class FutureRead implements Future { private ByteBuffer dst; private Future integer; + private FutureRead(ByteBuffer dst) { this.dst = dst; if (unwrapBeforeRead || netInBuffer.position() > 0) { @@ -710,61 +736,69 @@ this.integer = sc.read(netInBuffer); } } + @Override public boolean cancel(boolean mayInterruptIfRunning) { - return (integer == null) ? false : integer.cancel(mayInterruptIfRunning); + return integer != null && integer.cancel(mayInterruptIfRunning); } + @Override public boolean isCancelled() { - return (integer == null) ? false : integer.isCancelled(); + return integer != null && integer.isCancelled(); } + @Override public boolean isDone() { - return (integer == null) ? true : integer.isDone(); + return integer == null || integer.isDone(); } + @Override public Integer get() throws InterruptedException, ExecutionException { try { - return (integer == null) ? unwrap(netInBuffer.position(), -1, TimeUnit.MILLISECONDS) : unwrap(integer.get().intValue(), -1, TimeUnit.MILLISECONDS); + return (integer == null) ? unwrap(netInBuffer.position(), -1, TimeUnit.MILLISECONDS) : + unwrap(integer.get().intValue(), -1, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { // Cannot happen: no timeout throw new ExecutionException(e); } } + @Override public Integer get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, - TimeoutException { - return (integer == null) ? unwrap(netInBuffer.position(), timeout, unit) : unwrap(integer.get(timeout, unit).intValue(), timeout, unit); + throws InterruptedException, ExecutionException, TimeoutException { + return (integer == null) ? unwrap(netInBuffer.position(), timeout, unit) : + unwrap(integer.get(timeout, unit).intValue(), timeout, unit); } - private Integer unwrap(int nRead, long timeout, TimeUnit unit) throws ExecutionException, TimeoutException, InterruptedException { - //are we in the middle of closing or closed? + + private Integer unwrap(int nRead, long timeout, TimeUnit unit) + throws ExecutionException, TimeoutException, InterruptedException { + // are we in the middle of closing or closed? if (closing || closed) { return Integer.valueOf(-1); } - //did we reach EOF? if so send EOF up one layer. + // did we reach EOF? if so send EOF up one layer. if (nRead < 0) { return Integer.valueOf(-1); } - //the data read + // the data read int read = 0; - //the SSL engine result + // the SSL engine result SSLEngineResult unwrap; do { - //prepare the buffer + // prepare the buffer netInBuffer.flip(); - //unwrap the data + // unwrap the data try { unwrap = sslEngine.unwrap(netInBuffer, dst); } catch (SSLException e) { throw new ExecutionException(e); } - //compact the buffer + // compact the buffer netInBuffer.compact(); if (unwrap.getStatus() == Status.OK || unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { - //we did receive some data, add it to our total + // we did receive some data, add it to our total read += unwrap.bytesProduced(); - //perform any tasks if needed + // perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { @@ -773,7 +807,7 @@ new IOException(sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong"))); } } - //if we need more network data, then bail out for now. + // if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { if (read == 0) { integer = sc.read(netInBuffer); @@ -797,29 +831,25 @@ // since the buffer was created. if (dst == getBufHandler().getReadBuffer()) { // This is the normal case for this code - getBufHandler() - .expand(sslEngine.getSession().getApplicationBufferSize()); + getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); dst = getBufHandler().getReadBuffer(); } else if (dst == getAppReadBufHandler().getByteBuffer()) { - getAppReadBufHandler() - .expand(sslEngine.getSession().getApplicationBufferSize()); + getAppReadBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); dst = getAppReadBufHandler().getByteBuffer(); } else { // Can't expand the buffer as there is no way to signal // to the caller that the buffer has been replaced. - throw new ExecutionException(new IOException(sm.getString("channel.nio.ssl.unwrapFailResize", unwrap.getStatus()))); + throw new ExecutionException(new IOException( + sm.getString("channel.nio.ssl.unwrapFailResize", unwrap.getStatus()))); } } } else { // Something else went wrong - throw new ExecutionException(new IOException(sm.getString("channel.nio.ssl.unwrapFail", unwrap.getStatus()))); + throw new ExecutionException( + new IOException(sm.getString("channel.nio.ssl.unwrapFail", unwrap.getStatus()))); } - } while (netInBuffer.position() != 0); //continue to unwrapping as long as the input buffer has stuff - if (!dst.hasRemaining()) { - unwrapBeforeRead = true; - } else { - unwrapBeforeRead = false; - } + } while (netInBuffer.position() != 0); // continue to unwrapping as long as the input buffer has stuff + unwrapBeforeRead = !dst.hasRemaining(); return Integer.valueOf(read); } } @@ -828,7 +858,9 @@ * Reads a sequence of bytes from this channel into the given buffer. * * @param dst The buffer into which bytes are to be transferred + * * @return The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream + * * @throws IllegalStateException if the handshake was not completed */ @Override @@ -844,27 +876,32 @@ private Future integer = null; private int written = 0; private Throwable t = null; + private FutureWrite(ByteBuffer src) { this.src = src; - //are we closing or closed? + // are we closing or closed? if (closing || closed) { t = new IOException(sm.getString("channel.nio.ssl.closing")); } else { wrap(); } } + @Override public boolean cancel(boolean mayInterruptIfRunning) { return integer.cancel(mayInterruptIfRunning); } + @Override public boolean isCancelled() { return integer.isCancelled(); } + @Override public boolean isDone() { return integer.isDone(); } + @Override public Integer get() throws InterruptedException, ExecutionException { if (t != null) { @@ -880,10 +917,10 @@ return Integer.valueOf(written); } } + @Override public Integer get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, - TimeoutException { + throws InterruptedException, ExecutionException, TimeoutException { if (t != null) { throw new ExecutionException(t); } @@ -897,6 +934,7 @@ return Integer.valueOf(written); } } + protected void wrap() { try { if (!netOutBuffer.hasRemaining()) { @@ -925,6 +963,7 @@ * Writes a sequence of bytes to this channel from the given buffer. * * @param src The buffer from which bytes are to be retrieved + * * @return The number of bytes written, possibly zero */ @Override @@ -933,9 +972,8 @@ } @Override - public void read(final ByteBuffer dst, - final long timeout, final TimeUnit unit, final A attachment, - final CompletionHandler handler) { + public void read(final ByteBuffer dst, final long timeout, final TimeUnit unit, final A attachment, + final CompletionHandler handler) { // Check state if (closing || closed) { handler.completed(Integer.valueOf(-1), attachment); @@ -944,7 +982,7 @@ if (!handshakeComplete) { throw new IllegalStateException(sm.getString("channel.nio.ssl.incompleteHandshake")); } - CompletionHandler readCompletionHandler = new CompletionHandler<>() { + CompletionHandler readCompletionHandler = new CompletionHandler<>() { @Override public void completed(Integer nBytes, A attach) { if (nBytes.intValue() < 0) { @@ -952,30 +990,31 @@ } else { try { ByteBuffer dst2 = dst; - //the data read + // the data read int read = 0; - //the SSL engine result + // the SSL engine result SSLEngineResult unwrap; do { - //prepare the buffer + // prepare the buffer netInBuffer.flip(); - //unwrap the data + // unwrap the data unwrap = sslEngine.unwrap(netInBuffer, dst2); - //compact the buffer + // compact the buffer netInBuffer.compact(); if (unwrap.getStatus() == Status.OK || unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { - //we did receive some data, add it to our total + // we did receive some data, add it to our total read += unwrap.bytesProduced(); - //perform any tasks if needed + // perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { - if (handshakeWrapQueueLength.incrementAndGet() > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) { + if (handshakeWrapQueueLength + .incrementAndGet() > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) { throw new ExecutionException(new IOException( sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong"))); } } - //if we need more network data, then bail out for now. + // if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { if (read == 0) { sc.read(netInBuffer, timeout, unit, attachment, this); @@ -995,10 +1034,10 @@ // since the buffer was created. if (dst2 == getBufHandler().getReadBuffer()) { // This is the normal case for this code - getBufHandler().expand( - sslEngine.getSession().getApplicationBufferSize()); + getBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); dst2 = getBufHandler().getReadBuffer(); - } else if (getAppReadBufHandler() != null && dst2 == getAppReadBufHandler().getByteBuffer()) { + } else if (getAppReadBufHandler() != null && + dst2 == getAppReadBufHandler().getByteBuffer()) { getAppReadBufHandler() .expand(sslEngine.getSession().getApplicationBufferSize()); dst2 = getAppReadBufHandler().getByteBuffer(); @@ -1013,13 +1052,9 @@ // Something else went wrong throw new IOException(sm.getString("channel.nio.ssl.unwrapFail", unwrap.getStatus())); } - // continue to unwrap as long as the input buffer has stuff + // continue to unwrap as long as the input buffer has stuff } while (netInBuffer.position() != 0); - if (!dst2.hasRemaining()) { - unwrapBeforeRead = true; - } else { - unwrapBeforeRead = false; - } + unwrapBeforeRead = !dst2.hasRemaining(); // If everything is OK, so complete handler.completed(Integer.valueOf(read), attach); } catch (Exception e) { @@ -1027,6 +1062,7 @@ } } } + @Override public void failed(Throwable exc, A attach) { handler.failed(exc, attach); @@ -1040,9 +1076,8 @@ } @Override - public void read(final ByteBuffer[] dsts, final int offset, final int length, - final long timeout, final TimeUnit unit, final A attachment, - final CompletionHandler handler) { + public void read(final ByteBuffer[] dsts, final int offset, final int length, final long timeout, + final TimeUnit unit, final A attachment, final CompletionHandler handler) { if (offset < 0 || dsts == null || (offset + length) > dsts.length) { throw new IllegalArgumentException(); } @@ -1053,16 +1088,16 @@ if (!handshakeComplete) { throw new IllegalStateException(sm.getString("channel.nio.ssl.incompleteHandshake")); } - CompletionHandler readCompletionHandler = new CompletionHandler<>() { + CompletionHandler readCompletionHandler = new CompletionHandler<>() { @Override public void completed(Integer nBytes, A attach) { if (nBytes.intValue() < 0) { failed(new EOFException(), attach); } else { try { - //the data read + // the data read long read = 0; - //the SSL engine result + // the SSL engine result SSLEngineResult unwrap; ByteBuffer[] dsts2 = dsts; int length2 = length; @@ -1071,29 +1106,30 @@ if (overflowState == OverflowState.PROCESSING) { overflowState = OverflowState.DONE; } - //prepare the buffer + // prepare the buffer netInBuffer.flip(); - //unwrap the data + // unwrap the data unwrap = sslEngine.unwrap(netInBuffer, dsts2, offset, length2); - //compact the buffer + // compact the buffer netInBuffer.compact(); if (unwrap.getStatus() == Status.OK || unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { - //we did receive some data, add it to our total + // we did receive some data, add it to our total read += unwrap.bytesProduced(); if (overflowState == OverflowState.DONE) { // Remove the data read into the overflow buffer read -= getBufHandler().getReadBuffer().position(); } - //perform any tasks if needed + // perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { - if (handshakeWrapQueueLength.incrementAndGet() > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) { + if (handshakeWrapQueueLength + .incrementAndGet() > HANDSHAKE_WRAP_QUEUE_LENGTH_LIMIT) { throw new ExecutionException(new IOException( sm.getString("channel.nio.ssl.handshakeWrapQueueTooLong"))); } } - //if we need more network data, then bail out for now. + // if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { if (read == 0) { sc.read(netInBuffer, timeout, unit, attachment, this); @@ -1103,13 +1139,13 @@ } } } else if (unwrap.getStatus() == Status.BUFFER_OVERFLOW && read > 0) { - //buffer overflow can happen, if we have read data, then - //empty out the dst buffer before we do another read + // buffer overflow can happen, if we have read data, then + // empty out the dst buffer before we do another read break; } else if (unwrap.getStatus() == Status.BUFFER_OVERFLOW) { - //here we should trap BUFFER_OVERFLOW and call expand on the buffer - //for now, throw an exception, as we initialized the buffers - //in the constructor + // here we should trap BUFFER_OVERFLOW and call expand on the buffer + // for now, throw an exception, as we initialized the buffers + // in the constructor ByteBuffer readBuffer = getBufHandler().getReadBuffer(); boolean found = false; boolean resized = true; @@ -1123,8 +1159,10 @@ } dsts[offset + i] = getBufHandler().getReadBuffer(); found = true; - } else if (getAppReadBufHandler() != null && dsts[offset + i] == getAppReadBufHandler().getByteBuffer()) { - getAppReadBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); + } else if (getAppReadBufHandler() != null && + dsts[offset + i] == getAppReadBufHandler().getByteBuffer()) { + getAppReadBufHandler() + .expand(sslEngine.getSession().getApplicationBufferSize()); if (dsts[offset + i] == getAppReadBufHandler().getByteBuffer()) { resized = false; } @@ -1134,7 +1172,8 @@ } if (found) { if (!resized) { - throw new IOException(sm.getString("channel.nio.ssl.unwrapFail", unwrap.getStatus())); + throw new IOException( + sm.getString("channel.nio.ssl.unwrapFail", unwrap.getStatus())); } } else { // Add the main read buffer in the destinations and try again @@ -1164,11 +1203,7 @@ for (int i = offset; i < endOffset; i++) { capacity += dsts[i].remaining(); } - if (capacity == 0) { - unwrapBeforeRead = true; - } else { - unwrapBeforeRead = false; - } + unwrapBeforeRead = capacity == 0; // If everything is OK, so complete handler.completed(Long.valueOf(read), attach); } catch (Exception e) { @@ -1176,6 +1211,7 @@ } } } + @Override public void failed(Throwable exc, A attach) { handler.failed(exc, attach); @@ -1189,8 +1225,8 @@ } @Override - public void write(final ByteBuffer src, final long timeout, final TimeUnit unit, - final A attachment, final CompletionHandler handler) { + public void write(final ByteBuffer src, final long timeout, final TimeUnit unit, final A attachment, + final CompletionHandler handler) { // Check state if (closing || closed) { handler.failed(new IOException(sm.getString("channel.nio.ssl.closing")), attachment); @@ -1210,8 +1246,7 @@ tasks(); } // Write data to the channel - sc.write(netOutBuffer, timeout, unit, attachment, - new CompletionHandler() { + sc.write(netOutBuffer, timeout, unit, attachment, new CompletionHandler<>() { @Override public void completed(Integer nBytes, A attach) { if (nBytes.intValue() < 0) { @@ -1227,6 +1262,7 @@ handler.completed(Integer.valueOf(written), attach); } } + @Override public void failed(Throwable exc, A attach) { handler.failed(exc, attach); @@ -1241,9 +1277,8 @@ } @Override - public void write(final ByteBuffer[] srcs, final int offset, final int length, - final long timeout, final TimeUnit unit, final A attachment, - final CompletionHandler handler) { + public void write(final ByteBuffer[] srcs, final int offset, final int length, final long timeout, + final TimeUnit unit, final A attachment, final CompletionHandler handler) { if ((offset < 0) || (length < 0) || (offset > srcs.length - length)) { throw new IndexOutOfBoundsException(); } @@ -1253,7 +1288,7 @@ return; } try { - // Prepare the output buffer + // Prepare the output buffer netOutBuffer.clear(); // Wrap the source data into the internal buffer SSLEngineResult result = sslEngine.wrap(srcs, offset, length, netOutBuffer); @@ -1264,7 +1299,7 @@ tasks(); } // Write data to the channel - sc.write(netOutBuffer, timeout, unit, attachment, new CompletionHandler() { + sc.write(netOutBuffer, timeout, unit, attachment, new CompletionHandler<>() { @Override public void completed(Integer nBytes, A attach) { if (nBytes.intValue() < 0) { @@ -1280,6 +1315,7 @@ handler.completed(Long.valueOf(written), attach); } } + @Override public void failed(Throwable exc, A attach) { handler.failed(exc, attach); @@ -1291,7 +1327,7 @@ } catch (Exception e) { handler.failed(e, attachment); } - } + } @Override public boolean isHandshakeComplete() { @@ -1315,6 +1351,6 @@ private enum OverflowState { NONE, PROCESSING, - DONE; + DONE } } \ No newline at end of file diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SecureNioChannel.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SecureNioChannel.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SecureNioChannel.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SecureNioChannel.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,6 +41,8 @@ import org.apache.tomcat.util.net.NioEndpoint.NioSocketWrapper; import org.apache.tomcat.util.net.TLSClientHelloExtractor.ExtractorResult; import org.apache.tomcat.util.net.openssl.ciphers.Cipher; +import org.apache.tomcat.util.net.openssl.ciphers.Group; +import org.apache.tomcat.util.net.openssl.ciphers.SignatureScheme; import org.apache.tomcat.util.res.StringManager; /** @@ -66,7 +68,7 @@ protected boolean handshakeComplete = false; protected boolean needHandshakeWrap = false; - protected HandshakeStatus handshakeStatus; //gets set by handshake + protected HandshakeStatus handshakeStatus; // gets set by handshake protected boolean closed = false; protected boolean closing = false; @@ -108,14 +110,17 @@ } } -//=========================================================================================== -// NIO SSL METHODS -//=========================================================================================== + // =========================================================================================== + // NIO SSL METHODS + // =========================================================================================== /** - * Flushes the buffer to the network, non blocking + * Flushes the buffer to the network, non-blocking + * * @param buf ByteBuffer + * * @return boolean true if the buffer has been emptied out, false otherwise + * * @throws IOException An IO error occurred writing data */ protected boolean flush(ByteBuffer buf) throws IOException { @@ -128,26 +133,23 @@ } /** - * Performs SSL handshake, non blocking, but performs NEED_TASK on the same - * thread. Hence, you should never call this method using your Acceptor - * thread, as you would slow down your system significantly. If the return - * value from this method is positive, the selection key should be - * registered interestOps given by the return value. + * Performs SSL handshake, non-blocking, but performs NEED_TASK on the same thread. Hence, you should never call + * this method using your Acceptor thread, as you would slow down your system significantly. If the return value + * from this method is positive, the selection key should be registered interestOps given by the return value. * - * @param read boolean - true if the underlying channel is readable + * @param read boolean - true if the underlying channel is readable * @param write boolean - true if the underlying channel is writable * - * @return 0 if hand shake is complete, -1 if an error (other than an - * IOException) occurred, otherwise it returns a SelectionKey - * interestOps value + * @return 0 if handshake is complete, -1 if an error (other than an IOException) occurred, otherwise it returns a + * SelectionKey interestOps value * - * @throws IOException If an I/O error occurs during the handshake or if the - * handshake fails during wrapping or unwrapping + * @throws IOException If an I/O error occurs during the handshake or if the handshake fails during wrapping or + * unwrapping */ @Override public int handshake(boolean read, boolean write) throws IOException { if (handshakeComplete) { - return 0; //we have done our initial handshake + return 0; // we have done our initial handshake } if (!sniComplete) { @@ -160,31 +162,31 @@ } if (!flush(netOutBuffer)) { - return SelectionKey.OP_WRITE; //we still have data to write + return SelectionKey.OP_WRITE; // we still have data to write } - SSLEngineResult handshake = null; + SSLEngineResult handshake; while (!handshakeComplete) { switch (handshakeStatus) { case NOT_HANDSHAKING: - //should never happen + // should never happen throw new IOException(sm.getString("channel.nio.ssl.notHandshaking")); case FINISHED: if (endpoint.hasNegotiableProtocols()) { if (sslEngine instanceof SSLUtil.ProtocolInfo) { - socketWrapper.setNegotiatedProtocol( - ((SSLUtil.ProtocolInfo) sslEngine).getNegotiatedProtocol()); + socketWrapper + .setNegotiatedProtocol(((SSLUtil.ProtocolInfo) sslEngine).getNegotiatedProtocol()); } else { socketWrapper.setNegotiatedProtocol(sslEngine.getApplicationProtocol()); } } - //we are complete if we have delivered the last package + // we are complete if we have delivered the last package handshakeComplete = !netOutBuffer.hasRemaining(); - //return 0 if we are complete, otherwise we still have data to write + // return 0 if we are complete, otherwise we still have data to write return handshakeComplete ? 0 : SelectionKey.OP_WRITE; case NEED_WRAP: - //perform the wrap function + // perform the wrap function try { handshake = handshakeWrap(write); } catch (SSLException e) { @@ -199,28 +201,30 @@ flush(netOutBuffer); return -1; } else { - //wrap should always work with our buffers - throw new IOException(sm.getString("channel.nio.ssl.unexpectedStatusDuringWrap", handshake.getStatus())); + // wrap should always work with our buffers + throw new IOException( + sm.getString("channel.nio.ssl.unexpectedStatusDuringWrap", handshake.getStatus())); } if (handshakeStatus != HandshakeStatus.NEED_UNWRAP || (!flush(netOutBuffer))) { - //should actually return OP_READ if we have NEED_UNWRAP + // should actually return OP_READ if we have NEED_UNWRAP return SelectionKey.OP_WRITE; } - //fall down to NEED_UNWRAP on the same call, will result in a - //BUFFER_UNDERFLOW if it needs data - //$FALL-THROUGH$ + // fall down to NEED_UNWRAP on the same call, will result in a + // BUFFER_UNDERFLOW if it needs data + // $FALL-THROUGH$ case NEED_UNWRAP: - //perform the unwrap function + // perform the unwrap function handshake = handshakeUnwrap(read); if (handshake.getStatus() == Status.OK) { if (handshakeStatus == HandshakeStatus.NEED_TASK) { handshakeStatus = tasks(); } - } else if ( handshake.getStatus() == Status.BUFFER_UNDERFLOW ){ - //read more data, reregister for OP_READ + } else if (handshake.getStatus() == Status.BUFFER_UNDERFLOW) { + // read more data, register again for OP_READ return SelectionKey.OP_READ; } else { - throw new IOException(sm.getString("channel.nio.ssl.unexpectedStatusDuringWrap", handshake.getStatus())); + throw new IOException( + sm.getString("channel.nio.ssl.unexpectedStatusDuringWrap", handshake.getStatus())); } break; case NEED_TASK: @@ -236,13 +240,11 @@ /* - * Peeks at the initial network bytes to determine if the SNI extension is - * present and, if it is, what host name has been requested. Based on the - * provided host name, configure the SSLEngine for this connection. - * - * @return 0 if SNI processing is complete, -1 if an error (other than an - * IOException) occurred, otherwise it returns a SelectionKey - * interestOps value + * Peeks at the initial network bytes to determine if the SNI extension is present and, if it is, what host name has + * been requested. Based on the provided host name, configure the SSLEngine for this connection. + * + * @return 0 if SNI processing is complete, -1 if an error (other than an IOException) occurred, otherwise it + * returns a SelectionKey interestOps value * * @throws IOException If an I/O error occurs during the SNI processing */ @@ -260,8 +262,7 @@ // extractor needed more data to process but netInBuffer was full so // expand the buffer and read some more data. int newLimit = Math.min(netInBuffer.capacity() * 2, endpoint.getSniParseLimit()); - log.info(sm.getString("channel.nio.ssl.expandNetInBuffer", - Integer.toString(newLimit))); + log.info(sm.getString("channel.nio.ssl.expandNetInBuffer", Integer.toString(newLimit))); netInBuffer = ByteBufferUtils.expand(netInBuffer, newLimit); if (sc.read(netInBuffer) < 0) { @@ -273,46 +274,57 @@ String hostName = null; List clientRequestedCiphers = null; List clientRequestedApplicationProtocols = null; + List clientSupportedGroups = null; + List clientSignatureSchemes = null; switch (extractor.getResult()) { - case COMPLETE: - hostName = extractor.getSNIValue(); - clientRequestedApplicationProtocols = - extractor.getClientRequestedApplicationProtocols(); - //$FALL-THROUGH$ to set the client requested ciphers - case NOT_PRESENT: - clientRequestedCiphers = extractor.getClientRequestedCiphers(); - break; - case NEED_READ: - return SelectionKey.OP_READ; - case UNDERFLOW: - // Unable to buffer enough data to read SNI extension data - if (log.isDebugEnabled()) { - log.debug(sm.getString("channel.nio.ssl.sniDefault")); - } - hostName = endpoint.getDefaultSSLHostConfigName(); - clientRequestedCiphers = Collections.emptyList(); - break; - case NON_SECURE: - netOutBuffer.clear(); - netOutBuffer.put(TLSClientHelloExtractor.USE_TLS_RESPONSE); - netOutBuffer.flip(); - flushOutbound(); - throw new IOException(sm.getString("channel.nio.ssl.foundHttp")); + case COMPLETE: + hostName = extractor.getSNIValue(); + socketWrapper.setSniHostName(hostName); + clientRequestedApplicationProtocols = extractor.getClientRequestedApplicationProtocols(); + //$FALL-THROUGH$ to set the client requested ciphers + case NOT_PRESENT: + clientRequestedCiphers = extractor.getClientRequestedCiphers(); + clientSupportedGroups = extractor.getClientSupportedGroups(); + clientSignatureSchemes = extractor.getClientSignatureSchemes(); + break; + case NEED_READ: + return SelectionKey.OP_READ; + case UNDERFLOW: + // Unable to buffer enough data to read SNI extension data + if (log.isDebugEnabled()) { + log.debug(sm.getString("channel.nio.ssl.sniDefault")); + } + hostName = endpoint.getDefaultSSLHostConfigName(); + clientRequestedCiphers = Collections.emptyList(); + break; + case NON_SECURE: + netOutBuffer.clear(); + netOutBuffer.put(TLSClientHelloExtractor.USE_TLS_RESPONSE); + netOutBuffer.flip(); + flushOutbound(); + throw new IOException(sm.getString("channel.nio.ssl.foundHttp")); } if (log.isTraceEnabled()) { log.trace(sm.getString("channel.nio.ssl.sniHostName", sc, hostName)); } - sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers, - clientRequestedApplicationProtocols); + try { + AbstractJsseEndpoint.clientRequestedProtocolsThreadLocal.set(extractor.getClientRequestedProtocols()); + AbstractJsseEndpoint.clientSupportedGroupsThreadLocal.set(clientSupportedGroups); + AbstractJsseEndpoint.clientSignatureSchemesThreadLocal.set(clientSignatureSchemes); + sslEngine = endpoint.createSSLEngine(hostName, clientRequestedCiphers, clientRequestedApplicationProtocols); + } finally { + AbstractJsseEndpoint.clientRequestedProtocolsThreadLocal.set(null); + AbstractJsseEndpoint.clientSupportedGroupsThreadLocal.set(null); + AbstractJsseEndpoint.clientSignatureSchemesThreadLocal.set(null); + } // Populate additional TLS attributes obtained from the handshake that // aren't available from the session additionalTlsAttributes.put(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY, extractor.getClientRequestedProtocols()); - additionalTlsAttributes.put(SSLSupport.REQUESTED_CIPHERS_KEY, - extractor.getClientRequestedCipherNames()); + additionalTlsAttributes.put(SSLSupport.REQUESTED_CIPHERS_KEY, extractor.getClientRequestedCipherNames()); // Ensure the application buffers (which have to be created earlier) are // big enough. @@ -338,16 +350,17 @@ /** - * Force a blocking handshake to take place for this key. - * This requires that both network and application buffers have been emptied out prior to this call taking place, or a - * IOException will be thrown. + * Force a blocking handshake to take place for this key. This requires that both network and application buffers + * have been emptied out prior to this call taking place, or a IOException will be thrown. + * * @param timeout - timeout in milliseconds for each socket operation - * @throws IOException - if an IO exception occurs or if application or network buffers contain data + * + * @throws IOException - if an IO exception occurs or if application or network buffers contain data * @throws SocketTimeoutException - if a socket operation timed out */ @SuppressWarnings("null") // key cannot be null public void rehandshake(long timeout) throws IOException { - //validate the network buffers are empty + // validate the network buffers are empty if (netInBuffer.position() > 0 && netInBuffer.position() < netInBuffer.limit()) { throw new IOException(sm.getString("channel.nio.ssl.netInputNotEmpty")); } @@ -372,12 +385,12 @@ while (handshaking) { int hsStatus = this.handshake(isReadable, isWritable); switch (hsStatus) { - case -1 : + case -1: throw new EOFException(sm.getString("channel.nio.ssl.eofDuringHandshake")); - case 0 : + case 0: handshaking = false; break; - default : + default: long now = System.currentTimeMillis(); if (selector == null) { selector = Selector.open(); @@ -386,44 +399,45 @@ key.interestOps(hsStatus); // null warning suppressed } int keyCount = selector.select(timeout); - if (keyCount == 0 && ((System.currentTimeMillis()-now) >= timeout)) { + if (keyCount == 0 && ((System.currentTimeMillis() - now) >= timeout)) { throw new SocketTimeoutException(sm.getString("channel.nio.ssl.timeoutDuringHandshake")); } isReadable = key.isReadable(); isWritable = key.isWritable(); } } - } catch (IOException x) { + } catch (IOException ioe) { closeSilently(); - throw x; - } catch (Exception cx) { + throw ioe; + } catch (Exception e) { closeSilently(); - IOException x = new IOException(cx); - throw x; + throw new IOException(e); } finally { if (key != null) { try { key.cancel(); } catch (Exception ignore) { + // Ignore } } if (selector != null) { try { selector.close(); } catch (Exception ignore) { + // Ignore } } } } - /** * Executes all the tasks needed on the same thread. + * * @return the status */ protected SSLEngineResult.HandshakeStatus tasks() { - Runnable r = null; + Runnable r; while ((r = sslEngine.getDelegatedTask()) != null) { r.run(); } @@ -432,22 +446,25 @@ /** * Performs the WRAP function + * * @param doWrite boolean + * * @return the result + * * @throws IOException An IO error occurred */ protected SSLEngineResult handshakeWrap(boolean doWrite) throws IOException { - //this should never be called with a network buffer that contains data - //so we can clear it here. + // this should never be called with a network buffer that contains data + // so we can clear it here. netOutBuffer.clear(); - //perform the wrap + // perform the wrap getBufHandler().configureWriteBufferForRead(); SSLEngineResult result = sslEngine.wrap(getBufHandler().getWriteBuffer(), netOutBuffer); - //prepare the results to be written + // prepare the results to be written netOutBuffer.flip(); - //set the status + // set the status handshakeStatus = result.getHandshakeStatus(); - //optimization, if we do have a writable channel, write it now + // optimization, if we do have a writable channel, write it now if (doWrite) { flush(netOutBuffer); } @@ -456,45 +473,47 @@ /** * Perform handshake unwrap + * * @param doread boolean + * * @return the result + * * @throws IOException An IO error occurred */ protected SSLEngineResult handshakeUnwrap(boolean doread) throws IOException { - if (doread) { - //if we have data to read, read it + if (doread) { + // if we have data to read, read it int read = sc.read(netInBuffer); if (read == -1) { throw new IOException(sm.getString("channel.nio.ssl.eofDuringHandshake")); } } SSLEngineResult result; - boolean cont = false; - //loop while we can perform pure SSLEngine data + boolean cont; + // loop while we can perform pure SSLEngine data do { - //prepare the buffer with the incoming data + // prepare the buffer with the incoming data netInBuffer.flip(); - //call unwrap + // call unwrap getBufHandler().configureReadBufferForWrite(); result = sslEngine.unwrap(netInBuffer, getBufHandler().getReadBuffer()); /* * ByteBuffer.compact() is an optional method but netInBuffer is created from either ByteBuffer.allocate() - * or ByteBuffer.allocateDirect() and the ByteBuffers returned by those methods do implement compact(). - * The ByteBuffer must be in 'read from' mode when compact() is called and will be in 'write to' mode + * or ByteBuffer.allocateDirect() and the ByteBuffers returned by those methods do implement compact(). The + * ByteBuffer must be in 'read from' mode when compact() is called and will be in 'write to' mode * afterwards. */ netInBuffer.compact(); - //read in the status + // read in the status handshakeStatus = result.getHandshakeStatus(); if (result.getStatus() == SSLEngineResult.Status.OK && - result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { - //execute tasks if we need to + result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { + // execute tasks if we need to handshakeStatus = tasks(); } - //perform another unwrap? - cont = result.getStatus() == SSLEngineResult.Status.OK && - handshakeStatus == HandshakeStatus.NEED_UNWRAP; + // perform another unwrap? + cont = result.getStatus() == SSLEngineResult.Status.OK && handshakeStatus == HandshakeStatus.NEED_UNWRAP; } while (cont); return result; } @@ -508,16 +527,19 @@ } /** - * Sends an SSL close message, will not physically close the connection here. - *
    To close the connection, you could do something like - *
    
    +     * Sends an SSL close message, will not physically close the connection here. 
    + * To close the connection, you could do something like + * + *
    +     * 
          *   close();
          *   while (isOpen() && !myTimeoutFunction()) Thread.sleep(25);
          *   if ( isOpen() ) close(true); //forces a close if you timed out
    -     * 
    + *
    + *
    + * * @throws IOException if an I/O error occurs - * @throws IOException if there is data on the outgoing network buffer and - * we are unable to flush it + * @throws IOException if there is data on the outgoing network buffer, and we are unable to flush it */ @Override public void close() throws IOException { @@ -535,20 +557,20 @@ if (!flush(netOutBuffer)) { throw new IOException(sm.getString("channel.nio.ssl.remainingDataDuringClose")); } - //prep the buffer for the close message + // prep the buffer for the close message netOutBuffer.clear(); - //perform the close, since we called sslEngine.closeOutbound + // perform the close, since we called sslEngine.closeOutbound SSLEngineResult handshake = sslEngine.wrap(getEmptyBuf(), netOutBuffer); - //we should be in a close state + // we should be in a close state if (handshake.getStatus() != SSLEngineResult.Status.CLOSED) { throw new IOException(sm.getString("channel.nio.ssl.invalidCloseState")); } - //prepare the buffer for writing + // prepare the buffer for writing netOutBuffer.flip(); - //if there is data to be written + // if there is data to be written flush(netOutBuffer); - //is the channel closed? + // is the channel closed? closed = (!netOutBuffer.hasRemaining() && (handshake.getHandshakeStatus() != HandshakeStatus.NEED_WRAP)); } @@ -572,7 +594,9 @@ } catch (IOException ioe) { // This is expected - swallowing the exception is the reason this // method exists. Log at debug in case someone is interested. - log.debug(sm.getString("channel.nio.ssl.closeSilentError"), ioe); + if (log.isDebugEnabled()) { + log.debug(sm.getString("channel.nio.ssl.closeSilentError"), ioe); + } } } @@ -581,46 +605,46 @@ * Reads a sequence of bytes from this channel into the given buffer. * * @param dst The buffer into which bytes are to be transferred - * @return The number of bytes read, possibly zero, or -1 if - * the channel has reached end-of-stream - * @throws IOException If some other I/O error occurs - * @throws IllegalArgumentException if the destination buffer is different - * than getBufHandler().getReadBuffer() + * + * @return The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream + * + * @throws IOException If some other I/O error occurs + * @throws IllegalStateException if the handshake was not completed */ @Override public int read(ByteBuffer dst) throws IOException { - //are we in the middle of closing or closed? + // are we in the middle of closing or closed? if (closing || closed) { return -1; } - //did we finish our handshake? + // did we finish our handshake? if (!handshakeComplete) { throw new IllegalStateException(sm.getString("channel.nio.ssl.incompleteHandshake")); } - //read from the network + // read from the network int netread = sc.read(netInBuffer); - //did we reach EOF? if so send EOF up one layer. + // did we reach EOF? if so send EOF up one layer. if (netread == -1) { return -1; } - //the data read + // the data read int read = 0; - //the SSL engine result + // the SSL engine result SSLEngineResult unwrap; do { - //prepare the buffer + // prepare the buffer netInBuffer.flip(); - //unwrap the data + // unwrap the data unwrap = sslEngine.unwrap(netInBuffer, dst); - //compact the buffer + // compact the buffer netInBuffer.compact(); if (unwrap.getStatus() == Status.OK || unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { - //we did receive some data, add it to our total + // we did receive some data, add it to our total read += unwrap.bytesProduced(); - //perform any tasks if needed + // perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { @@ -632,7 +656,7 @@ needHandshakeWrap = true; } } - //if we need more network data, then bail out for now. + // if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { break; } @@ -655,8 +679,7 @@ } else { // Can't expand the buffer as there is no way to signal // to the caller that the buffer has been replaced. - throw new IOException( - sm.getString("channel.nio.ssl.unwrapFailResize", unwrap.getStatus())); + throw new IOException(sm.getString("channel.nio.ssl.unwrapFailResize", unwrap.getStatus())); } } } else if (unwrap.getStatus() == Status.CLOSED && netInBuffer.position() == 0 && read > 0) { @@ -671,53 +694,52 @@ // Something else went wrong throw new IOException(sm.getString("channel.nio.ssl.unwrapFail", unwrap.getStatus())); } - } while (netInBuffer.position() != 0); //continue to unwrapping as long as the input buffer has stuff + } while (netInBuffer.position() != 0); // continue unwrapping as long as the input buffer has stuff return read; } @Override - public long read(ByteBuffer[] dsts, int offset, int length) - throws IOException { - //are we in the middle of closing or closed? + public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { + // are we in the middle of closing or closed? if (closing || closed) { return -1; } - //did we finish our handshake? + // did we finish our handshake? if (!handshakeComplete) { throw new IllegalStateException(sm.getString("channel.nio.ssl.incompleteHandshake")); } - //read from the network + // read from the network int netread = sc.read(netInBuffer); - //did we reach EOF? if so send EOF up one layer. + // did we reach EOF? if so send EOF up one layer. if (netread == -1) { return -1; } - //the data read + // the data read int read = 0; - //the SSL engine result + // the SSL engine result SSLEngineResult unwrap; OverflowState overflowState = OverflowState.NONE; do { if (overflowState == OverflowState.PROCESSING) { overflowState = OverflowState.DONE; } - //prepare the buffer + // prepare the buffer netInBuffer.flip(); - //unwrap the data + // unwrap the data unwrap = sslEngine.unwrap(netInBuffer, dsts, offset, length); - //compact the buffer + // compact the buffer netInBuffer.compact(); if (unwrap.getStatus() == Status.OK || unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { - //we did receive some data, add it to our total + // we did receive some data, add it to our total read += unwrap.bytesProduced(); if (overflowState == OverflowState.DONE) { // Remove the data read into the overflow buffer read -= getBufHandler().getReadBuffer().position(); } - //perform any tasks if needed + // perform any tasks if needed if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { tasks(); } else if (unwrap.getHandshakeStatus() == HandshakeStatus.NEED_WRAP) { @@ -729,7 +751,7 @@ needHandshakeWrap = true; } } - //if we need more network data, then bail out for now. + // if we need more network data, then bail out for now. if (unwrap.getStatus() == Status.BUFFER_UNDERFLOW) { break; } @@ -753,7 +775,8 @@ } dsts[offset + i] = getBufHandler().getReadBuffer(); found = true; - } else if (getAppReadBufHandler() != null && dsts[offset + i] == getAppReadBufHandler().getByteBuffer()) { + } else if (getAppReadBufHandler() != null && + dsts[offset + i] == getAppReadBufHandler().getByteBuffer()) { getAppReadBufHandler().expand(sslEngine.getSession().getApplicationBufferSize()); if (dsts[offset + i] == getAppReadBufHandler().getByteBuffer()) { resized = false; @@ -797,15 +820,16 @@ * Writes a sequence of bytes to this channel from the given buffer. * * @param src The buffer from which bytes are to be retrieved + * * @return The number of bytes written, possibly zero + * * @throws IOException If some other I/O error occurs */ @Override public int write(ByteBuffer src) throws IOException { checkInterruptStatus(); if (src == this.netOutBuffer) { - int written = sc.write(src); - return written; + return sc.write(src); } else { // Are we closing or closed? if (closing || closed) { @@ -848,8 +872,7 @@ } @Override - public long write(ByteBuffer[] srcs, int offset, int length) - throws IOException { + public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { checkInterruptStatus(); // Are we closing or closed? if (closing || closed) { @@ -918,6 +941,6 @@ private enum OverflowState { NONE, PROCESSING, - DONE; + DONE } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SendfileDataBase.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SendfileDataBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SendfileDataBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SendfileDataBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,30 +19,26 @@ public abstract class SendfileDataBase { /** - * Is the current request being processed on a keep-alive connection? This - * determines if the socket is closed once the send file completes or if - * processing continues with the next request on the connection or waiting - * for that next request to arrive. + * Is the current request being processed on a keep-alive connection? This determines if the socket is closed once + * the send file completes or if processing continues with the next request on the connection or waiting for that + * next request to arrive. */ public SendfileKeepAliveState keepAliveState = SendfileKeepAliveState.NONE; /** - * The full path to the file that contains the data to be written to the - * socket. + * The full path to the file that contains the data to be written to the socket. */ public final String fileName; /** - * The position of the next byte in the file to be written to the socket. - * This is initialised to the start point and then updated as the file is - * written. + * The position of the next byte in the file to be written to the socket. This is initialised to the start point and + * then updated as the file is written. */ public long pos; /** - * The number of bytes remaining to be written from the file (from the - * current {@link #pos}. This is initialised to the end point - the start - * point and then updated as the file is written. + * The number of bytes remaining to be written from the file (from the current {@link #pos}). This is initialised to + * the end point - the start point and then updated as the file is written. */ public long length; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SendfileKeepAliveState.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SendfileKeepAliveState.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SendfileKeepAliveState.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SendfileKeepAliveState.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,21 +19,19 @@ public enum SendfileKeepAliveState { /** - * Keep-alive is not in use. The socket can be closed when the response has - * been written. + * Keep-alive is not in use. The socket can be closed when the response has been written. */ NONE, /** - * Keep-alive is in use and there is pipelined data in the input buffer to - * be read as soon as the current response has been written. + * Keep-alive is in use and there is pipelined data in the input buffer to be read as soon as the current response + * has been written. */ PIPELINED, /** - * Keep-alive is in use. The socket should be added to the poller (or - * equivalent) to await more data as soon as the current response has been - * written. + * Keep-alive is in use. The socket should be added to the poller (or equivalent) to await more data as soon as the + * current response has been written. */ OPEN } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SendfileState.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SendfileState.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SendfileState.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SendfileState.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,8 +19,7 @@ public enum SendfileState { /** - * The sending of the file has started but has not completed. Sendfile is - * still using the socket. + * The sending of the file has started but has not completed. Sendfile is still using the socket. */ PENDING, @@ -30,8 +29,7 @@ DONE, /** - * Something went wrong. The file may or may not have been sent. The socket - * is in an unknown state. + * Something went wrong. The file may or may not have been sent. The socket is in an unknown state. */ ERROR } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SocketBufferHandler.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketBufferHandler.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SocketBufferHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketBufferHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,13 +27,12 @@ @Override public void expand(int newSize) { } + /* - * Http2AsyncParser$FrameCompletionHandler will return incomplete - * frame(s) to the buffer. If the previous frame (or concurrent write to - * a stream) triggered a connection close this call would fail with a - * BufferOverflowException as data can't be returned to a buffer of zero - * length. Override the method and make it a NO-OP to avoid triggering - * the exception. + * Http2AsyncParser$FrameCompletionHandler will return incomplete frame(s) to the buffer. If the previous frame + * (or concurrent write to a stream) triggered a connection close this call would fail with a + * BufferOverflowException as data can't be returned to a buffer of zero length. Override the method and make it + * a NO-OP to avoid triggering the exception. */ @Override public void unReadReadBuffer(ByteBuffer returnedData) { @@ -48,8 +47,7 @@ private final boolean direct; - public SocketBufferHandler(int readBufferSize, int writeBufferSize, - boolean direct) { + public SocketBufferHandler(int readBufferSize, int writeBufferSize, boolean direct) { this.direct = direct; if (direct) { readBuffer = ByteBuffer.allocateDirect(readBufferSize); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SocketEvent.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketEvent.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SocketEvent.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketEvent.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,9 +17,8 @@ package org.apache.tomcat.util.net; /** - * Defines events that occur per socket that require further processing by the - * container. Usually these events are triggered by the socket implementation - * but they may be triggered by the container. + * Defines events that occur per socket that require further processing by the container. Usually these events are + * triggered by the socket implementation, but they may be triggered by the container. */ public enum SocketEvent { @@ -34,14 +33,13 @@ OPEN_WRITE, /** - * The associated Connector/Endpoint is stopping and the connection/socket - * needs to be closed cleanly. + * The associated Connector/Endpoint is stopping and the connection/socket needs to be closed cleanly. */ STOP, /** - * A timeout has occurred and the connection needs to be closed cleanly. - * Currently this is only used by the Servlet 3.0 async processing. + * A timeout has occurred and the connection needs to be closed cleanly. Currently, this is only used by the Servlet + * 3.0 async processing. */ TIMEOUT, @@ -51,20 +49,18 @@ DISCONNECT, /** - * An error has occurred on a non-container thread and processing needs to - * return to the container for any necessary clean-up. Examples of where - * this is used include: + * An error has occurred on a non-container thread and processing needs to return to the container for any necessary + * clean-up. Examples of where this is used include: *
      *
    • by NIO2 to signal the failure of a completion handler
    • - *
    • by the container to signal an I/O error on a non-container thread - * during Servlet 3.0 asynchronous processing.
    • + *
    • by the container to signal an I/O error on a non-container thread during Servlet 3.0 asynchronous + * processing.
    • *
    */ ERROR, /** - * A client attempted to establish a connection but failed. Examples of - * where this is used include: + * A client attempted to establish a connection but failed. Examples of where this is used include: *
      *
    • TLS handshake failures
    • *
    diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SocketProperties.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketProperties.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SocketProperties.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketProperties.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,9 +31,8 @@ import org.apache.tomcat.util.res.StringManager; /** - * Properties that can be set in the <Connector> element - * in server.xml. All properties are prefixed with "socket." - * and are currently only working for the Nio connector + * Properties that can be set in the <Connector> element in server.xml. All properties are prefixed with + * "socket." and are currently only working for the Nio connector */ public class SocketProperties { @@ -41,75 +40,71 @@ private static final StringManager sm = StringManager.getManager(SocketProperties.class); /** - * Enable/disable socket processor cache, this bounded cache stores - * SocketProcessor objects to reduce GC - * Default is 0 - * -1 is unlimited + * Enable/disable socket processor cache, this bounded cache stores SocketProcessor objects to reduce GC. + *

    + * Default is 0
    + * -1 is unlimited
    * 0 is disabled */ protected int processorCache = 0; /** - * Enable/disable poller event cache, this bounded cache stores - * PollerEvent objects to reduce GC for the poller - * Default is 0 - * -1 is unlimited - * 0 is disabled + * Enable/disable poller event cache, this bounded cache stores PollerEvent objects to reduce GC for the poller + *

    + * Default is 0
    + * -1 is unlimited
    + * 0 is disabled
    * >0 the max number of objects to keep in cache. */ protected int eventCache = 0; /** - * Enable/disable direct buffers for the network buffers - * Default value is disabled + * Enable/disable direct buffers for the network buffers. Default value is disabled. */ protected boolean directBuffer = false; /** - * Enable/disable direct buffers for the network buffers for SSL - * Default value is disabled + * Enable/disable direct buffers for the network buffers for SSL. Default value is disabled. */ protected boolean directSslBuffer = false; /** - * Socket receive buffer size in bytes (SO_RCVBUF). - * JVM default used if not set. + * Socket receive buffer size in bytes (SO_RCVBUF). JVM default used if not set. */ protected Integer rxBufSize = null; /** - * Socket send buffer size in bytes (SO_SNDBUF). - * JVM default used if not set. + * Socket send buffer size in bytes (SO_SNDBUF). JVM default used if not set. */ protected Integer txBufSize = null; /** - * The application read buffer size in bytes. - * Default value is 8192 + * The application read buffer size in bytes. Default value is 8192. */ protected int appReadBufSize = 8192; /** - * The application write buffer size in bytes - * Default value is 8192 + * The application write buffer size in bytes. Default value is 8192. */ protected int appWriteBufSize = 8192; /** - * NioChannel pool size for the endpoint, - * this value is how many channels - * -1 means unlimited cached, 0 means no cache, - * -2 means bufferPoolSize will be used + * NioChannel pool size for the endpoint, this value is how many channels. + *

    + * 0 means no cache
    + * -1 means unlimited cached
    + * -2 means bufferPoolSize will be used
    * Default value is -2 */ protected int bufferPool = -2; /** - * Buffer pool size in bytes to be cached - * -1 means unlimited, 0 means no cache - * Default value is based on the max memory reported by the JVM, - * if less than 1GB, then 0, else the value divided by 32. This value - * will then be used to compute bufferPool if its value is -2 + * Buffer pool size in bytes to be cached. + *

    + * -1 means unlimited
    + * 0 means no cache
    + * Default value is based on the max memory reported by the JVM, if less than 1GB, then 0, else the value divided by + * 32. This value will then be used to compute bufferPool if its value is -2 */ protected int bufferPoolSize = -2; @@ -134,14 +129,14 @@ protected Boolean soReuseAddress = null; /** - * SO_LINGER option, paired with the soLingerTime value. - * JVM defaults used unless both attributes are set. + * SO_LINGER option, paired with the soLingerTime value. JVM defaults used unless both attributes are + * set. */ protected Boolean soLingerOn = null; /** - * SO_LINGER option, paired with the soLingerOn value. - * JVM defaults used unless both attributes are set. + * SO_LINGER option, paired with the soLingerOn value. JVM defaults used unless both attributes are + * set. */ protected Integer soLingerTime = null; @@ -151,32 +146,28 @@ protected Integer soTimeout = Integer.valueOf(20000); /** - * Performance preferences according to - * https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/Socket.html#setPerformancePreferences(int,int,int) - * All three performance attributes must be set or the JVM defaults will be - * used. + * Performance preferences according to
    setPerformancePreferences + * All three performance attributes must be set or the JVM defaults will be used. */ protected Integer performanceConnectionTime = null; /** - * Performance preferences according to - * https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/Socket.html#setPerformancePreferences(int,int,int) - * All three performance attributes must be set or the JVM defaults will be - * used. + * Performance preferences according to setPerformancePreferences + * All three performance attributes must be set or the JVM defaults will be used. */ protected Integer performanceLatency = null; /** - * Performance preferences according to - * https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/Socket.html#setPerformancePreferences(int,int,int) - * All three performance attributes must be set or the JVM defaults will be - * used. + * Performance preferences according to setPerformancePreferences + * All three performance attributes must be set or the JVM defaults will be used. */ protected Integer performanceBandwidth = null; /** - * The minimum frequency of the timeout interval to avoid excess load from - * the poller during high traffic + * The minimum frequency of the timeout interval to avoid excess load from the poller during high traffic */ protected long timeoutInterval = 1000; @@ -188,32 +179,28 @@ private ObjectName oname = null; - public void setProperties(Socket socket) throws SocketException{ + public void setProperties(Socket socket) throws SocketException { if (rxBufSize != null) { socket.setReceiveBufferSize(rxBufSize.intValue()); } if (txBufSize != null) { socket.setSendBufferSize(txBufSize.intValue()); } - if (ooBInline !=null) { + if (ooBInline != null) { socket.setOOBInline(ooBInline.booleanValue()); } if (soKeepAlive != null) { socket.setKeepAlive(soKeepAlive.booleanValue()); } - if (performanceConnectionTime != null && performanceLatency != null && - performanceBandwidth != null) { - socket.setPerformancePreferences( - performanceConnectionTime.intValue(), - performanceLatency.intValue(), + if (performanceConnectionTime != null && performanceLatency != null && performanceBandwidth != null) { + socket.setPerformancePreferences(performanceConnectionTime.intValue(), performanceLatency.intValue(), performanceBandwidth.intValue()); } if (soReuseAddress != null) { socket.setReuseAddress(soReuseAddress.booleanValue()); } if (soLingerOn != null && soLingerTime != null) { - socket.setSoLinger(soLingerOn.booleanValue(), - soLingerTime.intValue()); + socket.setSoLinger(soLingerOn.booleanValue(), soLingerTime.intValue()); } if (soTimeout != null && soTimeout.intValue() >= 0) { socket.setSoTimeout(soTimeout.intValue()); @@ -227,15 +214,12 @@ } } - public void setProperties(ServerSocket socket) throws SocketException{ + public void setProperties(ServerSocket socket) throws SocketException { if (rxBufSize != null) { socket.setReceiveBufferSize(rxBufSize.intValue()); } - if (performanceConnectionTime != null && performanceLatency != null && - performanceBandwidth != null) { - socket.setPerformancePreferences( - performanceConnectionTime.intValue(), - performanceLatency.intValue(), + if (performanceConnectionTime != null && performanceLatency != null && performanceBandwidth != null) { + socket.setPerformancePreferences(performanceConnectionTime.intValue(), performanceLatency.intValue(), performanceBandwidth.intValue()); } if (soReuseAddress != null) { @@ -365,8 +349,7 @@ } public void setPerformanceConnectionTime(int performanceConnectionTime) { - this.performanceConnectionTime = - Integer.valueOf(performanceConnectionTime); + this.performanceConnectionTime = Integer.valueOf(performanceConnectionTime); } public void setTxBufSize(int txBufSize) { @@ -467,8 +450,10 @@ /** * Get the actual buffer pool size to use. - * @param bufferOverhead When TLS is enabled, additional network buffers - * are needed and will be added to the application buffer size + * + * @param bufferOverhead When TLS is enabled, additional network buffers are needed and will be added to the + * application buffer size + * * @return the actual buffer pool size that will be used */ public int getActualBufferPool(int bufferOverhead) { @@ -481,7 +466,7 @@ return 0; } else { long actualBufferPoolSize = bufferPoolSize; - long poolSize = 0; + long poolSize; if (actualBufferPoolSize == -2) { long maxMemory = Runtime.getRuntime().maxMemory(); if (maxMemory > Integer.MAX_VALUE) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/SocketWrapperBase.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketWrapperBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/SocketWrapperBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/SocketWrapperBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,14 +48,12 @@ protected static final StringManager sm = StringManager.getManager(SocketWrapperBase.class); /* - * At 100,000 connections a second there are enough IDs here for ~3,000,000 - * years before it overflows (and then we have another 3,000,000 years - * before it gets back to zero). - * - * Local testing shows that 5 threads can obtain 60,000,000+ IDs a second - * from a single AtomicLong. That is about about 17ns per request. It does - * not appear that the introduction of this counter will cause a bottleneck - * for connection processing. + * At 100,000 connections a second there are enough IDs here for ~3,000,000 years before it overflows (and then we + * have another 3,000,000 years before it gets back to zero). + * + * Local testing shows that 5 threads can obtain 60,000,000+ IDs a second from a single AtomicLong. That is about + * 17ns per request. It does not appear that the introduction of this counter will cause a bottleneck for connection + * processing. */ private static final AtomicLong connectionIdGenerator = new AtomicLong(0); @@ -88,10 +86,11 @@ protected int remotePort = -1; protected volatile ServletConnection servletConnection = null; + protected String sniHostName = null; + /** - * Used to record the first IOException that occurs during non-blocking - * read/writes that can't be usefully propagated up the stack since there is - * no user code or appropriate container code in the stack to handle it. + * Used to record the first IOException that occurs during non-blocking read/writes that can't be usefully + * propagated up the stack since there is no user code or appropriate container code in the stack to handle it. */ private volatile IOException error = null; @@ -106,13 +105,10 @@ protected int bufferedWriteSize = 64 * 1024; // 64k default write buffer /** - * Additional buffer used for non-blocking writes. Non-blocking writes need - * to return immediately even if the data cannot be written immediately but - * the socket buffer may not be big enough to hold all of the unwritten - * data. This structure provides an additional buffer to hold the data until - * it can be written. - * Not that while the Servlet API only allows one non-blocking write at a - * time, due to buffering and the possible need to write HTTP headers, this + * Additional buffer used for non-blocking writes. Non-blocking writes need to return immediately even if the data + * cannot be written immediately but the socket buffer may not be big enough to hold all of the unwritten data. This + * structure provides an additional buffer to hold the data until it can be written. Not that while the Servlet API + * only allows one non-blocking write at a time, due to buffering and the possible need to write HTTP headers, this * layer may see multiple writes. */ protected final WriteBuffer nonBlockingWriteBuffer = new WriteBuffer(bufferedWriteSize); @@ -126,9 +122,8 @@ protected volatile OperationState writeOperation = null; /** - * The org.apache.coyote.Processor instance currently associated with the - * wrapper. Only populated when required to maintain wrapper<->Processor - * mapping between calls to + * The org.apache.coyote.Processor instance currently associated with the wrapper. Only populated when required to + * maintain wrapper<->Processor mapping between calls to * {@link AbstractEndpoint.Handler#process(SocketWrapperBase, SocketEvent)}. */ private final AtomicReference currentProcessor = new AtomicReference<>(); @@ -189,7 +184,10 @@ executor.execute(runnable); } - public IOException getError() { return error; } + public IOException getError() { + return error; + } + public void setError(IOException error) { // Not perfectly thread-safe but good enough. Just needs to ensure that // once this.error is non-null, it can never be null. @@ -198,23 +196,39 @@ } this.error = error; } + public void checkError() throws IOException { if (error != null) { throw error; } } - public String getNegotiatedProtocol() { return negotiatedProtocol; } + public String getNegotiatedProtocol() { + return negotiatedProtocol; + } + public void setNegotiatedProtocol(String negotiatedProtocol) { this.negotiatedProtocol = negotiatedProtocol; } /** - * Set the timeout for reading. Values of zero or less will be changed to - * -1. + * @return the sniHostName + */ + public String getSniHostName() { + return this.sniHostName; + } + + /** + * @param sniHostName the SNI host name to set + */ + public void setSniHostName(String sniHostName) { + this.sniHostName = sniHostName; + } + + /** + * Set the timeout for reading. Values of zero or less will be changed to -1. * - * @param readTimeout The timeout in milliseconds. A value of -1 indicates - * an infinite timeout. + * @param readTimeout The timeout in milliseconds. A value of -1 indicates an infinite timeout. */ public void setReadTimeout(long readTimeout) { if (readTimeout > 0) { @@ -229,11 +243,9 @@ } /** - * Set the timeout for writing. Values of zero or less will be changed to - * -1. + * Set the timeout for writing. Values of zero or less will be changed to -1. * - * @param writeTimeout The timeout in milliseconds. A value of zero or less - * indicates an infinite timeout. + * @param writeTimeout The timeout in milliseconds. A value of zero or less indicates an infinite timeout. */ public void setWriteTimeout(long writeTimeout) { if (writeTimeout > 0) { @@ -248,8 +260,13 @@ } - public void setKeepAliveLeft(int keepAliveLeft) { this.keepAliveLeft = keepAliveLeft; } - public int decrementKeepAlive() { return (--keepAliveLeft); } + public void setKeepAliveLeft(int keepAliveLeft) { + this.keepAliveLeft = keepAliveLeft; + } + + public int decrementKeepAlive() { + return (--keepAliveLeft); + } public String getRemoteHost() { if (remoteHost == null) { @@ -257,6 +274,7 @@ } return remoteHost; } + protected abstract void populateRemoteHost(); public String getRemoteAddr() { @@ -265,6 +283,7 @@ } return remoteAddr; } + protected abstract void populateRemoteAddr(); public int getRemotePort() { @@ -273,6 +292,7 @@ } return remotePort; } + protected abstract void populateRemotePort(); public String getLocalName() { @@ -281,6 +301,7 @@ } return localName; } + protected abstract void populateLocalName(); public String getLocalAddr() { @@ -289,6 +310,7 @@ } return localAddr; } + protected abstract void populateLocalAddr(); public int getLocalPort() { @@ -297,9 +319,12 @@ } return localPort; } + protected abstract void populateLocalPort(); - public SocketBufferHandler getSocketBufferHandler() { return socketBufferHandler; } + public SocketBufferHandler getSocketBufferHandler() { + return socketBufferHandler; + } public boolean hasDataToRead() { // Return true because it is always safe to make a read attempt @@ -311,18 +336,14 @@ } /** - * Checks to see if there are any writes pending and if there are calls - * {@link #registerWriteInterest()} to trigger a callback once the pending - * writes have completed. + * Checks to see if there are any writes pending and if there are calls {@link #registerWriteInterest()} to trigger + * a callback once the pending writes have completed. *

    - * Note: Once this method has returned false it MUST NOT - * be called again until the pending write has completed and the - * callback has been fired. - * TODO: Modify {@link #registerWriteInterest()} so the above - * restriction is enforced there rather than relying on the caller. + * Note: Once this method has returned false it MUST NOT be called again until the pending write + * has completed and the callback has been fired. TODO: Modify {@link #registerWriteInterest()} so the above + * restriction is enforced there rather than relying on the caller. * - * @return true if no writes are pending and data can be - * written otherwise false + * @return true if no writes are pending and data can be written otherwise false */ public boolean isReadyForWrite() { boolean result = canWrite(); @@ -342,8 +363,8 @@ /** - * Overridden for debug purposes. No guarantees are made about the format of - * this message which may vary significantly between point releases. + * Overridden for debug purposes. No guarantees are made about the format of this message which may vary + * significantly between point releases. *

    * {@inheritDoc} */ @@ -354,8 +375,11 @@ public abstract int read(boolean block, byte[] b, int off, int len) throws IOException; + public abstract int read(boolean block, ByteBuffer to) throws IOException; + public abstract boolean isReadyForRead() throws IOException; + public abstract void setAppReadBufHandler(ApplicationBufferHandler handler); protected int populateReadBuffer(byte[] b, int off, int len) { @@ -391,13 +415,11 @@ /** - * Return input that has been read to the input buffer for re-reading by the - * correct component. There are times when a component may read more data - * than it needs before it passes control to another component. One example - * of this is during HTTP upgrade. If an (arguably misbehaving client) sends - * data associated with the upgraded protocol before the HTTP upgrade - * completes, the HTTP handler may read it. This method provides a way for - * that data to be returned so it can be processed by the correct component. + * Return input that has been read to the input buffer for re-reading by the correct component. There are times when + * a component may read more data than it needs before it passes control to another component. One example of this + * is during HTTP upgrade. If an (arguably misbehaving client) sends data associated with the upgraded protocol + * before the HTTP upgrade completes, the HTTP handler may read it. This method provides a way for that data to be + * returned so it can be processed by the correct component. * * @param returnedInput The input to return to the input buffer. */ @@ -415,10 +437,10 @@ if (closed.compareAndSet(false, true)) { try { getEndpoint().getHandler().release(this); - } catch (Throwable e) { - ExceptionUtils.handleThrowable(e); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); if (log.isDebugEnabled()) { - log.error(sm.getString("endpoint.debug.handlerRelease"), e); + log.error(sm.getString("endpoint.debug.handlerRelease"), t); } } finally { getEndpoint().countDownConnection(); @@ -428,8 +450,7 @@ } /** - * Perform the actual close. The closed atomic boolean guarantees this will - * be called only once per wrapper. + * Perform the actual close. The closed atomic boolean guarantees this will be called only once per wrapper. */ protected abstract void doClose(); @@ -442,25 +463,19 @@ /** - * Writes the provided data to the socket write buffer. If the socket write - * buffer fills during the write, the content of the socket write buffer is - * written to the network and this method starts to fill the socket write - * buffer again. Depending on the size of the data to write, there may be - * multiple writes to the network. + * Writes the provided data to the socket write buffer. If the socket write buffer fills during the write, the + * content of the socket write buffer is written to the network and this method starts to fill the socket write + * buffer again. Depending on the size of the data to write, there may be multiple writes to the network. *

    - * Non-blocking writes must return immediately and the byte array holding - * the data to be written must be immediately available for re-use. It may - * not be possible to write sufficient data to the network to allow this to - * happen. In this case data that cannot be written to the network and - * cannot be held by the socket buffer is stored in the non-blocking write - * buffer. + * Non-blocking writes must return immediately and the byte array holding the data to be written must be immediately + * available for re-use. It may not be possible to write sufficient data to the network to allow this to happen. In + * this case data that cannot be written to the network and cannot be held by the socket buffer is stored in the + * non-blocking write buffer. *

    - * Note: There is an implementation assumption that, before switching from - * non-blocking writes to blocking writes, any data remaining in the - * non-blocking write buffer will have been written to the network. + * Note: There is an implementation assumption that, before switching from non-blocking writes to blocking writes, + * any data remaining in the non-blocking write buffer will have been written to the network. * - * @param block true if a blocking write should be used, - * otherwise a non-blocking write will be used + * @param block true if a blocking write should be used, otherwise a non-blocking write will be used * @param buf The byte array containing the data to be written * @param off The offset within the byte array of the data to be written * @param len The length of the data to be written @@ -473,14 +488,11 @@ } /* - * While the implementations for blocking and non-blocking writes are - * very similar they have been split into separate methods: - * - To allow sub-classes to override them individually. NIO2, for - * example, overrides the non-blocking write but not the blocking - * write. - * - To enable a marginally more efficient implemented for blocking - * writes which do not require the additional checks related to the - * use of the non-blocking write buffer + * While the implementations for blocking and non-blocking writes are very similar they have been split into + * separate methods: - To allow subclasses to override them individually. NIO2, for example, overrides the + * non-blocking write but not the blocking write. - To enable a marginally more efficient implemented for + * blocking writes which do not require the additional checks related to the use of the non-blocking write + * buffer */ if (block) { writeBlocking(buf, off, len); @@ -491,26 +503,20 @@ /** - * Writes the provided data to the socket write buffer. If the socket write - * buffer fills during the write, the content of the socket write buffer is - * written to the network and this method starts to fill the socket write - * buffer again. Depending on the size of the data to write, there may be - * multiple writes to the network. + * Writes the provided data to the socket write buffer. If the socket write buffer fills during the write, the + * content of the socket write buffer is written to the network and this method starts to fill the socket write + * buffer again. Depending on the size of the data to write, there may be multiple writes to the network. *

    - * Non-blocking writes must return immediately and the ByteBuffer holding - * the data to be written must be immediately available for re-use. It may - * not be possible to write sufficient data to the network to allow this to - * happen. In this case data that cannot be written to the network and - * cannot be held by the socket buffer is stored in the non-blocking write - * buffer. + * Non-blocking writes must return immediately and the ByteBuffer holding the data to be written must be immediately + * available for re-use. It may not be possible to write sufficient data to the network to allow this to happen. In + * this case data that cannot be written to the network and cannot be held by the socket buffer is stored in the + * non-blocking write buffer. *

    - * Note: There is an implementation assumption that, before switching from - * non-blocking writes to blocking writes, any data remaining in the - * non-blocking write buffer will have been written to the network. - * - * @param block true if a blocking write should be used, - * otherwise a non-blocking write will be used - * @param from The ByteBuffer containing the data to be written + * Note: There is an implementation assumption that, before switching from non-blocking writes to blocking writes, + * any data remaining in the non-blocking write buffer will have been written to the network. + * + * @param block true if a blocking write should be used, otherwise a non-blocking write will be used + * @param from The ByteBuffer containing the data to be written * * @throws IOException If an IO error occurs during the write */ @@ -520,14 +526,11 @@ } /* - * While the implementations for blocking and non-blocking writes are - * very similar they have been split into separate methods: - * - To allow sub-classes to override them individually. NIO2, for - * example, overrides the non-blocking write but not the blocking - * write. - * - To enable a marginally more efficient implemented for blocking - * writes which do not require the additional checks related to the - * use of the non-blocking write buffer + * While the implementations for blocking and non-blocking writes are very similar they have been split into + * separate methods: - To allow subclasses to override them individually. NIO2, for example, overrides the + * non-blocking write but not the blocking write. - To enable a marginally more efficient implemented for + * blocking writes which do not require the additional checks related to the use of the non-blocking write + * buffer */ if (block) { writeBlocking(from); @@ -538,17 +541,15 @@ /** - * Writes the provided data to the socket write buffer. If the socket write - * buffer fills during the write, the content of the socket write buffer is - * written to the network using a blocking write. Once that blocking write - * is complete, this method starts to fill the socket write buffer again. - * Depending on the size of the data to write, there may be multiple writes - * to the network. On completion of this method there will always be space - * remaining in the socket write buffer. - * - * @param buf The byte array containing the data to be written - * @param off The offset within the byte array of the data to be written - * @param len The length of the data to be written + * Writes the provided data to the socket write buffer. If the socket write buffer fills during the write, the + * content of the socket write buffer is written to the network using a blocking write. Once that blocking write is + * complete, this method starts to fill the socket write buffer again. Depending on the size of the data to write, + * there may be multiple writes to the network. On completion of this method there will always be space remaining in + * the socket write buffer. + * + * @param buf The byte array containing the data to be written + * @param off The offset within the byte array of the data to be written + * @param len The length of the data to be written * * @throws IOException If an IO error occurs during the write */ @@ -569,13 +570,11 @@ /** - * Writes the provided data to the socket write buffer. If the socket write - * buffer fills during the write, the content of the socket write buffer is - * written to the network using a blocking write. Once that blocking write - * is complete, this method starts to fill the socket write buffer again. - * Depending on the size of the data to write, there may be multiple writes - * to the network. On completion of this method there will always be space - * remaining in the socket write buffer. + * Writes the provided data to the socket write buffer. If the socket write buffer fills during the write, the + * content of the socket write buffer is written to the network using a blocking write. Once that blocking write is + * complete, this method starts to fill the socket write buffer again. Depending on the size of the data to write, + * there may be multiple writes to the network. On completion of this method there will always be space remaining in + * the socket write buffer. * * @param from The ByteBuffer containing the data to be written * @@ -595,37 +594,33 @@ /** - * Transfers the data to the socket write buffer (writing that data to the - * socket if the buffer fills up using a non-blocking write) until either - * all the data has been transferred and space remains in the socket write - * buffer or a non-blocking write leaves data in the socket write buffer. - * After an incomplete write, any data remaining to be transferred to the - * socket write buffer will be copied to the socket write buffer. If the - * remaining data is too big for the socket write buffer, the socket write - * buffer will be filled and the additional data written to the non-blocking - * write buffer. - * - * @param buf The byte array containing the data to be written - * @param off The offset within the byte array of the data to be written - * @param len The length of the data to be written + * Transfers the data to the socket write buffer (writing that data to the socket if the buffer fills up using a + * non-blocking write) until either all the data has been transferred and space remains in the socket write buffer + * or a non-blocking write leaves data in the socket write buffer. After an incomplete write, any data remaining to + * be transferred to the socket write buffer will be copied to the socket write buffer. If the remaining data is too + * big for the socket write buffer, the socket write buffer will be filled and the additional data written to the + * non-blocking write buffer. + * + * @param buf The byte array containing the data to be written + * @param off The offset within the byte array of the data to be written + * @param len The length of the data to be written * * @throws IOException If an IO error occurs during the write */ protected void writeNonBlocking(byte[] buf, int off, int len) throws IOException { - if (len > 0 && nonBlockingWriteBuffer.isEmpty() - && socketBufferHandler.isWriteBufferWritable()) { + if (len > 0 && nonBlockingWriteBuffer.isEmpty() && socketBufferHandler.isWriteBufferWritable()) { socketBufferHandler.configureWriteBufferForWrite(); int thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer()); len -= thisTime; while (len > 0) { off = off + thisTime; doWrite(false); - if (len > 0 && socketBufferHandler.isWriteBufferWritable()) { + if (socketBufferHandler.isWriteBufferWritable()) { socketBufferHandler.configureWriteBufferForWrite(); thisTime = transfer(buf, off, len, socketBufferHandler.getWriteBuffer()); } else { // Didn't write any data in the last non-blocking write. - // Therefore the write buffer will still be full. Nothing + // Therefore, the write buffer will still be full. Nothing // else to do here. Exit the loop. break; } @@ -641,25 +636,20 @@ /** - * Transfers the data to the socket write buffer (writing that data to the - * socket if the buffer fills up using a non-blocking write) until either - * all the data has been transferred and space remains in the socket write - * buffer or a non-blocking write leaves data in the socket write buffer. - * After an incomplete write, any data remaining to be transferred to the - * socket write buffer will be copied to the socket write buffer. If the - * remaining data is too big for the socket write buffer, the socket write - * buffer will be filled and the additional data written to the non-blocking - * write buffer. + * Transfers the data to the socket write buffer (writing that data to the socket if the buffer fills up using a + * non-blocking write) until either all the data has been transferred and space remains in the socket write buffer + * or a non-blocking write leaves data in the socket write buffer. After an incomplete write, any data remaining to + * be transferred to the socket write buffer will be copied to the socket write buffer. If the remaining data is too + * big for the socket write buffer, the socket write buffer will be filled and the additional data written to the + * non-blocking write buffer. * * @param from The ByteBuffer containing the data to be written * * @throws IOException If an IO error occurs during the write */ - protected void writeNonBlocking(ByteBuffer from) - throws IOException { + protected void writeNonBlocking(ByteBuffer from) throws IOException { - if (from.hasRemaining() && nonBlockingWriteBuffer.isEmpty() - && socketBufferHandler.isWriteBufferWritable()) { + if (from.hasRemaining() && nonBlockingWriteBuffer.isEmpty() && socketBufferHandler.isWriteBufferWritable()) { writeNonBlockingInternal(from); } @@ -671,8 +661,7 @@ /** - * Separate method so it can be re-used by the socket write buffer to write - * data to the network + * Separate method so it can be re-used by the socket write buffer to write data to the network * * @param from The ByteBuffer containing the data to be written * @@ -696,12 +685,10 @@ /** * Writes as much data as possible from any that remains in the buffers. * - * @param block true if a blocking write should be used, - * otherwise a non-blocking write will be used + * @param block true if a blocking write should be used, otherwise a non-blocking write will be used * - * @return true if data remains to be flushed after this method - * completes, otherwise false. In blocking mode - * therefore, the return value should always be false + * @return true if data remains to be flushed after this method completes, otherwise + * false. In blocking mode therefore, the return value should always be false * * @throws IOException If an IO error occurs during the write */ @@ -719,8 +706,7 @@ /** - * Writes all remaining data from the buffers and blocks until the write is - * complete. + * Writes all remaining data from the buffers and blocks until the write is complete. * * @throws IOException If an IO error occurs during the write */ @@ -741,8 +727,8 @@ /** * Writes as much data as possible from any that remains in the buffers. * - * @return true if data remains to be flushed after this method - * completes, otherwise false. + * @return true if data remains to be flushed after this method completes, otherwise + * false. * * @throws IOException If an IO error occurs during the write */ @@ -750,14 +736,12 @@ /** - * Write the contents of the socketWriteBuffer to the socket. For blocking - * writes either then entire contents of the buffer will be written or an - * IOException will be thrown. Partial blocking writes will not occur. + * Write the contents of the socketWriteBuffer to the socket. For blocking writes either then entire contents of the + * buffer will be written or an IOException will be thrown. Partial blocking writes will not occur. * * @param block Should the write be blocking or not? * - * @throws IOException If an I/O error such as a timeout occurs during the - * write + * @throws IOException If an I/O error such as a timeout occurs during the write */ protected void doWrite(boolean block) throws IOException { socketBufferHandler.configureWriteBufferForRead(); @@ -766,15 +750,13 @@ /** - * Write the contents of the ByteBuffer to the socket. For blocking writes - * either then entire contents of the buffer will be written or an - * IOException will be thrown. Partial blocking writes will not occur. + * Write the contents of the ByteBuffer to the socket. For blocking writes either then entire contents of the buffer + * will be written or an IOException will be thrown. Partial blocking writes will not occur. * * @param block Should the write be blocking or not? - * @param from the ByteBuffer containing the data to be written + * @param from the ByteBuffer containing the data to be written * - * @throws IOException If an I/O error such as a timeout occurs during the - * write + * @throws IOException If an I/O error such as a timeout occurs during the write */ protected abstract void doWrite(boolean block, ByteBuffer from) throws IOException; @@ -791,10 +773,9 @@ public abstract SendfileDataBase createSendfileData(String filename, long pos, long length); /** - * Starts the sendfile process. It is expected that if the sendfile process - * does not complete during this call and does not report an error, that the - * caller will not add the socket to the poller (or equivalent). That - * is the responsibility of this method. + * Starts the sendfile process. It is expected that if the sendfile process does not complete during this call and + * does not report an error, that the caller will not add the socket to the poller (or equivalent). That is + * the responsibility of this method. * * @param sendfileData Data representing the file to send * @@ -803,16 +784,13 @@ public abstract SendfileState processSendfile(SendfileDataBase sendfileData); /** - * Require the client to perform CLIENT-CERT authentication if it hasn't - * already done so. + * Require the client to perform CLIENT-CERT authentication if it hasn't already done so. * - * @param sslSupport The SSL/TLS support instance currently being used by - * the connection that may need updating after the client - * authentication - * - * @throws IOException If authentication is required then there will be I/O - * with the client and this exception will be thrown if - * that goes wrong + * @param sslSupport The SSL/TLS support instance currently being used by the connection that may need updating + * after the client authentication + * + * @throws IOException If authentication is required then there will be I/O with the client and this exception will + * be thrown if that goes wrong */ public abstract void doClientAuth(SSLSupport sslSupport) throws IOException; @@ -829,18 +807,16 @@ public enum BlockingMode { /** - * The operation will not block. If there are pending operations, - * the operation will throw a pending exception. + * The operation will not block. If there are pending operations, the operation will throw a pending exception. */ CLASSIC, /** - * The operation will not block. If there are pending operations, - * the operation will return CompletionState.NOT_DONE. + * The operation will not block. If there are pending operations, the operation will return + * CompletionState.NOT_DONE. */ NON_BLOCK, /** - * The operation will block until pending operations are completed, but - * will not block after performing it. + * The operation will block until pending operations are completed, but will not block after performing it. */ SEMI_BLOCK, /** @@ -855,7 +831,7 @@ */ PENDING, /** - * Operation was pending and non blocking. + * Operation was pending and non-blocking. */ NOT_DONE, /** @@ -874,101 +850,75 @@ public enum CompletionHandlerCall { /** - * Operation should continue, the completion handler shouldn't be - * called. + * Operation should continue, the completion handler shouldn't be called. */ CONTINUE, /** - * The operation completed but the completion handler shouldn't be - * called. + * The operation completed but the completion handler shouldn't be called. */ NONE, /** - * The operation is complete, the completion handler should be - * called. + * The operation is complete, the completion handler should be called. */ DONE } public interface CompletionCheck { /** - * Determine what call, if any, should be made to the completion - * handler. + * Determine what call, if any, should be made to the completion handler. * - * @param state of the operation (done or done in-line since the - * IO call is done) - * @param buffers ByteBuffer[] that has been passed to the - * original IO call - * @param offset that has been passed to the original IO call - * @param length that has been passed to the original IO call + * @param state of the operation (done or done in-line since the IO call is done) + * @param buffers ByteBuffer[] that has been passed to the original IO call + * @param offset that has been passed to the original IO call + * @param length that has been passed to the original IO call * * @return The call, if any, to make to the completion handler */ - CompletionHandlerCall callHandler(CompletionState state, ByteBuffer[] buffers, - int offset, int length); + CompletionHandlerCall callHandler(CompletionState state, ByteBuffer[] buffers, int offset, int length); } /** - * This utility CompletionCheck will cause the write to fully write - * all remaining data. If the operation completes inline, the - * completion handler will not be called. + * This utility CompletionCheck will cause the write to fully write all remaining data. If the operation completes + * inline, the completion handler will not be called. */ - public static final CompletionCheck COMPLETE_WRITE = new CompletionCheck() { - @Override - public CompletionHandlerCall callHandler(CompletionState state, ByteBuffer[] buffers, - int offset, int length) { - for (int i = 0; i < length; i++) { - if (buffers[offset + i].hasRemaining()) { - return CompletionHandlerCall.CONTINUE; - } + public static final CompletionCheck COMPLETE_WRITE = (state, buffers, offset, length) -> { + for (int i = 0; i < length; i++) { + if (buffers[offset + i].hasRemaining()) { + return CompletionHandlerCall.CONTINUE; } - return (state == CompletionState.DONE) ? CompletionHandlerCall.DONE - : CompletionHandlerCall.NONE; } + return (state == CompletionState.DONE) ? CompletionHandlerCall.DONE : CompletionHandlerCall.NONE; }; /** - * This utility CompletionCheck will cause the write to fully write - * all remaining data. The completion handler will then be called. + * This utility CompletionCheck will cause the write to fully write all remaining data. The completion handler will + * then be called. */ - public static final CompletionCheck COMPLETE_WRITE_WITH_COMPLETION = new CompletionCheck() { - @Override - public CompletionHandlerCall callHandler(CompletionState state, ByteBuffer[] buffers, - int offset, int length) { - for (int i = 0; i < length; i++) { - if (buffers[offset + i].hasRemaining()) { - return CompletionHandlerCall.CONTINUE; - } + public static final CompletionCheck COMPLETE_WRITE_WITH_COMPLETION = (state, buffers, offset, length) -> { + for (int i = 0; i < length; i++) { + if (buffers[offset + i].hasRemaining()) { + return CompletionHandlerCall.CONTINUE; } - return CompletionHandlerCall.DONE; } + return CompletionHandlerCall.DONE; }; /** - * This utility CompletionCheck will cause the completion handler - * to be called once some data has been read. If the operation - * completes inline, the completion handler will not be called. + * This utility CompletionCheck will cause the completion handler to be called once some data has been read. If the + * operation completes inline, the completion handler will not be called. */ - public static final CompletionCheck READ_DATA = new CompletionCheck() { - @Override - public CompletionHandlerCall callHandler(CompletionState state, ByteBuffer[] buffers, - int offset, int length) { - return (state == CompletionState.DONE) ? CompletionHandlerCall.DONE - : CompletionHandlerCall.NONE; - } - }; + public static final CompletionCheck READ_DATA = (state, buffers, offset, + length) -> (state == CompletionState.DONE) ? CompletionHandlerCall.DONE : CompletionHandlerCall.NONE; /** - * This utility CompletionCheck will cause the completion handler - * to be called once the given buffers are full. The completion - * handler will then be called. + * This utility CompletionCheck will cause the completion handler to be called once the given buffers are full. The + * completion handler will then be called. */ public static final CompletionCheck COMPLETE_READ_WITH_COMPLETION = COMPLETE_WRITE_WITH_COMPLETION; /** - * This utility CompletionCheck will cause the completion handler - * to be called once the given buffers are full. If the operation - * completes inline, the completion handler will not be called. + * This utility CompletionCheck will cause the completion handler to be called once the given buffers are full. If + * the operation completes inline, the completion handler will not be called. */ public static final CompletionCheck COMPLETE_READ = COMPLETE_WRITE; @@ -985,14 +935,15 @@ protected final TimeUnit unit; protected final BlockingMode block; protected final CompletionCheck check; - protected final CompletionHandler handler; + protected final CompletionHandler handler; protected final Semaphore semaphore; protected final VectoredIOCompletionHandler completion; protected final AtomicBoolean callHandler; - protected OperationState(boolean read, ByteBuffer[] buffers, int offset, int length, - BlockingMode block, long timeout, TimeUnit unit, A attachment, - CompletionCheck check, CompletionHandler handler, - Semaphore semaphore, VectoredIOCompletionHandler completion) { + + protected OperationState(boolean read, ByteBuffer[] buffers, int offset, int length, BlockingMode block, + long timeout, TimeUnit unit, A attachment, CompletionCheck check, + CompletionHandler handler, Semaphore semaphore, + VectoredIOCompletionHandler completion) { this.read = read; this.buffers = buffers; this.offset = offset; @@ -1007,13 +958,14 @@ this.completion = completion; callHandler = (handler != null) ? new AtomicBoolean(true) : null; } + protected volatile long nBytes = 0; protected volatile CompletionState state = CompletionState.PENDING; protected boolean completionDone = true; /** - * @return true if the operation is still inline, false if the operation - * is running on a thread that is not the original caller + * @return true if the operation is still inline, false if the operation is running on a thread that is not the + * original caller */ protected abstract boolean isInline(); @@ -1026,15 +978,15 @@ /** * Process the operation using the connector executor. - * @return true if the operation was accepted, false if the executor - * rejected execution + * + * @return true if the operation was accepted, false if the executor rejected execution */ protected boolean process() { try { getEndpoint().getExecutor().execute(this); return true; } catch (RejectedExecutionException ree) { - log.warn(sm.getString("endpoint.executor.fail", SocketWrapperBase.this) , ree); + log.warn(sm.getString("endpoint.executor.fail", SocketWrapperBase.this), ree); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // This means we got an OOM or similar creating a thread, or that @@ -1060,10 +1012,10 @@ } /** - * Completion handler for vectored operations. This will check the completion of the operation, - * then either continue or call the user provided completion handler. + * Completion handler for vectored operations. This will check the completion of the operation, then either continue + * or call the user provided completion handler. */ - protected class VectoredIOCompletionHandler implements CompletionHandler> { + protected class VectoredIOCompletionHandler implements CompletionHandler> { @Override public void completed(Long nBytes, OperationState state) { if (nBytes.longValue() < 0) { @@ -1074,7 +1026,8 @@ boolean complete = true; boolean completion = true; if (state.check != null) { - CompletionHandlerCall call = state.check.callHandler(currentState, state.buffers, state.offset, state.length); + CompletionHandlerCall call = + state.check.callHandler(currentState, state.buffers, state.offset, state.length); if (call == CompletionHandlerCall.CONTINUE || (!state.read && state.hasOutboundRemaining())) { complete = false; } else if (call == CompletionHandlerCall.NONE) { @@ -1117,6 +1070,7 @@ } } } + @Override public void failed(Throwable exc, OperationState state) { IOException ioe = null; @@ -1187,8 +1141,9 @@ /** * Allows checking if an asynchronous read operation is currently pending. - * @return true if the endpoint supports asynchronous IO and - * a read operation is being processed asynchronously + * + * @return true if the endpoint supports asynchronous IO and a read operation is being processed + * asynchronously */ public boolean isReadPending() { return false; @@ -1196,30 +1151,30 @@ /** * Allows checking if an asynchronous write operation is currently pending. - * @return true if the endpoint supports asynchronous IO and - * a write operation is being processed asynchronously + * + * @return true if the endpoint supports asynchronous IO and a write operation is being processed + * asynchronously */ public boolean isWritePending() { return false; } /** - * Scatter read. The completion handler will be called once some - * data has been read or an error occurred. The default NIO2 - * behavior is used: the completion handler will be called as soon - * as some data has been read, even if the read has completed inline. - * - * @param timeout timeout duration for the read - * @param unit units for the timeout duration - * @param attachment an object to attach to the I/O operation that will be - * used when calling the completion handler - * @param handler to call when the IO is complete - * @param dsts buffers - * @param The attachment type + * Scatter read. The completion handler will be called once some data has been read or an error occurred. The + * default NIO2 behavior is used: the completion handler will be called as soon as some data has been read, even if + * the read has completed inline. + * + * @param timeout timeout duration for the read + * @param unit units for the timeout duration + * @param attachment an object to attach to the I/O operation that will be used when calling the completion handler + * @param handler to call when the IO is complete + * @param dsts buffers + * @param The attachment type + * * @return the completion state (done, done inline, or still pending) */ public final CompletionState read(long timeout, TimeUnit unit, A attachment, - CompletionHandler handler, ByteBuffer... dsts) { + CompletionHandler handler, ByteBuffer... dsts) { if (dsts == null) { throw new IllegalArgumentException(); } @@ -1227,28 +1182,24 @@ } /** - * Scatter read. The completion handler will be called once some - * data has been read or an error occurred. If a CompletionCheck - * object has been provided, the completion handler will only be - * called if the callHandler method returned true. If no - * CompletionCheck object has been provided, the default NIO2 - * behavior is used: the completion handler will be called as soon - * as some data has been read, even if the read has completed inline. - * - * @param block is the blocking mode that will be used for this operation - * @param timeout timeout duration for the read - * @param unit units for the timeout duration - * @param attachment an object to attach to the I/O operation that will be - * used when calling the completion handler - * @param check for the IO operation completion - * @param handler to call when the IO is complete - * @param dsts buffers - * @param The attachment type + * Scatter read. The completion handler will be called once some data has been read or an error occurred. If a + * CompletionCheck object has been provided, the completion handler will only be called if the callHandler method + * returned true. If no CompletionCheck object has been provided, the default NIO2 behavior is used: the completion + * handler will be called as soon as some data has been read, even if the read has completed inline. + * + * @param block is the blocking mode that will be used for this operation + * @param timeout timeout duration for the read + * @param unit units for the timeout duration + * @param attachment an object to attach to the I/O operation that will be used when calling the completion handler + * @param check for the IO operation completion + * @param handler to call when the IO is complete + * @param dsts buffers + * @param The attachment type + * * @return the completion state (done, done inline, or still pending) */ - public final CompletionState read(BlockingMode block, long timeout, - TimeUnit unit, A attachment, CompletionCheck check, - CompletionHandler handler, ByteBuffer... dsts) { + public final CompletionState read(BlockingMode block, long timeout, TimeUnit unit, A attachment, + CompletionCheck check, CompletionHandler handler, ByteBuffer... dsts) { if (dsts == null) { throw new IllegalArgumentException(); } @@ -1256,51 +1207,45 @@ } /** - * Scatter read. The completion handler will be called once some - * data has been read or an error occurred. If a CompletionCheck - * object has been provided, the completion handler will only be - * called if the callHandler method returned true. If no - * CompletionCheck object has been provided, the default NIO2 - * behavior is used: the completion handler will be called as soon - * as some data has been read, even if the read has completed inline. - * - * @param dsts buffers - * @param offset in the buffer array - * @param length in the buffer array - * @param block is the blocking mode that will be used for this operation - * @param timeout timeout duration for the read - * @param unit units for the timeout duration - * @param attachment an object to attach to the I/O operation that will be - * used when calling the completion handler - * @param check for the IO operation completion - * @param handler to call when the IO is complete - * @param The attachment type + * Scatter read. The completion handler will be called once some data has been read or an error occurred. If a + * CompletionCheck object has been provided, the completion handler will only be called if the callHandler method + * returned true. If no CompletionCheck object has been provided, the default NIO2 behavior is used: the completion + * handler will be called as soon as some data has been read, even if the read has completed inline. + * + * @param dsts buffers + * @param offset in the buffer array + * @param length in the buffer array + * @param block is the blocking mode that will be used for this operation + * @param timeout timeout duration for the read + * @param unit units for the timeout duration + * @param attachment an object to attach to the I/O operation that will be used when calling the completion handler + * @param check for the IO operation completion + * @param handler to call when the IO is complete + * @param The attachment type + * * @return the completion state (done, done inline, or still pending) */ - public final CompletionState read(ByteBuffer[] dsts, int offset, int length, - BlockingMode block, long timeout, TimeUnit unit, A attachment, - CompletionCheck check, CompletionHandler handler) { + public final CompletionState read(ByteBuffer[] dsts, int offset, int length, BlockingMode block, long timeout, + TimeUnit unit, A attachment, CompletionCheck check, CompletionHandler handler) { return vectoredOperation(true, dsts, offset, length, block, timeout, unit, attachment, check, handler); } /** - * Gather write. The completion handler will be called once some - * data has been written or an error occurred. The default NIO2 - * behavior is used: the completion handler will be called, even - * if the write is incomplete and data remains in the buffers, or - * if the write completed inline. - * - * @param timeout timeout duration for the write - * @param unit units for the timeout duration - * @param attachment an object to attach to the I/O operation that will be - * used when calling the completion handler - * @param handler to call when the IO is complete - * @param srcs buffers - * @param The attachment type + * Gather write. The completion handler will be called once some data has been written or an error occurred. The + * default NIO2 behavior is used: the completion handler will be called, even if the write is incomplete and data + * remains in the buffers, or if the write completed inline. + * + * @param timeout timeout duration for the write + * @param unit units for the timeout duration + * @param attachment an object to attach to the I/O operation that will be used when calling the completion handler + * @param handler to call when the IO is complete + * @param srcs buffers + * @param The attachment type + * * @return the completion state (done, done inline, or still pending) */ public final CompletionState write(long timeout, TimeUnit unit, A attachment, - CompletionHandler handler, ByteBuffer... srcs) { + CompletionHandler handler, ByteBuffer... srcs) { if (srcs == null) { throw new IllegalArgumentException(); } @@ -1308,29 +1253,25 @@ } /** - * Gather write. The completion handler will be called once some - * data has been written or an error occurred. If a CompletionCheck - * object has been provided, the completion handler will only be - * called if the callHandler method returned true. If no - * CompletionCheck object has been provided, the default NIO2 - * behavior is used: the completion handler will be called, even - * if the write is incomplete and data remains in the buffers, or - * if the write completed inline. - * - * @param block is the blocking mode that will be used for this operation - * @param timeout timeout duration for the write - * @param unit units for the timeout duration - * @param attachment an object to attach to the I/O operation that will be - * used when calling the completion handler - * @param check for the IO operation completion - * @param handler to call when the IO is complete - * @param srcs buffers - * @param The attachment type + * Gather write. The completion handler will be called once some data has been written or an error occurred. If a + * CompletionCheck object has been provided, the completion handler will only be called if the callHandler method + * returned true. If no CompletionCheck object has been provided, the default NIO2 behavior is used: the completion + * handler will be called, even if the write is incomplete and data remains in the buffers, or if the write + * completed inline. + * + * @param block is the blocking mode that will be used for this operation + * @param timeout timeout duration for the write + * @param unit units for the timeout duration + * @param attachment an object to attach to the I/O operation that will be used when calling the completion handler + * @param check for the IO operation completion + * @param handler to call when the IO is complete + * @param srcs buffers + * @param The attachment type + * * @return the completion state (done, done inline, or still pending) */ - public final CompletionState write(BlockingMode block, long timeout, - TimeUnit unit, A attachment, CompletionCheck check, - CompletionHandler handler, ByteBuffer... srcs) { + public final CompletionState write(BlockingMode block, long timeout, TimeUnit unit, A attachment, + CompletionCheck check, CompletionHandler handler, ByteBuffer... srcs) { if (srcs == null) { throw new IllegalArgumentException(); } @@ -1338,62 +1279,54 @@ } /** - * Gather write. The completion handler will be called once some - * data has been written or an error occurred. If a CompletionCheck - * object has been provided, the completion handler will only be - * called if the callHandler method returned true. If no - * CompletionCheck object has been provided, the default NIO2 - * behavior is used: the completion handler will be called, even - * if the write is incomplete and data remains in the buffers, or - * if the write completed inline. - * - * @param srcs buffers - * @param offset in the buffer array - * @param length in the buffer array - * @param block is the blocking mode that will be used for this operation - * @param timeout timeout duration for the write - * @param unit units for the timeout duration - * @param attachment an object to attach to the I/O operation that will be - * used when calling the completion handler - * @param check for the IO operation completion - * @param handler to call when the IO is complete - * @param The attachment type + * Gather write. The completion handler will be called once some data has been written or an error occurred. If a + * CompletionCheck object has been provided, the completion handler will only be called if the callHandler method + * returned true. If no CompletionCheck object has been provided, the default NIO2 behavior is used: the completion + * handler will be called, even if the write is incomplete and data remains in the buffers, or if the write + * completed inline. + * + * @param srcs buffers + * @param offset in the buffer array + * @param length in the buffer array + * @param block is the blocking mode that will be used for this operation + * @param timeout timeout duration for the write + * @param unit units for the timeout duration + * @param attachment an object to attach to the I/O operation that will be used when calling the completion handler + * @param check for the IO operation completion + * @param handler to call when the IO is complete + * @param The attachment type + * * @return the completion state (done, done inline, or still pending) */ - public final CompletionState write(ByteBuffer[] srcs, int offset, int length, - BlockingMode block, long timeout, TimeUnit unit, A attachment, - CompletionCheck check, CompletionHandler handler) { + public final CompletionState write(ByteBuffer[] srcs, int offset, int length, BlockingMode block, long timeout, + TimeUnit unit, A attachment, CompletionCheck check, CompletionHandler handler) { return vectoredOperation(false, srcs, offset, length, block, timeout, unit, attachment, check, handler); } /** - * Vectored operation. The completion handler will be called once - * the operation is complete or an error occurred. If a CompletionCheck - * object has been provided, the completion handler will only be - * called if the callHandler method returned true. If no - * CompletionCheck object has been provided, the default NIO2 - * behavior is used: the completion handler will be called, even - * if the operation is incomplete, or if the operation completed inline. - * - * @param read true if the operation is a read, false if it is a write - * @param buffers buffers - * @param offset in the buffer array - * @param length in the buffer array - * @param block is the blocking mode that will be used for this operation - * @param timeout timeout duration for the write - * @param unit units for the timeout duration - * @param attachment an object to attach to the I/O operation that will be - * used when calling the completion handler - * @param check for the IO operation completion - * @param handler to call when the IO is complete - * @param The attachment type + * Vectored operation. The completion handler will be called once the operation is complete or an error occurred. If + * a CompletionCheck object has been provided, the completion handler will only be called if the callHandler method + * returned true. If no CompletionCheck object has been provided, the default NIO2 behavior is used: the completion + * handler will be called, even if the operation is incomplete, or if the operation completed inline. + * + * @param read true if the operation is a read, false if it is a write + * @param buffers buffers + * @param offset in the buffer array + * @param length in the buffer array + * @param block is the blocking mode that will be used for this operation + * @param timeout timeout duration for the write + * @param unit units for the timeout duration + * @param attachment an object to attach to the I/O operation that will be used when calling the completion handler + * @param check for the IO operation completion + * @param handler to call when the IO is complete + * @param The attachment type + * * @return the completion state (done, done inline, or still pending) */ - protected final CompletionState vectoredOperation(boolean read, - ByteBuffer[] buffers, int offset, int length, - BlockingMode block, long timeout, TimeUnit unit, A attachment, - CompletionCheck check, CompletionHandler handler) { + protected final CompletionState vectoredOperation(boolean read, ByteBuffer[] buffers, int offset, int length, + BlockingMode block, long timeout, TimeUnit unit, A attachment, CompletionCheck check, + CompletionHandler handler) { IOException ioe = getError(); if (ioe != null) { handler.failed(ioe, attachment); @@ -1402,7 +1335,8 @@ if (timeout == -1) { timeout = AbstractEndpoint.toTimeout(read ? getReadTimeout() : getWriteTimeout()); unit = TimeUnit.MILLISECONDS; - } else if (!hasPerOperationTimeout() && (unit.toMillis(timeout) != (read ? getReadTimeout() : getWriteTimeout()))) { + } else if (!hasPerOperationTimeout() && + (unit.toMillis(timeout) != (read ? getReadTimeout() : getWriteTimeout()))) { if (read) { setReadTimeout(unit.toMillis(timeout)); } else { @@ -1430,8 +1364,8 @@ } } VectoredIOCompletionHandler completion = new VectoredIOCompletionHandler<>(); - OperationState state = newOperationState(read, buffers, offset, length, block, timeout, unit, - attachment, check, handler, read ? readPending : writePending, completion); + OperationState state = newOperationState(read, buffers, offset, length, block, timeout, unit, attachment, + check, handler, read ? readPending : writePending, completion); if (read) { readOperation = state; } else { @@ -1442,7 +1376,13 @@ synchronized (state) { if (state.state == CompletionState.PENDING) { try { - state.wait(unit.toMillis(timeout)); + long timeoutExpiry = System.nanoTime() + unit.toNanos(timeout); + long timeoutMillis = unit.toMillis(timeout); + // Spurious wake-ups are possible. Keep waiting until state changes or timeout expires. + while (state.state == CompletionState.PENDING && timeoutMillis > 0) { + state.wait(unit.toMillis(timeout)); + timeoutMillis = (timeoutExpiry - System.nanoTime()) / 1_000_000; + } if (state.state == CompletionState.PENDING) { if (handler != null && state.callHandler.compareAndSet(true, false)) { handler.failed(new SocketTimeoutException(getTimeoutMsg(read)), attachment); @@ -1471,11 +1411,9 @@ } - protected abstract OperationState newOperationState(boolean read, - ByteBuffer[] buffers, int offset, int length, - BlockingMode block, long timeout, TimeUnit unit, A attachment, - CompletionCheck check, CompletionHandler handler, - Semaphore semaphore, VectoredIOCompletionHandler completion); + protected abstract OperationState newOperationState(boolean read, ByteBuffer[] buffers, int offset, + int length, BlockingMode block, long timeout, TimeUnit unit, A attachment, CompletionCheck check, + CompletionHandler handler, Semaphore semaphore, VectoredIOCompletionHandler completion); // --------------------------------------------------------- Utility methods @@ -1513,8 +1451,8 @@ public ServletConnection getServletConnection(String protocol, String protocolConnectionId) { if (servletConnection == null) { - servletConnection = new ServletConnectionImpl( - connectionId, protocol, protocolConnectionId, endpoint.isSSLEnabled()); + servletConnection = + new ServletConnectionImpl(connectionId, protocol, protocolConnectionId, endpoint.isSSLEnabled()); } return servletConnection; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/TLSClientHelloExtractor.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,11 +29,12 @@ import org.apache.tomcat.util.buf.HexUtils; import org.apache.tomcat.util.http.parser.HttpParser; import org.apache.tomcat.util.net.openssl.ciphers.Cipher; +import org.apache.tomcat.util.net.openssl.ciphers.Group; +import org.apache.tomcat.util.net.openssl.ciphers.SignatureScheme; import org.apache.tomcat.util.res.StringManager; /** - * This class extracts the SNI host name and ALPN protocols from a TLS - * client-hello message. + * This class extracts the SNI host name and ALPN protocols from a TLS client-hello message. */ public class TLSClientHelloExtractor { @@ -46,28 +47,33 @@ private final String sniValue; private final List clientRequestedApplicationProtocols; private final List clientRequestedProtocols; + private final List clientSupportedGroups; + private final List clientSignatureSchemes; private static final int TLS_RECORD_HEADER_LEN = 5; private static final int TLS_EXTENSION_SERVER_NAME = 0; + private static final int TLS_EXTENSION_SUPPORTED_GROUPS = 10; + // Note: Signature algorithms is the name of the extension + // Starting with TLS 1.3, this contains signature schemes + // For TLS before 1.3, this contains signature algorithms + private static final int TLS_EXTENSION_SIGNATURE_ALGORITHMS = 13; private static final int TLS_EXTENSION_ALPN = 16; private static final int TLS_EXTENSION_SUPPORTED_VERSION = 43; - public static byte[] USE_TLS_RESPONSE = ("HTTP/1.1 400 \r\n" + - "Content-Type: text/plain;charset=UTF-8\r\n" + - "Connection: close\r\n" + - "\r\n" + - "Bad Request\r\n" + - "This combination of host and port requires TLS.\r\n").getBytes(StandardCharsets.UTF_8); + public static byte[] USE_TLS_RESPONSE = + ("HTTP/1.1 400 \r\n" + "Content-Type: text/plain;charset=UTF-8\r\n" + "Connection: close\r\n" + "\r\n" + + "Bad Request\r\n" + "This combination of host and port requires TLS.\r\n") + .getBytes(StandardCharsets.UTF_8); /** - * Creates the instance of the parser and processes the provided buffer. The - * buffer position and limit will be modified during the execution of this - * method but they will be returned to the original values before the method + * Creates the instance of the parser and processes the provided buffer. The buffer position and limit will be + * modified during the execution of this method, but they will be returned to the original values before the method * exits. * * @param netInBuffer The buffer containing the TLS data to process + * * @throws IOException If the client hello message is malformed */ public TLSClientHelloExtractor(ByteBuffer netInBuffer) throws IOException { @@ -80,6 +86,8 @@ List clientRequestedCipherNames = new ArrayList<>(); List clientRequestedApplicationProtocols = new ArrayList<>(); List clientRequestedProtocols = new ArrayList<>(); + List clientSupportedGroups = new ArrayList<>(); + List clientSignatureSchemes = new ArrayList<>(); String sniValue = null; try { // Switch to read mode. @@ -150,26 +158,32 @@ skipBytes(netInBuffer, 2); // Read the extensions until we run out of data or find the data // we need - while (netInBuffer.hasRemaining() && (sniValue == null || - clientRequestedApplicationProtocols.isEmpty() || clientRequestedProtocols.isEmpty())) { + while (netInBuffer.hasRemaining() && (sniValue == null || clientRequestedApplicationProtocols.isEmpty() || + clientRequestedProtocols.isEmpty())) { // Extension type is two byte char extensionType = netInBuffer.getChar(); // Extension size is another two bytes char extensionDataSize = netInBuffer.getChar(); switch (extensionType) { - case TLS_EXTENSION_SERVER_NAME: { - sniValue = readSniExtension(netInBuffer); - break; - } - case TLS_EXTENSION_ALPN: - readAlpnExtension(netInBuffer, clientRequestedApplicationProtocols); - break; - case TLS_EXTENSION_SUPPORTED_VERSION: - readSupportedVersions(netInBuffer, clientRequestedProtocols); - break; - default: { - skipBytes(netInBuffer, extensionDataSize); - } + case TLS_EXTENSION_SERVER_NAME: { + sniValue = readSniExtension(netInBuffer); + break; + } + case TLS_EXTENSION_SUPPORTED_GROUPS: + readSupportedGroups(netInBuffer, clientSupportedGroups); + break; + case TLS_EXTENSION_SIGNATURE_ALGORITHMS: + readSignatureSchemes(netInBuffer, clientSignatureSchemes); + break; + case TLS_EXTENSION_ALPN: + readAlpnExtension(netInBuffer, clientRequestedApplicationProtocols); + break; + case TLS_EXTENSION_SUPPORTED_VERSION: + readSupportedVersions(netInBuffer, clientRequestedProtocols); + break; + default: { + skipBytes(netInBuffer, extensionDataSize); + } } } if (clientRequestedProtocols.isEmpty()) { @@ -185,6 +199,14 @@ this.clientRequestedApplicationProtocols = clientRequestedApplicationProtocols; this.sniValue = sniValue; this.clientRequestedProtocols = clientRequestedProtocols; + this.clientSupportedGroups = clientSupportedGroups; + this.clientSignatureSchemes = clientSignatureSchemes; + if (log.isTraceEnabled()) { + log.trace("TLS Client Hello: " + clientRequestedCiphers + " Names " + clientRequestedCipherNames + + " Protocols " + clientRequestedApplicationProtocols + " sniValue " + sniValue + + " clientRequestedProtocols " + clientRequestedProtocols + " clientSupportedGroups " + clientSupportedGroups + + " clientSignatureSchemes " + clientSignatureSchemes); + } // Whatever happens, return the buffer to its original state netInBuffer.limit(limit); netInBuffer.position(pos); @@ -198,8 +220,7 @@ /** - * @return The SNI value provided by the client converted to lower case if - * not already lower case. + * @return The SNI value provided by the client converted to lower case if not already lower case. */ public String getSNIValue() { if (result == ExtractorResult.COMPLETE) { @@ -246,6 +267,24 @@ } + public List getClientSupportedGroups() { + if (result == ExtractorResult.COMPLETE || result == ExtractorResult.NOT_PRESENT) { + return clientSupportedGroups; + } else { + throw new IllegalStateException(sm.getString("sniExtractor.tooEarly")); + } + } + + + public List getClientSignatureSchemes() { + if (result == ExtractorResult.COMPLETE || result == ExtractorResult.NOT_PRESENT) { + return clientSignatureSchemes; + } else { + throw new IllegalStateException(sm.getString("sniExtractor.tooEarly")); + } + } + + private static ExtractorResult handleIncompleteRead(ByteBuffer bb) { if (bb.limit() == bb.capacity()) { // Buffer not big enough @@ -274,19 +313,16 @@ // Next two bytes are major/minor version. We need at least 3.1. byte b2 = bb.get(); byte b3 = bb.get(); - if (b2 < 3 || b2 == 3 && b3 == 0) { - return false; - } - return true; + return b2 >= 3 && (b2 != 3 || b3 != 0); } private static boolean isHttp(ByteBuffer bb) { // Based on code in Http11InputBuffer // Note: The actual request is not important. This code only checks that - // the buffer contains a correctly formatted HTTP request line. - // The method, target and protocol are not validated. - byte chr = 0; + // the buffer contains a correctly formatted HTTP request line. + // The method, target and protocol are not validated. + byte chr; bb.position(0); // Skip blank lines @@ -352,10 +388,7 @@ private static boolean isClientHello(ByteBuffer bb) { // Client hello is handshake type 1 - if (bb.get() == 1) { - return true; - } - return false; + return bb.get() == 1; } @@ -419,7 +452,7 @@ bb.get(inputBuffer, 0, len); protocolNames.add(new String(inputBuffer, 0, len, StandardCharsets.UTF_8)); toRead--; - toRead -= len; + toRead -= (char) len; } } @@ -433,6 +466,34 @@ } } + + private static void readSupportedGroups(ByteBuffer bb, List groups) { + // First 2 bytes are size of the group list + int toRead = bb.getChar() / 2; + // Then the list of groups + for (int i = 0; i < toRead; i++) { + char id = bb.getChar(); + Group group = Group.valueOf(id); + if (group != null) { + groups.add(group); + } + } + } + + + private static void readSignatureSchemes(ByteBuffer bb, List signatureSchemes) { + // First 2 bytes are size of the signature algorithm list + int toRead = bb.getChar() / 2; + // Then the list of schemes + for (int i = 0; i < toRead; i++) { + char id = bb.getChar(); + SignatureScheme signatureScheme = SignatureScheme.valueOf(id); + if (signatureScheme != null) { + signatureSchemes.add(signatureScheme); + } + } + } + public enum ExtractorResult { COMPLETE, diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/WriteBuffer.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/WriteBuffer.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/WriteBuffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/WriteBuffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,12 +26,10 @@ import org.apache.tomcat.util.buf.ByteBufferHolder; /** - * Provides an expandable set of buffers for writes. Non-blocking writes can be - * of any size and may not be able to be written immediately or wholly contained - * in the buffer used to perform the writes to the next layer. This class - * provides a buffering capability to allow such writes to return immediately - * and also allows for the user provided buffers to be re-used / recycled as - * required. + * Provides an expandable set of buffers for writes. Non-blocking writes can be of any size and may not be able to be + * written immediately or wholly contained in the buffer used to perform the writes to the next layer. This class + * provides a buffering capability to allow such writes to return immediately and also allows for the user provided + * buffers to be re-used / recycled as required. */ public class WriteBuffer { @@ -76,14 +74,11 @@ /** - * Create an array of ByteBuffers from the current WriteBuffer, prefixing - * that array with the provided ByteBuffers. + * Create an array of ByteBuffers from the current WriteBuffer, prefixing that array with the provided ByteBuffers. * - * @param prefixes The additional ByteBuffers to add to the start of the - * array + * @param prefixes The additional ByteBuffers to add to the start of the array * - * @return an array of ByteBuffers from the current WriteBuffer prefixed by - * the provided ByteBuffers + * @return an array of ByteBuffers from the current WriteBuffer prefixed by the provided ByteBuffers */ ByteBuffer[] toArray(ByteBuffer... prefixes) { List result = new ArrayList<>(); @@ -138,8 +133,7 @@ /** - * Interface implemented by clients of the WriteBuffer to enable data to be - * written back out from the buffer. + * Interface implemented by clients of the WriteBuffer to enable data to be written back out from the buffer. */ public interface Sink { boolean writeFromBuffer(ByteBuffer buffer, boolean block) throws IOException; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSEImplementation.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,13 +26,6 @@ import org.apache.tomcat.util.net.SSLSupport; import org.apache.tomcat.util.net.SSLUtil; -/* JSSEImplementation: - - Concrete implementation class for JSSE - - @author EKR - */ - public class JSSEImplementation extends SSLImplementation { public JSSEImplementation() { @@ -43,7 +36,7 @@ } @Override - public SSLSupport getSSLSupport(SSLSession session, Map> additionalAttributes) { + public SSLSupport getSSLSupport(SSLSession session, Map> additionalAttributes) { return new JSSESupport(session, additionalAttributes); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSEKeyManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,24 +26,20 @@ import javax.net.ssl.X509KeyManager; /** - * X509KeyManager which allows selection of a specific key pair and certificate - * chain (identified by their keystore alias name) to be used by the server to - * authenticate itself to SSL clients. - * - * @author Jan Luehe + * X509KeyManager which allows selection of a specific key pair and certificate chain (identified by their keystore + * alias name) to be used by the server to authenticate itself to SSL clients. */ public final class JSSEKeyManager extends X509ExtendedKeyManager { - private X509KeyManager delegate; - private String serverKeyAlias; + private final X509KeyManager delegate; + private final String serverKeyAlias; /** * Constructor. * - * @param mgr The X509KeyManager used as a delegate - * @param serverKeyAlias The alias name of the server's key pair and - * supporting certificate chain + * @param mgr The X509KeyManager used as a delegate + * @param serverKeyAlias The alias name of the server's key pair and supporting certificate chain */ public JSSEKeyManager(X509KeyManager mgr, String serverKeyAlias) { super(); @@ -53,9 +49,8 @@ /** - * Returns the server key alias that was provided in the constructor or the - * result from {@link X509KeyManager#chooseServerAlias(String, Principal[], - * Socket)} for the delegate if no alias is specified. + * Returns the server key alias that was provided in the constructor or the result from + * {@link X509KeyManager#chooseServerAlias(String, Principal[], Socket)} for the delegate if no alias is specified. */ @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { @@ -68,14 +63,13 @@ /** - * Returns the server key alias that was provided in the constructor or the - * result from {@link X509ExtendedKeyManager#chooseEngineServerAlias(String, - * Principal[], SSLEngine)} for the delegate if no alias is specified. + * Returns the server key alias that was provided in the constructor or the result from + * {@link X509ExtendedKeyManager#chooseEngineServerAlias(String, Principal[], SSLEngine)} for the delegate if no + * alias is specified. */ @Override - public String chooseEngineServerAlias(String keyType, Principal[] issuers, - SSLEngine engine) { - if (serverKeyAlias!=null) { + public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { + if (serverKeyAlias != null) { return serverKeyAlias; } @@ -84,8 +78,7 @@ @Override - public String chooseClientAlias(String[] keyType, Principal[] issuers, - Socket socket) { + public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { return delegate.chooseClientAlias(keyType, issuers, socket); } @@ -115,8 +108,7 @@ @Override - public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, - SSLEngine engine) { + public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) { return delegate.chooseClientAlias(keyType, issuers, null); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSESSLContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,7 +37,7 @@ class JSSESSLContext implements SSLContext { - private javax.net.ssl.SSLContext context; + private final javax.net.ssl.SSLContext context; private KeyManager[] kms; private TrustManager[] tms; @@ -46,8 +46,7 @@ } @Override - public void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) - throws KeyManagementException { + public void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) throws KeyManagementException { this.kms = kms; this.tms = tms; context.init(kms, tms, sr); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSESupport.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSESupport.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSESupport.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSESupport.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,14 +36,7 @@ import org.apache.tomcat.util.res.StringManager; /** - * JSSESupport. - * - * Concrete implementation class for JSSE Support classes. - * - * @author EKR - * @author Craig R. McClanahan - * Parts cribbed from JSSECertCompat - * Parts cribbed from CertificatesValve + * JSSESupport. Concrete implementation class for JSSE Support classes. */ public class JSSESupport implements SSLSupport, SSLSessionManager { @@ -62,16 +55,15 @@ } /* - * NO-OP method provided to make it easy for other classes in this package - * to trigger the loading of this class and the population of the - * keySizeCache. + * NO-OP method provided to make it easy for other classes in this package to trigger the loading of this class and + * the population of the keySizeCache. */ static void init() { // NO-OP } private SSLSession session; - private Map> additionalAttributes; + private final Map> additionalAttributes; public JSSESupport(SSLSession session, Map> additionalAttributes) { this.session = session; @@ -102,11 +94,13 @@ return null; } - Certificate [] certs=null; + Certificate[] certs; try { certs = session.getPeerCertificates(); - } catch( Throwable t ) { - log.debug(sm.getString("jsseSupport.clientCertError"), t); + } catch (Throwable t) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("jsseSupport.clientCertError"), t); + } return null; } @@ -115,31 +109,31 @@ private static X509Certificate[] convertCertificates(Certificate[] certs) { - if( certs==null ) { + if (certs == null) { return null; } - X509Certificate [] x509Certs = new X509Certificate[certs.length]; - for(int i=0; i < certs.length; i++) { - if (certs[i] instanceof X509Certificate ) { + X509Certificate[] x509Certs = new X509Certificate[certs.length]; + for (int i = 0; i < certs.length; i++) { + if (certs[i] instanceof X509Certificate) { // always currently true with the JSSE 1.1.x x509Certs[i] = (X509Certificate) certs[i]; } else { try { - byte [] buffer = certs[i].getEncoded(); + byte[] buffer = certs[i].getEncoded(); CertificateFactory cf = CertificateFactory.getInstance("X.509"); ByteArrayInputStream stream = new ByteArrayInputStream(buffer); x509Certs[i] = (X509Certificate) cf.generateCertificate(stream); - } catch(Exception ex) { - log.info(sm.getString("jsseSupport.certTranslationError", certs[i]), ex); + } catch (Exception e) { + log.info(sm.getString("jsseSupport.certTranslationError", certs[i]), e); return null; } } - if(log.isTraceEnabled()) { + if (log.isTraceEnabled()) { log.trace("Cert #" + i + " = " + x509Certs[i]); } } - if(x509Certs.length < 1) { + if (x509Certs.length < 1) { return null; } return x509Certs; @@ -162,18 +156,17 @@ } @Override - public String getSessionId() - throws IOException { + public String getSessionId() throws IOException { // Look up the current SSLSession if (session == null) { return null; } // Expose ssl_session (getId) - byte [] ssl_session = session.getId(); + byte[] ssl_session = session.getId(); if (ssl_session == null || ssl_session.length == 0) { return null; } - StringBuilder buf=new StringBuilder(); + StringBuilder buf = new StringBuilder(); for (byte b : ssl_session) { String digit = Integer.toHexString(b); if (digit.length() < 2) { @@ -204,9 +197,9 @@ @Override public String getProtocol() throws IOException { if (session == null) { - return null; + return null; } - return session.getProtocol(); + return session.getProtocol(); } @Override diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/JSSEUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,12 +34,6 @@ /** * SSLUtil implementation for JSSE. - * - * @author Harish Prabandham - * @author Costin Manolache - * @author Stefan Freyr Stefansson - * @author EKR - * @author Jan Luehe */ public class JSSEUtil extends SSLUtilBase { @@ -52,12 +46,12 @@ private volatile Set implementedCiphers; - public JSSEUtil (SSLHostConfigCertificate certificate) { + public JSSEUtil(SSLHostConfigCertificate certificate) { this(certificate, true); } - public JSSEUtil (SSLHostConfigCertificate certificate, boolean warnOnSkip) { + public JSSEUtil(SSLHostConfigCertificate certificate, boolean warnOnSkip) { super(certificate, warnOnSkip); } @@ -90,8 +84,7 @@ @Override - public SSLContext createSSLContextInternal(List negotiableProtocols) - throws NoSuchAlgorithmException { + public SSLContext createSSLContextInternal(List negotiableProtocols) throws NoSuchAlgorithmException { return new JSSESSLContext(sslHostConfig.getSslProtocol()); } @@ -103,7 +96,7 @@ SSLContext context; try { context = new JSSESSLContext(sslHostConfig.getSslProtocol()); - context.init(null, null, null); + context.init(null, null, null); } catch (NoSuchAlgorithmException | KeyManagementException e) { // This is fatal for the connector so throw an exception to prevent // it from starting @@ -129,13 +122,13 @@ implementedProtocols.add(protocol); } - if (implementedProtocols.size() == 0) { + if (implementedProtocols.isEmpty()) { log.warn(sm.getString("jsseUtil.noDefaultProtocols")); } String[] implementedCipherSuiteArray = context.getSupportedSSLParameters().getCipherSuites(); // The IBM JRE will accept cipher suites names SSL_xxx or TLS_xxx but - // only returns the SSL_xxx form for supported cipher suites. Therefore + // only returns the SSL_xxx form for supported cipher suites. Therefore, // need to filter the requested cipher suites using both forms with an // IBM JRE. if (JreVendor.IS_IBM_JVM) { @@ -149,6 +142,11 @@ } else { implementedCiphers = new HashSet<>(Arrays.asList(implementedCipherSuiteArray)); } + + if (sslHostConfig.getOpenSslConf() != null) { + log.warn(sm.getString("jsseUtil.opensslconf.present")); + } + initialized = true; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -21,12 +21,14 @@ jsseUtil.excludeProtocol=The SSL protocol [{0}] which is supported in this JRE was excluded from the protocols available to Tomcat jsseUtil.noDefaultProtocols=Unable to determine a default for sslEnabledProtocols. Set an explicit value to ensure the connector can start. +jsseUtil.opensslconf.present=A connector is configured to use a JSSE TLS implementation with OpenSSL specific OpenSSLConf configuration elements. The OpenSSLConf configuration elements will be ignored. pemFile.noMultiPrimes=The PKCS#1 certificate is in multi-prime format and Java does not provide an API for constructing an RSA private key object from that format pemFile.noPassword=A password is required to decrypt the private key pemFile.notPbkdf2=The OID [{0}] is not the correct OID for PKBDF2 which is the only permitted KDF for PBES2 pemFile.notValidRFC5915=The provided key file does not conform to RFC 5915 pemFile.parseError=Unable to parse the key from [{0}] +pemFile.parseError.algorithm=Unable to parse the key using algorithm [{0}] pemFile.unknownEncryptedFormat=The format [{0}] is not a recognised encrypted PEM file format pemFile.unknownEncryptionAlgorithm=The encryption algorithm with DER encoded OID of [{0}] was not recognised pemFile.unknownPkcs8Algorithm=The PKCS#8 encryption algorithm with DER encoded OID of [{0}] was not recognised diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -27,6 +27,7 @@ pemFile.notPbkdf2=L''OID [{0}] n''est pas un OID correct pour PKBDF2 qui est le seul KDF autorisé pour PBES2 pemFile.notValidRFC5915=La fichier de clé fourni ne se conforme pas à la RFC 5915 pemFile.parseError=Impossible de parser la clé de [{0}] +pemFile.parseError.algorithm=Impossible de traiter la clé en utilisant l''algorithme [{0}] pemFile.unknownEncryptedFormat=Le format [{0}] n''est pas un format de cryptage reconnu pour un fichier PEM pemFile.unknownEncryptionAlgorithm=L''algorithme de cryptage avec un OID encodé en DER de [{0}] n''est pas reconnu pemFile.unknownPkcs8Algorithm=L''algorithme de cryptage PKCS#8 avec un OID encodé en DER de [{0}] n''est pas reconnu diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -27,6 +27,7 @@ pemFile.notPbkdf2=OID [{0}] は、PBES2 に唯一許可される KDF である PKBDF2 の正しい OID ではありません pemFile.notValidRFC5915=与えられたキーファイルは RFC 5915 に準拠していません pemFile.parseError=秘密鍵ファイル [{0}] を解析できません +pemFile.parseError.algorithm=アルゴリズム [{0}] を使用してキーを解析できません pemFile.unknownEncryptedFormat=フォーマット [{0}] は認識される暗号化 PEM ファイル形式ではありません pemFile.unknownEncryptionAlgorithm=[{0}] の DER エンコードされた OID を持つ暗号化アルゴリズムが認識されませんでした pemFile.unknownPkcs8Algorithm=[{0}] の DER エンコードされた OID を持つ PKCS#8 暗号化アルゴリズムが認識されませんでした diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/PEMFile.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/PEMFile.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/jsse/PEMFile.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/jsse/PEMFile.java 2026-01-23 19:33:36.000000000 +0000 @@ -107,15 +107,12 @@ } public static String toPEM(X509Certificate certificate) throws CertificateEncodingException { - StringBuilder result = new StringBuilder(); - result.append(Part.BEGIN_BOUNDARY + Part.CERTIFICATE + Part.FINISH_BOUNDARY); - result.append(System.lineSeparator()); - result.append(Base64.getMimeEncoder().encodeToString(certificate.getEncoded())); - result.append(Part.END_BOUNDARY + Part.CERTIFICATE + Part.FINISH_BOUNDARY); - return result.toString(); + return Part.BEGIN_BOUNDARY + Part.CERTIFICATE + Part.FINISH_BOUNDARY + System.lineSeparator() + + Base64.getMimeEncoder().encodeToString(certificate.getEncoded()) + Part.END_BOUNDARY + + Part.CERTIFICATE + Part.FINISH_BOUNDARY; } - private List certificates = new ArrayList<>(); + private final List certificates = new ArrayList<>(); private PrivateKey privateKey; public List getCertificates() { @@ -193,7 +190,7 @@ } } - String passwordToUse = null; + String passwordToUse; if (passwordFileStream != null) { try (BufferedReader reader = new BufferedReader(new InputStreamReader(passwordFileStream, StandardCharsets.UTF_8))) { @@ -408,13 +405,14 @@ // PBKDF2 PRF p.parseTagSequence(); p.parseLength(); - String prf = null; - // This tag is optional. If present the nested sequence level will be 6 else if will be 4. + String prf; + // This tag is optional. If present the nested sequence level will be 6 else it will be 4. if (p.getNestedSequenceLevel() == 6) { byte[] oidPRF = p.parseOIDAsBytes(); prf = OID_TO_PRF.get(HexUtils.toHexString(oidPRF)); if (prf == null) { - throw new NoSuchAlgorithmException(sm.getString("pemFile.unknownPrfAlgorithm", toDottedOidString(oidPRF))); + throw new NoSuchAlgorithmException( + sm.getString("pemFile.unknownPrfAlgorithm", toDottedOidString(oidPRF))); } p.parseNull(); @@ -484,11 +482,12 @@ InvalidKeyException exception = new InvalidKeyException(sm.getString("pemFile.parseError", filename)); if (keyAlgorithm == null) { - for (String algorithm : new String[] { "RSA", "DSA", "EC" }) { + for (String algorithm : new String[] { "RSA", "DSA", "EC", "ML-DSA" }) { try { return KeyFactory.getInstance(algorithm).generatePrivate(keySpec); } catch (InvalidKeySpecException e) { - exception.addSuppressed(e); + exception.addSuppressed(new InvalidKeySpecException( + sm.getString("pemFile.parseError.algorithm", algorithm), e)); } } } else { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/mbeans-descriptors.xml tomcat10-10.1.52/java/org/apache/tomcat/util/net/mbeans-descriptors.xml --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/mbeans-descriptors.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/mbeans-descriptors.xml 2026-01-23 19:33:36.000000000 +0000 @@ -151,6 +151,9 @@ + + negotiableProtocols) - throws SSLException { + public OpenSSLContext(SSLHostConfigCertificate certificate, List negotiableProtocols) throws SSLException { this.sslHostConfig = certificate.getSSLHostConfig(); this.certificate = certificate; long aprPool = Pool.create(0); @@ -102,17 +102,24 @@ boolean success = false; try { // Create OpenSSLConfCmd context if used - OpenSSLConf openSslConf = sslHostConfig.getOpenSslConf(); - if (openSslConf != null) { + if (sslHostConfig.getOpenSslConf() == null && sslHostConfig.getTrustManagerClassName() == null && + sslHostConfig.getTruststore() == null) { + /* + * If an instance of OpenSSLConf is required, it must be created here so the reference can be placed in + * the (immutable) OpenSSLState record. + * + * If OpenSSL managed trust is used, an instance of OpenSSLConf is required to pass OCSP configuration + * parameters to Tomcat Native. Create one if one hasn't already been created. + */ + sslHostConfig.setOpenSslConf(new OpenSSLConf()); + } + if (sslHostConfig.getOpenSslConf() != null) { try { if (log.isTraceEnabled()) { log.trace(sm.getString("openssl.makeConf")); } - cctx = SSLConf.make(aprPool, - SSL.SSL_CONF_FLAG_FILE | - SSL.SSL_CONF_FLAG_SERVER | - SSL.SSL_CONF_FLAG_CERTIFICATE | - SSL.SSL_CONF_FLAG_SHOW_ERRORS); + cctx = SSLConf.make(aprPool, SSL.SSL_CONF_FLAG_FILE | SSL.SSL_CONF_FLAG_SERVER | + SSL.SSL_CONF_FLAG_CERTIFICATE | SSL.SSL_CONF_FLAG_SHOW_ERRORS); } catch (Exception e) { throw new SSLException(sm.getString("openssl.errMakeConf"), e); } @@ -158,20 +165,17 @@ this.negotiableProtocols = negotiableProtocols; success = true; - } catch(Exception e) { + } catch (Exception e) { throw new SSLException(sm.getString("openssl.errorSSLCtxInit"), e); } finally { state = new OpenSSLState(aprPool, cctx, ctx); /* - * When an SSLHostConfig is replaced at runtime, it is not possible to - * call destroy() on the associated OpenSSLContext since it is likely - * that there will be in-progress connections using the OpenSSLContext. - * A reference chain has been deliberately established (see - * OpenSSLSessionContext) to ensure that the OpenSSLContext remains - * ineligible for GC while those connections are alive. Once those - * connections complete, the OpenSSLContext will become eligible for GC - * and this method will ensure that the associated native resources are - * cleaned up. + * When an SSLHostConfig is replaced at runtime, it is not possible to call destroy() on the associated + * OpenSSLContext since it is likely that there will be in-progress connections using the OpenSSLContext. A + * reference chain has been deliberately established (see OpenSSLSessionContext) to ensure that the + * OpenSSLContext remains ineligible for GC while those connections are alive. Once those connections + * complete, the OpenSSLContext will become eligible for GC and this method will ensure that the associated + * native resources are cleaned up. */ cleanable = cleaner.register(this, state); @@ -223,12 +227,10 @@ return false; } if (rc <= 0) { - log.error(sm.getString("opensslconf.failedCommand", name, value, - Integer.toString(rc))); + log.error(sm.getString("opensslconf.failedCommand", name, value, Integer.toString(rc))); result = false; } else if (log.isTraceEnabled()) { - log.trace(sm.getString("opensslconf.resultCommand", name, value, - Integer.toString(rc))); + log.trace(sm.getString("opensslconf.resultCommand", name, value, Integer.toString(rc))); } } if (!result) { @@ -263,12 +265,10 @@ return false; } if (rc <= 0) { - log.error(sm.getString("opensslconf.failedCommand", name, value, - Integer.toString(rc))); + log.error(sm.getString("opensslconf.failedCommand", name, value, Integer.toString(rc))); result = false; } else if (log.isTraceEnabled()) { - log.trace(sm.getString("opensslconf.resultCommand", name, value, - Integer.toString(rc))); + log.trace(sm.getString("opensslconf.resultCommand", name, value, Integer.toString(rc))); } } rc = SSLConf.finish(cctx); @@ -285,16 +285,14 @@ /** * Setup the SSL_CTX. * - * @param kms Must contain a KeyManager of the type - * {@code OpenSSLKeyManager} - * @param tms Must contain a TrustManager of the type - * {@code X509TrustManager} - * @param sr Is not used for this implementation. + * @param kms Must contain a KeyManager of the type {@code OpenSSLKeyManager} + * @param tms Must contain a TrustManager of the type {@code X509TrustManager} + * @param sr Is not used for this implementation. + * * @throws KeyManagementException if an error occurs */ @Override - public void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) - throws KeyManagementException { + public void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) throws KeyManagementException { if (initialized) { log.warn(sm.getString("openssl.doubleInit")); return; @@ -328,8 +326,9 @@ SSLContext.clearOptions(state.ctx, SSL.SSL_OP_NO_TICKET); } - // List the ciphers that the client is permitted to negotiate + // Configure the ciphers that the client is permitted to negotiate SSLContext.setCipherSuite(state.ctx, sslHostConfig.getCiphers()); + SSLContext.setCipherSuitesEx(state.ctx, sslHostConfig.getCipherSuites()); // If there is no certificate file must be using a KeyStore so a KeyManager is required. // If there is a certificate file a KeyManager is helpful but not strictly necessary. @@ -341,37 +340,25 @@ // Client certificate verification int value = 0; switch (sslHostConfig.getCertificateVerification()) { - case NONE: - value = SSL.SSL_CVERIFY_NONE; - break; - case OPTIONAL: - value = SSL.SSL_CVERIFY_OPTIONAL; - break; - case OPTIONAL_NO_CA: - value = SSL.SSL_CVERIFY_OPTIONAL_NO_CA; - break; - case REQUIRED: - value = SSL.SSL_CVERIFY_REQUIRE; - break; + case NONE: + value = SSL.SSL_CVERIFY_NONE; + break; + case OPTIONAL: + value = SSL.SSL_CVERIFY_OPTIONAL; + break; + case OPTIONAL_NO_CA: + value = SSL.SSL_CVERIFY_OPTIONAL_NO_CA; + break; + case REQUIRED: + value = SSL.SSL_CVERIFY_REQUIRE; + break; } SSLContext.setVerify(state.ctx, value, sslHostConfig.getCertificateVerificationDepth()); if (tms != null) { // Client certificate verification based on custom trust managers x509TrustManager = chooseTrustManager(tms); - SSLContext.setCertVerifyCallback(state.ctx, new CertificateVerifier() { - @Override - public boolean verify(long ssl, byte[][] chain, String auth) { - X509Certificate[] peerCerts = certificates(chain); - try { - x509TrustManager.checkClientTrusted(peerCerts, auth); - return true; - } catch (Exception e) { - log.debug(sm.getString("openssl.certificateVerificationFailed"), e); - } - return false; - } - }); + SSLContext.setCertVerifyCallback(state.ctx, new OpenSSLCertificateVerifier(x509TrustManager)); // Pass along the DER encoded certificates of the accepted client // certificate issuers, so that their subjects can be presented // by the server during the handshake to allow the client choosing @@ -387,9 +374,17 @@ SSLContext.setCACertificate(state.ctx, SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()), SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath())); + sslHostConfig.getOpenSslConf().addCmd(new OpenSSLConfCmd(OpenSSLConfCmd.NO_OCSP_CHECK, + Boolean.toString(!sslHostConfig.getOcspEnabled()))); + sslHostConfig.getOpenSslConf().addCmd(new OpenSSLConfCmd(OpenSSLConfCmd.OCSP_SOFT_FAIL, + Boolean.toString(sslHostConfig.getOcspSoftFail()))); + sslHostConfig.getOpenSslConf().addCmd(new OpenSSLConfCmd(OpenSSLConfCmd.OCSP_TIMEOUT, + Integer.toString(sslHostConfig.getOcspTimeout()))); + sslHostConfig.getOpenSslConf().addCmd(new OpenSSLConfCmd(OpenSSLConfCmd.OCSP_VERIFY_FLAGS, + Integer.toString(sslHostConfig.getOcspVerifyFlags()))); } - if (negotiableProtocols != null && negotiableProtocols.size() > 0) { + if (negotiableProtocols != null && !negotiableProtocols.isEmpty()) { List protocols = new ArrayList<>(negotiableProtocols); protocols.add("http/1.1"); String[] protocolsArray = protocols.toArray(new String[0]); @@ -443,8 +438,7 @@ if ((opts & SSL.SSL_OP_NO_SSLv3) == 0) { enabled.add(Constants.SSL_PROTO_SSLv3); } - sslHostConfig.setEnabledProtocols( - enabled.toArray(new String[0])); + sslHostConfig.setEnabledProtocols(enabled.toArray(new String[0])); // Reconfigure the enabled ciphers sslHostConfig.setEnabledCiphers(SSLContext.getCiphers(state.ctx)); } @@ -467,31 +461,27 @@ // Load Server key and certificate if (certificate.getCertificateFile() != null) { // Set certificate - String passwordToUse = null; + String passwordToUse; if (certificate.getCertificateKeyPasswordFile() != null) { - try (BufferedReader reader = - new BufferedReader(new InputStreamReader( - new FileInputStream( + try (BufferedReader reader = new BufferedReader(new InputStreamReader( + new FileInputStream( SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyPasswordFile())), - StandardCharsets.UTF_8))) { + StandardCharsets.UTF_8))) { passwordToUse = reader.readLine(); } } else { passwordToUse = certificate.getCertificateKeyPassword(); } - SSLContext.setCertificate(state.ctx, - SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), - SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()), - passwordToUse, getCertificateIndex(certificate)); + SSLContext.setCertificate(state.ctx, SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), + SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()), passwordToUse, + getCertificateIndex(certificate)); // Set certificate chain file SSLContext.setCertificateChainFile(state.ctx, SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), false); // Set revocation SSLContext.setCARevocation(state.ctx, - SSLHostConfig.adjustRelativePath( - sslHostConfig.getCertificateRevocationListFile()), - SSLHostConfig.adjustRelativePath( - sslHostConfig.getCertificateRevocationListPath())); + SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListFile()), + SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListPath())); } else { String alias = certificate.getCertificateKeyAlias(); X509KeyManager x509KeyManager = certificate.getCertificateKeyManager(); @@ -504,12 +494,10 @@ chain = x509KeyManager.getCertificateChain(alias); } PrivateKey key = x509KeyManager.getPrivateKey(alias); - StringBuilder sb = new StringBuilder(BEGIN_KEY); - sb.append(Base64.getMimeEncoder(64, new byte[] {'\n'}).encodeToString(key.getEncoded())); - sb.append(END_KEY); + String encodedKey = BEGIN_KEY + + Base64.getMimeEncoder(64, new byte[] { '\n' }).encodeToString(key.getEncoded()) + END_KEY; SSLContext.setCertificateRaw(state.ctx, chain[0].getEncoded(), - sb.toString().getBytes(StandardCharsets.US_ASCII), - getCertificateIndex(certificate)); + encodedKey.getBytes(StandardCharsets.US_ASCII), getCertificateIndex(certificate)); for (int i = 1; i < chain.length; i++) { SSLContext.addChainCertificateRaw(state.ctx, chain[i].getEncoded()); } @@ -525,7 +513,7 @@ result = SSL.SSL_AIDX_RSA; } else if (certificate.getType() == Type.EC) { result = SSL.SSL_AIDX_ECC; - } else if (certificate.getType() == Type.DSA) { + } else if (certificate.getType() == Type.DSA || certificate.getType() == Type.MLDSA) { result = SSL.SSL_AIDX_DSA; } else { result = SSL.SSL_AIDX_MAX; @@ -537,8 +525,7 @@ /* * Find a valid alias when none was specified in the config. */ - private static String findAlias(X509KeyManager keyManager, - SSLHostConfigCertificate certificate) { + private static String findAlias(X509KeyManager keyManager, SSLHostConfigCertificate certificate) { Type type = certificate.getType(); String result = null; @@ -555,7 +542,7 @@ Iterator iter = candidateTypes.iterator(); while (result == null && iter.hasNext()) { - result = keyManager.chooseServerAlias(iter.next().toString(), null, null); + result = keyManager.chooseServerAlias(iter.next().getKeyType(), null, null); } return result; @@ -570,14 +557,6 @@ throw new IllegalStateException(sm.getString("openssl.trustManagerMissing")); } - private static X509Certificate[] certificates(byte[][] chain) { - X509Certificate[] peerCerts = new X509Certificate[chain.length]; - for (int i = 0; i < peerCerts.length; i++) { - peerCerts[i] = new OpenSSLX509Certificate(chain[i]); - } - return peerCerts; - } - long getSSLContextID() { return state.ctx; @@ -592,7 +571,7 @@ @Override public SSLEngine createSSLEngine() { return new OpenSSLEngine(cleaner, state.ctx, defaultProtocol, false, sessionContext, - (negotiableProtocols != null && negotiableProtocols.size() > 0), initialized, + (negotiableProtocols != null && !negotiableProtocols.isEmpty()), initialized, sslHostConfig.getCertificateVerificationDepth(), sslHostConfig.getCertificateVerification() == CertificateVerification.OPTIONAL_NO_CA); } @@ -651,14 +630,33 @@ @Override public void run() { - if (ctx != 0) { - SSLContext.free(ctx); - } - if (cctx != 0) { - SSLConf.free(cctx); - } - if (aprPool != 0) { - Pool.destroy(aprPool); + /* + * During shutdown there is a possibility that both the cleaner and the APR library termination code try and + * free these resources. If both call free, there will be a JVM crash. + * + * If the cleaner frees the resources, the APR library termination won't try free them as well. + * + * If the APR library termination frees the resources, the cleaner MUST NOT attempt to do so. + * + * The locks and checks below ensure that a) the cleaner only runs if the APR library has not yet been + * terminated and that the APR library status will not change while the cleaner is running. + */ + Lock readLock = AprStatus.getStatusLock().readLock(); + readLock.lock(); + try { + if (AprStatus.isAprInitialized()) { + if (ctx != 0) { + SSLContext.free(ctx); + } + if (cctx != 0) { + SSLConf.free(cctx); + } + if (aprPool != 0) { + Pool.destroy(aprPool); + } + } + } finally { + readLock.unlock(); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,6 +30,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.Lock; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -42,6 +43,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.jni.AprStatus; import org.apache.tomcat.jni.Buffer; import org.apache.tomcat.jni.Pool; import org.apache.tomcat.jni.SSL; @@ -53,9 +55,8 @@ import org.apache.tomcat.util.res.StringManager; /** - * Implements a {@link SSLEngine} using - * OpenSSL - * BIO abstractions. + * Implements a {@link SSLEngine} using OpenSSL BIO + * abstractions. */ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolInfo { @@ -78,9 +79,9 @@ SSLContext.setCipherSuite(sslCtx, "ALL"); final long ssl = SSL.newSSL(sslCtx, true); try { - for (String c: SSL.getCiphers(ssl)) { + for (String c : SSL.getCiphers(ssl)) { // Filter out bad input. - if (c == null || c.length() == 0 || availableCipherSuites.contains(c)) { + if (c == null || c.isEmpty() || availableCipherSuites.contains(c)) { continue; } availableCipherSuites.add(OpenSSLCipherConfigurationParser.openSSLToJsse(c)); @@ -137,7 +138,12 @@ private final OpenSSLState state; private final Cleanable cleanable; - private enum Accepted { NOT, IMPLICIT, EXPLICIT } + private enum Accepted { + NOT, + IMPLICIT, + EXPLICIT + } + private Accepted accepted = Accepted.NOT; private boolean handshakeFinished; private int currentHandshake; @@ -145,7 +151,8 @@ private volatile boolean destroyed; // Use an invalid cipherSuite until the handshake is completed - // See https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/Socket.html#setPerformancePreferences(int,int,int) + // See + // https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/Socket.html#setPerformancePreferences(int,int,int) private volatile String version; private volatile String cipher; private volatile String applicationProtocol; @@ -176,25 +183,20 @@ /** * Creates a new instance * - * @param cleaner Used to clean up references to instances before they are - * garbage collected - * @param sslCtx an OpenSSL {@code SSL_CTX} object - * @param fallbackApplicationProtocol the fallback application protocol - * @param clientMode {@code true} if this is used for clients, {@code false} - * otherwise - * @param sessionContext the {@link OpenSSLSessionContext} this - * {@link SSLEngine} belongs to. - * @param alpn {@code true} if alpn should be used, {@code false} - * otherwise - * @param initialized {@code true} if this instance gets its protocol, - * cipher and client verification from the {@code SSL_CTX} {@code sslCtx} - * @param certificateVerificationDepth Certificate verification depth - * @param certificateVerificationOptionalNoCA Skip CA verification in - * optional mode + * @param cleaner Used to clean up references to instances before they are garbage + * collected + * @param sslCtx an OpenSSL {@code SSL_CTX} object + * @param fallbackApplicationProtocol the fallback application protocol + * @param clientMode {@code true} if this is used for clients, {@code false} otherwise + * @param sessionContext the {@link OpenSSLSessionContext} this {@link SSLEngine} belongs to. + * @param alpn {@code true} if alpn should be used, {@code false} otherwise + * @param initialized {@code true} if this instance gets its protocol, cipher and client + * verification from the {@code SSL_CTX} {@code sslCtx} + * @param certificateVerificationDepth Certificate verification depth + * @param certificateVerificationOptionalNoCA Skip CA verification in optional mode */ - OpenSSLEngine(Cleaner cleaner, long sslCtx, String fallbackApplicationProtocol, - boolean clientMode, OpenSSLSessionContext sessionContext, boolean alpn, - boolean initialized, int certificateVerificationDepth, + OpenSSLEngine(Cleaner cleaner, long sslCtx, String fallbackApplicationProtocol, boolean clientMode, + OpenSSLSessionContext sessionContext, boolean alpn, boolean initialized, int certificateVerificationDepth, boolean certificateVerificationOptionalNoCA) { if (sslCtx == 0) { throw new IllegalArgumentException(sm.getString("engine.noSSLContext")); @@ -224,16 +226,15 @@ public synchronized void shutdown() { if (!destroyed) { destroyed = true; - cleanable.clean(); // internal errors can cause shutdown without marking the engine closed isInboundDone = isOutboundDone = engineClosed = true; + cleanable.clean(); } } /** - * Write plain text data to the OpenSSL internal BIO + * Write plain text data to the OpenSSL internal BIO Calling this function with src.remaining == 0 is undefined. * - * Calling this function with src.remaining == 0 is undefined. * @throws SSLException if the OpenSSL error check fails */ private int writePlaintextData(final long ssl, final ByteBuffer src) throws SSLException { @@ -279,12 +280,12 @@ } } - throw new IllegalStateException( - sm.getString("engine.writeToSSLFailed", Integer.toString(sslWrote))); + throw new IllegalStateException(sm.getString("engine.writeToSSLFailed", Integer.toString(sslWrote))); } /** * Write encrypted data to the OpenSSL network BIO. + * * @throws SSLException if the OpenSSL error check fails */ private int writeEncryptedData(final long networkBIO, final ByteBuffer src) throws SSLException { @@ -329,6 +330,7 @@ /** * Read plain text data from the OpenSSL internal BIO + * * @throws SSLException if the OpenSSL error check fails */ private int readPlaintextData(final long ssl, final ByteBuffer dst) throws SSLException { @@ -373,6 +375,7 @@ /** * Read encrypted data from the OpenSSL network BIO + * * @throws SSLException if the OpenSSL error check fails */ private int readEncryptedData(final long networkBIO, final ByteBuffer dst, final int pending) throws SSLException { @@ -413,11 +416,13 @@ } @Override - public synchronized SSLEngineResult wrap(final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException { + public synchronized SSLEngineResult wrap(final ByteBuffer[] srcs, final int offset, final int length, + final ByteBuffer dst) throws SSLException { // Check to make sure the engine has not been closed if (destroyed) { - return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0); + return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, + 0, 0); } // Throw required runtime exceptions @@ -425,9 +430,8 @@ throw new IllegalArgumentException(sm.getString("engine.nullBuffer")); } if (offset >= srcs.length || offset + length > srcs.length) { - throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", - Integer.toString(offset), Integer.toString(length), - Integer.toString(srcs.length))); + throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", Integer.toString(offset), + Integer.toString(length), Integer.toString(srcs.length))); } if (dst.isReadOnly()) { throw new ReadOnlyBufferException(); @@ -498,8 +502,8 @@ // Do we have enough room in dst to write encrypted data? int capacity = dst.remaining(); if (capacity < pendingNet) { - return new SSLEngineResult( - SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced); + return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatus(), + bytesConsumed, bytesProduced); } // Write the pending data from the network BIO into the dst buffer @@ -517,10 +521,12 @@ } @Override - public synchronized SSLEngineResult unwrap(final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException { + public synchronized SSLEngineResult unwrap(final ByteBuffer src, final ByteBuffer[] dsts, final int offset, + final int length) throws SSLException { // Check to make sure the engine has not been closed if (destroyed) { - return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0); + return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, + 0, 0); } // Throw required runtime exceptions @@ -528,9 +534,8 @@ throw new IllegalArgumentException(sm.getString("engine.nullBuffer")); } if (offset >= dsts.length || offset + length > dsts.length) { - throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", - Integer.toString(offset), Integer.toString(length), - Integer.toString(dsts.length))); + throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", Integer.toString(offset), + Integer.toString(length), Integer.toString(dsts.length))); } int capacity = 0; final int endOffset = offset + length; @@ -566,7 +571,7 @@ } // Write encrypted data to network BIO - int written = 0; + int written; try { written = writeEncryptedData(state.networkBIO, src); } catch (Exception e) { @@ -634,7 +639,8 @@ } // Check to see if we received a close_notify message from the peer - if (!receivedShutdown && (SSL.getShutdown(state.ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) { + if (!receivedShutdown && + (SSL.getShutdown(state.ssl) & SSL.SSL_RECEIVED_SHUTDOWN) == SSL.SSL_RECEIVED_SHUTDOWN) { receivedShutdown = true; closeInbound(); } @@ -645,8 +651,7 @@ } } - private int pendingReadableBytesInSSL() - throws SSLException { + private int pendingReadableBytesInSSL() throws SSLException { // NOTE: Calling a fake read is necessary before calling pendingReadableBytesInSSL because // SSL_pending will return 0 if OpenSSL has not started the current TLS record // See https://www.openssl.org/docs/manmaster/man3/SSL_pending.html @@ -662,8 +667,7 @@ // TLS 1.0 needs additional handling // TODO Figure out why this is necessary and if a simpler / better // solution is available - if (Constants.SSL_PROTO_TLSv1.equals(version) && lastPrimingReadResult == 0 && - pendingReadableBytesInSSL == 0) { + if (Constants.SSL_PROTO_TLSv1.equals(version) && lastPrimingReadResult == 0 && pendingReadableBytesInSSL == 0) { // Perform another priming read lastPrimingReadResult = SSL.readFromSSL(state.ssl, EMPTY_ADDR, 0); if (lastPrimingReadResult <= 0) { @@ -732,8 +736,7 @@ @Override public String[] getSupportedCipherSuites() { - Set availableCipherSuites = AVAILABLE_CIPHER_SUITES; - return availableCipherSuites.toArray(new String[0]); + return AVAILABLE_CIPHER_SUITES.toArray(new String[0]); } @Override @@ -892,22 +895,23 @@ throw new SSLException(sm.getString("engine.engineClosed")); } switch (accepted) { - case NOT: - handshake(); - accepted = Accepted.EXPLICIT; - break; - case IMPLICIT: - // A user did not start handshake by calling this method by themselves, - // but handshake has been started already by wrap() or unwrap() implicitly. - // Because it's the user's first time to call this method, it is unfair to - // raise an exception. From the user's standpoint, they never asked for - // renegotiation. - - accepted = Accepted.EXPLICIT; // Next time this method is invoked by the user, we should raise an exception. - break; - case EXPLICIT: - renegotiate(); - break; + case NOT: + handshake(); + accepted = Accepted.EXPLICIT; + break; + case IMPLICIT: + // A user did not start handshake by calling this method by themselves, + // but handshake has been started already by wrap() or unwrap() implicitly. + // Because it's the user's first time to call this method, it is unfair to + // raise an exception. From the user's standpoint, they never asked for + // renegotiation. + + accepted = Accepted.EXPLICIT; // Next time this method is invoked by the user, we should raise an + // exception. + break; + case EXPLICIT: + renegotiate(); + break; } } @@ -975,12 +979,11 @@ } /** - * Many calls to SSL methods do not check the last error. Those that do - * check the last error need to ensure that any previously ignored error is - * cleared prior to the method call else errors may be falsely reported. - * Ideally, before any SSL_read, SSL_write, clearLastError should always - * be called, and getLastError should be called after on any negative or - * zero result. + * Many calls to SSL methods do not check the last error. Those that do check the last error need to ensure that any + * previously ignored error is cleared prior to the method call else errors may be falsely reported. Ideally, before + * any SSL_read, SSL_write, clearLastError should always be called, and getLastError should be called after on any + * negative or zero result. + * * @return the first error in the stack */ private static String getLastError() { @@ -1023,34 +1026,24 @@ } /* - * Tomcat Native stores a count of the completed handshakes in the - * SSL instance and increments it every time a handshake is - * completed. Comparing the handshake count when the handshake - * started to the current handshake count enables this code to - * detect when the handshake has completed. + * Tomcat Native stores a count of the completed handshakes in the SSL instance and increments it every time + * a handshake is completed. Comparing the handshake count when the handshake started to the current + * handshake count enables this code to detect when the handshake has completed. * - * Obtaining client certificates after the connection has been - * established requires additional checks. We need to trigger - * additional reads until the certificates have been read but we - * don't know how many reads we will need as it depends on both - * client and network behaviour. + * Obtaining client certificates after the connection has been established requires additional checks. We + * need to trigger additional reads until the certificates have been read, but we don't know how many reads + * we will need as it depends on both client and network behaviour. * - * The additional reads are triggered by returning NEED_UNWRAP - * rather than FINISHED. This allows the standard I/O code to be - * used. + * The additional reads are triggered by returning NEED_UNWRAP rather than FINISHED. This allows the + * standard I/O code to be used. * - * For TLSv1.2 and below, the handshake completes before the - * renegotiation. We therefore use SSL.renegotiatePending() to - * check on the current status of the renegotiation and return - * NEED_UNWRAP until it completes which means the client - * certificates will have been read from the client. + * For TLSv1.2 and below, the handshake completes before the renegotiation. We therefore use + * SSL.renegotiatePending() to check on the current status of the renegotiation and return NEED_UNWRAP until + * it completes which means the client certificates will have been read from the client. * - * For TLSv1.3, Tomcat Native sets a flag when post handshake - * authentication is started and updates it once the client - * certificate has been received. We therefore use - * SSL.getPostHandshakeAuthInProgress() to check the current status - * and return NEED_UNWRAP until that methods indicates that PHA is - * no longer in progress. + * For TLSv1.3, Tomcat Native sets a flag when post handshake authentication is started and updates it once + * the client certificate has been received. We therefore use SSL.getPostHandshakeAuthInProgress() to check + * the current status and return NEED_UNWRAP until that methods indicates that PHA is no longer in progress. */ // No pending data to be sent to the peer @@ -1138,9 +1131,8 @@ SSL.setVerify(state.ssl, SSL.SSL_CVERIFY_REQUIRE, certificateVerificationDepth); break; case OPTIONAL: - SSL.setVerify(state.ssl, - certificateVerificationOptionalNoCA ? SSL.SSL_CVERIFY_OPTIONAL_NO_CA : SSL.SSL_CVERIFY_OPTIONAL, - certificateVerificationDepth); + SSL.setVerify(state.ssl, certificateVerificationOptionalNoCA ? SSL.SSL_CVERIFY_OPTIONAL_NO_CA : + SSL.SSL_CVERIFY_OPTIONAL, certificateVerificationDepth); break; } clientAuth = mode; @@ -1164,7 +1156,7 @@ private class OpenSSLSession implements SSLSession { // lazy init for memory reasons - private Map values; + private Map values; // Last accessed time private long lastAccessedTime = -1; @@ -1221,7 +1213,7 @@ if (value == null) { throw new IllegalArgumentException(sm.getString("engine.nullValue")); } - Map values = this.values; + Map values = this.values; if (values == null) { // Use size of 2 to keep the memory overhead small values = this.values = new HashMap<>(2); @@ -1249,7 +1241,7 @@ if (name == null) { throw new IllegalArgumentException(sm.getString("engine.nullName")); } - Map values = this.values; + Map values = this.values; if (values == null) { return; } @@ -1259,7 +1251,7 @@ @Override public String[] getValueNames() { - Map values = this.values; + Map values = this.values; if (values == null || values.isEmpty()) { return new String[0]; } @@ -1285,7 +1277,8 @@ } chain = SSL.getPeerCertChain(state.ssl); if (!clientMode) { - // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate. + // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer + // certificate. // We use SSL_get_peer_certificate to get it in this case and add it to our array later. // // See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html @@ -1330,8 +1323,7 @@ @Deprecated @Override - public javax.security.cert.X509Certificate[] getPeerCertificateChain() - throws SSLPeerUnverifiedException { + public javax.security.cert.X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { // these are lazy created to reduce memory overhead javax.security.cert.X509Certificate[] c = x509PeerCerts; if (c == null) { @@ -1345,8 +1337,7 @@ if (chain == null) { throw new SSLPeerUnverifiedException(sm.getString("engine.unverifiedPeer")); } - javax.security.cert.X509Certificate[] peerCerts = - new javax.security.cert.X509Certificate[chain.length]; + javax.security.cert.X509Certificate[] peerCerts = new javax.security.cert.X509Certificate[chain.length]; for (int i = 0; i < peerCerts.length; i++) { try { peerCerts[i] = javax.security.cert.X509Certificate.getInstance(chain[i]); @@ -1378,7 +1369,7 @@ } private Principal principal(Certificate[] certs) { - return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal(); + return ((java.security.cert.X509Certificate) certs[0]).getSubjectX500Principal(); } @Override @@ -1463,11 +1454,19 @@ @Override public void run() { - if (networkBIO != 0) { - SSL.freeBIO(networkBIO); - } - if (ssl != 0) { - SSL.freeSSL(ssl); + Lock readLock = AprStatus.getStatusLock().readLock(); + readLock.lock(); + try { + if (AprStatus.isAprInitialized()) { + if (networkBIO != 0) { + SSL.freeBIO(networkBIO); + } + if (ssl != 0) { + SSL.freeSSL(ssl); + } + } + } finally { + readLock.unlock(); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLImplementation.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,7 +30,7 @@ public class OpenSSLImplementation extends SSLImplementation { @Override - public SSLSupport getSSLSupport(SSLSession session, Map> additionalAttributes) { + public SSLSupport getSSLSupport(SSLSession session, Map> additionalAttributes) { return new JSSESupport(session, additionalAttributes); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -81,8 +81,7 @@ } /** - * @return {@code true} if caching of SSL sessions is enabled, {@code false} - * otherwise. + * @return {@code true} if caching of SSL sessions is enabled, {@code false} otherwise. */ public boolean isSessionCacheEnabled() { return SSLContext.getSessionCacheMode(contextID) == SSL.SSL_SESS_CACHE_SERVER; @@ -122,12 +121,13 @@ } /** - * Set the context within which session be reused (server side only) - * See - * man SSL_CTX_set_session_id_context + * Set the context within which session be reused (server side only) See + * man + * SSL_CTX_set_session_id_context + * + * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name of the application + * and/or the hostname and/or service name * - * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name - * of the application and/or the hostname and/or service name * @return {@code true} if success, {@code false} otherwise. */ public boolean setSessionIdContext(byte[] sidCtx) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLSessionStats.java 2026-01-23 19:33:36.000000000 +0000 @@ -81,44 +81,40 @@ } /** - * @return The number of successfully reused sessions. In client mode, a - * session set with {@code SSL_set_session} successfully reused is - * counted as a hit. In server mode, a session successfully - * retrieved from internal or external cache is counted as a hit. + * @return The number of successfully reused sessions. In client mode, a session set with {@code SSL_set_session} + * successfully reused is counted as a hit. In server mode, a session successfully retrieved from + * internal or external cache is counted as a hit. */ public long hits() { return SSLContext.sessionHits(context); } /** - * @return The number of successfully retrieved sessions from the external - * session cache in server mode. + * @return The number of successfully retrieved sessions from the external session cache in server mode. */ public long cbHits() { return SSLContext.sessionCbHits(context); } /** - * @return The number of sessions proposed by clients that were not found in - * the internal session cache in server mode. + * @return The number of sessions proposed by clients that were not found in the internal session cache in server + * mode. */ public long misses() { return SSLContext.sessionMisses(context); } /** - * @return The number of sessions proposed by clients and either found in - * the internal or external session cache in server mode, but that - * were invalid due to timeout. These sessions are not included in - * the {@link #hits()} count. + * @return The number of sessions proposed by clients and either found in the internal or external session cache in + * server mode, but that were invalid due to timeout. These sessions are not included in the + * {@link #hits()} count. */ public long timeouts() { return SSLContext.sessionTimeouts(context); } /** - * @return The number of sessions that were removed because the maximum - * session cache size was exceeded. + * @return The number of sessions that were removed because the maximum session cache size was exceeded. */ public long cacheFull() { return SSLContext.sessionCacheFull(context); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLStatus.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLStatus.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLStatus.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLStatus.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,7 +25,11 @@ * OpenSSL library variant that has been identified */ public enum Name { - OPENSSL, OPENSSL3, LIBRESSL, BORINGSSL, UNKNOWN + OPENSSL, + OPENSSL3, + LIBRESSL, + BORINGSSL, + UNKNOWN } private static volatile boolean libraryInitialized = false; @@ -34,9 +38,10 @@ private static volatile boolean useOpenSSL = true; private static volatile boolean instanceCreated = false; private static volatile long version = 0; + private static volatile int majorVersion = 0; + private static volatile int minorVersion = 0; private static volatile Name name = Name.UNKNOWN; - public static boolean isLibraryInitialized() { return libraryInitialized; } @@ -92,6 +97,34 @@ } /** + * @return the majorVersion + */ + public static int getMajorVersion() { + return majorVersion; + } + + /** + * @param majorVersion the majorVersion to set + */ + public static void setMajorVersion(int majorVersion) { + OpenSSLStatus.majorVersion = majorVersion; + } + + /** + * @return the minorVersion + */ + public static int getMinorVersion() { + return minorVersion; + } + + /** + * @param minorVersion the minorVersion to set + */ + public static void setMinorVersion(int minorVersion) { + OpenSSLStatus.minorVersion = minorVersion; + } + + /** * @return the library name */ public static Name getName() { @@ -112,4 +145,18 @@ return Name.OPENSSL3.equals(name); } + /** + * @return true if running with BoringSSL + */ + public static boolean isBoringSSL() { + return Name.BORINGSSL.equals(name); + } + + /** + * @return true if running with LibreSSL < 3.5 + */ + public static boolean isLibreSSLPre35() { + return Name.LIBRESSL.equals(name) && ((majorVersion == 3 && minorVersion < 5) || majorVersion < 3); + } + } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ package org.apache.tomcat.util.net.openssl; import java.io.IOException; +import java.security.KeyException; import java.security.KeyStoreException; import java.util.List; import java.util.Set; @@ -110,20 +111,20 @@ // No (or invalid?) certificate chain was provided for the cert String msg = sm.getString("openssl.nonJsseChain", certificate.getCertificateChainFile()); if (log.isDebugEnabled()) { - log.info(msg, e); + log.debug(msg, e); } else { log.info(msg); } return null; - } catch (KeyStoreException | IOException e) { + } catch (KeyStoreException | KeyException | IOException e) { // Depending on what is presented, JSSE may also throw // KeyStoreException or IOException if it doesn't understand the // provided file. if (certificate.getCertificateFile() != null) { - String msg = sm.getString("openssl.nonJsseCertificate", - certificate.getCertificateFile(), certificate.getCertificateKeyFile()); + String msg = sm.getString("openssl.nonJsseCertificate", certificate.getCertificateFile(), + certificate.getCertificateKeyFile()); if (log.isDebugEnabled()) { - log.info(msg, e); + log.debug(msg, e); } else { log.info(msg); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/OpenSSLX509Certificate.java 2026-01-23 19:33:36.000000000 +0000 @@ -134,16 +134,14 @@ } @Override - public void verify(PublicKey key) - throws CertificateException, NoSuchAlgorithmException, - InvalidKeyException, NoSuchProviderException, SignatureException { + public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { unwrap().verify(key); } @Override - public void verify(PublicKey key, String sigProvider) - throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, - NoSuchProviderException, SignatureException { + public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { unwrap().verify(key, sigProvider); } @@ -181,8 +179,8 @@ X509Certificate wrapped = this.wrapped; if (wrapped == null) { try { - wrapped = this.wrapped = (X509Certificate) OpenSSLContext.X509_CERT_FACTORY.generateCertificate( - new ByteArrayInputStream(bytes)); + wrapped = this.wrapped = (X509Certificate) OpenSSLContext.X509_CERT_FACTORY + .generateCertificate(new ByteArrayInputStream(bytes)); } catch (CertificateException e) { throw new IllegalStateException(e); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Authentication.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,17 +17,19 @@ package org.apache.tomcat.util.net.openssl.ciphers; public enum Authentication { - RSA /* RSA auth */, - DSS /* DSS auth */, - aNULL /* no auth (i.e. use ADH or AECDH) */, - DH /* Fixed DH auth (kDHd or kDHr) */, - ECDH /* Fixed ECDH auth (kECDHe or kECDHr) */, - KRB5 /* KRB5 auth */, - ECDSA /* ECDSA auth*/, - PSK /* PSK auth */, + RSA /* RSA auth */, + DSS /* DSS auth */, + aNULL /* no auth (i.e. use ADH or AECDH) */, + DH /* Fixed DH auth (kDHd or kDHr) */, + ECDH /* Fixed ECDH auth (kECDHe or kECDHr) */, + KRB5 /* KRB5 auth */, + ECDSA /* ECDSA auth */, + PSK /* PSK auth */, GOST94 /* GOST R 34.10-94 signature auth */, GOST01 /* GOST R 34.10-2001 */, - FZA /* Fortezza */, - SRP /* Secure Remote Password */, - ANY /* TLS 1.3 */ + FZA /* Fortezza */, + SRP /* Secure Remote Password */, + EdDSA /* EdDSA */, + MLDSA /* ML-DSA */, + ANY /* TLS 1.3 */ } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,23 +27,23 @@ /** * All the standard cipher suites for SSL/TSL. * - * @see OpenSSL cipher definitions - * @see The cipher suite registry - * @see Another list of cipher suites with some non-standard IDs - * @see Oracle standard names for cipher suites - * @see Mapping of OpenSSL cipher suites names to registry names - * @see SSL Labs tool - list of ciphers - * @see OpenJDK source code + * @see OpenSSL cipher definitions + * @see The cipher suite + * registry + * @see Another list of cipher suites with some + * non-standard IDs + * @see Oracle + * standard names for cipher suites + * @see Mapping of OpenSSL cipher suites names to registry + * names + * @see SSL Labs tool - list of ciphers + * @see OpenJDK source code */ public enum Cipher { + // @formatter:off /* Cipher 0 * TLS_NULL_WITH_NULL_NULL * Must never be negotiated. Used internally to represent the initial @@ -512,7 +512,7 @@ new String[] {"SSL_DH_anon_WITH_3DES_EDE_CBC_SHA"}, null ), - /* Fortezza ciphersuite from SSL 3.0 spec + /* Fortezza cipher suite from SSL 3.0 spec * Neither OpenSSL nor Java implement these ciphers and the IDs used * overlap partially with the IDs used by the Kerberos ciphers // Cipher 1C @@ -844,7 +844,7 @@ null, null ), - /* New AES ciphersuites */ + /* New AES cipher suites */ // Cipher 2F TLS_RSA_WITH_AES_128_CBC_SHA( 0x002f, @@ -1049,7 +1049,7 @@ null, null ), - /* TLS v1.2 ciphersuites */ + /* TLS v1.2 cipher suites */ // Cipher 3B TLS_RSA_WITH_NULL_SHA256( 0x003B, @@ -1152,7 +1152,7 @@ null, null ), - /* Camellia ciphersuites from RFC4132 ( + /* Camellia cipher suites from RFC4132 ( 128-bit portion) */ // Cipher 41 TLS_RSA_WITH_CAMELLIA_128_CBC_SHA( @@ -1379,7 +1379,7 @@ null ), - /* TLS v1.2 ciphersuites */ + /* TLS v1.2 cipher suites */ // Cipher 67 TLS_DHE_RSA_WITH_AES_128_CBC_SHA256( 0x0067, @@ -1499,7 +1499,7 @@ null, null ), - /* GOST Ciphersuites. Unsupported by Java. OpenSSL lists them with IDs + /* GOST cipher suites. Unsupported by Java. OpenSSL lists them with IDs * 0x3000080 to 0x3000083 * The ciphers are not listed in the IANA registry. */ /* @@ -1567,7 +1567,7 @@ null, null ),*/ - /* Camellia ciphersuites from RFC4132 ( + /* Camellia cipher suites from RFC4132 ( 256-bit portion) */ // Cipher 84 TLS_RSA_WITH_CAMELLIA_256_CBC_SHA( @@ -1875,7 +1875,7 @@ null, null ), - /* SEED ciphersuites from RFC4162 */ + /* SEED cipher suites from RFC4162 */ // Cipher 96 TLS_RSA_WITH_SEED_CBC_SHA( 0x0096, @@ -1978,7 +1978,7 @@ null, null ), - /* GCM ciphersuites from RFC5288 */ + /* GCM cipher suites from RFC5288 */ // Cipher 9C TLS_RSA_WITH_AES_128_GCM_SHA256( 0x009C, @@ -2790,7 +2790,7 @@ * No other ciphers defined until 0xC001 below */ - /* ECC ciphersuites from draft-ietf-tls-ecc-01.txt ( + /* ECC cipher suites from draft-ietf-tls-ecc-01.txt ( Mar 15, 2001) */ // Cipher C001 TLS_ECDH_ECDSA_WITH_NULL_SHA( @@ -3217,7 +3217,7 @@ null, null ), - /* SRP ciphersuite from RFC 5054 */ + /* SRP cipher suite from RFC 5054 */ // Cipher C01A TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA( 0xC01A, @@ -3371,7 +3371,7 @@ null, null ), - /* HMAC based TLS v1.2 ciphersuites from RFC5289 */ + /* HMAC based TLS v1.2 cipher suites from RFC5289 */ // Cipher C023 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256( 0xC023, @@ -3508,7 +3508,7 @@ null, null ), - /* GCM based TLS v1.2 ciphersuites from RFC5289 */ + /* GCM based TLS v1.2 cipher suites from RFC5289 */ // Cipher C02B TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256( 0xC02B, @@ -4353,7 +4353,7 @@ null, null ), - // CCM ciphersuites from RFC6655 + // CCM cipher suites from RFC6655 // Cipher C09C TLS_RSA_WITH_AES_128_CCM( 0xC09C, @@ -4626,7 +4626,7 @@ null, null ), - // CCM ciphersuites from RFC7251 + // CCM cipher suites from RFC7251 // Cipher C0AC TLS_ECDHE_ECDSA_WITH_AES_128_CCM( 0xC0AC, @@ -4988,7 +4988,7 @@ 256, 256 },*/ - + // @formatter:on private final int id; private final String openSSLAlias; @@ -5011,10 +5011,9 @@ */ private final int alg_bits; - Cipher(int id, String openSSLAlias, KeyExchange kx, Authentication au, Encryption enc, - MessageDigest mac, Protocol protocol, boolean export, EncryptionLevel level, - boolean fipsCompatible, int strength_bits, int alg_bits, String[] jsseAltNames, - String[] openSSlAltNames) { + Cipher(int id, String openSSLAlias, KeyExchange kx, Authentication au, Encryption enc, MessageDigest mac, + Protocol protocol, boolean export, EncryptionLevel level, boolean fipsCompatible, int strength_bits, + int alg_bits, String[] jsseAltNames, String[] openSSlAltNames) { this.id = id; this.openSSLAlias = openSSLAlias; if (openSSlAltNames != null && openSSlAltNames.length != 0) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/Group.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Group.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/Group.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Group.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * 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.openssl.ciphers; + +import java.util.HashMap; +import java.util.Map; + +/** + * All the supported named groups for TLS 1.3. + * @see The supported groups + * registry + */ +public enum Group { + + // Elliptic Curve Groups (ECDHE) + secp256r1(0x0017), + secp384r1(0x0018), + secp521r1(0x0019), + x25519(0x001D), + x448(0x001E), + + // Finite Field Groups (DHE) + ffdhe2048(0x0100), + ffdhe3072(0x0101), + ffdhe4096(0x0102), + ffdhe6144(0x0103), + ffdhe8192(0x0104), + + // Post-Quantum Key Exchange + MLKEM512(0x0200), + MLKEM768(0x0201), + MLKEM1024(0x0202), + + // Hybrid Key Exchange + SecP256r1MLKEM768(0x11EB), + X25519MLKEM768(0x11EC), + SecP384r1MLKEM1024(0x11ED); + + private final int id; + + Group(int id) { + this.id = id; + } + + /** + * @return the id + */ + public int getId() { + return this.id; + } + + private static final Map idMap = new HashMap<>(); + + static { + for (Group group : values()) { + int id = group.getId(); + + if (id > 0 && id < 0xFFFF) { + idMap.put(Integer.valueOf(id), group); + } + } + } + + + public static Group valueOf(int groupId) { + return idMap.get(Integer.valueOf(groupId)); + } +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/KeyExchange.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/KeyExchange.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/KeyExchange.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/KeyExchange.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,19 +18,19 @@ public enum KeyExchange { EECDH /* SSL_kEECDH - ephemeral ECDH */, - RSA /* SSL_kRSA - RSA key exchange */, - DHr /* SSL_kDHr - DH cert, RSA CA cert */ /* no such ciphersuites supported! */, - DHd /* SSL_kDHd - DH cert, DSA CA cert */ /* no such ciphersuite supported! */, - EDH /* SSL_kDHE - tmp DH key no DH cert */, - PSK /* SSK_kPSK - PSK */, - FZA /* SSL_kFZA - Fortezza */ /* no such ciphersuite supported! */, - KRB5 /* SSL_kKRB5 - Kerberos 5 key exchange */, + RSA /* SSL_kRSA - RSA key exchange */, + DHr /* SSL_kDHr - DH cert, RSA CA cert */ /* no such cipher suites supported */, + DHd /* SSL_kDHd - DH cert, DSA CA cert */ /* no such cipher suite supported */, + EDH /* SSL_kDHE - tmp DH key no DH cert */, + PSK /* SSK_kPSK - PSK */, + FZA /* SSL_kFZA - Fortezza */ /* no such cipher suite supported */, + KRB5 /* SSL_kKRB5 - Kerberos 5 key exchange */, ECDHr /* SSL_kECDHr - ECDH cert, RSA CA cert */, ECDHe /* SSL_kECDHe - ECDH cert, ECDSA CA cert */, - GOST /* SSL_kGOST - GOST key exchange */, - SRP /* SSL_kSRP - SRP */, + GOST /* SSL_kGOST - GOST key exchange */, + SRP /* SSL_kSRP - SRP */, RSAPSK, ECDHEPSK, DHEPSK, - ANY /* TLS 1.3 */ + ANY /* TLS 1.3 */ } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,3 +18,4 @@ opensslCipherConfigurationParser.effectiveCiphers=使用的密码:[{0}] opensslCipherConfigurationParser.unknownElement=密码字符串中的未知元素:[{0}]。 +opensslCipherConfigurationParser.unknownProfile=无法使用OpenSSL来解析配置文件[{0}],它将作为密码套件传递 diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/OpenSSLCipherConfigurationParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,7 +30,10 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.ExceptionUtils; +import org.apache.tomcat.util.compat.JreCompat; import org.apache.tomcat.util.net.Constants; +import org.apache.tomcat.util.net.openssl.OpenSSLStatus; import org.apache.tomcat.util.res.StringManager; /** @@ -45,54 +48,55 @@ private static final String SEPARATOR = ":|,| "; /** - * If ! is used then the ciphers are permanently deleted from the list. The ciphers deleted can never reappear in the list - * even if they are explicitly stated. + * If ! is used then the ciphers are permanently deleted from the list. The ciphers deleted can never reappear in + * the list even if they are explicitly stated. */ private static final String EXCLUDE = "!"; /** - * If - is used then the ciphers are deleted from the list, but some or all of the ciphers can be added again by later - * options. + * If - is used then the ciphers are deleted from the list, but some or all of the ciphers can be added again by + * later options. */ private static final String DELETE = "-"; /** - * If + is used then the ciphers are moved to the end of the list. This option doesn't add any new ciphers it just moves - * matching existing ones. + * If + is used then the ciphers are moved to the end of the list. This option doesn't add any new ciphers it just + * moves matching existing ones. */ private static final String TO_END = "+"; - /** - * Lists of cipher suites can be combined in a single cipher string using the + character. - * This is used as a logical and operation. - * For example SHA1+DES represents all cipher suites containing the SHA1 and the DES algorithms. + /** + * Lists of cipher suites can be combined in a single cipher string using the + character. This is used as a logical + * and operation. For example SHA1+DES represents all cipher suites containing the SHA1 and the DES algorithms. */ private static final String AND = "+"; /** * All ciphers by their openssl alias name. */ - private static final Map> aliases = new LinkedHashMap<>(); + private static final Map> aliases = new LinkedHashMap<>(); + + private static final Set tls13CipherSuiteNames = new HashSet<>(); /** - * the 'NULL' ciphers that is those offering no encryption. Because these offer no encryption at all and are a security risk - * they are disabled unless explicitly included. + * the 'NULL' ciphers that is those offering no encryption. Because these offer no encryption at all and are a + * security risk they are disabled unless explicitly included. */ private static final String eNULL = "eNULL"; /** - * The cipher suites offering no authentication. This is currently the anonymous DH algorithms. T These cipher suites are - * vulnerable to a 'man in the middle' attack and so their use is normally discouraged. + * The cipher suites offering no authentication. This is currently the anonymous DH algorithms. T These cipher + * suites are vulnerable to a 'man in the middle' attack and so their use is normally discouraged. */ private static final String aNULL = "aNULL"; /** - * 'high' encryption cipher suites. This currently means those with key lengths larger than 128 bits, and some cipher suites - * with 128-bit keys. + * 'high' encryption cipher suites. This currently means those with key lengths larger than 128 bits, and some + * cipher suites with 128-bit keys. */ private static final String HIGH = "HIGH"; /** - * 'medium' encryption cipher suites, currently some of those using 128 bit encryption. + * 'medium' encryption cipher suites, currently some of those using 128-bit encryption. */ private static final String MEDIUM = "MEDIUM"; /** - * 'low' encryption cipher suites, currently those using 64 or 56 bit encryption algorithms but excluding export cipher - * suites. + * 'low' encryption cipher suites, currently those using 64 or 56 bit encryption algorithms but excluding export + * cipher suites. */ private static final String LOW = "LOW"; /** @@ -116,8 +120,7 @@ */ private static final String aRSA = "aRSA"; /** - * Cipher suites using RSA for key exchange - * Despite what the docs say, RSA is equivalent to kRSA. + * Cipher suites using RSA for key exchange Despite what the docs say, RSA is equivalent to kRSA. */ private static final String RSA = "RSA"; /** @@ -165,12 +168,11 @@ */ private static final String kEECDH = "kEECDH"; /** - * Cipher suites using ephemeral ECDH key agreement, excluding anonymous cipher suites. - * Same as "kEECDH:-AECDH" + * Cipher suites using ephemeral ECDH key agreement, excluding anonymous cipher suites. Same as "kEECDH:-AECDH" */ private static final String EECDH = "EECDH"; /** - * Cipher suitesusing ECDH key exchange, including anonymous, ephemeral and fixed ECDH. + * Cipher suites using ECDH key exchange, including anonymous, ephemeral and fixed ECDH. */ private static final String ECDH = "ECDH"; /** @@ -234,15 +236,15 @@ */ private static final String ADH = "ADH"; /** - * Cipher suites using 128 bit AES. + * Cipher suites using 128-bit AES. */ private static final String AES128 = "AES128"; /** - * Cipher suites using 256 bit AES. + * Cipher suites using 256-bit AES. */ private static final String AES256 = "AES256"; /** - * Cipher suites using either 128 or 256 bit AES. + * Cipher suites using either 128 or 256-bit AES. */ private static final String AES = "AES"; /** @@ -347,7 +349,7 @@ */ private static final String aGOST94 = "aGOST94"; /** - * Cipher suites using using VKO 34.10 key exchange, specified in the RFC 4357. + * Cipher suites using VKO 34.10 key exchange, specified in the RFC 4357. */ private static final String kGOST = "kGOST"; /** @@ -423,6 +425,16 @@ for (String jsseName : jsseNames) { jsseToOpenSSL.put(jsseName, cipher.getOpenSSLAlias()); } + + if (cipher.getProtocol().equals(Protocol.TLSv1_3)) { + tls13CipherSuiteNames.add(cipher.getOpenSSLAlias()); + /* + * The TLS 1.3 cipher suites do not, currently (January 2026), have any alternative names defined so the + * following two calls are NO-OPs but are implemented in case alternative names are used in the future. + */ + tls13CipherSuiteNames.addAll(cipher.getOpenSSLAltNames()); + tls13CipherSuiteNames.addAll(cipher.getJsseNames()); + } } List allCiphersList = Arrays.asList(Cipher.values()); Collections.reverse(allCiphersList); @@ -434,7 +446,8 @@ addListAlias(HIGH, filterByEncryptionLevel(allCiphers, Collections.singleton(EncryptionLevel.HIGH))); addListAlias(MEDIUM, filterByEncryptionLevel(allCiphers, Collections.singleton(EncryptionLevel.MEDIUM))); addListAlias(LOW, filterByEncryptionLevel(allCiphers, Collections.singleton(EncryptionLevel.LOW))); - addListAlias(EXPORT, filterByEncryptionLevel(allCiphers, new HashSet<>(Arrays.asList(EncryptionLevel.EXP40, EncryptionLevel.EXP56)))); + addListAlias(EXPORT, filterByEncryptionLevel(allCiphers, + new HashSet<>(Arrays.asList(EncryptionLevel.EXP40, EncryptionLevel.EXP56)))); aliases.put("EXP", aliases.get(EXPORT)); addListAlias(EXPORT40, filterByEncryptionLevel(allCiphers, Collections.singleton(EncryptionLevel.EXP40))); addListAlias(EXPORT56, filterByEncryptionLevel(allCiphers, Collections.singleton(EncryptionLevel.EXP56))); @@ -453,12 +466,15 @@ addListAlias(DHE, edh); addListAlias(kDHr, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.DHr))); addListAlias(kDHd, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.DHd))); - addListAlias(kDH, filterByKeyExchange(allCiphers, new HashSet<>(Arrays.asList(KeyExchange.DHr, KeyExchange.DHd)))); + addListAlias(kDH, + filterByKeyExchange(allCiphers, new HashSet<>(Arrays.asList(KeyExchange.DHr, KeyExchange.DHd)))); addListAlias(kECDHr, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.ECDHr))); addListAlias(kECDHe, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.ECDHe))); - addListAlias(kECDH, filterByKeyExchange(allCiphers, new HashSet<>(Arrays.asList(KeyExchange.ECDHe, KeyExchange.ECDHr)))); - addListAlias(ECDH, filterByKeyExchange(allCiphers, new HashSet<>(Arrays.asList(KeyExchange.ECDHe, KeyExchange.ECDHr, KeyExchange.EECDH)))); + addListAlias(kECDH, + filterByKeyExchange(allCiphers, new HashSet<>(Arrays.asList(KeyExchange.ECDHe, KeyExchange.ECDHr)))); + addListAlias(ECDH, filterByKeyExchange(allCiphers, + new HashSet<>(Arrays.asList(KeyExchange.ECDHe, KeyExchange.ECDHr, KeyExchange.EECDH)))); addListAlias(kECDHE, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH))); Set ecdhe = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EECDH)); @@ -480,26 +496,40 @@ addListAlias(kFZA, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.FZA))); addListAlias(aFZA, filterByAuthentication(allCiphers, Collections.singleton(Authentication.FZA))); addListAlias(eFZA, filterByEncryption(allCiphers, Collections.singleton(Encryption.FZA))); - addListAlias(FZA, filter(allCiphers, null, Collections.singleton(KeyExchange.FZA), Collections.singleton(Authentication.FZA), Collections.singleton(Encryption.FZA), null, null)); - addListAlias(Constants.SSL_PROTO_TLSv1_2, filterByProtocol(allCiphers, Collections.singleton(Protocol.TLSv1_2))); + addListAlias(FZA, filter(allCiphers, null, Collections.singleton(KeyExchange.FZA), + Collections.singleton(Authentication.FZA), Collections.singleton(Encryption.FZA), null, null)); + addListAlias(Constants.SSL_PROTO_TLSv1_2, + filterByProtocol(allCiphers, Collections.singleton(Protocol.TLSv1_2))); addListAlias(Constants.SSL_PROTO_TLSv1_0, filterByProtocol(allCiphers, Collections.singleton(Protocol.TLSv1))); addListAlias(Constants.SSL_PROTO_SSLv3, filterByProtocol(allCiphers, Collections.singleton(Protocol.SSLv3))); aliases.put(Constants.SSL_PROTO_TLSv1, aliases.get(Constants.SSL_PROTO_TLSv1_0)); addListAlias(Constants.SSL_PROTO_SSLv2, filterByProtocol(allCiphers, Collections.singleton(Protocol.SSLv2))); - addListAlias(DH, filterByKeyExchange(allCiphers, new HashSet<>(Arrays.asList(KeyExchange.DHr, KeyExchange.DHd, KeyExchange.EDH)))); + addListAlias(DH, filterByKeyExchange(allCiphers, + new HashSet<>(Arrays.asList(KeyExchange.DHr, KeyExchange.DHd, KeyExchange.EDH)))); Set adh = filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.EDH)); adh.retainAll(filterByAuthentication(allCiphers, Collections.singleton(Authentication.aNULL))); addListAlias(ADH, adh); - addListAlias(AES128, filterByEncryption(allCiphers, new HashSet<>(Arrays.asList(Encryption.AES128, Encryption.AES128CCM, Encryption.AES128CCM8, Encryption.AES128GCM)))); - addListAlias(AES256, filterByEncryption(allCiphers, new HashSet<>(Arrays.asList(Encryption.AES256, Encryption.AES256CCM, Encryption.AES256CCM8, Encryption.AES256GCM)))); - addListAlias(AES, filterByEncryption(allCiphers, new HashSet<>(Arrays.asList(Encryption.AES128, Encryption.AES128CCM, Encryption.AES128CCM8, Encryption.AES128GCM, Encryption.AES256, Encryption.AES256CCM, Encryption.AES256CCM8, Encryption.AES256GCM)))); + addListAlias(AES128, filterByEncryption(allCiphers, new HashSet<>( + Arrays.asList(Encryption.AES128, Encryption.AES128CCM, Encryption.AES128CCM8, Encryption.AES128GCM)))); + addListAlias(AES256, filterByEncryption(allCiphers, new HashSet<>( + Arrays.asList(Encryption.AES256, Encryption.AES256CCM, Encryption.AES256CCM8, Encryption.AES256GCM)))); + addListAlias(AES, + filterByEncryption(allCiphers, + new HashSet<>(Arrays.asList(Encryption.AES128, Encryption.AES128CCM, Encryption.AES128CCM8, + Encryption.AES128GCM, Encryption.AES256, Encryption.AES256CCM, Encryption.AES256CCM8, + Encryption.AES256GCM)))); addListAlias(ARIA128, filterByEncryption(allCiphers, Collections.singleton(Encryption.ARIA128GCM))); addListAlias(ARIA256, filterByEncryption(allCiphers, Collections.singleton(Encryption.ARIA256GCM))); - addListAlias(ARIA, filterByEncryption(allCiphers, new HashSet<>(Arrays.asList(Encryption.ARIA128GCM, Encryption.ARIA256GCM)))); - addListAlias(AESGCM, filterByEncryption(allCiphers, new HashSet<>(Arrays.asList(Encryption.AES128GCM, Encryption.AES256GCM)))); - addListAlias(AESCCM, filterByEncryption(allCiphers, new HashSet<>(Arrays.asList(Encryption.AES128CCM, Encryption.AES128CCM8, Encryption.AES256CCM, Encryption.AES256CCM8)))); - addListAlias(AESCCM8, filterByEncryption(allCiphers, new HashSet<>(Arrays.asList(Encryption.AES128CCM8, Encryption.AES256CCM8)))); - addListAlias(CAMELLIA, filterByEncryption(allCiphers, new HashSet<>(Arrays.asList(Encryption.CAMELLIA128, Encryption.CAMELLIA256)))); + addListAlias(ARIA, filterByEncryption(allCiphers, + new HashSet<>(Arrays.asList(Encryption.ARIA128GCM, Encryption.ARIA256GCM)))); + addListAlias(AESGCM, filterByEncryption(allCiphers, + new HashSet<>(Arrays.asList(Encryption.AES128GCM, Encryption.AES256GCM)))); + addListAlias(AESCCM, filterByEncryption(allCiphers, new HashSet<>(Arrays.asList(Encryption.AES128CCM, + Encryption.AES128CCM8, Encryption.AES256CCM, Encryption.AES256CCM8)))); + addListAlias(AESCCM8, filterByEncryption(allCiphers, + new HashSet<>(Arrays.asList(Encryption.AES128CCM8, Encryption.AES256CCM8)))); + addListAlias(CAMELLIA, filterByEncryption(allCiphers, + new HashSet<>(Arrays.asList(Encryption.CAMELLIA128, Encryption.CAMELLIA256)))); addListAlias(CAMELLIA128, filterByEncryption(allCiphers, Collections.singleton(Encryption.CAMELLIA128))); addListAlias(CAMELLIA256, filterByEncryption(allCiphers, Collections.singleton(Encryption.CAMELLIA256))); addListAlias(CHACHA20, filterByEncryption(allCiphers, Collections.singleton(Encryption.CHACHA20POLY1305))); @@ -514,27 +544,34 @@ aliases.put(SHA, aliases.get(SHA1)); addListAlias(SHA256, filterByMessageDigest(allCiphers, Collections.singleton(MessageDigest.SHA256))); addListAlias(SHA384, filterByMessageDigest(allCiphers, Collections.singleton(MessageDigest.SHA384))); - addListAlias(aGOST, filterByAuthentication(allCiphers, new HashSet<>(Arrays.asList(Authentication.GOST01, Authentication.GOST94)))); + addListAlias(aGOST, filterByAuthentication(allCiphers, + new HashSet<>(Arrays.asList(Authentication.GOST01, Authentication.GOST94)))); addListAlias(aGOST01, filterByAuthentication(allCiphers, Collections.singleton(Authentication.GOST01))); addListAlias(aGOST94, filterByAuthentication(allCiphers, Collections.singleton(Authentication.GOST94))); addListAlias(kGOST, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.GOST))); addListAlias(GOST94, filterByMessageDigest(allCiphers, Collections.singleton(MessageDigest.GOST94))); addListAlias(GOST89MAC, filterByMessageDigest(allCiphers, Collections.singleton(MessageDigest.GOST89MAC))); - addListAlias(PSK, filter(allCiphers, null, new HashSet<>(Arrays.asList(KeyExchange.PSK, KeyExchange.RSAPSK, KeyExchange.DHEPSK, KeyExchange.ECDHEPSK)), Collections.singleton(Authentication.PSK), null, null, null)); + addListAlias(PSK, + filter(allCiphers, null, new HashSet<>( + Arrays.asList(KeyExchange.PSK, KeyExchange.RSAPSK, KeyExchange.DHEPSK, KeyExchange.ECDHEPSK)), + Collections.singleton(Authentication.PSK), null, null, null)); addListAlias(aPSK, filterByAuthentication(allCiphers, Collections.singleton(Authentication.PSK))); addListAlias(kPSK, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.PSK))); addListAlias(kRSAPSK, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.RSAPSK))); addListAlias(kECDHEPSK, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.ECDHEPSK))); addListAlias(kDHEPSK, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.DHEPSK))); - addListAlias(KRB5, filter(allCiphers, null, Collections.singleton(KeyExchange.KRB5), Collections.singleton(Authentication.KRB5), null, null, null)); + addListAlias(KRB5, filter(allCiphers, null, Collections.singleton(KeyExchange.KRB5), + Collections.singleton(Authentication.KRB5), null, null, null)); addListAlias(aSRP, filterByAuthentication(allCiphers, Collections.singleton(Authentication.SRP))); addListAlias(kSRP, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.SRP))); addListAlias(SRP, filterByKeyExchange(allCiphers, Collections.singleton(KeyExchange.SRP))); initialized = true; // Despite what the OpenSSL docs say, DEFAULT also excludes SSLv2 - addListAlias(DEFAULT, parse("ALL:!EXPORT:!eNULL:!aNULL:!SSLv2:!DES:!RC2:!RC4:!DSS:!SEED:!IDEA:!CAMELLIA:!AESCCM:!3DES:!ARIA")); + addListAlias(DEFAULT, parse( + "ALL:!EXPORT:!eNULL:!aNULL:!SSLv2:!DES:!RC2:!RC4:!DSS:!SEED:!IDEA:!CAMELLIA:!AESCCM:!3DES:!ARIA")); // COMPLEMENTOFDEFAULT is also not exactly as defined by the docs - LinkedHashSet complementOfDefault = filterByKeyExchange(all, new HashSet<>(Arrays.asList(KeyExchange.EDH,KeyExchange.EECDH))); + LinkedHashSet complementOfDefault = + filterByKeyExchange(all, new HashSet<>(Arrays.asList(KeyExchange.EDH, KeyExchange.EECDH))); complementOfDefault = filterByAuthentication(complementOfDefault, Collections.singleton(Authentication.aNULL)); aliases.get(eNULL).forEach(complementOfDefault::remove); complementOfDefault.addAll(aliases.get(Constants.SSL_PROTO_SSLv2)); @@ -587,9 +624,8 @@ static LinkedHashSet strengthSort(final LinkedHashSet ciphers) { /* - * This routine sorts the ciphers with descending strength. The sorting - * must keep the pre-sorted sequence, so we apply the normal sorting - * routine as '+' movement to the end of the list. + * This routine sorts the ciphers with descending strength. The sorting must keep the pre-sorted sequence, so we + * apply the normal sorting routine as '+' movement to the end of the list. */ Set keySizes = new HashSet<>(); for (Cipher cipher : ciphers) { @@ -606,8 +642,7 @@ } /* - * See - * https://github.com/openssl/openssl/blob/7c96dbcdab959fef74c4caae63cdebaa354ab252/ssl/ssl_ciph.c#L1371 + * See https://github.com/openssl/openssl/blob/7c96dbcdab959fef74c4caae63cdebaa354ab252/ssl/ssl_ciph.c#L1371 */ static LinkedHashSet defaultSort(final LinkedHashSet ciphers) { final LinkedHashSet result = new LinkedHashSet<>(ciphers.size()); @@ -617,9 +652,9 @@ ecdh.addAll(filterByKeyExchange(ciphers, Collections.singleton(KeyExchange.EECDH))); /* AES is our preferred symmetric cipher */ - Set aes = new HashSet<>(Arrays.asList(Encryption.AES128, Encryption.AES128CCM, - Encryption.AES128CCM8, Encryption.AES128GCM, Encryption.AES256, - Encryption.AES256CCM, Encryption.AES256CCM8, Encryption.AES256GCM)); + Set aes = new HashSet<>( + Arrays.asList(Encryption.AES128, Encryption.AES128CCM, Encryption.AES128CCM8, Encryption.AES128GCM, + Encryption.AES256, Encryption.AES256CCM, Encryption.AES256CCM8, Encryption.AES256GCM)); /* Now arrange all ciphers by preference: */ result.addAll(filterByEncryption(ecdh, aes)); @@ -632,9 +667,10 @@ /* Low priority for MD5 */ moveToEnd(result, filterByMessageDigest(result, Collections.singleton(MessageDigest.MD5))); - /* Move anonymous ciphers to the end. Usually, these will remain disabled. - * (For applications that allow them, they aren't too bad, but we prefer - * authenticated ciphers.) */ + /* + * Move anonymous ciphers to the end. Usually, these will remain disabled. (For applications that allow them, + * they aren't too bad, but we prefer authenticated ciphers.) + */ moveToEnd(result, filterByAuthentication(result, Collections.singleton(Authentication.aNULL))); /* Move ciphers without forward secrecy to the end */ @@ -642,7 +678,7 @@ moveToEnd(result, filterByKeyExchange(result, Collections.singleton(KeyExchange.RSA))); moveToEnd(result, filterByKeyExchange(result, Collections.singleton(KeyExchange.PSK))); - /* RC4 is sort-of broken -- move the the end */ + /* RC4 is sort-of broken -- move to the end */ moveToEnd(result, filterByEncryption(result, Collections.singleton(Encryption.RC4))); return strengthSort(result); } @@ -712,6 +748,35 @@ init(); } String[] elements = expression.split(SEPARATOR); + // Handle PROFILE= using OpenSSL (if present, otherwise warn), then replace elements with that + if (elements.length == 1 && elements[0].startsWith("PROFILE=")) { + // Only use with Java 22 and if OpenSSL has been successfully loaded before + if (JreCompat.isJre22Available()) { + if (OpenSSLStatus.isLibraryInitialized()) { + try { + Class openSSLLibraryClass = + Class.forName("org.apache.tomcat.util.net.openssl.panama.OpenSSLLibrary"); + @SuppressWarnings("unchecked") + List cipherList = (List) openSSLLibraryClass + .getMethod("findCiphers", String.class).invoke(null, elements[0]); + // Replace the original list with the profile contents + elements = cipherList.toArray(new String[0]); + } catch (Throwable t) { + Throwable throwable = ExceptionUtils.unwrapInvocationTargetException(t); + ExceptionUtils.handleThrowable(throwable); + log.error(sm.getString("opensslCipherConfigurationParser.unknownProfile", elements[0]), + throwable); + } + } else { + // OpenSSL is not available + log.error(sm.getString("opensslCipherConfigurationParser.unknownProfile", elements[0])); + } + } else { + // No way to resolve using OpenSSL, log an info about this, + // but it might still work if using tomcat-native + log.info(sm.getString("opensslCipherConfigurationParser.unknownProfile", elements[0])); + } + } LinkedHashSet ciphers = new LinkedHashSet<>(); Set removedCiphers = new HashSet<>(); for (String element : elements) { @@ -739,14 +804,14 @@ add(ciphers, element); } else if (element.contains(AND)) { String[] intersections = element.split("\\" + AND); - if(intersections.length > 0 && aliases.containsKey(intersections[0])) { + if (intersections.length > 0 && aliases.containsKey(intersections[0])) { List result = new ArrayList<>(aliases.get(intersections[0])); - for(int i = 1; i < intersections.length; i++) { - if(aliases.containsKey(intersections[i])) { + for (int i = 1; i < intersections.length; i++) { + if (aliases.containsKey(intersections[i])) { result.retainAll(aliases.get(intersections[i])); } } - ciphers.addAll(result); + ciphers.addAll(result); } } } @@ -760,16 +825,31 @@ result.addAll(cipher.getJsseNames()); } if (log.isDebugEnabled()) { - log.debug(sm.getString("opensslCipherConfigurationParser.effectiveCiphers", displayResult(ciphers, true, ","))); + log.debug(sm.getString("opensslCipherConfigurationParser.effectiveCiphers", + displayResult(ciphers, true, ","))); } return result; } /** - * Parse the specified expression according to the OpenSSL syntax and - * returns a list of standard JSSE cipher names. + * Determines if the provided name is the name of a TLS 1.3 cipher suite. + * + * @param cipherSuiteName The name to test + * + * @return {@code true} if the provided String is recognised as the name of a TLS 1.3 cipherSuite. + */ + public static boolean isTls13Cipher(String cipherSuiteName) { + if (!initialized) { + init(); + } + return tls13CipherSuiteNames.contains(cipherSuiteName); + } + + /** + * Parse the specified expression according to the OpenSSL syntax and returns a list of standard JSSE cipher names. * * @param expression the openssl expression to define a list of cipher. + * * @return the corresponding list of ciphers. */ public static List parseExpression(String expression) { @@ -797,8 +877,8 @@ * * @param opensslCipherName The OpenSSL name for a cipher * - * @return The JSSE name for the specified OpenSSL cipher. If none is known, - * the IANA standard name will be returned instead + * @return The JSSE name for the specified OpenSSL cipher. If none is known, the IANA standard name will be returned + * instead */ public static String openSSLToJsse(String opensslCipherName) { if (!initialized) { @@ -831,13 +911,14 @@ } builder.append(separator); } - return builder.toString().substring(0, builder.length() - 1); + return builder.substring(0, builder.length() - 1); } public static void usage() { - System.out.println("Usage: java " + OpenSSLCipherConfigurationParser.class.getName() + " [options] cipherspec"); + System.out + .println("Usage: java " + OpenSSLCipherConfigurationParser.class.getName() + " [options] cipher spec"); System.out.println(); - System.out.println("Displays the TLS cipher suites matching the cipherspec."); + System.out.println("Displays the TLS cipher suites matching the cipher spec."); System.out.println(); System.out.println(" --help,"); System.out.println(" -h Print this help message"); @@ -846,26 +927,23 @@ System.out.println(" -v Provide detailed cipher listing"); } - public static void main(String[] args) throws Exception - { + public static void main(String[] args) throws Exception { boolean verbose = false; boolean useOpenSSLNames = false; int argindex; - for(argindex = 0; argindex < args.length; ++argindex) - { + for (argindex = 0; argindex < args.length; ++argindex) { String arg = args[argindex]; - if("--verbose".equals(arg) || "-v".equals(arg)) { + if ("--verbose".equals(arg) || "-v".equals(arg)) { verbose = true; - } else if("--openssl".equals(arg)) { + } else if ("--openssl".equals(arg)) { useOpenSSLNames = true; - } else if("--help".equals(arg) || "-h".equals(arg)) { + } else if ("--help".equals(arg) || "-h".equals(arg)) { usage(); System.exit(0); - } - else if("--".equals(arg)) { + } else if ("--".equals(arg)) { ++argindex; break; - } else if(arg.startsWith("-")) { + } else if (arg.startsWith("-")) { System.out.println("Unknown option: " + arg); usage(); System.exit(1); @@ -876,30 +954,30 @@ } String cipherSpec; - if(argindex < args.length) { + if (argindex < args.length) { cipherSpec = args[argindex]; } else { cipherSpec = "DEFAULT"; } Set ciphers = parse(cipherSpec); boolean first = true; - if(null != ciphers && 0 < ciphers.size()) { - for(Cipher cipher : ciphers) - { - if(first) { + if (!ciphers.isEmpty()) { + for (Cipher cipher : ciphers) { + if (first) { first = false; } else { - if(!verbose) { + if (!verbose) { System.out.print(','); } } - if(useOpenSSLNames) { + if (useOpenSSLNames) { System.out.print(cipher.getOpenSSLAlias()); } else { System.out.print(cipher.name()); } - if(verbose) { - System.out.println("\t" + cipher.getProtocol() + "\tKx=" + cipher.getKx() + "\tAu=" + cipher.getAu() + "\tEnc=" + cipher.getEnc() + "\tMac=" + cipher.getMac()); + if (verbose) { + System.out.println("\t" + cipher.getProtocol() + "\tKx=" + cipher.getKx() + "\tAu=" + + cipher.getAu() + "\tEnc=" + cipher.getEnc() + "\tMac=" + cipher.getMac()); } } System.out.println(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/Protocol.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Protocol.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/Protocol.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/Protocol.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,9 +33,8 @@ } /** - * The name returned by OpenSSL in the protocol column when using - * openssl ciphers -v. This is currently only used by the unit - * tests hence it is package private. + * The name returned by OpenSSL in the protocol column when using openssl ciphers -v. This is currently + * only used by the unit tests hence it is package private. */ String getOpenSSLName() { return openSSLName; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/SignatureScheme.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/SignatureScheme.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/ciphers/SignatureScheme.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/ciphers/SignatureScheme.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,100 @@ +/* + * 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.openssl.ciphers; + +import java.util.HashMap; +import java.util.Map; + +/** + * All the signature schemes for TLS 1.3. + * @see The signature schemes + * registry + */ +public enum SignatureScheme { + + // RSASSA-PKCS1-v1_5 algorithms + rsa_pkcs1_sha256(0x0401, Authentication.RSA), + rsa_pkcs1_sha384(0x0501, Authentication.RSA), + rsa_pkcs1_sha512(0x0601, Authentication.RSA), + + // ECDSA algorithms + ecdsa_secp256r1_sha256(0x0403, Authentication.ECDSA), + ecdsa_secp384r1_sha384(0x0503, Authentication.ECDSA), + ecdsa_secp521r1_sha512(0x0603, Authentication.ECDSA), + + // RSASSA-PSS algorithms with public key OID rsaEncryption + rsa_pss_rsae_sha256(0x0804, Authentication.RSA), + rsa_pss_rsae_sha384(0x0805, Authentication.RSA), + rsa_pss_rsae_sha512(0x0806, Authentication.RSA), + + // EdDSA algorithms + ed25519(0x0807, Authentication.EdDSA), + ed448(0x0808, Authentication.EdDSA), + + // RSASSA-PSS algorithms with public key OID RSASSA-PSS + rsa_pss_pss_sha256(0x0809, Authentication.RSA), + rsa_pss_pss_sha384(0x080a, Authentication.RSA), + rsa_pss_pss_sha512(0x080b, Authentication.RSA), + + // Legacy algorithms + rsa_pkcs1_sha1(0x0201, Authentication.RSA), + ecdsa_sha1(0x0203, Authentication.ECDSA), + + // ML-DSA algorithms + mldsa44(0x0904, Authentication.MLDSA), + mldsa65(0x0905, Authentication.MLDSA), + mldsa87(0x0906, Authentication.MLDSA); + + private final int id; + private final Authentication auth; + + SignatureScheme(int id, Authentication auth) { + this.id = id; + this.auth = auth; + } + + /** + * @return the id + */ + public int getId() { + return this.id; + } + + /** + * @return the auth + */ + public Authentication getAuth() { + return this.auth; + } + + private static final Map idMap = new HashMap<>(); + + static { + for (SignatureScheme scheme : values()) { + int id = scheme.getId(); + + if (id > 0 && id < 0xFFFF) { + idMap.put(Integer.valueOf(id), scheme); + } + } + } + + + public static SignatureScheme valueOf(int schemeId) { + return idMap.get(Integer.valueOf(schemeId)); + } +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -63,6 +63,7 @@ openssl.errorPrivateKeyCheck=Private key does not match the certificate public key: [{0}] openssl.errorReadingPEMParameters=Failed reading PEM parameters [{0}] for certificate [{1}] openssl.errorSSLCtxInit=Error initializing SSL context +openssl.errorSettingGroups=Error setting group list: [{0}] openssl.invalidSslProtocol=An invalid value [{0}] was provided for the SSLProtocol attribute openssl.keyManagerMissing=No key manager found openssl.makeConf=Creating OpenSSLConf context diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -63,6 +63,7 @@ openssl.errorPrivateKeyCheck=La clé privée ne correspond pas à la clé publique du certificat: [{0}] openssl.errorReadingPEMParameters=Erreur de lecture des paramètres PEM [{0}] pour le certificat [{1}] openssl.errorSSLCtxInit=Erreur d'initialisation du contexte SSL +openssl.errorSettingGroups=Erreur en définissant la liste de groupes: [{0}] openssl.invalidSslProtocol=La valeur invalide [{0}] a été fournie pour l''attribut SSLProtocol openssl.keyManagerMissing=Aucun gestionnaire de clés trouvé openssl.makeConf=Création du contexte de OpenSSLConf diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -63,6 +63,7 @@ openssl.errorPrivateKeyCheck=秘密鍵が証明書の公開鍵と一致しません: [{0}] openssl.errorReadingPEMParameters=証明書 [{1}] の PEM パラメータ [{0}] の読み取りに失敗しました openssl.errorSSLCtxInit=SSLコンテキストの初期化エラー +openssl.errorSettingGroups=グループリストの設定中にエラーが発生しました: [{0}] openssl.invalidSslProtocol=無効な値 [{0}] が SSLProtocol 属性に指定されました openssl.keyManagerMissing=キーマネージャが見つかりません openssl.makeConf=OpenSSLConf コンテキストの作成 diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -16,4 +16,70 @@ # Do not edit this file directly. # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations +engine.invalidOCSPURL=OCSP链接无效:[{0}] +engine.noSSLContext=无SSL上下文 engine.noSession=SSL会话ID不可用 +engine.nullBuffer=空缓冲区 +engine.nullBufferInArray=数组空缓冲区 +engine.nullCipherSuite=空密码套件 +engine.nullName=空值名称 +engine.nullValue=空值 +engine.ocspParseError=解析OCSP链接时错误 +engine.ocspRequestError=处理链接 [{0}]的OCSP请求出错 +engine.openSSLError=OpenSSL错误: [{0}] 消息: [{1}] +engine.oversizedPacket=加密包过大 +engine.unsupportedCipher=不支持的密码套件:[{0}] [{1}] +engine.unsupportedProtocol=不支持协议[{0}] + +openssl.X509FactoryError=获取X509工厂实例错误 +openssl.addedClientCaCert=添加客户端CA证书:[{0}] +openssl.applyConf=在SSL上下文中应用OpenSSLConfCmd +openssl.certificateVerificationFailed=证书验证失败 +openssl.checkConf=检查OpenSSLConf +openssl.doubleInit=SSL上下文已初始化,忽略 +openssl.errApplyConf=无法将OpenSSLConf应用于SSL上下文 +openssl.errCheckConf=检查OpenSSLConf时发生错误 +openssl.errMakeConf=无法创建OpenSSLConf上下文 [{0}] +openssl.errorAddingCertificate=向链中添加证书时错误:[{0}] +openssl.errorConfiguringLocations=配置CA证书位置错误:[{0}] +openssl.errorLoadingCertificate=加载证书错误:[{0}] +openssl.errorLoadingPassword=加载密码文件错误:[{0}] +openssl.errorLoadingPrivateKey=加载私钥错误:[{0}] +openssl.errorPrivateKeyCheck=私钥与证书公钥不匹配:[{0}] +openssl.errorSSLCtxInit=初始化SSL上下文错误 +openssl.keyManagerMissing=密钥管理器未找到 +openssl.makeConf=创建OpenSSLConf上下文 +openssl.noCACerts=未配置CA证书 +openssl.nonJsseCertificate=证书[{0}]或它的私钥[{1}]不能使用JSSE密钥管理器进行处理,将直接提供给OpenSSL +openssl.nonJsseChain=日志含义证书链[{0}]未指定或无效,JSSE需要一个有效的证书链,所以尝试直接使用OpenSSL +openssl.passwordTooLong=证书密码过长 +openssl.setCustomDHParameters=为密钥[{1}]设置自定义DH参数([{0}]比特) +openssl.setECDHCurve=设置键值[{1}]的ECDH曲线([{0}]) +openssl.trustManagerMissing=未找到信任管理器 + +opensslconf.applyCommand=OpenSSLConf应用命令(名称 [{0}], 值 [{1}]) +opensslconf.applyFailed=将OpenSSLConf应用到SSL上下文时失败 +opensslconf.badDirectory=命令名称[{0}]使用了丢失的目录[{1}]) +opensslconf.badFile=命令名称[{0}]使用已丢失或不可读的文件[{1}]) +opensslconf.checkCommand=OpenSSLConf检查命令 (名称 [{0}], 值 [{1}]) +opensslconf.checkFailed=检查OpenSSLConf [{0}]时失败 +opensslconf.commandError=OpenSSLConf 失败命令(名称 [{0}], 值 [{1}]) 导致错误 [{2}] +opensslconf.failedCommand=OpenSSLConf 失败命令(名称 [{0}], 值 [{1}]) 结果是 [{2}] - 将被忽略 +opensslconf.finishFailed=OpenSSLConf 结束失败结果是 [{0}] +opensslconf.noCommandName=OpenSSLConf 无命令名称 - 将被忽略 (命令值 [{0}]) +opensslconf.resultCommand=OpenSSLConf 命令 (名称 [{0}], 值 [{1}]) 返回 [{2}] +opensslconf.unknownCommandType=SSL_CONF 命令 [{0}] 未知类型 +opensslconf.unsupported=OpenSSL实现不支持OpenSSLConf + +openssllibrary.ciphersFailure=获取密码列表失败 +openssllibrary.currentFIPSMode=当前FIPS模式:[{0}] +openssllibrary.engineError=创建引擎错误 +openssllibrary.enterAlreadyInFIPSMode=AprLifecycleListener配置为强制进入FIPS模式,但库已处于FIPS模式[{0}] +openssllibrary.initializeFIPSFailed=进入FIPS模式失败 +openssllibrary.initializeFIPSSuccess=成功进入FIPS模式 +openssllibrary.initializedOpenSSL=成功使用FFM [{0}]初始化OpenSSL +openssllibrary.initializingFIPS=初始化FIPS模式... +openssllibrary.skipFIPSInitialization=已经处于FIPS模式;跳过FIPS初始化。 +openssllibrary.tooLateForFIPSMode=无法setFIPSMode: SSL已经初始化 +openssllibrary.tooLateForSSLEngine=无法setSSLEngine: SSL已经初始化 +openssllibrary.tooLateForSSLRandomSeed=无法setSSLRandomSeed: SSL已经初始化 diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -59,14 +59,17 @@ import org.apache.tomcat.util.net.SSLHostConfig.CertificateVerification; import org.apache.tomcat.util.net.SSLHostConfigCertificate; import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type; +import org.apache.tomcat.util.net.SSLUtilBase; import org.apache.tomcat.util.net.openssl.OpenSSLConf; import org.apache.tomcat.util.net.openssl.OpenSSLConfCmd; import org.apache.tomcat.util.net.openssl.OpenSSLStatus; import org.apache.tomcat.util.net.openssl.OpenSSLUtil; +import org.apache.tomcat.util.net.openssl.ciphers.Group; import org.apache.tomcat.util.openssl.SSL_CTX_set_alpn_select_cb$cb; import org.apache.tomcat.util.openssl.SSL_CTX_set_cert_verify_callback$cb; import org.apache.tomcat.util.openssl.SSL_CTX_set_tmp_dh_callback$dh; import org.apache.tomcat.util.openssl.SSL_CTX_set_verify$callback; +import org.apache.tomcat.util.openssl.openssl_h; import org.apache.tomcat.util.openssl.openssl_h_Compatibility; import org.apache.tomcat.util.openssl.pem_password_cb; import org.apache.tomcat.util.res.StringManager; @@ -80,31 +83,24 @@ private static final String defaultProtocol = "TLS"; - private static final int SSL_AIDX_RSA = 0; - private static final int SSL_AIDX_DSA = 1; - private static final int SSL_AIDX_ECC = 3; - private static final int SSL_AIDX_MAX = 4; - - public static final int SSL_PROTOCOL_NONE = 0; - public static final int SSL_PROTOCOL_SSLV2 = (1<<0); - public static final int SSL_PROTOCOL_SSLV3 = (1<<1); - public static final int SSL_PROTOCOL_TLSV1 = (1<<2); - public static final int SSL_PROTOCOL_TLSV1_1 = (1<<3); - public static final int SSL_PROTOCOL_TLSV1_2 = (1<<4); - public static final int SSL_PROTOCOL_TLSV1_3 = (1<<5); - public static final int SSL_PROTOCOL_ALL = (SSL_PROTOCOL_TLSV1 | SSL_PROTOCOL_TLSV1_1 | SSL_PROTOCOL_TLSV1_2 | - SSL_PROTOCOL_TLSV1_3); + public static final int SSL_PROTOCOL_NONE = 0; + public static final int SSL_PROTOCOL_SSLV2 = 1; + public static final int SSL_PROTOCOL_SSLV3 = (1 << 1); + public static final int SSL_PROTOCOL_TLSV1 = (1 << 2); + public static final int SSL_PROTOCOL_TLSV1_1 = (1 << 3); + public static final int SSL_PROTOCOL_TLSV1_2 = (1 << 4); + public static final int SSL_PROTOCOL_TLSV1_3 = (1 << 5); + public static final int SSL_PROTOCOL_ALL = + (SSL_PROTOCOL_TLSV1 | SSL_PROTOCOL_TLSV1_1 | SSL_PROTOCOL_TLSV1_2 | SSL_PROTOCOL_TLSV1_3); static final int OPTIONAL_NO_CA = 3; private static final String BEGIN_KEY = "-----BEGIN PRIVATE KEY-----\n"; private static final Object END_KEY = "\n-----END PRIVATE KEY-----"; - private static final byte[] HTTP_11_PROTOCOL = - new byte[] { 'h', 't', 't', 'p', '/', '1', '.', '1' }; + private static final byte[] HTTP_11_PROTOCOL = new byte[] { 'h', 't', 't', 'p', '/', '1', '.', '1' }; - private static final byte[] DEFAULT_SESSION_ID_CONTEXT = - new byte[] { 'd', 'e', 'f', 'a', 'u', 'l', 't' }; + private static final byte[] DEFAULT_SESSION_ID_CONTEXT = new byte[] { 'd', 'e', 'f', 'a', 'u', 'l', 't' }; static final CertificateFactory X509_CERT_FACTORY; static { @@ -127,6 +123,10 @@ private boolean initialized = false; private boolean noOcspCheck = false; + private boolean ocspSoftFail = true; + // 15s default - same as JSSE + private int ocspTimeout = 15000; + private int ocspVerifyFlags = 0; private X509TrustManager x509TrustManager; private final ContextState state; @@ -148,8 +148,7 @@ return ciphers.toArray(new String[0]); } - public OpenSSLContext(SSLHostConfigCertificate certificate, List negotiableProtocols) - throws SSLException { + public OpenSSLContext(SSLHostConfigCertificate certificate, List negotiableProtocols) throws SSLException { // Check that OpenSSL was initialized if (!OpenSSLStatus.isInitialized()) { @@ -180,10 +179,8 @@ if (MemorySegment.NULL.equals(confCtx)) { throw new SSLException(sm.getString("openssl.errMakeConf", OpenSSLLibrary.getLastError())); } - SSL_CONF_CTX_set_flags(confCtx, SSL_CONF_FLAG_FILE() | - SSL_CONF_FLAG_SERVER() | - SSL_CONF_FLAG_CERTIFICATE() | - SSL_CONF_FLAG_SHOW_ERRORS()); + SSL_CONF_CTX_set_flags(confCtx, SSL_CONF_FLAG_FILE() | SSL_CONF_FLAG_SERVER() | + SSL_CONF_FLAG_CERTIFICATE() | SSL_CONF_FLAG_SHOW_ERRORS()); } else { log.error(sm.getString("opensslconf.unsupported")); } @@ -266,10 +263,32 @@ SSL_CTX_set_timeout(sslCtx, 14400); // Set int pem_password_cb(char *buf, int size, int rwflag, void *u) callback - SSL_CTX_set_default_passwd_cb(sslCtx, - pem_password_cb.allocate(new PasswordCallback(null), contextArena)); + SSL_CTX_set_default_passwd_cb(sslCtx, pem_password_cb.allocate(new PasswordCallback(null), contextArena)); - if (negotiableProtocols != null && negotiableProtocols.size() > 0) { + // Set server groups + // Note: It is also possible to override setSSLParameters in OpenSSLEngine to set the final + // list of groups per connection, but this is less efficient than setting the configured + // group list on the SSL context and letting OpenSSL figure it out. + if (sslHostConfig.getGroupList() != null) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Group group : sslHostConfig.getGroupList()) { + if (first) { + first = false; + } else { + sb.append(':'); + } + sb.append(group.toString()); + } + try (var localArena = Arena.ofConfined()) { + if (SSL_CTX_set1_groups_list(sslCtx, localArena.allocateFrom(sb.toString())) <= 0) { + logLastError("openssl.errorSettingGroups"); + // Consider this is not fatal + } + } + } + + if (negotiableProtocols != null && !negotiableProtocols.isEmpty()) { alpn = true; negotiableProtocolsBytes = new ArrayList<>(negotiableProtocols.size() + 1); for (String negotiableProtocol : negotiableProtocols) { @@ -281,21 +300,18 @@ } success = true; - } catch(Exception e) { + } catch (Exception e) { throw new SSLException(sm.getString("openssl.errorSSLCtxInit"), e); } finally { this.negotiableProtocols = negotiableProtocolsBytes; state = new ContextState(sslCtx, confCtx); /* - * When an SSLHostConfig is replaced at runtime, it is not possible to - * call destroy() on the associated OpenSSLContext since it is likely - * that there will be in-progress connections using the OpenSSLContext. - * A reference chain has been deliberately established (see - * OpenSSLSessionContext) to ensure that the OpenSSLContext remains - * ineligible for GC while those connections are alive. Once those - * connections complete, the OpenSSLContext will become eligible for GC - * and the memory session will ensure that the associated native - * resources are cleaned up. + * When an SSLHostConfig is replaced at runtime, it is not possible to call destroy() on the associated + * OpenSSLContext since it is likely that there will be in-progress connections using the OpenSSLContext. A + * reference chain has been deliberately established (see OpenSSLSessionContext) to ensure that the + * OpenSSLContext remains ineligible for GC while those connections are alive. Once those connections + * complete, the OpenSSLContext will become eligible for GC and the memory session will ensure that the + * associated native resources are cleaned up. */ cleanable = cleaner.register(this, state); @@ -322,12 +338,12 @@ } - private boolean checkConf(OpenSSLConf conf) throws Exception { + private boolean checkConf(OpenSSLConf conf) { boolean result = true; OpenSSLConfCmd cmd; String name; String value; - int rc; + boolean ok; for (OpenSSLConfCmd command : conf.getCommands()) { cmd = command; name = cmd.getName(); @@ -341,26 +357,32 @@ log.trace(sm.getString("opensslconf.checkCommand", name, value)); } try (var localArena = Arena.ofConfined()) { - if (name.equals("NO_OCSP_CHECK")) { - rc = 1; + if (name.equals(OpenSSLConfCmd.NO_OCSP_CHECK)) { + ok = true; + } else if (name.equals(OpenSSLConfCmd.OCSP_SOFT_FAIL)) { + ok = true; + } else if (name.equals(OpenSSLConfCmd.OCSP_TIMEOUT)) { + ok = true; + } else if (name.equals(OpenSSLConfCmd.OCSP_VERIFY_FLAGS)) { + ok = true; } else { int code = SSL_CONF_cmd_value_type(state.confCtx, localArena.allocateFrom(name)); - rc = 1; + ok = true; String errorMessage = OpenSSLLibrary.getLastError(); if (errorMessage != null) { log.error(sm.getString("opensslconf.checkFailed", errorMessage)); - rc = 0; + ok = false; } if (code == SSL_CONF_TYPE_UNKNOWN()) { log.error(sm.getString("opensslconf.typeUnknown", name)); - rc = 0; + ok = false; } if (code == SSL_CONF_TYPE_FILE()) { // Check file File file = new File(value); if (!file.isFile() && !file.canRead()) { log.error(sm.getString("opensslconf.badFile", name, value)); - rc = 0; + ok = false; } } if (code == SSL_CONF_TYPE_DIR()) { @@ -368,21 +390,19 @@ File file = new File(value); if (!file.isDirectory()) { log.error(sm.getString("opensslconf.badDirectory", name, value)); - rc = 0; + ok = false; } } } } catch (Exception e) { - log.error(sm.getString("opensslconf.checkFailed", e.getLocalizedMessage())); + log.error(sm.getString("opensslconf.checkFailed", e.getLocalizedMessage()), e); return false; } - if (rc <= 0) { - log.error(sm.getString("opensslconf.failedCommand", name, value, - Integer.toString(rc))); + if (!ok) { + log.error(sm.getString("opensslconf.failedCommand", name, value, Boolean.FALSE)); result = false; } else if (log.isTraceEnabled()) { - log.trace(sm.getString("opensslconf.resultCommand", name, value, - Integer.toString(rc))); + log.trace(sm.getString("opensslconf.resultCommand", name, value, Boolean.TRUE)); } } if (!result) { @@ -392,7 +412,7 @@ } - private boolean applyConf(OpenSSLConf conf) throws Exception { + private boolean applyConf(OpenSSLConf conf) { boolean result = true; SSL_CONF_CTX_set_ssl_ctx(state.confCtx, state.sslCtx); OpenSSLConfCmd cmd; @@ -412,12 +432,20 @@ log.trace(sm.getString("opensslconf.applyCommand", name, value)); } try (var localArena = Arena.ofConfined()) { - if (name.equals("NO_OCSP_CHECK")) { - noOcspCheck = Boolean.parseBoolean(value); + if (name.equals(OpenSSLConfCmd.NO_OCSP_CHECK)) { + // Ignore - Tomcat internal - set directly + rc = 1; + } else if (name.equals(OpenSSLConfCmd.OCSP_SOFT_FAIL)) { + // Ignore - Tomcat internal - set directly + rc = 1; + } else if (name.equals(OpenSSLConfCmd.OCSP_TIMEOUT)) { + // Ignore - Tomcat internal - set directly + rc = 1; + } else if (name.equals(OpenSSLConfCmd.OCSP_VERIFY_FLAGS)) { + // Ignore - Tomcat internal - set directly rc = 1; } else { - rc = SSL_CONF_cmd(state.confCtx, localArena.allocateFrom(name), - localArena.allocateFrom(value)); + rc = SSL_CONF_cmd(state.confCtx, localArena.allocateFrom(name), localArena.allocateFrom(value)); String errorMessage = OpenSSLLibrary.getLastError(); if (rc <= 0 || errorMessage != null) { log.error(sm.getString("opensslconf.commandError", name, value, errorMessage)); @@ -425,16 +453,14 @@ } } } catch (Exception e) { - log.error(sm.getString("opensslconf.applyFailed")); + log.error(sm.getString("opensslconf.applyFailed"), e); return false; } if (rc <= 0) { - log.error(sm.getString("opensslconf.failedCommand", name, value, - Integer.toString(rc))); + log.error(sm.getString("opensslconf.failedCommand", name, value, Integer.toString(rc))); result = false; } else if (log.isTraceEnabled()) { - log.trace(sm.getString("opensslconf.resultCommand", name, value, - Integer.toString(rc))); + log.trace(sm.getString("opensslconf.resultCommand", name, value, Integer.toString(rc))); } } // rc = SSLConf.finish(confCtx); @@ -450,13 +476,12 @@ } /** - * Setup the SSL_CTX. + * Set up the SSL_CTX. + * + * @param kms Must contain a KeyManager of the type {@code OpenSSLKeyManager} + * @param tms Must contain a TrustManager of the type {@code X509TrustManager} + * @param sr Is not used for this implementation. * - * @param kms Must contain a KeyManager of the type - * {@code OpenSSLKeyManager} - * @param tms Must contain a TrustManager of the type - * {@code X509TrustManager} - * @param sr Is not used for this implementation. * @throws KeyManagementException if an error occurs */ @Override @@ -465,7 +490,7 @@ log.warn(sm.getString("openssl.doubleInit")); return; } - boolean success = true; + boolean success; Exception cause = null; try (var localArena = Arena.ofConfined()) { if (sslHostConfig.getInsecureRenegotiation()) { @@ -496,17 +521,35 @@ openssl_h_Compatibility.SSL_CTX_clear_options(state.sslCtx, SSL_OP_NO_TICKET()); } + boolean ciphersSet = false; + String tls12Warning = null; + String tls13Warning = null; // List the ciphers that the client is permitted to negotiate if (minTlsVersion <= TLS1_2_VERSION()) { - if (SSL_CTX_set_cipher_list(state.sslCtx, - localArena.allocateFrom(sslHostConfig.getCiphers())) <= 0) { - log.warn(sm.getString("engine.failedCipherList", sslHostConfig.getCiphers())); + if (SSL_CTX_set_cipher_list(state.sslCtx, localArena.allocateFrom(sslHostConfig.getCiphers())) <= 0) { + tls12Warning = sm.getString("engine.failedCipherList", sslHostConfig.getCiphers()); + } else { + ciphersSet = true; } } - if (maxTlsVersion >= TLS1_3_VERSION() && (sslHostConfig.getCiphers() != SSLHostConfig.DEFAULT_TLS_CIPHERS)) { - if (SSL_CTX_set_ciphersuites(state.sslCtx, - localArena.allocateFrom(sslHostConfig.getCiphers())) <= 0) { - log.warn(sm.getString("engine.failedCipherSuite", sslHostConfig.getCiphers())); + if (maxTlsVersion >= TLS1_3_VERSION()) { + try { + if (SSL_CTX_set_ciphersuites(state.sslCtx, localArena.allocateFrom(sslHostConfig.getCipherSuites())) <= 0) { + tls13Warning = sm.getString("engine.failedCipherSuite", sslHostConfig.getCipherSuites()); + } else { + ciphersSet = true; + } + } catch (NoClassDefFoundError | UnsatisfiedLinkError e) { + // Ignore unavailable TLS 1.3 call, which might be compiled out sometimes on LibreSSL + tls13Warning = sm.getString("engine.failedCipherSuite", sslHostConfig.getCipherSuites()); + } + } + if (!ciphersSet) { + if (tls12Warning != null) { + log.warn(tls12Warning); + } + if (tls13Warning != null) { + log.warn(tls13Warning); } } @@ -518,32 +561,30 @@ success = addCertificate(certificate, localArena); // Client certificate verification - int value = 0; - switch (sslHostConfig.getCertificateVerification()) { - case NONE: - value = SSL_VERIFY_NONE(); - break; - case OPTIONAL: - value = SSL_VERIFY_PEER(); - break; - case OPTIONAL_NO_CA: - value = OPTIONAL_NO_CA; - break; - case REQUIRED: - value = SSL_VERIFY_FAIL_IF_NO_PEER_CERT(); - break; - } + int value = switch (sslHostConfig.getCertificateVerification()) { + case NONE -> SSL_VERIFY_NONE(); + case OPTIONAL -> SSL_VERIFY_PEER(); + case OPTIONAL_NO_CA -> OPTIONAL_NO_CA; + case REQUIRED -> SSL_VERIFY_FAIL_IF_NO_PEER_CERT(); + }; + + if (value == OPTIONAL_NO_CA || !sslHostConfig.getOcspEnabled()) { + noOcspCheck = true; + } + ocspSoftFail = sslHostConfig.getOcspSoftFail(); + ocspTimeout = sslHostConfig.getOcspTimeout(); + ocspVerifyFlags = sslHostConfig.getOcspVerifyFlags(); // Set int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) callback SSL_CTX_set_verify(state.sslCtx, value, SSL_CTX_set_verify$callback.allocate(new OpenSSLEngine.VerifyCallback(), contextArena)); - // Trust and certificate verification + // Trust and certificate verification (optional - may not be configured) if (tms != null) { // Client certificate verification based on custom trust managers x509TrustManager = chooseTrustManager(tms); - SSL_CTX_set_cert_verify_callback(state.sslCtx, - SSL_CTX_set_cert_verify_callback$cb.allocate(new CertVerifyCallback(x509TrustManager), contextArena), state.sslCtx); + SSL_CTX_set_cert_verify_callback(state.sslCtx, SSL_CTX_set_cert_verify_callback$cb + .allocate(new CertVerifyCallback(x509TrustManager), contextArena), state.sslCtx); // Pass along the DER encoded certificates of the accepted client // certificate issuers, so that their subjects can be presented @@ -561,15 +602,17 @@ log.debug(sm.getString("openssl.addedClientCaCert", caCert.toString())); } } - } else { + } else if (sslHostConfig.getCaCertificateFile() != null || sslHostConfig.getCaCertificatePath() != null) { // Client certificate verification based on trusted CA files and dirs - MemorySegment caCertificateFileNative = sslHostConfig.getCaCertificateFile() != null - ? localArena.allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile())) : MemorySegment.NULL; - MemorySegment caCertificatePathNative = sslHostConfig.getCaCertificatePath() != null - ? localArena.allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath())) : MemorySegment.NULL; - if ((sslHostConfig.getCaCertificateFile() != null || sslHostConfig.getCaCertificatePath() != null) - && SSL_CTX_load_verify_locations(state.sslCtx, - caCertificateFileNative, caCertificatePathNative) <= 0) { + MemorySegment caCertificateFileNative = sslHostConfig.getCaCertificateFile() != null ? + localArena + .allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile())) : + MemorySegment.NULL; + MemorySegment caCertificatePathNative = sslHostConfig.getCaCertificatePath() != null ? + localArena + .allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath())) : + MemorySegment.NULL; + if (SSL_CTX_load_verify_locations(state.sslCtx, caCertificateFileNative, caCertificatePathNative) <= 0) { logLastError("openssl.errorConfiguringLocations"); } else { var caCerts = SSL_CTX_get_client_CA_list(state.sslCtx); @@ -580,8 +623,8 @@ } } else { // OpenSSL might crash here when passing null on some platforms - if (MemorySegment.NULL.equals(caCertificateFileNative) - || (SSL_add_file_cert_subjects_to_stack(caCerts, caCertificateFileNative) <= 0)) { + if (MemorySegment.NULL.equals(caCertificateFileNative) || + (SSL_add_file_cert_subjects_to_stack(caCerts, caCertificateFileNative) <= 0)) { caCerts = MemorySegment.NULL; } } @@ -591,9 +634,9 @@ } } - if (negotiableProtocols != null && negotiableProtocols.size() > 0) { - SSL_CTX_set_alpn_select_cb(state.sslCtx, - SSL_CTX_set_alpn_select_cb$cb.allocate(new ALPNSelectCallback(negotiableProtocols), contextArena), state.sslCtx); + if (negotiableProtocols != null && !negotiableProtocols.isEmpty()) { + SSL_CTX_set_alpn_select_cb(state.sslCtx, SSL_CTX_set_alpn_select_cb$cb + .allocate(new ALPNSelectCallback(negotiableProtocols), contextArena), state.sslCtx); } // Log any non fatal init errors @@ -651,8 +694,7 @@ if ((opts & SSL_OP_NO_SSLv3()) == 0) { enabled.add(Constants.SSL_PROTO_SSLv3); } - sslHostConfig.setEnabledProtocols( - enabled.toArray(new String[0])); + sslHostConfig.setEnabledProtocols(enabled.toArray(new String[0])); // Reconfigure the enabled ciphers sslHostConfig.setEnabledCiphers(getCiphers(state.sslCtx)); } @@ -686,16 +728,12 @@ var pkey = SSL_get_privatekey(ssl); int type = (MemorySegment.NULL.equals(pkey)) ? EVP_PKEY_NONE() : EVP_PKEY_base_id(pkey); /* - * OpenSSL will call us with either keylen == 512 or keylen == 1024 - * (see the definition of SSL_EXPORT_PKEYLENGTH in ssl_locl.h). - * Adjust the DH parameter length according to the size of the - * RSA/DSA private key used for the current connection, and always - * use at least 1024-bit parameters. - * Note: This may cause interoperability issues with implementations - * which limit their DH support to 1024 bit - e.g. Java 7 and earlier. - * In this case, SSLCertificateFile can be used to specify fixed - * 1024-bit DH parameters (with the effect that OpenSSL skips this - * callback). + * OpenSSL will call us with either keylen == 512 or keylen == 1024 (see the definition of + * SSL_EXPORT_PKEYLENGTH in ssl_locl.h). Adjust the DH parameter length according to the size of the RSA/DSA + * private key used for the current connection, and always use at least 1024-bit parameters. Note: This may + * cause interoperability issues with implementations which limit their DH support to 1024 bit - e.g. Java 7 + * and earlier. In this case, SSLCertificateFile can be used to specify fixed 1024-bit DH parameters (with + * the effect that OpenSSL skips this callback). */ int keylen = 0; if (type == EVP_PKEY_RSA() || type == EVP_PKEY_DSA()) { @@ -711,15 +749,17 @@ } // int SSL_callback_alpn_select_proto(SSL* ssl, const unsigned char **out, unsigned char *outlen, - // const unsigned char *in, unsigned int inlen, void *arg) + // const unsigned char *in, unsigned int inlen, void *arg) private static class ALPNSelectCallback implements SSL_CTX_set_alpn_select_cb$cb.Function { private final List negotiableProtocols; + ALPNSelectCallback(List negotiableProtocols) { this.negotiableProtocols = negotiableProtocols; } + @Override - public int apply(MemorySegment ssl, MemorySegment out, - MemorySegment outlen, MemorySegment in, int inlen, MemorySegment arg) { + public int apply(MemorySegment ssl, MemorySegment out, MemorySegment outlen, MemorySegment in, int inlen, + MemorySegment arg) { try (var localArena = Arena.ofConfined()) { MemorySegment inSeg = in.reinterpret(inlen, localArena, null); byte[] advertisedBytes = inSeg.toArray(ValueLayout.JAVA_BYTE); @@ -730,9 +770,11 @@ if (advertisedBytes[i + j] == negotiableProtocolBytes[j]) { if (j == negotiableProtocolBytes.length - 1) { // Match - MemorySegment outSeg = out.reinterpret(ValueLayout.ADDRESS.byteSize(), localArena, null); + MemorySegment outSeg = + out.reinterpret(ValueLayout.ADDRESS.byteSize(), localArena, null); outSeg.set(ValueLayout.ADDRESS, 0, inSeg.asSlice(i)); - MemorySegment outlenSeg = outlen.reinterpret(ValueLayout.JAVA_BYTE.byteSize(), localArena, null); + MemorySegment outlenSeg = + outlen.reinterpret(ValueLayout.JAVA_BYTE.byteSize(), localArena, null); outlenSeg.set(ValueLayout.JAVA_BYTE, 0, (byte) negotiableProtocolBytes.length); return SSL_TLSEXT_ERR_OK(); } @@ -751,11 +793,13 @@ private static class CertVerifyCallback implements SSL_CTX_set_cert_verify_callback$cb.Function { private final X509TrustManager x509TrustManager; + CertVerifyCallback(X509TrustManager x509TrustManager) { this.x509TrustManager = x509TrustManager; } + @Override - public int apply(MemorySegment /*X509_STORE_CTX*/ x509_ctx, MemorySegment param) { + public int apply(MemorySegment /* X509_STORE_CTX */ x509_ctx, MemorySegment param) { if (log.isTraceEnabled()) { log.trace("Certificate verification"); } @@ -763,12 +807,12 @@ return 0; } MemorySegment ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - MemorySegment /*STACK_OF(X509)*/ sk = X509_STORE_CTX_get0_untrusted(x509_ctx); + MemorySegment /* STACK_OF(X509) */ sk = X509_STORE_CTX_get0_untrusted(x509_ctx); int len = openssl_h_Compatibility.OPENSSL_sk_num(sk); byte[][] certificateChain = new byte[len][]; try (var localArena = Arena.ofConfined()) { for (int i = 0; i < len; i++) { - MemorySegment/*(X509*)*/ x509 = openssl_h_Compatibility.OPENSSL_sk_value(sk, i); + MemorySegment/* (X509*) */ x509 = openssl_h_Compatibility.OPENSSL_sk_value(sk, i); MemorySegment bufPointer = localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL); int length = i2d_X509(x509, bufPointer); if (length < 0) { @@ -780,30 +824,32 @@ OPENSSL_free(buf); } MemorySegment cipher = SSL_get_current_cipher(ssl); - String authMethod = (MemorySegment.NULL.equals(cipher)) ? "UNKNOWN" - : getCipherAuthenticationMethod(SSL_CIPHER_get_auth_nid(cipher), SSL_CIPHER_get_kx_nid(cipher)); + String authMethod = (MemorySegment.NULL.equals(cipher)) ? "UNKNOWN" : + getCipherAuthenticationMethod(SSL_CIPHER_get_auth_nid(cipher), SSL_CIPHER_get_kx_nid(cipher)); X509Certificate[] peerCerts = certificates(certificateChain); try { x509TrustManager.checkClientTrusted(peerCerts, authMethod); return 1; } catch (Exception e) { - log.debug(sm.getString("openssl.certificateVerificationFailed"), e); + if (log.isDebugEnabled()) { + log.debug(sm.getString("openssl.certificateVerificationFailed"), e); + } } } return 0; } } - private static final int NID_kx_rsa = 1037/*NID_kx_rsa()*/; - //private static final int NID_kx_dhe = NID_kx_dhe(); - //private static final int NID_kx_ecdhe = NID_kx_ecdhe(); - - //private static final int NID_auth_rsa = NID_auth_rsa(); - //private static final int NID_auth_dss = NID_auth_dss(); - //private static final int NID_auth_null = NID_auth_null(); - //private static final int NID_auth_ecdsa = NID_auth_ecdsa(); + private static final int NID_kx_rsa = 1037/* NID_kx_rsa() */; + // private static final int NID_kx_dhe = NID_kx_dhe(); + // private static final int NID_kx_ecdhe = NID_kx_ecdhe(); + + // private static final int NID_auth_rsa = NID_auth_rsa(); + // private static final int NID_auth_dss = NID_auth_dss(); + // private static final int NID_auth_null = NID_auth_null(); + // private static final int NID_auth_ecdsa = NID_auth_ecdsa(); - //private static final int SSL_kRSA = 1; + // private static final int SSL_kRSA = 1; private static final int SSL_kDHr = 2; private static final int SSL_kDHd = 4; private static final int SSL_kEDH = 8; @@ -813,21 +859,21 @@ private static final int SSL_kECDHe = 40; private static final int SSL_kEECDH = 80; private static final int SSL_kECDHE = SSL_kEECDH; - //private static final int SSL_kPSK = 100; - //private static final int SSL_kGOST = 200; - //private static final int SSL_kSRP = 400; + // private static final int SSL_kPSK = 100; + // private static final int SSL_kGOST = 200; + // private static final int SSL_kSRP = 400; private static final int SSL_aRSA = 1; private static final int SSL_aDSS = 2; private static final int SSL_aNULL = 4; - //private static final int SSL_aDH = 8; - //private static final int SSL_aECDH = 10; - //private static final int SSL_aKRB5 = 20; + // private static final int SSL_aDH = 8; + // private static final int SSL_aECDH = 10; + // private static final int SSL_aKRB5 = 20; private static final int SSL_aECDSA = 40; - //private static final int SSL_aPSK = 80; - //private static final int SSL_aGOST94 = 100; - //private static final int SSL_aGOST01 = 200; - //private static final int SSL_aSRP = 400; + // private static final int SSL_aPSK = 80; + // private static final int SSL_aGOST94 = 100; + // private static final int SSL_aGOST01 = 200; + // private static final int SSL_aSRP = 400; private static final String SSL_TXT_RSA = "RSA"; private static final String SSL_TXT_DH = "DH"; @@ -837,57 +883,42 @@ private static final String SSL_TXT_ECDSA = "ECDSA"; private static String getCipherAuthenticationMethod(int auth, int kx) { - switch (kx) { - case NID_kx_rsa: - return SSL_TXT_RSA; - case SSL_kDHr: - return SSL_TXT_DH + "_" + SSL_TXT_RSA; - case SSL_kDHd: - return SSL_TXT_DH + "_" + SSL_TXT_DSS; - case SSL_kDHE: - switch (auth) { - case SSL_aDSS: - return "DHE_" + SSL_TXT_DSS; - case SSL_aRSA: - return "DHE_" + SSL_TXT_RSA; - case SSL_aNULL: - return SSL_TXT_DH + "_anon"; - default: - return "UNKNOWN"; - } - case SSL_kKRB5: - return SSL_TXT_KRB5; - case SSL_kECDHr: - return SSL_TXT_ECDH + "_" + SSL_TXT_RSA; - case SSL_kECDHe: - return SSL_TXT_ECDH + "_" + SSL_TXT_ECDSA; - case SSL_kECDHE: - switch (auth) { - case SSL_aECDSA: - return "ECDHE_" + SSL_TXT_ECDSA; - case SSL_aRSA: - return "ECDHE_" + SSL_TXT_RSA; - case SSL_aNULL: - return SSL_TXT_ECDH + "_anon"; - default: - return "UNKNOWN"; - } - default: - return "UNKNOWN"; - } + return switch (kx) { + case NID_kx_rsa -> SSL_TXT_RSA; + case SSL_kDHr -> SSL_TXT_DH + "_" + SSL_TXT_RSA; + case SSL_kDHd -> SSL_TXT_DH + "_" + SSL_TXT_DSS; + case SSL_kDHE -> switch (auth) { + case SSL_aDSS -> "DHE_" + SSL_TXT_DSS; + case SSL_aRSA -> "DHE_" + SSL_TXT_RSA; + case SSL_aNULL -> SSL_TXT_DH + "_anon"; + default -> "UNKNOWN"; + }; + case SSL_kKRB5 -> SSL_TXT_KRB5; + case SSL_kECDHr -> SSL_TXT_ECDH + "_" + SSL_TXT_RSA; + case SSL_kECDHe -> SSL_TXT_ECDH + "_" + SSL_TXT_ECDSA; + case SSL_kECDHE -> switch (auth) { + case SSL_aECDSA -> "ECDHE_" + SSL_TXT_ECDSA; + case SSL_aRSA -> "ECDHE_" + SSL_TXT_RSA; + case SSL_aNULL -> SSL_TXT_ECDH + "_anon"; + default -> "UNKNOWN"; + }; + default -> "UNKNOWN"; + }; } private static class PasswordCallback implements pem_password_cb.Function { private final String callbackPassword; + PasswordCallback(String callbackPassword) { this.callbackPassword = callbackPassword; } + @Override public int apply(MemorySegment /* char **/ buf, int bufsiz, int verify, MemorySegment /* void **/ cb) { if (log.isTraceEnabled()) { log.trace("Return password for certificate"); } - if (callbackPassword != null && callbackPassword.length() > 0) { + if (callbackPassword != null && !callbackPassword.isEmpty()) { try (var localArena = Arena.ofConfined()) { MemorySegment callbackPasswordNative = localArena.allocateFrom(callbackPassword); if (callbackPasswordNative.byteSize() > bufsiz) { @@ -905,12 +936,11 @@ } - private boolean addCertificate(SSLHostConfigCertificate certificate, Arena localArena) throws Exception { - int index = getCertificateIndex(certificate); + public boolean addCertificate(SSLHostConfigCertificate certificate, Arena localArena) throws Exception { // Load Server key and certificate if (certificate.getCertificateFile() != null) { // Pick right key password - String keyPassToUse = null; + String keyPassToUse; String keyPass = certificate.getCertificateKeyPassword(); if (keyPass == null) { keyPass = certificate.getCertificateKeystorePassword(); @@ -920,36 +950,36 @@ keyPassFile = certificate.getCertificateKeystorePasswordFile(); } if (keyPassFile != null) { - try (BufferedReader reader = - new BufferedReader(new InputStreamReader( - ConfigFileLoader.getSource().getResource(keyPassFile).getInputStream(), + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(ConfigFileLoader.getSource().getResource(keyPassFile).getInputStream(), StandardCharsets.UTF_8))) { keyPassToUse = reader.readLine(); - } catch (IOException e) { - log.error(sm.getString("openssl.errorLoadingPassword", keyPassFile), e); + } catch (IOException ioe) { + log.error(sm.getString("openssl.errorLoadingPassword", keyPassFile), ioe); return false; } } else { keyPassToUse = keyPass; } // Set certificate - byte[] certificateFileBytes = null; + byte[] certificateFileBytes; try (Resource resource = ConfigFileLoader.getSource().getResource(certificate.getCertificateFile())) { certificateFileBytes = resource.getInputStream().readAllBytes(); - } catch (IOException e) { - log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateFile()), e); + } catch (IOException ioe) { + log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateFile()), ioe); return false; } - MemorySegment certificateFileBytesNative = localArena.allocateFrom(ValueLayout.JAVA_BYTE, certificateFileBytes); + MemorySegment certificateFileBytesNative = + localArena.allocateFrom(ValueLayout.JAVA_BYTE, certificateFileBytes); MemorySegment certificateBIO = BIO_new(BIO_s_mem()); try { if (BIO_write(certificateBIO, certificateFileBytesNative, certificateFileBytes.length) <= 0) { - log.error(sm.getString("openssl.errorLoadingCertificateWithError", - certificate.getCertificateFile(), OpenSSLLibrary.getLastError())); + log.error(sm.getString("openssl.errorLoadingCertificateWithError", certificate.getCertificateFile(), + OpenSSLLibrary.getLastError())); return false; } - MemorySegment cert = MemorySegment.NULL; - MemorySegment key = MemorySegment.NULL; + MemorySegment cert; + MemorySegment key; if (certificate.getCertificateFile().endsWith(".pkcs12")) { // Load pkcs12 MemorySegment p12 = d2i_PKCS12_bio(certificateBIO, MemorySegment.NULL); @@ -960,7 +990,7 @@ } MemorySegment passwordAddress = MemorySegment.NULL; int passwordLength = 0; - if (keyPassToUse != null && keyPassToUse.length() > 0) { + if (keyPassToUse != null && !keyPassToUse.isEmpty()) { passwordAddress = localArena.allocateFrom(keyPassToUse); passwordLength = (int) (passwordAddress.byteSize() - 1); } @@ -983,22 +1013,24 @@ cert = certPointer.get(ValueLayout.ADDRESS, 0); key = keyPointer.get(ValueLayout.ADDRESS, 0); } else { - String certificateKeyFileName = (certificate.getCertificateKeyFile() == null) - ? certificate.getCertificateFile() : certificate.getCertificateKeyFile(); + String certificateKeyFileName = + (certificate.getCertificateKeyFile() == null) ? certificate.getCertificateFile() : + certificate.getCertificateKeyFile(); // Load key - byte[] certificateKeyFileBytes = null; + byte[] certificateKeyFileBytes; try (Resource resource = ConfigFileLoader.getSource().getResource(certificateKeyFileName)) { certificateKeyFileBytes = resource.getInputStream().readAllBytes(); - } catch (IOException e) { - log.error(sm.getString("openssl.errorLoadingCertificate", certificateKeyFileName), e); + } catch (IOException ioe) { + log.error(sm.getString("openssl.errorLoadingCertificate", certificateKeyFileName), ioe); return false; } - MemorySegment certificateKeyFileBytesNative = localArena.allocateFrom(ValueLayout.JAVA_BYTE, certificateKeyFileBytes); + MemorySegment certificateKeyFileBytesNative = + localArena.allocateFrom(ValueLayout.JAVA_BYTE, certificateKeyFileBytes); MemorySegment keyBIO = BIO_new(BIO_s_mem()); try { if (BIO_write(keyBIO, certificateKeyFileBytesNative, certificateKeyFileBytes.length) <= 0) { - log.error(sm.getString("openssl.errorLoadingCertificateWithError", - certificateKeyFileName, OpenSSLLibrary.getLastError())); + log.error(sm.getString("openssl.errorLoadingCertificateWithError", certificateKeyFileName, + OpenSSLLibrary.getLastError())); return false; } key = MemorySegment.NULL; @@ -1023,8 +1055,8 @@ } } if (MemorySegment.NULL.equals(key)) { - log.error(sm.getString("openssl.errorLoadingCertificateWithError", - certificateKeyFileName, OpenSSLLibrary.getLastError())); + log.error(sm.getString("openssl.errorLoadingCertificateWithError", certificateKeyFileName, + OpenSSLLibrary.getLastError())); return false; } // Load certificate @@ -1056,12 +1088,13 @@ logLastError("openssl.errorPrivateKeyCheck"); return false; } - // Try to read DH parameters from the (first) SSLCertificateFile - if (index == SSL_AIDX_RSA) { + // Try to read DH parameters from the SSLCertificateFile + if (certificate.getType() == Type.RSA) { BIO_reset(certificateBIO); if (!openssl_h_Compatibility.BORINGSSL) { if (!openssl_h_Compatibility.OPENSSL3) { - var dh = PEM_read_bio_DHparams(certificateBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); + var dh = PEM_read_bio_DHparams(certificateBIO, MemorySegment.NULL, MemorySegment.NULL, + MemorySegment.NULL); if (!MemorySegment.NULL.equals(dh)) { SSL_CTX_set_tmp_dh(state.sslCtx, dh); DH_free(dh); @@ -1073,14 +1106,16 @@ if (SSL_CTX_set0_tmp_dh_pkey(state.sslCtx, pkey) <= 0) { EVP_PKEY_free(pkey); } else { - log.debug(sm.getString("openssl.setCustomDHParameters", Integer.valueOf(numBits), certificate.getCertificateFile())); + log.debug(sm.getString("openssl.setCustomDHParameters", Integer.valueOf(numBits), + certificate.getCertificateFile())); } } else { String errMessage = OpenSSLLibrary.getLastError(); if (errMessage != null) { - log.debug(sm.getString("openssl.errorReadingPEMParameters", errMessage, certificate.getCertificateFile())); + log.debug(sm.getString("openssl.errorReadingPEMParameters", errMessage, + certificate.getCertificateFile())); } - SSL_CTX_ctrl(state.sslCtx, SSL_CTRL_SET_DH_AUTO(), 1, MemorySegment.NULL); + SSL_CTX_set_dh_auto(state.sslCtx, 1); } } } @@ -1089,7 +1124,8 @@ BIO_reset(certificateBIO); if (!openssl_h_Compatibility.BORINGSSL) { if (!openssl_h_Compatibility.OPENSSL3) { - var ecparams = PEM_read_bio_ECPKParameters(certificateBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); + var ecparams = PEM_read_bio_ECPKParameters(certificateBIO, MemorySegment.NULL, + MemorySegment.NULL, MemorySegment.NULL); if (!MemorySegment.NULL.equals(ecparams)) { int nid = EC_GROUP_get_curve_name(ecparams); var eckey = EC_KEY_new_by_curve_name(nid); @@ -1098,10 +1134,11 @@ EC_GROUP_free(ecparams); } // Set callback for DH parameters - SSL_CTX_set_tmp_dh_callback(state.sslCtx, SSL_CTX_set_tmp_dh_callback$dh.allocate(new TmpDHCallback(), contextArena)); + SSL_CTX_set_tmp_dh_callback(state.sslCtx, + SSL_CTX_set_tmp_dh_callback$dh.allocate(new TmpDHCallback(), contextArena)); } else { - var ecparams = PEM_ASN1_read_bio(d2i_ECPKParameters$SYMBOL(), - PEM_STRING_ECPARAMETERS(), certificateBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); + var ecparams = PEM_ASN1_read_bio(d2i_ECPKParameters$SYMBOL(), PEM_STRING_ECPARAMETERS(), + certificateBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); if (!MemorySegment.NULL.equals(ecparams)) { int curveNid = EC_GROUP_get_curve_name(ecparams); var curveNidAddress = localArena.allocateFrom(ValueLayout.JAVA_INT, curveNid); @@ -1118,30 +1155,35 @@ } // Set certificate chain file if (certificate.getCertificateChainFile() != null) { - byte[] certificateChainBytes = null; - try (Resource resource = ConfigFileLoader.getSource().getResource(certificate.getCertificateChainFile())) { + byte[] certificateChainBytes; + try (Resource resource = + ConfigFileLoader.getSource().getResource(certificate.getCertificateChainFile())) { certificateChainBytes = resource.getInputStream().readAllBytes(); - } catch (IOException e) { - log.error(sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateChainFile()), e); + } catch (IOException ioe) { + log.error( + sm.getString("openssl.errorLoadingCertificate", certificate.getCertificateChainFile()), + ioe); return false; } - MemorySegment certificateChainBytesNative = localArena.allocateFrom(ValueLayout.JAVA_BYTE, certificateChainBytes); + MemorySegment certificateChainBytesNative = + localArena.allocateFrom(ValueLayout.JAVA_BYTE, certificateChainBytes); MemorySegment certificateChainBIO = BIO_new(BIO_s_mem()); try { - if (BIO_write(certificateChainBIO, certificateChainBytesNative, certificateChainBytes.length) <= 0) { + if (BIO_write(certificateChainBIO, certificateChainBytesNative, + certificateChainBytes.length) <= 0) { log.error(sm.getString("openssl.errorLoadingCertificateWithError", certificate.getCertificateChainFile(), OpenSSLLibrary.getLastError())); return false; } - MemorySegment certChainEntry = - PEM_read_bio_X509_AUX(certificateChainBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); + MemorySegment certChainEntry = PEM_read_bio_X509_AUX(certificateChainBIO, MemorySegment.NULL, + MemorySegment.NULL, MemorySegment.NULL); while (!MemorySegment.NULL.equals(certChainEntry)) { if (SSL_CTX_add0_chain_cert(state.sslCtx, certChainEntry) <= 0) { log.error(sm.getString("openssl.errorLoadingCertificateWithError", certificate.getCertificateChainFile(), OpenSSLLibrary.getLastError())); } - certChainEntry = - PEM_read_bio_X509_AUX(certificateChainBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); + certChainEntry = PEM_read_bio_X509_AUX(certificateChainBIO, MemorySegment.NULL, + MemorySegment.NULL, MemorySegment.NULL); } // EOF is accepted, otherwise log an error if ((ERR_peek_last_error() & ERR_REASON_MASK()) == PEM_R_NO_START_LINE()) { @@ -1158,8 +1200,8 @@ MemorySegment certificateStore = SSL_CTX_get_cert_store(state.sslCtx); if (sslHostConfig.getCertificateRevocationListFile() != null) { MemorySegment x509Lookup = X509_STORE_add_lookup(certificateStore, X509_LOOKUP_file()); - var certificateRevocationListFileNative = - localArena.allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListFile())); + var certificateRevocationListFileNative = localArena.allocateFrom( + SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListFile())); if (X509_LOOKUP_load_file(x509Lookup, certificateRevocationListFileNative, X509_FILETYPE_PEM()) <= 0) { log.error(sm.getString("openssl.errorLoadingCertificateRevocationListWithError", @@ -1168,8 +1210,8 @@ } if (sslHostConfig.getCertificateRevocationListPath() != null) { MemorySegment x509Lookup = X509_STORE_add_lookup(certificateStore, X509_LOOKUP_hash_dir()); - var certificateRevocationListPathNative = - localArena.allocateFrom(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListPath())); + var certificateRevocationListPathNative = localArena.allocateFrom( + SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListPath())); if (X509_LOOKUP_add_dir(x509Lookup, certificateRevocationListPathNative, X509_FILETYPE_PEM()) <= 0) { log.error(sm.getString("openssl.errorLoadingCertificateRevocationListWithError", @@ -1184,19 +1226,18 @@ String alias = certificate.getCertificateKeyAlias(); X509KeyManager x509KeyManager = certificate.getCertificateKeyManager(); if (alias == null) { - alias = "tomcat"; + alias = SSLUtilBase.DEFAULT_KEY_ALIAS; } X509Certificate[] chain = x509KeyManager.getCertificateChain(alias); if (chain == null) { alias = findAlias(x509KeyManager, certificate); chain = x509KeyManager.getCertificateChain(alias); } - StringBuilder sb = new StringBuilder(BEGIN_KEY); - sb.append(Base64.getMimeEncoder(64, new byte[] {'\n'}).encodeToString(x509KeyManager.getPrivateKey(alias).getEncoded())); - sb.append(END_KEY); + String encodedKey = BEGIN_KEY + Base64.getMimeEncoder(64, new byte[] { '\n' }) + .encodeToString(x509KeyManager.getPrivateKey(alias).getEncoded()) + END_KEY; var rawCertificate = localArena.allocateFrom(ValueLayout.JAVA_BYTE, chain[0].getEncoded()); var rawCertificatePointer = localArena.allocateFrom(ValueLayout.ADDRESS, rawCertificate); - var rawKey = localArena.allocateFrom(ValueLayout.JAVA_BYTE, sb.toString().getBytes(StandardCharsets.US_ASCII)); + var rawKey = localArena.allocateFrom(ValueLayout.JAVA_BYTE, encodedKey.getBytes(StandardCharsets.US_ASCII)); var x509cert = d2i_X509(MemorySegment.NULL, rawCertificatePointer, rawCertificate.byteSize()); if (MemorySegment.NULL.equals(x509cert)) { logLastError("openssl.errorLoadingCertificate"); @@ -1205,7 +1246,8 @@ MemorySegment keyBIO = BIO_new(BIO_s_mem()); try { BIO_write(keyBIO, rawKey, (int) rawKey.byteSize()); - MemorySegment privateKeyAddress = PEM_read_bio_PrivateKey(keyBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); + MemorySegment privateKeyAddress = + PEM_read_bio_PrivateKey(keyBIO, MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); if (MemorySegment.NULL.equals(privateKeyAddress)) { logLastError("openssl.errorLoadingPrivateKey"); return false; @@ -1243,13 +1285,14 @@ log.debug(sm.getString("openssl.errorReadingPEMParameters", errMessage, x509KeyManager.toString())); } - SSL_CTX_ctrl(state.sslCtx, SSL_CTRL_SET_DH_AUTO(), 1, MemorySegment.NULL); + SSL_CTX_set_dh_auto(state.sslCtx, 1); } } for (int i = 1; i < chain.length; i++) { var rawCertificateChain = localArena.allocateFrom(ValueLayout.JAVA_BYTE, chain[i].getEncoded()); var rawCertificateChainPointer = localArena.allocateFrom(ValueLayout.ADDRESS, rawCertificateChain); - var x509certChain = d2i_X509(MemorySegment.NULL, rawCertificateChainPointer, rawCertificateChain.byteSize()); + var x509certChain = + d2i_X509(MemorySegment.NULL, rawCertificateChainPointer, rawCertificateChain.byteSize()); if (MemorySegment.NULL.equals(x509certChain)) { logLastError("openssl.errorLoadingCertificate"); return false; @@ -1267,35 +1310,17 @@ } - private static int getCertificateIndex(SSLHostConfigCertificate certificate) { - int result = -1; - // If the type is undefined there will only be one certificate (enforced - // in SSLHostConfig) so use the RSA slot. - if (certificate.getType() == Type.RSA || certificate.getType() == Type.UNDEFINED) { - result = SSL_AIDX_RSA; - } else if (certificate.getType() == Type.EC) { - result = SSL_AIDX_ECC; - } else if (certificate.getType() == Type.DSA) { - result = SSL_AIDX_DSA; - } else { - result = SSL_AIDX_MAX; - } - return result; - } - - /* * Find a valid alias when none was specified in the config. */ - private static String findAlias(X509KeyManager keyManager, - SSLHostConfigCertificate certificate) { + private static String findAlias(X509KeyManager keyManager, SSLHostConfigCertificate certificate) { Type type = certificate.getType(); String result = null; List candidateTypes = new ArrayList<>(); if (Type.UNDEFINED.equals(type)) { - // Try all types to find an suitable alias + // Try all types to find a suitable alias candidateTypes.addAll(Arrays.asList(Type.values())); candidateTypes.remove(Type.UNDEFINED); } else { @@ -1305,7 +1330,7 @@ Iterator iter = candidateTypes.iterator(); while (result == null && iter.hasNext()) { - result = keyManager.chooseServerAlias(iter.next().toString(), null, null); + result = keyManager.chooseServerAlias(iter.next().getKeyType(), null, null); } return result; @@ -1344,11 +1369,10 @@ @Override public SSLEngine createSSLEngine() { - return new OpenSSLEngine(cleaner, state.sslCtx, defaultProtocol, false, sessionContext, - alpn, initialized, + return new OpenSSLEngine(cleaner, state.sslCtx, defaultProtocol, false, sessionContext, alpn, initialized, sslHostConfig.getCertificateVerificationDepth(), sslHostConfig.getCertificateVerification() == CertificateVerification.OPTIONAL_NO_CA, - noOcspCheck); + noOcspCheck, ocspSoftFail, ocspTimeout, ocspVerifyFlags); } @Override @@ -1367,7 +1391,7 @@ X509KeyManager x509KeyManager = certificate.getCertificateKeyManager(); if (x509KeyManager != null) { if (alias == null) { - alias = "tomcat"; + alias = SSLUtilBase.DEFAULT_KEY_ALIAS; } chain = x509KeyManager.getCertificateChain(alias); if (chain == null) { @@ -1398,11 +1422,10 @@ private ContextState(MemorySegment sslCtx, MemorySegment confCtx) { // Use another arena to avoid keeping a reference through segments // This also allows making further accesses to the main pointers safer - this.sslCtx = sslCtx.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, - (MemorySegment t) -> SSL_CTX_free(t)); + this.sslCtx = sslCtx.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, openssl_h::SSL_CTX_free); if (!MemorySegment.NULL.equals(confCtx)) { - this.confCtx = confCtx.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, - (MemorySegment t) -> SSL_CONF_CTX_free(t)); + this.confCtx = + confCtx.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, openssl_h::SSL_CONF_CTX_free); } else { this.confCtx = MemorySegment.NULL; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLEngine.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ package org.apache.tomcat.util.net.openssl.panama; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; @@ -36,7 +37,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -57,19 +57,20 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.Asn1Parser; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.net.Constants; import org.apache.tomcat.util.net.SSLUtil; import org.apache.tomcat.util.net.openssl.ciphers.OpenSSLCipherConfigurationParser; import org.apache.tomcat.util.openssl.SSL_CTX_set_verify$callback; import org.apache.tomcat.util.openssl.SSL_set_info_callback$cb; import org.apache.tomcat.util.openssl.SSL_set_verify$callback; +import org.apache.tomcat.util.openssl.openssl_h; import org.apache.tomcat.util.openssl.openssl_h_Compatibility; import org.apache.tomcat.util.res.StringManager; /** - * Implements a {@link SSLEngine} using - * OpenSSL - * BIO abstractions. + * Implements a {@link SSLEngine} using OpenSSL BIO + * abstractions. */ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolInfo { @@ -86,20 +87,16 @@ final Set availableCipherSuites = new LinkedHashSet<>(128); availableCipherSuites.addAll(OpenSSLLibrary.findCiphers("ALL")); AVAILABLE_CIPHER_SUITES = Collections.unmodifiableSet(availableCipherSuites); - HashSet protocols = new HashSet<>(); - protocols.add(Constants.SSL_PROTO_SSLv2Hello); - protocols.add(Constants.SSL_PROTO_SSLv2); - protocols.add(Constants.SSL_PROTO_SSLv3); - protocols.add(Constants.SSL_PROTO_TLSv1); - protocols.add(Constants.SSL_PROTO_TLSv1_1); - protocols.add(Constants.SSL_PROTO_TLSv1_2); - protocols.add(Constants.SSL_PROTO_TLSv1_3); - IMPLEMENTED_PROTOCOLS_SET = Collections.unmodifiableSet(protocols); + IMPLEMENTED_PROTOCOLS_SET = Set.of(Constants.SSL_PROTO_SSLv2Hello, Constants.SSL_PROTO_SSLv2, + Constants.SSL_PROTO_SSLv3, Constants.SSL_PROTO_TLSv1, Constants.SSL_PROTO_TLSv1_1, + Constants.SSL_PROTO_TLSv1_2, Constants.SSL_PROTO_TLSv1_3); } private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14 private static final int MAX_COMPRESSED_LENGTH = MAX_PLAINTEXT_LENGTH + 1024; private static final int MAX_CIPHERTEXT_LENGTH = MAX_COMPRESSED_LENGTH + 1024; + // 15 minutes aligns with JSSE + private static final int OCSP_MAX_SKEW = 60 * 15; // Header (5) + Data (2^14) + Compression (1024) + Encryption (1024) + MAC (20) + Padding (256) private static final int MAX_ENCRYPTED_PACKET_LENGTH = MAX_CIPHERTEXT_LENGTH + 5 + 20 + 256; @@ -112,7 +109,8 @@ private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL"; - private static final ConcurrentHashMap states = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap states = new ConcurrentHashMap<>(); + private static EngineState getState(MemorySegment ssl) { return states.get(Long.valueOf(ssl.address())); } @@ -120,11 +118,22 @@ private final EngineState state; private final Arena engineArena; private final Cleanable cleanable; - private MemorySegment bufSegment = null; + private MemorySegment bufSegment; + + private enum Accepted { + NOT, + IMPLICIT, + EXPLICIT + } - private enum Accepted { NOT, IMPLICIT, EXPLICIT } private Accepted accepted = Accepted.NOT; - private enum PHAState { NONE, START, COMPLETE } + + private enum PHAState { + NONE, + START, + COMPLETE + } + private boolean handshakeFinished; private int currentHandshake; private boolean receivedShutdown; @@ -159,24 +168,24 @@ /** * Creates a new instance * - * @param sslCtx an OpenSSL {@code SSL_CTX} object - * @param fallbackApplicationProtocol the fallback application protocol - * @param clientMode {@code true} if this is used for clients, {@code false} - * otherwise - * @param sessionContext the {@link OpenSSLSessionContext} this - * {@link SSLEngine} belongs to. - * @param alpn {@code true} if alpn should be used, {@code false} - * otherwise - * @param initialized {@code true} if this instance gets its protocol, - * cipher and client verification from the {@code SSL_CTX} {@code sslCtx} - * @param certificateVerificationDepth Certificate verification depth - * @param certificateVerificationOptionalNoCA Skip CA verification in - * optional mode + * @param sslCtx an OpenSSL {@code SSL_CTX} object + * @param fallbackApplicationProtocol the fallback application protocol + * @param clientMode {@code true} if this is used for clients, {@code false} otherwise + * @param sessionContext the {@link OpenSSLSessionContext} this {@link SSLEngine} belongs to. + * @param alpn {@code true} if alpn should be used, {@code false} otherwise + * @param initialized {@code true} if this instance gets its protocol, cipher and client + * verification from the {@code SSL_CTX} {@code sslCtx} + * @param certificateVerificationDepth Certificate verification depth + * @param certificateVerificationOptionalNoCA Skip CA verification in optional mode + * @param noOcspCheck Enable OCSP if true + * @param ocspSoftFail Allow OCSP checks to pass if the responder can't be contacted + * @param ocspTimeout Timout in ms to use for OCSP requests + * @param ocspVerifyFlags Verification flags for OCSP */ - OpenSSLEngine(Cleaner cleaner, MemorySegment sslCtx, String fallbackApplicationProtocol, - boolean clientMode, OpenSSLSessionContext sessionContext, boolean alpn, - boolean initialized, int certificateVerificationDepth, - boolean certificateVerificationOptionalNoCA, boolean noOcspCheck) { + OpenSSLEngine(Cleaner cleaner, MemorySegment sslCtx, String fallbackApplicationProtocol, boolean clientMode, + OpenSSLSessionContext sessionContext, boolean alpn, boolean initialized, int certificateVerificationDepth, + boolean certificateVerificationOptionalNoCA, boolean noOcspCheck, boolean ocspSoftFail, + int ocspTimeout, int ocspVerifyFlags) { if (sslCtx == null) { throw new IllegalArgumentException(sm.getString("engine.noSSLContext")); } @@ -199,7 +208,8 @@ var internalBIO = internalBIOPointer.get(ValueLayout.ADDRESS, 0); var networkBIO = networkBIOPointer.get(ValueLayout.ADDRESS, 0); SSL_set_bio(ssl, internalBIO, internalBIO); - state = new EngineState(ssl, networkBIO, certificateVerificationDepth, noOcspCheck); + state = new EngineState(ssl, networkBIO, certificateVerificationDepth, noOcspCheck, ocspSoftFail, + ocspTimeout, ocspVerifyFlags); } this.fallbackApplicationProtocol = fallbackApplicationProtocol; this.clientMode = clientMode; @@ -229,9 +239,8 @@ } /** - * Write plain text data to the OpenSSL internal BIO + * Write plain text data to the OpenSSL internal BIO Calling this function with src.remaining == 0 is undefined. * - * Calling this function with src.remaining == 0 is undefined. * @throws SSLException if the OpenSSL error check fails */ private int writePlaintextData(final MemorySegment ssl, final ByteBuffer src) throws SSLException { @@ -254,6 +263,7 @@ /** * Write encrypted data to the OpenSSL network BIO. + * * @throws SSLException if the OpenSSL error check fails */ private int writeEncryptedData(final MemorySegment networkBIO, final ByteBuffer src) throws SSLException { @@ -276,6 +286,7 @@ /** * Read plain text data from the OpenSSL internal BIO + * * @throws SSLException if the OpenSSL error check fails */ private int readPlaintextData(final MemorySegment ssl, final ByteBuffer dst) throws SSLException { @@ -298,9 +309,11 @@ /** * Read encrypted data from the OpenSSL network BIO + * * @throws SSLException if the OpenSSL error check fails */ - private int readEncryptedData(final MemorySegment networkBIO, final ByteBuffer dst, final int pending) throws SSLException { + private int readEncryptedData(final MemorySegment networkBIO, final ByteBuffer dst, final int pending) + throws SSLException { clearLastError(); final int pos = dst.position(); MemorySegment dstSegment = dst.isDirect() ? MemorySegment.ofBuffer(dst) : bufSegment; @@ -318,10 +331,12 @@ } @Override - public synchronized SSLEngineResult wrap(final ByteBuffer[] srcs, final int offset, final int length, final ByteBuffer dst) throws SSLException { + public synchronized SSLEngineResult wrap(final ByteBuffer[] srcs, final int offset, final int length, + final ByteBuffer dst) throws SSLException { // Check to make sure the engine has not been closed if (destroyed) { - return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0); + return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, + 0, 0); } // Throw required runtime exceptions @@ -329,9 +344,8 @@ throw new IllegalArgumentException(sm.getString("engine.nullBuffer")); } if (offset >= srcs.length || offset + length > srcs.length) { - throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", - Integer.toString(offset), Integer.toString(length), - Integer.toString(srcs.length))); + throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", Integer.toString(offset), + Integer.toString(length), Integer.toString(srcs.length))); } if (dst.isReadOnly()) { throw new ReadOnlyBufferException(); @@ -388,7 +402,7 @@ } while (src.hasRemaining()) { - int bytesWritten = 0; + int bytesWritten; // Write plain text application data to the SSL engine try { bytesWritten = writePlaintextData(state.ssl, src); @@ -407,8 +421,8 @@ // Do we have enough room in dst to write encrypted data? int capacity = dst.remaining(); if (capacity < pendingNet) { - return new SSLEngineResult( - SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatus(), bytesConsumed, bytesProduced); + return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, getHandshakeStatus(), + bytesConsumed, bytesProduced); } // Write the pending data from the network BIO into the dst buffer @@ -427,10 +441,12 @@ } @Override - public synchronized SSLEngineResult unwrap(final ByteBuffer src, final ByteBuffer[] dsts, final int offset, final int length) throws SSLException { + public synchronized SSLEngineResult unwrap(final ByteBuffer src, final ByteBuffer[] dsts, final int offset, + final int length) throws SSLException { // Check to make sure the engine has not been closed if (destroyed) { - return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, 0, 0); + return new SSLEngineResult(SSLEngineResult.Status.CLOSED, SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING, + 0, 0); } // Throw required runtime exceptions @@ -438,9 +454,8 @@ throw new IllegalArgumentException(sm.getString("engine.nullBuffer")); } if (offset >= dsts.length || offset + length > dsts.length) { - throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", - Integer.toString(offset), Integer.toString(length), - Integer.toString(dsts.length))); + throw new IndexOutOfBoundsException(sm.getString("engine.invalidBufferArray", Integer.toString(offset), + Integer.toString(length), Integer.toString(dsts.length))); } int capacity = 0; final int endOffset = offset + length; @@ -476,7 +491,7 @@ } // Write encrypted data to network BIO - int written = 0; + int written; try { written = writeEncryptedData(state.networkBIO, src); } catch (Exception e) { @@ -556,8 +571,7 @@ } } - private int pendingReadableBytesInSSL() - throws SSLException { + private int pendingReadableBytesInSSL() throws SSLException { // NOTE: Calling a fake read is necessary before calling pendingReadableBytesInSSL because // SSL_pending will return 0 if OpenSSL has not started the current TLS record // See https://www.openssl.org/docs/manmaster/man3/SSL_pending.html @@ -571,8 +585,7 @@ int pendingReadableBytesInSSL = SSL_pending(state.ssl); // TLS 1.0 needs additional handling - if (Constants.SSL_PROTO_TLSv1.equals(version) && lastPrimingReadResult == 0 && - pendingReadableBytesInSSL == 0) { + if (Constants.SSL_PROTO_TLSv1.equals(version) && lastPrimingReadResult == 0 && pendingReadableBytesInSSL == 0) { // Perform another priming read lastPrimingReadResult = SSL_read(state.ssl, MemorySegment.NULL, 0); if (lastPrimingReadResult <= 0) { @@ -641,8 +654,7 @@ @Override public String[] getSupportedCipherSuites() { - Set availableCipherSuites = AVAILABLE_CIPHER_SUITES; - return availableCipherSuites.toArray(new String[0]); + return AVAILABLE_CIPHER_SUITES.toArray(new String[0]); } @Override @@ -651,17 +663,13 @@ return new String[0]; } String[] enabled = OpenSSLLibrary.getCiphers(state.ssl); - if (enabled == null) { - return new String[0]; - } else { - for (int i = 0; i < enabled.length; i++) { - String mapped = OpenSSLCipherConfigurationParser.openSSLToJsse(enabled[i]); - if (mapped != null) { - enabled[i] = mapped; - } + for (int i = 0; i < enabled.length; i++) { + String mapped = OpenSSLCipherConfigurationParser.openSSLToJsse(enabled[i]); + if (mapped != null) { + enabled[i] = mapped; } - return enabled; } + return enabled; } @Override @@ -692,7 +700,7 @@ buf.append(':'); } - if (buf.length() == 0) { + if (buf.isEmpty()) { throw new IllegalArgumentException(sm.getString("engine.emptyCipherSuite")); } buf.setLength(buf.length() - 1); @@ -738,11 +746,7 @@ enabled.add(Constants.SSL_PROTO_SSLv3); } int size = enabled.size(); - if (size == 0) { - return new String[0]; - } else { - return enabled.toArray(new String[size]); - } + return enabled.toArray(new String[size]); } @Override @@ -767,18 +771,13 @@ if (!IMPLEMENTED_PROTOCOLS_SET.contains(p)) { throw new IllegalArgumentException(sm.getString("engine.unsupportedProtocol", p)); } - if (p.equals(Constants.SSL_PROTO_SSLv2)) { - sslv2 = true; - } else if (p.equals(Constants.SSL_PROTO_SSLv3)) { - sslv3 = true; - } else if (p.equals(Constants.SSL_PROTO_TLSv1)) { - tlsv1 = true; - } else if (p.equals(Constants.SSL_PROTO_TLSv1_1)) { - tlsv1_1 = true; - } else if (p.equals(Constants.SSL_PROTO_TLSv1_2)) { - tlsv1_2 = true; - } else if (p.equals(Constants.SSL_PROTO_TLSv1_3)) { - tlsv1_3 = true; + switch (p) { + case Constants.SSL_PROTO_SSLv2 -> sslv2 = true; + case Constants.SSL_PROTO_SSLv3 -> sslv3 = true; + case Constants.SSL_PROTO_TLSv1 -> tlsv1 = true; + case Constants.SSL_PROTO_TLSv1_1 -> tlsv1_1 = true; + case Constants.SSL_PROTO_TLSv1_2 -> tlsv1_2 = true; + case Constants.SSL_PROTO_TLSv1_3 -> tlsv1_3 = true; } } // Enable all and then disable what we not want @@ -815,28 +814,29 @@ throw new SSLException(sm.getString("engine.engineClosed")); } switch (accepted) { - case NOT: - handshake(); - accepted = Accepted.EXPLICIT; - break; - case IMPLICIT: - // A user did not start handshake by calling this method by themselves, - // but handshake has been started already by wrap() or unwrap() implicitly. - // Because it's the user's first time to call this method, it is unfair to - // raise an exception. From the user's standpoint, they never asked for - // renegotiation. - - accepted = Accepted.EXPLICIT; // Next time this method is invoked by the user, we should raise an exception. - break; - case EXPLICIT: - renegotiate(); - break; + case NOT: + handshake(); + accepted = Accepted.EXPLICIT; + break; + case IMPLICIT: + // A user did not start handshake by calling this method by themselves, + // but handshake has been started already by wrap() or unwrap() implicitly. + // Because it's the user's first time to call this method, it is unfair to + // raise an exception. From the user's standpoint, they never asked for + // renegotiation. + + accepted = Accepted.EXPLICIT; // Next time this method is invoked by the user, we should raise an + // exception. + break; + case EXPLICIT: + renegotiate(); + break; } } private byte[] getPeerCertificate() { try (var localArena = Arena.ofConfined()) { - MemorySegment/*(X509*)*/ x509 = openssl_h_Compatibility.SSL_get_peer_certificate(state.ssl); + MemorySegment/* (X509*) */ x509 = openssl_h_Compatibility.SSL_get_peer_certificate(state.ssl); MemorySegment bufPointer = localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL); int length = i2d_X509(x509, bufPointer); if (length <= 0) { @@ -851,7 +851,7 @@ } private byte[][] getPeerCertChain() { - MemorySegment/*STACK_OF(X509)*/ sk = SSL_get_peer_cert_chain(state.ssl); + MemorySegment/* STACK_OF(X509) */ sk = SSL_get_peer_cert_chain(state.ssl); int len = openssl_h_Compatibility.OPENSSL_sk_num(sk); if (len <= 0) { return null; @@ -859,7 +859,7 @@ byte[][] certificateChain = new byte[len][]; try (var localArena = Arena.ofConfined()) { for (int i = 0; i < len; i++) { - MemorySegment/*(X509*)*/ x509 = openssl_h_Compatibility.OPENSSL_sk_value(sk, i); + MemorySegment/* (X509*) */ x509 = openssl_h_Compatibility.OPENSSL_sk_value(sk, i); MemorySegment bufPointer = localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL); int length = i2d_X509(x509, bufPointer); if (length < 0) { @@ -986,34 +986,24 @@ } /* - * Tomcat Native stores a count of the completed handshakes in the - * SSL instance and increments it every time a handshake is - * completed. Comparing the handshake count when the handshake - * started to the current handshake count enables this code to - * detect when the handshake has completed. + * Tomcat Native stores a count of the completed handshakes in the SSL instance and increments it every time + * a handshake is completed. Comparing the handshake count when the handshake started to the current + * handshake count enables this code to detect when the handshake has completed. * - * Obtaining client certificates after the connection has been - * established requires additional checks. We need to trigger - * additional reads until the certificates have been read but we - * don't know how many reads we will need as it depends on both - * client and network behaviour. + * Obtaining client certificates after the connection has been established requires additional checks. We + * need to trigger additional reads until the certificates have been read, but we don't know how many reads + * we will need as it depends on both client and network behaviour. * - * The additional reads are triggered by returning NEED_UNWRAP - * rather than FINISHED. This allows the standard I/O code to be - * used. + * The additional reads are triggered by returning NEED_UNWRAP rather than FINISHED. This allows the + * standard I/O code to be used. * - * For TLSv1.2 and below, the handshake completes before the - * renegotiation. We therefore use SSL.renegotiatePending() to - * check on the current status of the renegotiation and return - * NEED_UNWRAP until it completes which means the client - * certificates will have been read from the client. + * For TLSv1.2 and below, the handshake completes before the renegotiation. We therefore use + * SSL.renegotiatePending() to check on the current status of the renegotiation and return NEED_UNWRAP until + * it completes which means the client certificates will have been read from the client. * - * For TLSv1.3, Tomcat Native sets a flag when post handshake - * authentication is started and updates it once the client - * certificate has been received. We therefore use - * SSL.getPostHandshakeAuthInProgress() to check the current status - * and return NEED_UNWRAP until that methods indicates that PHA is - * no longer in progress. + * For TLSv1.3, Tomcat Native sets a flag when post handshake authentication is started and updates it once + * the client certificate has been received. We therefore use SSL.getPostHandshakeAuthInProgress() to check + * the current status and return NEED_UNWRAP until that methods indicates that PHA is no longer in progress. */ // No pending data to be sent to the peer @@ -1094,7 +1084,8 @@ state.certificateVerifyMode = switch (mode) { case NONE -> SSL_VERIFY_NONE(); case REQUIRE -> SSL_VERIFY_FAIL_IF_NO_PEER_CERT(); - case OPTIONAL -> certificateVerificationOptionalNoCA ? OpenSSLContext.OPTIONAL_NO_CA : SSL_VERIFY_PEER(); + case OPTIONAL -> + certificateVerificationOptionalNoCA ? OpenSSLContext.OPTIONAL_NO_CA : SSL_VERIFY_PEER(); }; // Set int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) callback int value = switch (mode) { @@ -1103,7 +1094,7 @@ case OPTIONAL -> SSL_VERIFY_PEER(); }; // Note: Since a callback is always set by the context, the callback here could in theory - // be set to NULL (at the time of creation of the SSL, the SSL_CTX will have a non null callback) + // be set to NULL (at the time of creation of the SSL, the SSL_CTX will have a non-null callback) SSL_set_verify(state.ssl, value, SSL_set_verify$callback.allocate(new VerifyCallback(), engineArena)); clientAuth = mode; } @@ -1125,7 +1116,7 @@ static class VerifyCallback implements SSL_set_verify$callback.Function, SSL_CTX_set_verify$callback.Function { @Override - public int apply(int preverify_ok, MemorySegment /*X509_STORE_CTX*/ x509ctx) { + public int apply(int preverify_ok, MemorySegment /* X509_STORE_CTX */ x509ctx) { MemorySegment ssl = X509_STORE_CTX_get_ex_data(x509ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); EngineState state = getState(ssl); if (state == null) { @@ -1139,34 +1130,34 @@ int errnum = X509_STORE_CTX_get_error(x509ctx); int errdepth = X509_STORE_CTX_get_error_depth(x509ctx); state.phaState = PHAState.COMPLETE; - if (state.certificateVerifyMode == -1 /*SSL_CVERIFY_UNSET*/ || state.certificateVerifyMode == SSL_VERIFY_NONE()) { + if (state.certificateVerifyMode == -1 /* SSL_CVERIFY_UNSET */ || + state.certificateVerifyMode == SSL_VERIFY_NONE()) { return 1; } - /*SSL_VERIFY_ERROR_IS_OPTIONAL(errnum) -> ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) - || (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) - || (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) - || (errnum == X509_V_ERR_CERT_UNTRUSTED) - || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))*/ - boolean verifyErrorIsOptional = (errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT()) - || (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN()) - || (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY()) - || (errnum == X509_V_ERR_CERT_UNTRUSTED()) - || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE()); - if (verifyErrorIsOptional && (state.certificateVerifyMode == OpenSSLContext.OPTIONAL_NO_CA)) { + /* + * SSL_VERIFY_ERROR_IS_OPTIONAL(errnum) -> ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || (errnum == + * X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) || (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) || + * (errnum == X509_V_ERR_CERT_UNTRUSTED) || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)) + */ + boolean verifyErrorIsOptional = (errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT()) || + (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN()) || + (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY()) || + (errnum == X509_V_ERR_CERT_UNTRUSTED()) || (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE()); + if ((verifyErrorIsOptional || errnum == X509_V_OK()) && (state.certificateVerifyMode == OpenSSLContext.OPTIONAL_NO_CA)) { ok = 1; openssl_h_Compatibility.SSL_set_verify_result(state.ssl, X509_V_OK()); } + if (ok == 0 && errnum == X509_V_ERR_UNABLE_TO_GET_CRL()) { + ok = 1; + } /* - * Expired certificates vs. "expired" CRLs: by default, OpenSSL - * turns X509_V_ERR_CRL_HAS_EXPIRED into a "certificate_expired(45)" - * SSL alert, but that's not really the message we should convey to the - * peer (at the very least, it's confusing, and in many cases, it's also - * inaccurate, as the certificate itself may very well not have expired - * yet). We set the X509_STORE_CTX error to something which OpenSSL's - * s3_both.c:ssl_verify_alarm_type() maps to SSL_AD_CERTIFICATE_UNKNOWN, - * i.e. the peer will receive a "certificate_unknown(46)" alert. - * We do not touch errnum, though, so that later on we will still log - * the "real" error, as returned by OpenSSL. + * Expired certificates vs. "expired" CRLs: by default, OpenSSL turns X509_V_ERR_CRL_HAS_EXPIRED into a + * "certificate_expired(45)" SSL alert, but that's not really the message we should convey to the peer (at + * the very least, it's confusing, and in many cases, it's also inaccurate, as the certificate itself may + * very well not have expired yet). We set the X509_STORE_CTX error to something which OpenSSL's + * s3_both.c:ssl_verify_alarm_type() maps to SSL_AD_CERTIFICATE_UNKNOWN, i.e. the peer will receive a + * "certificate_unknown(46)" alert. We do not touch errnum, though, so that later on we will still log the + * "real" error, as returned by OpenSSL. */ if (ok == 0 && errnum == X509_V_ERR_CRL_HAS_EXPIRED()) { X509_STORE_CTX_set_error(x509ctx, -1); @@ -1174,24 +1165,23 @@ // OCSP if (!state.noOcspCheck && (ok > 0)) { - /* If there was an optional verification error, it's not - * possible to perform OCSP validation since the issuer may be - * missing/untrusted. Fail in that case. + /* + * If there was an optional verification error, it's not possible to perform OCSP validation since the + * issuer may be missing/untrusted. Fail in that case. */ if (verifyErrorIsOptional) { - if (state.certificateVerifyMode != OpenSSLContext.OPTIONAL_NO_CA) { - X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_APPLICATION_VERIFICATION()); - errnum = X509_V_ERR_APPLICATION_VERIFICATION(); - ok = 0; - } + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_APPLICATION_VERIFICATION()); + errnum = X509_V_ERR_APPLICATION_VERIFICATION(); + ok = 0; } else { - int ocspResponse = processOCSP(x509ctx); + int ocspResponse = processOCSP(state, x509ctx); if (ocspResponse == V_OCSP_CERTSTATUS_REVOKED()) { ok = 0; errnum = X509_STORE_CTX_get_error(x509ctx); + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_CERT_REVOKED()); } else if (ocspResponse == V_OCSP_CERTSTATUS_UNKNOWN()) { errnum = X509_STORE_CTX_get_error(x509ctx); - if (errnum <= 0) { + if (errnum != 0 && !(state.ocspSoftFail && errnum == X509_V_ERR_UNABLE_TO_GET_CRL())) { ok = 0; } } @@ -1206,7 +1196,7 @@ } } - private static int processOCSP(MemorySegment /*X509_STORE_CTX*/ x509ctx) { + private static int processOCSP(EngineState state, MemorySegment /* X509_STORE_CTX */ x509ctx) { int ocspResponse = V_OCSP_CERTSTATUS_UNKNOWN(); MemorySegment x509 = X509_STORE_CTX_get_current_cert(x509ctx); if (!MemorySegment.NULL.equals(x509)) { @@ -1217,46 +1207,60 @@ // don't do OCSP checking for valid self-issued certs X509_STORE_CTX_set_error(x509ctx, X509_V_OK()); } else { - // If we can't get the issuer, we cannot perform OCSP verification - MemorySegment issuer = X509_STORE_CTX_get0_current_issuer(x509ctx); - if (!MemorySegment.NULL.equals(issuer)) { - // sslutils.c ssl_ocsp_request(x509, issuer, x509ctx); - int nid = X509_get_ext_by_NID(x509, NID_info_access(), -1); - if (nid >= 0) { - try (var localArenal = Arena.ofConfined()) { - MemorySegment ext = X509_get_ext(x509, nid); - MemorySegment os = X509_EXTENSION_get_data(ext); - int length = ASN1_STRING_length(os); - MemorySegment data = ASN1_STRING_get0_data(os); - // ocsp_urls = decode_OCSP_url(os); - byte[] asn1String = data.reinterpret(length, localArenal, null).toArray(ValueLayout.JAVA_BYTE); - Asn1Parser parser = new Asn1Parser(asn1String); - // Parse the byte sequence - ArrayList urls = new ArrayList<>(); - try { - parseOCSPURLs(parser, urls); - } catch (Exception e) { - log.error(sm.getString("engine.ocspParseError"), e); + try (var localArena = Arena.ofConfined()) { + // If we can't get the issuer, we cannot perform OCSP verification + MemorySegment issuer = MemorySegment.NULL; + try { + if (openssl_h_Compatibility.OPENSSL && !openssl_h_Compatibility.OPENSSL3) { + issuer = openssl_h_Compatibility.X509_STORE_CTX_get0_current_issuer(x509ctx); + } else { + MemorySegment x509IssuerPointer = localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL); + int res = X509_STORE_CTX_get1_issuer(x509IssuerPointer, x509ctx, x509); + if (res > 0) { + issuer = x509IssuerPointer.get(ValueLayout.ADDRESS, 0); } - if (!urls.isEmpty()) { - // Use OpenSSL to build OCSP request - for (String urlString : urls) { - try { - URL url = (new URI(urlString)).toURL(); - ocspResponse = processOCSPRequest(url, issuer, x509, x509ctx, localArenal); - if (log.isDebugEnabled()) { - log.debug(sm.getString("engine.ocspResponse", urlString, - Integer.toString(ocspResponse))); + } + if (!MemorySegment.NULL.equals(issuer)) { + // sslutils.c ssl_ocsp_request(x509, issuer, x509ctx); + int nid = X509_get_ext_by_NID(x509, NID_info_access(), -1); + if (nid >= 0) { + MemorySegment ext = X509_get_ext(x509, nid); + MemorySegment os = X509_EXTENSION_get_data(ext); + int length = ASN1_STRING_length(os); + MemorySegment data = ASN1_STRING_get0_data(os); + // ocsp_urls = decode_OCSP_url(os); + byte[] asn1String = + data.reinterpret(length, localArena, null).toArray(ValueLayout.JAVA_BYTE); + Asn1Parser parser = new Asn1Parser(asn1String); + // Parse the byte sequence + ArrayList urls = new ArrayList<>(); + try { + parseOCSPURLs(parser, urls); + } catch (Exception e) { + log.error(sm.getString("engine.ocspParseError"), e); + } + if (!urls.isEmpty()) { + // Use OpenSSL to build OCSP request + for (String urlString : urls) { + try { + URL url = (new URI(urlString)).toURL(); + ocspResponse = processOCSPRequest(state, url, issuer, x509, x509ctx, localArena); + if (log.isDebugEnabled()) { + log.debug(sm.getString("engine.ocspResponse", urlString, + Integer.toString(ocspResponse))); + } + } catch (MalformedURLException | URISyntaxException e) { + log.warn(sm.getString("engine.invalidOCSPURL", urlString)); + } + if (ocspResponse != V_OCSP_CERTSTATUS_UNKNOWN()) { + break; } - } catch (MalformedURLException | URISyntaxException e) { - log.warn(sm.getString("engine.invalidOCSPURL", urlString)); - } - if (ocspResponse != V_OCSP_CERTSTATUS_UNKNOWN()) { - break; } } } } + } finally { + X509_free(issuer); } } } @@ -1265,9 +1269,9 @@ } private static final int ASN1_SEQUENCE = 0x30; - private static final int ASN1_OID = 0x06; - private static final int ASN1_STRING = 0x86; - private static final byte[] OCSP_OID = {0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01}; + private static final int ASN1_OID = 0x06; + private static final int ASN1_STRING = 0x86; + private static final byte[] OCSP_OID = { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01 }; private static void parseOCSPURLs(Asn1Parser parser, ArrayList urls) { while (!parser.eof()) { @@ -1293,12 +1297,15 @@ } } - private static int processOCSPRequest(URL url, MemorySegment issuer, MemorySegment x509, - MemorySegment /*X509_STORE_CTX*/ x509ctx, Arena localArena) { + private static int processOCSPRequest(EngineState state, URL url, MemorySegment issuer, MemorySegment x509, + MemorySegment /* X509_STORE_CTX */ x509ctx, Arena localArena) { + if (openssl_h_Compatibility.BORINGSSL || openssl_h_Compatibility.isLibreSSLPre35()) { + return V_OCSP_CERTSTATUS_UNKNOWN(); + } MemorySegment ocspRequest = MemorySegment.NULL; MemorySegment ocspResponse = MemorySegment.NULL; - MemorySegment id = MemorySegment.NULL; - MemorySegment ocspOneReq = MemorySegment.NULL; + MemorySegment id; + MemorySegment ocspOneReq; HttpURLConnection connection = null; MemorySegment basicResponse = MemorySegment.NULL; MemorySegment certId = MemorySegment.NULL; @@ -1315,6 +1322,7 @@ if (MemorySegment.NULL.equals(ocspOneReq)) { return V_OCSP_CERTSTATUS_UNKNOWN(); } + OCSP_request_add1_nonce(ocspRequest, (char) 0, -1); MemorySegment bufPointer = localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL); int requestLength = i2d_OCSP_REQUEST(ocspRequest, bufPointer); if (requestLength <= 0) { @@ -1328,7 +1336,9 @@ // Content-Length: ocspRequestData.length byte[] ocspRequestData = buf.reinterpret(requestLength, localArena, null).toArray(ValueLayout.JAVA_BYTE); connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("POST"); + connection.setConnectTimeout(state.ocspTimeout); + connection.setReadTimeout(state.ocspTimeout); + connection.setRequestMethod(Method.POST); connection.setDoInput(true); connection.setDoOutput(true); connection.setFixedLengthStreamingMode(requestLength); @@ -1340,7 +1350,7 @@ return V_OCSP_CERTSTATUS_UNKNOWN(); } InputStream is = connection.getInputStream(); - int read = 0; + int read; byte[] responseBuf = new byte[1024]; while ((read = is.read(responseBuf)) > 0) { baos.write(responseBuf, 0, read); @@ -1349,26 +1359,54 @@ var nativeResponseData = localArena.allocateFrom(ValueLayout.JAVA_BYTE, responseData); var nativeResponseDataPointer = localArena.allocateFrom(ValueLayout.ADDRESS, nativeResponseData); ocspResponse = d2i_OCSP_RESPONSE(MemorySegment.NULL, nativeResponseDataPointer, responseData.length); - if (!MemorySegment.NULL.equals(ocspResponse)) { + if (MemorySegment.NULL.equals(ocspResponse)) { + // Failed to get a valid response + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_APPLICATION_VERIFICATION()); + } else { if (OCSP_response_status(ocspResponse) == OCSP_RESPONSE_STATUS_SUCCESSFUL()) { basicResponse = OCSP_response_get1_basic(ocspResponse); + if (OCSP_check_nonce(ocspRequest, basicResponse) == 0) { + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_OCSP_RESP_INVALID()); + return V_OCSP_CERTSTATUS_UNKNOWN(); + } + MemorySegment certStack = OCSP_resp_get0_certs(basicResponse); + if (OCSP_basic_verify(basicResponse, certStack, X509_STORE_CTX_get0_store(x509ctx), state.ocspVerifyFlags) <= 0) { + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_OCSP_SIGNATURE_FAILURE()); + return V_OCSP_CERTSTATUS_UNKNOWN(); + } certId = OCSP_cert_to_id(MemorySegment.NULL, x509, issuer); if (MemorySegment.NULL.equals(certId)) { + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_OCSP_RESP_INVALID()); return V_OCSP_CERTSTATUS_UNKNOWN(); } // Find by serial number and get the matching response - MemorySegment singleResponse = OCSP_resp_get0(basicResponse, OCSP_resp_find(basicResponse, certId, -1)); - return OCSP_single_get0_status(singleResponse, MemorySegment.NULL, - MemorySegment.NULL, MemorySegment.NULL, MemorySegment.NULL); + MemorySegment singleResponse = + OCSP_resp_get0(basicResponse, OCSP_resp_find(basicResponse, certId, -1)); + MemorySegment thisUpdatePointer = localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL); + MemorySegment nextUpdatePointer = localArena.allocateFrom(ValueLayout.ADDRESS, MemorySegment.NULL); + int status = OCSP_single_get0_status(singleResponse, MemorySegment.NULL, MemorySegment.NULL, + thisUpdatePointer, nextUpdatePointer); + if (OCSP_check_validity(thisUpdatePointer.get(ValueLayout.ADDRESS, 0), + nextUpdatePointer.get(ValueLayout.ADDRESS, 0), OCSP_MAX_SKEW, -1) <= 0) { + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_OCSP_NOT_YET_VALID()); + return V_OCSP_CERTSTATUS_UNKNOWN(); + } + if (OCSP_check_validity(thisUpdatePointer.get(ValueLayout.ADDRESS, 0), + nextUpdatePointer.get(ValueLayout.ADDRESS, 0), OCSP_MAX_SKEW, OCSP_MAX_SKEW) <= 0) { + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_OCSP_HAS_EXPIRED()); + return V_OCSP_CERTSTATUS_UNKNOWN(); + } + return status; } } + } catch (IOException ioe) { + // Timeout or network error. Responder is not available. + log.warn(sm.getString("engine.ocspRequestError", url.toString()), ioe); + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_UNABLE_TO_GET_CRL()); } catch (Exception e) { log.warn(sm.getString("engine.ocspRequestError", url.toString()), e); + X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_APPLICATION_VERIFICATION()); } finally { - if (MemorySegment.NULL.equals(ocspResponse)) { - // Failed to get a valid response - X509_STORE_CTX_set_error(x509ctx, X509_V_ERR_APPLICATION_VERIFICATION()); - } OCSP_CERTID_free(certId); OCSP_BASICRESP_free(basicResponse); OCSP_RESPONSE_free(ocspResponse); @@ -1397,7 +1435,7 @@ private class OpenSSLSession implements SSLSession { // lazy init for memory reasons - private Map values; + private Map values; // Last accessed time private long lastAccessedTime = -1; @@ -1415,8 +1453,8 @@ } MemorySegment sessionId = SSL_SESSION_get_id(session, lenPointer); int len = lenPointer.get(ValueLayout.JAVA_INT, 0); - id = (len == 0) ? new byte[0] - : sessionId.reinterpret(len, localArena, null).toArray(ValueLayout.JAVA_BYTE); + id = (len == 0) ? new byte[0] : + sessionId.reinterpret(len, localArena, null).toArray(ValueLayout.JAVA_BYTE); } } } @@ -1467,7 +1505,7 @@ if (value == null) { throw new IllegalArgumentException(sm.getString("engine.nullValue")); } - Map values = this.values; + Map values = this.values; if (values == null) { // Use size of 2 to keep the memory overhead small values = this.values = new HashMap<>(2); @@ -1495,7 +1533,7 @@ if (name == null) { throw new IllegalArgumentException(sm.getString("engine.nullName")); } - Map values = this.values; + Map values = this.values; if (values == null) { return; } @@ -1505,7 +1543,7 @@ @Override public String[] getValueNames() { - Map values = this.values; + Map values = this.values; if (values == null || values.isEmpty()) { return new String[0]; } @@ -1531,7 +1569,8 @@ } chain = getPeerCertChain(); if (!clientMode) { - // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate. + // if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer + // certificate. // We use SSL_get_peer_certificate to get it in this case and add it to our array later. // See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html clientCert = getPeerCertificate(); @@ -1592,7 +1631,7 @@ } private Principal principal(Certificate[] certs) { - return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal(); + return ((java.security.cert.X509Certificate) certs[0]).getSubjectX500Principal(); } @Override @@ -1671,22 +1710,26 @@ private final MemorySegment networkBIO; private final int certificateVerificationDepth; private final boolean noOcspCheck; + private final boolean ocspSoftFail; + private final int ocspTimeout; + private final int ocspVerifyFlags; private PHAState phaState = PHAState.NONE; private int certificateVerifyMode = 0; private int handshakeCount = 0; - private EngineState(MemorySegment ssl, MemorySegment networkBIO, - int certificateVerificationDepth, boolean noOcspCheck) { + private EngineState(MemorySegment ssl, MemorySegment networkBIO, int certificateVerificationDepth, + boolean noOcspCheck, boolean ocspSoftFail, int ocspTimeout, int ocspVerifyFlags) { states.put(Long.valueOf(ssl.address()), this); this.certificateVerificationDepth = certificateVerificationDepth; this.noOcspCheck = noOcspCheck; + this.ocspSoftFail = ocspSoftFail; + this.ocspTimeout = ocspTimeout; + this.ocspVerifyFlags = ocspVerifyFlags; // Use another arena to avoid keeping a reference through segments // This also allows making further accesses to the main pointers safer - this.ssl = ssl.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, - (MemorySegment t) -> SSL_free(t)); - this.networkBIO = networkBIO.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, - (MemorySegment t) -> BIO_free(t)); + this.ssl = ssl.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, openssl_h::SSL_free); + this.networkBIO = networkBIO.reinterpret(ValueLayout.ADDRESS.byteSize(), stateArena, openssl_h::BIO_free); } @Override diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLImplementation.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLImplementation.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLImplementation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLImplementation.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,7 +30,7 @@ public class OpenSSLImplementation extends SSLImplementation { @Override - public SSLSupport getSSLSupport(SSLSession session, Map> additionalAttributes) { + public SSLSupport getSSLSupport(SSLSession session, Map> additionalAttributes) { return new JSSESupport(session, additionalAttributes); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLLibrary.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,12 +34,10 @@ import org.apache.tomcat.util.res.StringManager; - /** - * Implementation of a global initialization of OpenSSL according to specified - * configuration parameters. - * Using this from a listener is completely optional, but is needed for - * configuration and full cleanup of a few native memory allocations. + * Implementation of a global initialization of OpenSSL according to specified configuration parameters. Using this from + * a listener is completely optional, but is needed for configuration and full cleanup of a few native memory + * allocations. */ public class OpenSSLLibrary { @@ -52,20 +50,19 @@ // ---------------------------------------------- Properties - protected static String SSLEngine = "on"; //default on + protected static String SSLEngine = "on"; // default on protected static String FIPSMode = "off"; // default off, valid only when SSLEngine="on" protected static String SSLRandomSeed = "builtin"; protected static boolean fipsModeActive = false; /** - * The "FIPS mode" level that we use as the argument to OpenSSL method - * FIPS_mode_set() to enable FIPS mode and that we expect as - * the return value of FIPS_mode() when FIPS mode is enabled. + * The "FIPS mode" level that we use as the argument to OpenSSL method FIPS_mode_set() to enable FIPS + * mode and that we expect as the return value of FIPS_mode() when FIPS mode is enabled. *

    - * In the future the OpenSSL library might grow support for different - * non-zero "FIPS" modes that specify different allowed subsets of ciphers - * or whatever, but nowadays only "1" is the supported value. + * In the future the OpenSSL library might grow support for different non-zero "FIPS" modes that specify different + * allowed subsets of ciphers or whatever, but nowadays only "1" is the supported value. *

    + * * @see OpenSSL method FIPS_mode_set() * @see OpenSSL method FIPS_mode() */ @@ -92,21 +89,20 @@ } /* - { BN_get_rfc3526_prime_8192, NULL, 6145 }, - { BN_get_rfc3526_prime_6144, NULL, 4097 }, - { BN_get_rfc3526_prime_4096, NULL, 3073 }, - { BN_get_rfc3526_prime_3072, NULL, 2049 }, - { BN_get_rfc3526_prime_2048, NULL, 1025 }, - { BN_get_rfc2409_prime_1024, NULL, 0 } + * { BN_get_rfc3526_prime_8192, NULL, 6145 }, { BN_get_rfc3526_prime_6144, NULL, 4097 }, { + * BN_get_rfc3526_prime_4096, NULL, 3073 }, { BN_get_rfc3526_prime_3072, NULL, 2049 }, { BN_get_rfc3526_prime_2048, + * NULL, 1025 }, { BN_get_rfc2409_prime_1024, NULL, 0 } */ static final class DHParam { final MemorySegment dh; final int min; + private DHParam(MemorySegment dh, int min) { this.dh = dh; this.min = min; } } + static final DHParam[] dhParameters = new DHParam[6]; private static void initDHParameters() { @@ -182,6 +178,8 @@ initLibrary(); OpenSSLStatus.setVersion(OpenSSL_version_num()); + OpenSSLStatus.setMajorVersion(openssl_h_Compatibility.MAJOR); + OpenSSLStatus.setMinorVersion(openssl_h_Compatibility.MINOR); if (openssl_h_Compatibility.OPENSSL3) { OpenSSLStatus.setName(OpenSSLStatus.Name.OPENSSL3); } else if (openssl_h_Compatibility.OPENSSL) { @@ -205,8 +203,9 @@ if (MemorySegment.NULL.equals(enginePointer)) { enginePointer = ENGINE_by_id(memorySession.allocateFrom("dynamic")); if (enginePointer != null) { - if (ENGINE_ctrl_cmd_string(enginePointer, memorySession.allocateFrom("SO_PATH"), engine, 0) == 0 - || ENGINE_ctrl_cmd_string(enginePointer, memorySession.allocateFrom("LOAD"), + if (ENGINE_ctrl_cmd_string(enginePointer, memorySession.allocateFrom("SO_PATH"), engine, + 0) == 0 || + ENGINE_ctrl_cmd_string(enginePointer, memorySession.allocateFrom("LOAD"), MemorySegment.NULL, 0) == 0) { // Engine load error ENGINE_free(enginePointer); @@ -229,11 +228,12 @@ // Set the random seed, translated to the Java way boolean seedDone = false; - if (SSLRandomSeed != null && SSLRandomSeed.length() != 0 && !"builtin".equals(SSLRandomSeed)) { + if (SSLRandomSeed != null && !SSLRandomSeed.isEmpty() && !"builtin".equals(SSLRandomSeed)) { var randomSeed = memorySession.allocateFrom(SSLRandomSeed); seedDone = RAND_load_file(randomSeed, 128) > 0; if (!seedDone) { - log.warn(sm.getString("openssllibrary.errorSettingSSLRandomSeed", SSLRandomSeed, OpenSSLLibrary.getLastError())); + log.warn(sm.getString("openssllibrary.errorSettingSSLRandomSeed", SSLRandomSeed, + OpenSSLLibrary.getLastError())); } } if (!seedDone) { @@ -252,7 +252,8 @@ final boolean enterFipsMode; int fipsModeState = FIPS_OFF; if (openssl_h_Compatibility.OPENSSL3) { - var md = EVP_MD_fetch(MemorySegment.NULL, memorySession.allocateFrom("SHA-512"), MemorySegment.NULL); + var md = EVP_MD_fetch(MemorySegment.NULL, memorySession.allocateFrom("SHA-512"), + MemorySegment.NULL); var provider = EVP_MD_get0_provider(md); String name = OSSL_PROVIDER_get0_name(provider).getString(0); EVP_MD_free(md); @@ -263,7 +264,7 @@ fipsModeState = FIPS_mode(); } - if(log.isDebugEnabled()) { + if (log.isDebugEnabled()) { log.debug(sm.getString("openssllibrary.currentFIPSMode", Integer.valueOf(fipsModeState))); } @@ -281,7 +282,8 @@ enterFipsMode = false; } else { if (openssl_h_Compatibility.OPENSSL3) { - throw new IllegalStateException(sm.getString("openssllibrary.FIPSProviderNotDefault", FIPSMode)); + throw new IllegalStateException( + sm.getString("openssllibrary.FIPSProviderNotDefault", FIPSMode)); } else { enterFipsMode = true; } @@ -292,7 +294,8 @@ enterFipsMode = false; } else { if (openssl_h_Compatibility.OPENSSL3) { - throw new IllegalStateException(sm.getString("openssllibrary.FIPSProviderNotDefault", FIPSMode)); + throw new IllegalStateException( + sm.getString("openssllibrary.FIPSProviderNotDefault", FIPSMode)); } else { throw new IllegalStateException(sm.getString("openssllibrary.requireNotInFIPSMode")); } @@ -300,7 +303,8 @@ } else if ("enter".equalsIgnoreCase(FIPSMode)) { if (fipsModeState == FIPS_OFF) { if (openssl_h_Compatibility.OPENSSL3) { - throw new IllegalStateException(sm.getString("openssllibrary.FIPSProviderNotDefault", FIPSMode)); + throw new IllegalStateException( + sm.getString("openssllibrary.FIPSProviderNotDefault", FIPSMode)); } else { enterFipsMode = true; } @@ -309,13 +313,12 @@ fipsModeActive = true; enterFipsMode = false; } else { - throw new IllegalStateException(sm.getString( - "openssllibrary.enterAlreadyInFIPSMode", Integer.valueOf(fipsModeState))); + throw new IllegalStateException(sm.getString("openssllibrary.enterAlreadyInFIPSMode", + Integer.valueOf(fipsModeState))); } } } else { - throw new IllegalArgumentException(sm.getString( - "openssllibrary.wrongFIPSMode", FIPSMode)); + throw new IllegalArgumentException(sm.getString("openssllibrary.wrongFIPSMode", FIPSMode)); } if (enterFipsMode) { @@ -383,8 +386,7 @@ if (!SSLEngine.equals(OpenSSLLibrary.SSLEngine)) { // Ensure that the SSLEngine is consistent with that used for SSL init if (OpenSSLStatus.isInitialized()) { - throw new IllegalStateException( - sm.getString("openssllibrary.tooLateForSSLEngine")); + throw new IllegalStateException(sm.getString("openssllibrary.tooLateForSSLEngine")); } OpenSSLLibrary.SSLEngine = SSLEngine; @@ -399,8 +401,7 @@ if (!SSLRandomSeed.equals(OpenSSLLibrary.SSLRandomSeed)) { // Ensure that the random seed is consistent with that used for SSL init if (OpenSSLStatus.isInitialized()) { - throw new IllegalStateException( - sm.getString("openssllibrary.tooLateForSSLRandomSeed")); + throw new IllegalStateException(sm.getString("openssllibrary.tooLateForSSLRandomSeed")); } OpenSSLLibrary.SSLRandomSeed = SSLRandomSeed; @@ -415,8 +416,7 @@ if (!FIPSMode.equals(OpenSSLLibrary.FIPSMode)) { // Ensure that the FIPS mode is consistent with that used for SSL init if (OpenSSLStatus.isInitialized()) { - throw new IllegalStateException( - sm.getString("openssllibrary.tooLateForFIPSMode")); + throw new IllegalStateException(sm.getString("openssllibrary.tooLateForFIPSMode")); } OpenSSLLibrary.FIPSMode = FIPSMode; @@ -440,7 +440,7 @@ try { for (String c : getCiphers(ssl)) { // Filter out bad input. - if (c == null || c.length() == 0 || ciphersList.contains(c)) { + if (c == null || c.isEmpty() || ciphersList.contains(c)) { continue; } ciphersList.add(OpenSSLCipherConfigurationParser.openSSLToJsse(c)); @@ -461,7 +461,7 @@ MemorySegment sk = SSL_get_ciphers(ssl); int len = openssl_h_Compatibility.OPENSSL_sk_num(sk); if (len <= 0) { - return null; + return new String[0]; } ArrayList ciphers = new ArrayList<>(len); for (int i = 0; i < len; i++) { @@ -475,12 +475,11 @@ private static final int OPENSSL_ERROR_MESSAGE_BUFFER_SIZE = 256; /** - * Many calls to SSL methods do not check the last error. Those that do - * check the last error need to ensure that any previously ignored error is - * cleared prior to the method call else errors may be falsely reported. - * Ideally, before any SSL_read, SSL_write, clearLastError should always - * be called, and getLastError should be called after on any negative or - * zero result. + * Many calls to SSL methods do not check the last error. Those that do check the last error need to ensure that any + * previously ignored error is cleared prior to the method call else errors may be falsely reported. Ideally, before + * any SSL_read, SSL_write, clearLastError should always be called, and getLastError should be called after on any + * negative or zero result. + * * @return the first error in the stack */ static String getLastError() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionContext.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionContext.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -65,8 +65,8 @@ throw new IllegalArgumentException(sm.getString("sessionContext.nullTicketKeys")); } if (keys.length != TICKET_KEYS_SIZE) { - throw new IllegalArgumentException(sm.getString("sessionContext.invalidTicketKeysLength", - Integer.valueOf(keys.length))); + throw new IllegalArgumentException( + sm.getString("sessionContext.invalidTicketKeysLength", Integer.valueOf(keys.length))); } try (var memorySession = Arena.ofConfined()) { var array = memorySession.allocateFrom(ValueLayout.JAVA_BYTE, keys); @@ -85,8 +85,7 @@ } /** - * @return {@code true} if caching of SSL sessions is enabled, {@code false} - * otherwise. + * @return {@code true} if caching of SSL sessions is enabled, {@code false} otherwise. */ public boolean isSessionCacheEnabled() { return SSL_CTX_get_session_cache_mode(context.getSSLContext()) == SSL_SESS_CACHE_SERVER(); @@ -126,12 +125,13 @@ } /** - * Set the context within which session be reused (server side only) - * See - * man SSL_CTX_set_session_id_context + * Set the context within which session be reused (server side only) See + * man + * SSL_CTX_set_session_id_context + * + * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name of the application + * and/or the hostname and/or service name * - * @param sidCtx can be any kind of binary data, it is therefore possible to use e.g. the name - * of the application and/or the hostname and/or service name * @return {@code true} if success, {@code false} otherwise. */ public boolean setSessionIdContext(byte[] sidCtx) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionStats.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionStats.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionStats.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLSessionStats.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,21 +44,21 @@ * @return The number of started SSL/TLS handshakes in client mode. */ public long connect() { - return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CONNECT() ,0, null); + return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CONNECT(), 0, null); } /** * @return The number of successfully established SSL/TLS sessions in client mode. */ public long connectGood() { - return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CONNECT_GOOD() , 0, null); + return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CONNECT_GOOD(), 0, null); } /** * @return The number of start renegotiations in client mode. */ public long connectRenegotiate() { - return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CONNECT_RENEGOTIATE() , 0, null); + return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CONNECT_RENEGOTIATE(), 0, null); } /** @@ -83,44 +83,40 @@ } /** - * @return The number of successfully reused sessions. In client mode, a - * session set with {@code SSL_set_session} successfully reused is - * counted as a hit. In server mode, a session successfully - * retrieved from internal or external cache is counted as a hit. + * @return The number of successfully reused sessions. In client mode, a session set with {@code SSL_set_session} + * successfully reused is counted as a hit. In server mode, a session successfully retrieved from + * internal or external cache is counted as a hit. */ public long hits() { return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_HIT(), 0, null); } /** - * @return The number of successfully retrieved sessions from the external - * session cache in server mode. + * @return The number of successfully retrieved sessions from the external session cache in server mode. */ public long cbHits() { return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CB_HIT(), 0, null); } /** - * @return The number of sessions proposed by clients that were not found in - * the internal session cache in server mode. + * @return The number of sessions proposed by clients that were not found in the internal session cache in server + * mode. */ public long misses() { return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_MISSES(), 0, null); } /** - * @return The number of sessions proposed by clients and either found in - * the internal or external session cache in server mode, but that - * were invalid due to timeout. These sessions are not included in - * the {@link #hits()} count. + * @return The number of sessions proposed by clients and either found in the internal or external session cache in + * server mode, but that were invalid due to timeout. These sessions are not included in the + * {@link #hits()} count. */ public long timeouts() { return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_TIMEOUTS(), 0, null); } /** - * @return The number of sessions that were removed because the maximum - * session cache size was exceeded. + * @return The number of sessions that were removed because the maximum session cache size was exceeded. */ public long cacheFull() { return SSL_CTX_ctrl(ctx, SSL_CTRL_SESS_CACHE_FULL(), 0, null); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ package org.apache.tomcat.util.net.openssl.panama; import java.io.IOException; +import java.security.KeyException; import java.security.KeyStoreException; import java.util.List; import java.util.Set; @@ -80,20 +81,20 @@ // No (or invalid?) certificate chain was provided for the cert String msg = sm.getString("openssl.nonJsseChain", certificate.getCertificateChainFile()); if (log.isDebugEnabled()) { - log.info(msg, e); + log.debug(msg, e); } else { log.info(msg); } return null; - } catch (KeyStoreException | IOException e) { + } catch (KeyStoreException | KeyException | IOException e) { // Depending on what is presented, JSSE may also throw // KeyStoreException or IOException if it doesn't understand the // provided file. if (certificate.getCertificateFile() != null) { - String msg = sm.getString("openssl.nonJsseCertificate", - certificate.getCertificateFile(), certificate.getCertificateKeyFile()); + String msg = sm.getString("openssl.nonJsseCertificate", certificate.getCertificateFile(), + certificate.getCertificateKeyFile()); if (log.isDebugEnabled()) { - log.info(msg, e); + log.debug(msg, e); } else { log.info(msg); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLX509Certificate.java tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLX509Certificate.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLX509Certificate.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLX509Certificate.java 2026-01-23 19:33:36.000000000 +0000 @@ -134,16 +134,14 @@ } @Override - public void verify(PublicKey key) - throws CertificateException, NoSuchAlgorithmException, - InvalidKeyException, NoSuchProviderException, SignatureException { + public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, + NoSuchProviderException, SignatureException { unwrap().verify(key); } @Override - public void verify(PublicKey key, String sigProvider) - throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, - NoSuchProviderException, SignatureException { + public void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, + InvalidKeyException, NoSuchProviderException, SignatureException { unwrap().verify(key, sigProvider); } @@ -181,8 +179,8 @@ X509Certificate wrapped = this.wrapped; if (wrapped == null) { try { - wrapped = this.wrapped = (X509Certificate) OpenSSLContext.X509_CERT_FACTORY.generateCertificate( - new ByteArrayInputStream(bytes)); + wrapped = this.wrapped = (X509Certificate) OpenSSLContext.X509_CERT_FACTORY + .generateCertificate(new ByteArrayInputStream(bytes)); } catch (CertificateException e) { throw new IllegalStateException(e); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_CTX_set_alpn_select_cb$cb.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_alpn_select_cb$cb.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_CTX_set_alpn_select_cb$cb.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_alpn_select_cb$cb.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,32 +26,26 @@ import java.lang.invoke.MethodHandle; /** - * {@snippet lang=c : - * SSL_CTX_alpn_select_cb_func cb + * {@snippet lang = c : * SSL_CTX_alpn_select_cb_func cb * } */ @SuppressWarnings("javadoc") public class SSL_CTX_set_alpn_select_cb$cb { public interface Function { - int apply(MemorySegment _x0, MemorySegment _x1, MemorySegment _x2, MemorySegment _x3, int _x4, MemorySegment _x5); + int apply(MemorySegment _x0, MemorySegment _x1, MemorySegment _x2, MemorySegment _x3, int _x4, + MemorySegment _x5); } - private static final FunctionDescriptor $DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_POINTER - ); + private static final FunctionDescriptor $DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT, openssl_h.C_POINTER); public static FunctionDescriptor descriptor() { return $DESC; } - private static final MethodHandle UP$MH = openssl_h.upcallHandle(SSL_CTX_set_alpn_select_cb$cb.Function.class, "apply", $DESC); + private static final MethodHandle UP$MH = + openssl_h.upcallHandle(SSL_CTX_set_alpn_select_cb$cb.Function.class, "apply", $DESC); public static MemorySegment allocate(SSL_CTX_set_alpn_select_cb$cb.Function fi, Arena scope) { return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, scope); @@ -59,7 +53,8 @@ private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); - public static int invoke(MemorySegment funcPtr,MemorySegment _x0, MemorySegment _x1, MemorySegment _x2, MemorySegment _x3, int _x4, MemorySegment _x5) { + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1, MemorySegment _x2, + MemorySegment _x3, int _x4, MemorySegment _x5) { try { return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2, _x3, _x4, _x5); } catch (Throwable ex$) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_CTX_set_cert_verify_callback$cb.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_cert_verify_callback$cb.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_CTX_set_cert_verify_callback$cb.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_cert_verify_callback$cb.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,7 @@ import java.lang.invoke.MethodHandle; /** - * {@snippet lang=c : - * int (*cb)(X509_STORE_CTX *, void *) + * {@snippet lang = c : * int (*cb)(X509_STORE_CTX *, void *) * } */ @SuppressWarnings("javadoc") @@ -37,17 +36,15 @@ int apply(MemorySegment _x0, MemorySegment _x1); } - private static final FunctionDescriptor $DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); + private static final FunctionDescriptor $DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); public static FunctionDescriptor descriptor() { return $DESC; } - private static final MethodHandle UP$MH = openssl_h.upcallHandle(SSL_CTX_set_cert_verify_callback$cb.Function.class, "apply", $DESC); + private static final MethodHandle UP$MH = + openssl_h.upcallHandle(SSL_CTX_set_cert_verify_callback$cb.Function.class, "apply", $DESC); public static MemorySegment allocate(SSL_CTX_set_cert_verify_callback$cb.Function fi, Arena scope) { return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, scope); @@ -55,7 +52,7 @@ private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); - public static int invoke(MemorySegment funcPtr,MemorySegment _x0, MemorySegment _x1) { + public static int invoke(MemorySegment funcPtr, MemorySegment _x0, MemorySegment _x1) { try { return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); } catch (Throwable ex$) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_CTX_set_tmp_dh_callback$dh.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_tmp_dh_callback$dh.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_CTX_set_tmp_dh_callback$dh.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_tmp_dh_callback$dh.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,7 @@ import java.lang.invoke.MethodHandle; /** - * {@snippet lang=c : - * DH *(*dh)(SSL *, int, int) + * {@snippet lang = c : * DH *(*dh)(SSL *, int, int) * } */ @SuppressWarnings("javadoc") @@ -37,18 +36,15 @@ MemorySegment apply(MemorySegment _x0, int _x1, int _x2); } - private static final FunctionDescriptor $DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_INT - ); + private static final FunctionDescriptor $DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT, openssl_h.C_INT); public static FunctionDescriptor descriptor() { return $DESC; } - private static final MethodHandle UP$MH = openssl_h.upcallHandle(SSL_CTX_set_tmp_dh_callback$dh.Function.class, "apply", $DESC); + private static final MethodHandle UP$MH = + openssl_h.upcallHandle(SSL_CTX_set_tmp_dh_callback$dh.Function.class, "apply", $DESC); public static MemorySegment allocate(SSL_CTX_set_tmp_dh_callback$dh.Function fi, Arena scope) { return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, scope); @@ -56,7 +52,7 @@ private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); - public static MemorySegment invoke(MemorySegment funcPtr,MemorySegment _x0, int _x1, int _x2) { + public static MemorySegment invoke(MemorySegment funcPtr, MemorySegment _x0, int _x1, int _x2) { try { return (MemorySegment) DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); } catch (Throwable ex$) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_CTX_set_verify$callback.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_verify$callback.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_CTX_set_verify$callback.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_CTX_set_verify$callback.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,7 @@ import java.lang.invoke.MethodHandle; /** - * {@snippet lang=c : - * SSL_verify_cb callback + * {@snippet lang = c : * SSL_verify_cb callback * } */ @SuppressWarnings("javadoc") @@ -37,17 +36,15 @@ int apply(int _x0, MemorySegment _x1); } - private static final FunctionDescriptor $DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_INT, - openssl_h.C_POINTER - ); + private static final FunctionDescriptor $DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_INT, openssl_h.C_POINTER); public static FunctionDescriptor descriptor() { return $DESC; } - private static final MethodHandle UP$MH = openssl_h.upcallHandle(SSL_CTX_set_verify$callback.Function.class, "apply", $DESC); + private static final MethodHandle UP$MH = + openssl_h.upcallHandle(SSL_CTX_set_verify$callback.Function.class, "apply", $DESC); public static MemorySegment allocate(SSL_CTX_set_verify$callback.Function fi, Arena scope) { return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, scope); @@ -55,7 +52,7 @@ private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); - public static int invoke(MemorySegment funcPtr,int _x0, MemorySegment _x1) { + public static int invoke(MemorySegment funcPtr, int _x0, MemorySegment _x1) { try { return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); } catch (Throwable ex$) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_set_info_callback$cb.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_set_info_callback$cb.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_set_info_callback$cb.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_set_info_callback$cb.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,7 @@ import java.lang.invoke.MethodHandle; /** - * {@snippet lang=c : - * void (*cb)(const SSL *, int, int) + * {@snippet lang = c : * void (*cb)(const SSL *, int, int) * } */ @SuppressWarnings("javadoc") @@ -37,17 +36,15 @@ void apply(MemorySegment _x0, int _x1, int _x2); } - private static final FunctionDescriptor $DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_INT - ); + private static final FunctionDescriptor $DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_INT, openssl_h.C_INT); public static FunctionDescriptor descriptor() { return $DESC; } - private static final MethodHandle UP$MH = openssl_h.upcallHandle(SSL_set_info_callback$cb.Function.class, "apply", $DESC); + private static final MethodHandle UP$MH = + openssl_h.upcallHandle(SSL_set_info_callback$cb.Function.class, "apply", $DESC); public static MemorySegment allocate(SSL_set_info_callback$cb.Function fi, Arena scope) { return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, scope); @@ -55,9 +52,9 @@ private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); - public static void invoke(MemorySegment funcPtr,MemorySegment _x0, int _x1, int _x2) { + public static void invoke(MemorySegment funcPtr, MemorySegment _x0, int _x1, int _x2) { try { - DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); + DOWN$MH.invokeExact(funcPtr, _x0, _x1, _x2); } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_set_verify$callback.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_set_verify$callback.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/SSL_set_verify$callback.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/SSL_set_verify$callback.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,7 @@ import java.lang.invoke.MethodHandle; /** - * {@snippet lang=c : - * SSL_verify_cb callback + * {@snippet lang = c : * SSL_verify_cb callback * } */ @SuppressWarnings("javadoc") @@ -37,17 +36,15 @@ int apply(int _x0, MemorySegment _x1); } - private static final FunctionDescriptor $DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_INT, - openssl_h.C_POINTER - ); + private static final FunctionDescriptor $DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_INT, openssl_h.C_POINTER); public static FunctionDescriptor descriptor() { return $DESC; } - private static final MethodHandle UP$MH = openssl_h.upcallHandle(SSL_set_verify$callback.Function.class, "apply", $DESC); + private static final MethodHandle UP$MH = + openssl_h.upcallHandle(SSL_set_verify$callback.Function.class, "apply", $DESC); public static MemorySegment allocate(SSL_set_verify$callback.Function fi, Arena scope) { return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, scope); @@ -55,7 +52,7 @@ private static final MethodHandle DOWN$MH = Linker.nativeLinker().downcallHandle($DESC); - public static int invoke(MemorySegment funcPtr,int _x0, MemorySegment _x1) { + public static int invoke(MemorySegment funcPtr, int _x0, MemorySegment _x1) { try { return (int) DOWN$MH.invokeExact(funcPtr, _x0, _x1); } catch (Throwable ex$) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/openssl_h.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/openssl_h.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/openssl_h.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/openssl_h.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,65 +29,23 @@ import java.lang.foreign.*; import static java.lang.foreign.ValueLayout.*; -@SuppressWarnings({"javadoc", "boxing"}) +@SuppressWarnings({ "javadoc", "boxing" }) public class openssl_h { - /* - * On Mac OS SymbolLookup.libraryLookup() appears to ignore java.library.path which means the LibreSSL - * library will be found which will then fail. Therefore, skip that lookup on Mac OS. - */ - public static final boolean USE_SYSTEM_LOAD_LIBRARY = Boolean.getBoolean("org.apache.tomcat.util.openssl.USE_SYSTEM_LOAD_LIBRARY"); - public static final String LIBRARY_NAME = System.getProperty("org.apache.tomcat.util.openssl.LIBRARY_NAME", - (JrePlatform.IS_MAC_OS) ? "ssl.48" : "ssl"); - openssl_h() { - // Suppresses public default constructor, ensuring non-instantiability, - // but allows generated subclasses in same package. + // Should not be called directly } - public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; - public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; - public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; - public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; - public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final AddressLayout C_POINTER = ValueLayout.ADDRESS - .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); - public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; - static final Arena LIBRARY_ARENA = Arena.ofAuto(); static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); - static final SymbolLookup SYMBOL_LOOKUP; - static { - if (USE_SYSTEM_LOAD_LIBRARY) { - System.loadLibrary(LIBRARY_NAME); - SYMBOL_LOOKUP = SymbolLookup.loaderLookup().or(Linker.nativeLinker().defaultLookup()); - } else { - SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName(LIBRARY_NAME), LIBRARY_ARENA) - .or(SymbolLookup.loaderLookup()) - .or(Linker.nativeLinker().defaultLookup()); - } - } static void traceDowncall(String name, Object... args) { - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("%s(%s)\n", name, traceArgs); + String traceArgs = Arrays.stream(args).map(Object::toString).collect(Collectors.joining(", ")); + System.out.printf("%s(%s)\n", name, traceArgs); } static MemorySegment findOrThrow(String symbol) { - return SYMBOL_LOOKUP.find(symbol) - .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: " + symbol)); - } - - static MemoryLayout[] inferVariadicLayouts(Object[] varargs) { - MemoryLayout[] result = new MemoryLayout[varargs.length]; - for (int i = 0; i < varargs.length; i++) { - result[i] = variadicLayout(varargs[i].getClass()); - } - return result; + return SYMBOL_LOOKUP.find(symbol).orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: " + symbol)); } static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { @@ -98,6382 +56,10111 @@ } } - static MethodHandle downcallHandleVariadic(String name, FunctionDescriptor baseDesc, MemoryLayout[] variadicLayouts) { - FunctionDescriptor variadicDesc = baseDesc.appendArgumentLayouts(variadicLayouts); - Linker.Option fva = Linker.Option.firstVariadicArg(baseDesc.argumentLayouts().size()); - return SYMBOL_LOOKUP.find(name) - .map(addr -> Linker.nativeLinker().downcallHandle(addr, variadicDesc, fva) - .asSpreader(Object[].class, variadicLayouts.length)) - .orElse(null); - } - - // Internals only below this point - - private static MemoryLayout variadicLayout(Class c) { - // apply default argument promotions per C spec - // note that all primitives are boxed, since they are passed through an Object[] - if (c == Boolean.class || c == Byte.class || c == Character.class || c == Short.class || c == Integer.class) { - return JAVA_INT; - } else if (c == Long.class) { - return JAVA_LONG; - } else if (c == Float.class || c == Double.class) { - return JAVA_DOUBLE; - } else if (MemorySegment.class.isAssignableFrom(c)) { - return ADDRESS; + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = + g.memberLayouts().stream().map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? MemoryLayout.structLayout(alignedMembers) : + MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } + + /* + * On macOS SymbolLookup.libraryLookup() appears to ignore java.library.path which means the LibreSSL library will + * be found which will then fail. Therefore, skip that lookup on macOS. On other platforms this can also be used to + * give more flexibility when testing. + */ + public static final boolean USE_SYSTEM_LOAD_LIBRARY = + Boolean.getBoolean("org.apache.tomcat.util.openssl.USE_SYSTEM_LOAD_LIBRARY"); + public static final String CRYPTO_LIBRARY_NAME = + System.getProperty("org.apache.tomcat.util.openssl.CRYPTO_LIBRARY_NAME"); + public static final String LIBRARY_NAME = System.getProperty("org.apache.tomcat.util.openssl.LIBRARY_NAME", + (JrePlatform.IS_MAC_OS) ? "ssl.48" : "ssl"); + + static final SymbolLookup SYMBOL_LOOKUP; + static { + if (USE_SYSTEM_LOAD_LIBRARY) { + if (CRYPTO_LIBRARY_NAME != null) { + System.loadLibrary(CRYPTO_LIBRARY_NAME); + } + System.loadLibrary(LIBRARY_NAME); + SYMBOL_LOOKUP = SymbolLookup.loaderLookup().or(Linker.nativeLinker().defaultLookup()); + } else { + SYMBOL_LOOKUP = SymbolLookup.libraryLookup(System.mapLibraryName(LIBRARY_NAME), LIBRARY_ARENA) + .or(SymbolLookup.loaderLookup()).or(Linker.nativeLinker().defaultLookup()); } - throw new IllegalArgumentException("Invalid type for ABI: " + c.getTypeName()); } - private static final int BIO_CLOSE = (int)1L; + + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout C_POINTER = + ValueLayout.ADDRESS.withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; + private static final int BIO_CLOSE = (int) 1L; + /** - * {@snippet lang=c : - * #define BIO_CLOSE 1 + * {@snippet lang = c : * #define BIO_CLOSE 1 * } */ public static int BIO_CLOSE() { return BIO_CLOSE; } - private static final int BIO_CTRL_RESET = (int)1L; + + private static final int BIO_CTRL_RESET = (int) 1L; + /** - * {@snippet lang=c : - * #define BIO_CTRL_RESET 1 + * {@snippet lang = c : * #define BIO_CTRL_RESET 1 * } */ public static int BIO_CTRL_RESET() { return BIO_CTRL_RESET; } - private static final int BIO_FP_READ = (int)2L; + + private static final int BIO_FP_READ = (int) 2L; + /** - * {@snippet lang=c : - * #define BIO_FP_READ 2 + * {@snippet lang = c : * #define BIO_FP_READ 2 * } */ public static int BIO_FP_READ() { return BIO_FP_READ; } - private static final int BIO_C_SET_FILENAME = (int)108L; + + private static final int BIO_C_SET_FILENAME = (int) 108L; + /** - * {@snippet lang=c : - * #define BIO_C_SET_FILENAME 108 + * {@snippet lang = c : * #define BIO_C_SET_FILENAME 108 * } */ public static int BIO_C_SET_FILENAME() { return BIO_C_SET_FILENAME; } - private static final int NID_info_access = (int)177L; + + private static final int NID_info_access = (int) 177L; + /** - * {@snippet lang=c : - * #define NID_info_access 177 + * {@snippet lang = c : * #define NID_info_access 177 * } */ public static int NID_info_access() { return NID_info_access; } - private static final int X509_FILETYPE_PEM = (int)1L; + + private static final int X509_FILETYPE_PEM = (int) 1L; + /** - * {@snippet lang=c : - * #define X509_FILETYPE_PEM 1 + * {@snippet lang = c : * #define X509_FILETYPE_PEM 1 * } */ public static int X509_FILETYPE_PEM() { return X509_FILETYPE_PEM; } - private static final int X509_L_FILE_LOAD = (int)1L; + + private static final int X509_L_FILE_LOAD = (int) 1L; + /** - * {@snippet lang=c : - * #define X509_L_FILE_LOAD 1 + * {@snippet lang = c : * #define X509_L_FILE_LOAD 1 * } */ public static int X509_L_FILE_LOAD() { return X509_L_FILE_LOAD; } - private static final int X509_L_ADD_DIR = (int)2L; + + private static final int X509_L_ADD_DIR = (int) 2L; + /** - * {@snippet lang=c : - * #define X509_L_ADD_DIR 2 + * {@snippet lang = c : * #define X509_L_ADD_DIR 2 * } */ public static int X509_L_ADD_DIR() { return X509_L_ADD_DIR; } - private static final int X509_V_OK = (int)0L; + + private static final int X509_V_OK = (int) 0L; + /** - * {@snippet lang=c : - * #define X509_V_OK 0 + * {@snippet lang = c : * #define X509_V_OK 0 * } */ public static int X509_V_OK() { return X509_V_OK; } - private static final int X509_V_ERR_CRL_HAS_EXPIRED = (int)12L; + + private static final int X509_V_ERR_UNABLE_TO_GET_CRL = (int) 3L; + /** - * {@snippet lang=c : - * #define X509_V_ERR_CRL_HAS_EXPIRED 12 + * {@snippet lang = c : * #define X509_V_ERR_UNABLE_TO_GET_CRL 3 + * } + */ + public static int X509_V_ERR_UNABLE_TO_GET_CRL() { + return X509_V_ERR_UNABLE_TO_GET_CRL; + } + + private static final int X509_V_ERR_CRL_HAS_EXPIRED = (int) 12L; + + /** + * {@snippet lang = c : * #define X509_V_ERR_CRL_HAS_EXPIRED 12 * } */ public static int X509_V_ERR_CRL_HAS_EXPIRED() { return X509_V_ERR_CRL_HAS_EXPIRED; } - private static final int X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT = (int)18L; + + private static final int X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT = (int) 18L; + /** - * {@snippet lang=c : - * #define X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT 18 + * {@snippet lang = c : * #define X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT 18 * } */ public static int X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT() { return X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; } - private static final int X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN = (int)19L; + + private static final int X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN = (int) 19L; + /** - * {@snippet lang=c : - * #define X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN 19 + * {@snippet lang = c : * #define X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN 19 * } */ public static int X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN() { return X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; } - private static final int X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY = (int)20L; + + private static final int X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY = (int) 20L; + /** - * {@snippet lang=c : - * #define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY 20 + * {@snippet lang = c : * #define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY 20 * } */ public static int X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY() { return X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY; } - private static final int X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = (int)21L; + + private static final int X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE = (int) 21L; + /** - * {@snippet lang=c : - * #define X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE 21 + * {@snippet lang = c : * #define X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE 21 * } */ public static int X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE() { return X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE; } - private static final int X509_V_ERR_CERT_UNTRUSTED = (int)27L; + + private static final int X509_V_ERR_CERT_REVOKED = (int) 23L; + /** - * {@snippet lang=c : - * #define X509_V_ERR_CERT_UNTRUSTED 27 + * {@snippet lang = c : * #define X509_V_ERR_CERT_REVOKED 23 + * } + */ + public static int X509_V_ERR_CERT_REVOKED() { + return X509_V_ERR_CERT_REVOKED; + } + + private static final int X509_V_ERR_CERT_UNTRUSTED = (int) 27L; + + /** + * {@snippet lang = c : * #define X509_V_ERR_CERT_UNTRUSTED 27 * } */ public static int X509_V_ERR_CERT_UNTRUSTED() { return X509_V_ERR_CERT_UNTRUSTED; } - private static final int X509_V_ERR_APPLICATION_VERIFICATION = (int)50L; + + private static final int X509_V_ERR_APPLICATION_VERIFICATION = (int) 50L; + /** - * {@snippet lang=c : - * #define X509_V_ERR_APPLICATION_VERIFICATION 50 + * {@snippet lang = c : * #define X509_V_ERR_APPLICATION_VERIFICATION 50 * } */ public static int X509_V_ERR_APPLICATION_VERIFICATION() { return X509_V_ERR_APPLICATION_VERIFICATION; } - private static final int X509_V_FLAG_CRL_CHECK = (int)4L; + + private static final int X509_V_ERR_OCSP_RESP_INVALID = (int) 96L; + /** - * {@snippet lang=c : - * #define X509_V_FLAG_CRL_CHECK 4 + * {@snippet lang = c : * #define X509_V_ERR_OCSP_RESP_INVALID 96 + * } + */ + public static int X509_V_ERR_OCSP_RESP_INVALID() { + return X509_V_ERR_OCSP_RESP_INVALID; + } + + private static final int X509_V_ERR_OCSP_SIGNATURE_FAILURE = (int) 97L; + + /** + * {@snippet lang = c : * #define X509_V_ERR_OCSP_SIGNATURE_FAILURE 97 + * } + */ + public static int X509_V_ERR_OCSP_SIGNATURE_FAILURE() { + return X509_V_ERR_OCSP_SIGNATURE_FAILURE; + } + + private static final int X509_V_ERR_OCSP_NOT_YET_VALID = (int) 98L; + + /** + * {@snippet lang = c : * #define X509_V_ERR_OCSP_NOT_YET_VALID 98 + * } + */ + public static int X509_V_ERR_OCSP_NOT_YET_VALID() { + return X509_V_ERR_OCSP_NOT_YET_VALID; + } + + private static final int X509_V_ERR_OCSP_HAS_EXPIRED = (int) 99L; + + /** + * {@snippet lang = c : * #define X509_V_ERR_OCSP_HAS_EXPIRED 99 + * } + */ + public static int X509_V_ERR_OCSP_HAS_EXPIRED() { + return X509_V_ERR_OCSP_HAS_EXPIRED; + } + + private static final int X509_V_FLAG_CRL_CHECK = (int) 4L; + + /** + * {@snippet lang = c : * #define X509_V_FLAG_CRL_CHECK 4 * } */ public static int X509_V_FLAG_CRL_CHECK() { return X509_V_FLAG_CRL_CHECK; } - private static final int X509_V_FLAG_CRL_CHECK_ALL = (int)8L; + + private static final int X509_V_FLAG_CRL_CHECK_ALL = (int) 8L; + /** - * {@snippet lang=c : - * #define X509_V_FLAG_CRL_CHECK_ALL 8 + * {@snippet lang = c : * #define X509_V_FLAG_CRL_CHECK_ALL 8 * } */ public static int X509_V_FLAG_CRL_CHECK_ALL() { return X509_V_FLAG_CRL_CHECK_ALL; } - private static final int PEM_R_NO_START_LINE = (int)108L; + + private static final int PEM_R_NO_START_LINE = (int) 108L; + /** - * {@snippet lang=c : - * #define PEM_R_NO_START_LINE 108 + * {@snippet lang = c : * #define PEM_R_NO_START_LINE 108 * } */ public static int PEM_R_NO_START_LINE() { return PEM_R_NO_START_LINE; } - private static final int SSL3_VERSION = (int)768L; + + private static final int SSL3_VERSION = (int) 768L; + /** - * {@snippet lang=c : - * #define SSL3_VERSION 768 + * {@snippet lang = c : * #define SSL3_VERSION 768 * } */ public static int SSL3_VERSION() { return SSL3_VERSION; } - private static final int TLS1_VERSION = (int)769L; + + private static final int TLS1_VERSION = (int) 769L; + /** - * {@snippet lang=c : - * #define TLS1_VERSION 769 + * {@snippet lang = c : * #define TLS1_VERSION 769 * } */ public static int TLS1_VERSION() { return TLS1_VERSION; } - private static final int TLS1_1_VERSION = (int)770L; + + private static final int TLS1_1_VERSION = (int) 770L; + /** - * {@snippet lang=c : - * #define TLS1_1_VERSION 770 + * {@snippet lang = c : * #define TLS1_1_VERSION 770 * } */ public static int TLS1_1_VERSION() { return TLS1_1_VERSION; } - private static final int TLS1_2_VERSION = (int)771L; + + private static final int TLS1_2_VERSION = (int) 771L; + /** - * {@snippet lang=c : - * #define TLS1_2_VERSION 771 + * {@snippet lang = c : * #define TLS1_2_VERSION 771 * } */ public static int TLS1_2_VERSION() { return TLS1_2_VERSION; } - private static final int TLS1_3_VERSION = (int)772L; + + private static final int TLS1_3_VERSION = (int) 772L; + /** - * {@snippet lang=c : - * #define TLS1_3_VERSION 772 + * {@snippet lang = c : * #define TLS1_3_VERSION 772 * } */ public static int TLS1_3_VERSION() { return TLS1_3_VERSION; } - private static final int SSL_SENT_SHUTDOWN = (int)1L; + + private static final int SSL_SENT_SHUTDOWN = (int) 1L; + /** - * {@snippet lang=c : - * #define SSL_SENT_SHUTDOWN 1 + * {@snippet lang = c : * #define SSL_SENT_SHUTDOWN 1 * } */ public static int SSL_SENT_SHUTDOWN() { return SSL_SENT_SHUTDOWN; } - private static final int SSL_RECEIVED_SHUTDOWN = (int)2L; + + private static final int SSL_RECEIVED_SHUTDOWN = (int) 2L; + /** - * {@snippet lang=c : - * #define SSL_RECEIVED_SHUTDOWN 2 + * {@snippet lang = c : * #define SSL_RECEIVED_SHUTDOWN 2 * } */ public static int SSL_RECEIVED_SHUTDOWN() { return SSL_RECEIVED_SHUTDOWN; } - private static final int SSL_OP_SINGLE_ECDH_USE = (int)0L; + + private static final int SSL_OP_SINGLE_ECDH_USE = (int) 0L; + /** - * {@snippet lang=c : - * #define SSL_OP_SINGLE_ECDH_USE 0 + * {@snippet lang = c : * #define SSL_OP_SINGLE_ECDH_USE 0 * } */ public static int SSL_OP_SINGLE_ECDH_USE() { return SSL_OP_SINGLE_ECDH_USE; } - private static final int SSL_OP_SINGLE_DH_USE = (int)0L; + + private static final int SSL_OP_SINGLE_DH_USE = (int) 0L; + /** - * {@snippet lang=c : - * #define SSL_OP_SINGLE_DH_USE 0 + * {@snippet lang = c : * #define SSL_OP_SINGLE_DH_USE 0 * } */ public static int SSL_OP_SINGLE_DH_USE() { return SSL_OP_SINGLE_DH_USE; } - private static final int SSL_OP_NO_SSLv2 = (int)0L; + + private static final int SSL_OP_NO_SSLv2 = (int) 0L; + /** - * {@snippet lang=c : - * #define SSL_OP_NO_SSLv2 0 + * {@snippet lang = c : * #define SSL_OP_NO_SSLv2 0 * } */ public static int SSL_OP_NO_SSLv2() { return SSL_OP_NO_SSLv2; } - private static final int SSL_CONF_FLAG_FILE = (int)2L; + + private static final int SSL_CONF_FLAG_FILE = (int) 2L; + /** - * {@snippet lang=c : - * #define SSL_CONF_FLAG_FILE 2 + * {@snippet lang = c : * #define SSL_CONF_FLAG_FILE 2 * } */ public static int SSL_CONF_FLAG_FILE() { return SSL_CONF_FLAG_FILE; } - private static final int SSL_CONF_FLAG_SERVER = (int)8L; + + private static final int SSL_CONF_FLAG_SERVER = (int) 8L; + /** - * {@snippet lang=c : - * #define SSL_CONF_FLAG_SERVER 8 + * {@snippet lang = c : * #define SSL_CONF_FLAG_SERVER 8 * } */ public static int SSL_CONF_FLAG_SERVER() { return SSL_CONF_FLAG_SERVER; } - private static final int SSL_CONF_FLAG_SHOW_ERRORS = (int)16L; + + private static final int SSL_CONF_FLAG_SHOW_ERRORS = (int) 16L; + /** - * {@snippet lang=c : - * #define SSL_CONF_FLAG_SHOW_ERRORS 16 + * {@snippet lang = c : * #define SSL_CONF_FLAG_SHOW_ERRORS 16 * } */ public static int SSL_CONF_FLAG_SHOW_ERRORS() { return SSL_CONF_FLAG_SHOW_ERRORS; } - private static final int SSL_CONF_FLAG_CERTIFICATE = (int)32L; + + private static final int SSL_CONF_FLAG_CERTIFICATE = (int) 32L; + /** - * {@snippet lang=c : - * #define SSL_CONF_FLAG_CERTIFICATE 32 + * {@snippet lang = c : * #define SSL_CONF_FLAG_CERTIFICATE 32 * } */ public static int SSL_CONF_FLAG_CERTIFICATE() { return SSL_CONF_FLAG_CERTIFICATE; } - private static final int SSL_CONF_TYPE_UNKNOWN = (int)0L; + + private static final int SSL_CONF_TYPE_UNKNOWN = (int) 0L; + /** - * {@snippet lang=c : - * #define SSL_CONF_TYPE_UNKNOWN 0 + * {@snippet lang = c : * #define SSL_CONF_TYPE_UNKNOWN 0 * } */ public static int SSL_CONF_TYPE_UNKNOWN() { return SSL_CONF_TYPE_UNKNOWN; } - private static final int SSL_CONF_TYPE_FILE = (int)2L; + + private static final int SSL_CONF_TYPE_FILE = (int) 2L; + /** - * {@snippet lang=c : - * #define SSL_CONF_TYPE_FILE 2 + * {@snippet lang = c : * #define SSL_CONF_TYPE_FILE 2 * } */ public static int SSL_CONF_TYPE_FILE() { return SSL_CONF_TYPE_FILE; } - private static final int SSL_CONF_TYPE_DIR = (int)3L; + + private static final int SSL_CONF_TYPE_DIR = (int) 3L; + /** - * {@snippet lang=c : - * #define SSL_CONF_TYPE_DIR 3 + * {@snippet lang = c : * #define SSL_CONF_TYPE_DIR 3 * } */ public static int SSL_CONF_TYPE_DIR() { return SSL_CONF_TYPE_DIR; } - private static final int SSL_SESS_CACHE_OFF = (int)0L; + + private static final int SSL_SESS_CACHE_OFF = (int) 0L; + /** - * {@snippet lang=c : - * #define SSL_SESS_CACHE_OFF 0 + * {@snippet lang = c : * #define SSL_SESS_CACHE_OFF 0 * } */ public static int SSL_SESS_CACHE_OFF() { return SSL_SESS_CACHE_OFF; } - private static final int SSL_SESS_CACHE_SERVER = (int)2L; + + private static final int SSL_SESS_CACHE_SERVER = (int) 2L; + /** - * {@snippet lang=c : - * #define SSL_SESS_CACHE_SERVER 2 + * {@snippet lang = c : * #define SSL_SESS_CACHE_SERVER 2 * } */ public static int SSL_SESS_CACHE_SERVER() { return SSL_SESS_CACHE_SERVER; } - private static final int SSL2_VERSION = (int)2L; + + private static final int SSL2_VERSION = (int) 2L; + /** - * {@snippet lang=c : - * #define SSL2_VERSION 2 + * {@snippet lang = c : * #define SSL2_VERSION 2 * } */ public static int SSL2_VERSION() { return SSL2_VERSION; } - private static final int SSL_TLSEXT_ERR_OK = (int)0L; + + private static final int SSL_TLSEXT_ERR_OK = (int) 0L; + /** - * {@snippet lang=c : - * #define SSL_TLSEXT_ERR_OK 0 + * {@snippet lang = c : * #define SSL_TLSEXT_ERR_OK 0 * } */ public static int SSL_TLSEXT_ERR_OK() { return SSL_TLSEXT_ERR_OK; } - private static final int SSL_TLSEXT_ERR_NOACK = (int)3L; + + private static final int SSL_TLSEXT_ERR_NOACK = (int) 3L; + /** - * {@snippet lang=c : - * #define SSL_TLSEXT_ERR_NOACK 3 + * {@snippet lang = c : * #define SSL_TLSEXT_ERR_NOACK 3 * } */ public static int SSL_TLSEXT_ERR_NOACK() { return SSL_TLSEXT_ERR_NOACK; } - private static final int SSL_CB_HANDSHAKE_DONE = (int)32L; + + private static final int SSL_CB_HANDSHAKE_DONE = (int) 32L; + /** - * {@snippet lang=c : - * #define SSL_CB_HANDSHAKE_DONE 32 + * {@snippet lang = c : * #define SSL_CB_HANDSHAKE_DONE 32 * } */ public static int SSL_CB_HANDSHAKE_DONE() { return SSL_CB_HANDSHAKE_DONE; } - private static final int SSL_VERIFY_NONE = (int)0L; + + private static final int SSL_VERIFY_NONE = (int) 0L; + /** - * {@snippet lang=c : - * #define SSL_VERIFY_NONE 0 + * {@snippet lang = c : * #define SSL_VERIFY_NONE 0 * } */ public static int SSL_VERIFY_NONE() { return SSL_VERIFY_NONE; } - private static final int SSL_VERIFY_PEER = (int)1L; + + private static final int SSL_VERIFY_PEER = (int) 1L; + /** - * {@snippet lang=c : - * #define SSL_VERIFY_PEER 1 + * {@snippet lang = c : * #define SSL_VERIFY_PEER 1 * } */ public static int SSL_VERIFY_PEER() { return SSL_VERIFY_PEER; } - private static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = (int)2L; + + private static final int SSL_VERIFY_FAIL_IF_NO_PEER_CERT = (int) 2L; + /** - * {@snippet lang=c : - * #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 2 + * {@snippet lang = c : * #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT 2 * } */ public static int SSL_VERIFY_FAIL_IF_NO_PEER_CERT() { return SSL_VERIFY_FAIL_IF_NO_PEER_CERT; } - private static final int SSL_ERROR_NONE = (int)0L; + + private static final int SSL_ERROR_NONE = (int) 0L; + /** - * {@snippet lang=c : - * #define SSL_ERROR_NONE 0 + * {@snippet lang = c : * #define SSL_ERROR_NONE 0 * } */ public static int SSL_ERROR_NONE() { return SSL_ERROR_NONE; } - private static final int SSL_CTRL_SET_TMP_DH = (int)3L; + + private static final int SSL_CTRL_SET_TMP_DH = (int) 3L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SET_TMP_DH 3 + * {@snippet lang = c : * #define SSL_CTRL_SET_TMP_DH 3 * } */ public static int SSL_CTRL_SET_TMP_DH() { return SSL_CTRL_SET_TMP_DH; } - private static final int SSL_CTRL_SET_TMP_ECDH = (int)4L; + + private static final int SSL_CTRL_SET_TMP_ECDH = (int) 4L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SET_TMP_ECDH 4 + * {@snippet lang = c : * #define SSL_CTRL_SET_TMP_ECDH 4 * } */ public static int SSL_CTRL_SET_TMP_ECDH() { return SSL_CTRL_SET_TMP_ECDH; } - private static final int SSL_CTRL_SESS_NUMBER = (int)20L; + + private static final int SSL_CTRL_SESS_NUMBER = (int) 20L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_NUMBER 20 + * {@snippet lang = c : * #define SSL_CTRL_SESS_NUMBER 20 * } */ public static int SSL_CTRL_SESS_NUMBER() { return SSL_CTRL_SESS_NUMBER; } - private static final int SSL_CTRL_SESS_CONNECT = (int)21L; + + private static final int SSL_CTRL_SESS_CONNECT = (int) 21L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_CONNECT 21 + * {@snippet lang = c : * #define SSL_CTRL_SESS_CONNECT 21 * } */ public static int SSL_CTRL_SESS_CONNECT() { return SSL_CTRL_SESS_CONNECT; } - private static final int SSL_CTRL_SESS_CONNECT_GOOD = (int)22L; + + private static final int SSL_CTRL_SESS_CONNECT_GOOD = (int) 22L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_CONNECT_GOOD 22 + * {@snippet lang = c : * #define SSL_CTRL_SESS_CONNECT_GOOD 22 * } */ public static int SSL_CTRL_SESS_CONNECT_GOOD() { return SSL_CTRL_SESS_CONNECT_GOOD; } - private static final int SSL_CTRL_SESS_CONNECT_RENEGOTIATE = (int)23L; + + private static final int SSL_CTRL_SESS_CONNECT_RENEGOTIATE = (int) 23L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_CONNECT_RENEGOTIATE 23 + * {@snippet lang = c : * #define SSL_CTRL_SESS_CONNECT_RENEGOTIATE 23 * } */ public static int SSL_CTRL_SESS_CONNECT_RENEGOTIATE() { return SSL_CTRL_SESS_CONNECT_RENEGOTIATE; } - private static final int SSL_CTRL_SESS_ACCEPT = (int)24L; + + private static final int SSL_CTRL_SESS_ACCEPT = (int) 24L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_ACCEPT 24 + * {@snippet lang = c : * #define SSL_CTRL_SESS_ACCEPT 24 * } */ public static int SSL_CTRL_SESS_ACCEPT() { return SSL_CTRL_SESS_ACCEPT; } - private static final int SSL_CTRL_SESS_ACCEPT_GOOD = (int)25L; + + private static final int SSL_CTRL_SESS_ACCEPT_GOOD = (int) 25L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_ACCEPT_GOOD 25 + * {@snippet lang = c : * #define SSL_CTRL_SESS_ACCEPT_GOOD 25 * } */ public static int SSL_CTRL_SESS_ACCEPT_GOOD() { return SSL_CTRL_SESS_ACCEPT_GOOD; } - private static final int SSL_CTRL_SESS_ACCEPT_RENEGOTIATE = (int)26L; + + private static final int SSL_CTRL_SESS_ACCEPT_RENEGOTIATE = (int) 26L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_ACCEPT_RENEGOTIATE 26 + * {@snippet lang = c : * #define SSL_CTRL_SESS_ACCEPT_RENEGOTIATE 26 * } */ public static int SSL_CTRL_SESS_ACCEPT_RENEGOTIATE() { return SSL_CTRL_SESS_ACCEPT_RENEGOTIATE; } - private static final int SSL_CTRL_SESS_HIT = (int)27L; + + private static final int SSL_CTRL_SESS_HIT = (int) 27L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_HIT 27 + * {@snippet lang = c : * #define SSL_CTRL_SESS_HIT 27 * } */ public static int SSL_CTRL_SESS_HIT() { return SSL_CTRL_SESS_HIT; } - private static final int SSL_CTRL_SESS_CB_HIT = (int)28L; + + private static final int SSL_CTRL_SESS_CB_HIT = (int) 28L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_CB_HIT 28 + * {@snippet lang = c : * #define SSL_CTRL_SESS_CB_HIT 28 * } */ public static int SSL_CTRL_SESS_CB_HIT() { return SSL_CTRL_SESS_CB_HIT; } - private static final int SSL_CTRL_SESS_MISSES = (int)29L; + + private static final int SSL_CTRL_SESS_MISSES = (int) 29L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_MISSES 29 + * {@snippet lang = c : * #define SSL_CTRL_SESS_MISSES 29 * } */ public static int SSL_CTRL_SESS_MISSES() { return SSL_CTRL_SESS_MISSES; } - private static final int SSL_CTRL_SESS_TIMEOUTS = (int)30L; + + private static final int SSL_CTRL_SESS_TIMEOUTS = (int) 30L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_TIMEOUTS 30 + * {@snippet lang = c : * #define SSL_CTRL_SESS_TIMEOUTS 30 * } */ public static int SSL_CTRL_SESS_TIMEOUTS() { return SSL_CTRL_SESS_TIMEOUTS; } - private static final int SSL_CTRL_SESS_CACHE_FULL = (int)31L; + + private static final int SSL_CTRL_SESS_CACHE_FULL = (int) 31L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SESS_CACHE_FULL 31 + * {@snippet lang = c : * #define SSL_CTRL_SESS_CACHE_FULL 31 * } */ public static int SSL_CTRL_SESS_CACHE_FULL() { return SSL_CTRL_SESS_CACHE_FULL; } - private static final int SSL_CTRL_SET_SESS_CACHE_SIZE = (int)42L; + + private static final int SSL_CTRL_SET_SESS_CACHE_SIZE = (int) 42L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SET_SESS_CACHE_SIZE 42 + * {@snippet lang = c : * #define SSL_CTRL_SET_SESS_CACHE_SIZE 42 * } */ public static int SSL_CTRL_SET_SESS_CACHE_SIZE() { return SSL_CTRL_SET_SESS_CACHE_SIZE; } - private static final int SSL_CTRL_GET_SESS_CACHE_SIZE = (int)43L; + + private static final int SSL_CTRL_GET_SESS_CACHE_SIZE = (int) 43L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_GET_SESS_CACHE_SIZE 43 + * {@snippet lang = c : * #define SSL_CTRL_GET_SESS_CACHE_SIZE 43 * } */ public static int SSL_CTRL_GET_SESS_CACHE_SIZE() { return SSL_CTRL_GET_SESS_CACHE_SIZE; } - private static final int SSL_CTRL_SET_SESS_CACHE_MODE = (int)44L; + + private static final int SSL_CTRL_SET_SESS_CACHE_MODE = (int) 44L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SET_SESS_CACHE_MODE 44 + * {@snippet lang = c : * #define SSL_CTRL_SET_SESS_CACHE_MODE 44 * } */ public static int SSL_CTRL_SET_SESS_CACHE_MODE() { return SSL_CTRL_SET_SESS_CACHE_MODE; } - private static final int SSL_CTRL_GET_SESS_CACHE_MODE = (int)45L; + + private static final int SSL_CTRL_GET_SESS_CACHE_MODE = (int) 45L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_GET_SESS_CACHE_MODE 45 + * {@snippet lang = c : * #define SSL_CTRL_GET_SESS_CACHE_MODE 45 * } */ public static int SSL_CTRL_GET_SESS_CACHE_MODE() { return SSL_CTRL_GET_SESS_CACHE_MODE; } - private static final int SSL_CTRL_SET_TLSEXT_TICKET_KEYS = (int)59L; + + private static final int SSL_CTRL_SET_TLSEXT_TICKET_KEYS = (int) 59L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SET_TLSEXT_TICKET_KEYS 59 + * {@snippet lang = c : * #define SSL_CTRL_SET_TLSEXT_TICKET_KEYS 59 * } */ public static int SSL_CTRL_SET_TLSEXT_TICKET_KEYS() { return SSL_CTRL_SET_TLSEXT_TICKET_KEYS; } - private static final int SSL_CTRL_CHAIN_CERT = (int)89L; + + private static final int SSL_CTRL_CHAIN_CERT = (int) 89L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_CHAIN_CERT 89 + * {@snippet lang = c : * #define SSL_CTRL_CHAIN_CERT 89 * } */ public static int SSL_CTRL_CHAIN_CERT() { return SSL_CTRL_CHAIN_CERT; } - private static final int SSL_CTRL_SET_GROUPS = (int)91L; + + private static final int SSL_CTRL_SET_GROUPS = (int) 91L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SET_GROUPS 91 + * {@snippet lang = c : * #define SSL_CTRL_SET_GROUPS 91 * } */ public static int SSL_CTRL_SET_GROUPS() { return SSL_CTRL_SET_GROUPS; } - private static final int SSL_CTRL_SET_DH_AUTO = (int)118L; + + private static final int SSL_CTRL_SET_GROUPS_LIST = (int) 92L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SET_DH_AUTO 118 + * {@snippet lang = c : * #define SSL_CTRL_SET_GROUPS_LIST 92 + * } + */ + public static int SSL_CTRL_SET_GROUPS_LIST() { + return SSL_CTRL_SET_GROUPS_LIST; + } + + private static final int SSL_CTRL_SET_DH_AUTO = (int) 118L; + + /** + * {@snippet lang = c : * #define SSL_CTRL_SET_DH_AUTO 118 * } */ public static int SSL_CTRL_SET_DH_AUTO() { return SSL_CTRL_SET_DH_AUTO; } - private static final int SSL_CTRL_SET_MIN_PROTO_VERSION = (int)123L; + + private static final int SSL_CTRL_SET_MIN_PROTO_VERSION = (int) 123L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SET_MIN_PROTO_VERSION 123 + * {@snippet lang = c : * #define SSL_CTRL_SET_MIN_PROTO_VERSION 123 * } */ public static int SSL_CTRL_SET_MIN_PROTO_VERSION() { return SSL_CTRL_SET_MIN_PROTO_VERSION; } - private static final int SSL_CTRL_SET_MAX_PROTO_VERSION = (int)124L; + + private static final int SSL_CTRL_SET_MAX_PROTO_VERSION = (int) 124L; + /** - * {@snippet lang=c : - * #define SSL_CTRL_SET_MAX_PROTO_VERSION 124 + * {@snippet lang = c : * #define SSL_CTRL_SET_MAX_PROTO_VERSION 124 * } */ public static int SSL_CTRL_SET_MAX_PROTO_VERSION() { return SSL_CTRL_SET_MAX_PROTO_VERSION; } - private static final int ERR_REASON_MASK = (int)8388607L; + + private static final int ERR_REASON_MASK = (int) 8388607L; + /** - * {@snippet lang=c : - * #define ERR_REASON_MASK 8388607 + * {@snippet lang = c : * #define ERR_REASON_MASK 8388607 * } */ public static int ERR_REASON_MASK() { return ERR_REASON_MASK; } - private static final int OCSP_RESPONSE_STATUS_SUCCESSFUL = (int)0L; + + private static final int OCSP_RESPONSE_STATUS_SUCCESSFUL = (int) 0L; + /** - * {@snippet lang=c : - * #define OCSP_RESPONSE_STATUS_SUCCESSFUL 0 + * {@snippet lang = c : * #define OCSP_RESPONSE_STATUS_SUCCESSFUL 0 * } */ public static int OCSP_RESPONSE_STATUS_SUCCESSFUL() { return OCSP_RESPONSE_STATUS_SUCCESSFUL; } - private static final int V_OCSP_CERTSTATUS_GOOD = (int)0L; + + private static final int V_OCSP_CERTSTATUS_GOOD = (int) 0L; + /** - * {@snippet lang=c : - * #define V_OCSP_CERTSTATUS_GOOD 0 + * {@snippet lang = c : * #define V_OCSP_CERTSTATUS_GOOD 0 * } */ public static int V_OCSP_CERTSTATUS_GOOD() { return V_OCSP_CERTSTATUS_GOOD; } - private static final int V_OCSP_CERTSTATUS_REVOKED = (int)1L; + + private static final int V_OCSP_CERTSTATUS_REVOKED = (int) 1L; + /** - * {@snippet lang=c : - * #define V_OCSP_CERTSTATUS_REVOKED 1 + * {@snippet lang = c : * #define V_OCSP_CERTSTATUS_REVOKED 1 * } */ public static int V_OCSP_CERTSTATUS_REVOKED() { return V_OCSP_CERTSTATUS_REVOKED; } - private static final int V_OCSP_CERTSTATUS_UNKNOWN = (int)2L; + + private static final int V_OCSP_CERTSTATUS_UNKNOWN = (int) 2L; + /** - * {@snippet lang=c : - * #define V_OCSP_CERTSTATUS_UNKNOWN 2 + * {@snippet lang = c : * #define V_OCSP_CERTSTATUS_UNKNOWN 2 * } */ public static int V_OCSP_CERTSTATUS_UNKNOWN() { return V_OCSP_CERTSTATUS_UNKNOWN; } - private static MethodHandle OPENSSL_sk_num$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OPENSSL_sk_num"), - DESC); - } - return Holder.MH; + private static class OPENSSL_sk_num { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OPENSSL_sk_num"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int OPENSSL_sk_num(const OPENSSL_STACK *) + * Function descriptor for: + * {@snippet lang = c : * int OPENSSL_sk_num(const OPENSSL_STACK *) + * } + */ + public static FunctionDescriptor OPENSSL_sk_num$descriptor() { + return OPENSSL_sk_num.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int OPENSSL_sk_num(const OPENSSL_STACK *) + * } + */ + public static MethodHandle OPENSSL_sk_num$handle() { + return OPENSSL_sk_num.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int OPENSSL_sk_num(const OPENSSL_STACK *) + * } + */ + public static MemorySegment OPENSSL_sk_num$address() { + return OPENSSL_sk_num.ADDR; + } + + /** + * {@snippet lang = c : * int OPENSSL_sk_num(const OPENSSL_STACK *) * } */ public static int OPENSSL_sk_num(MemorySegment x0) { - var mh$ = OPENSSL_sk_num$MH(); + var mh$ = OPENSSL_sk_num.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OPENSSL_sk_num", x0); } return (int) mh$.invokeExact(x0); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OPENSSL_sk_value$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OPENSSL_sk_value"), - DESC); - } - return Holder.MH; + private static class OPENSSL_sk_value { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OPENSSL_sk_value"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void *OPENSSL_sk_value(const OPENSSL_STACK *, int) + * Function descriptor for: + * {@snippet lang = c : * void *OPENSSL_sk_value(const OPENSSL_STACK *, int) + * } + */ + public static FunctionDescriptor OPENSSL_sk_value$descriptor() { + return OPENSSL_sk_value.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void *OPENSSL_sk_value(const OPENSSL_STACK *, int) + * } + */ + public static MethodHandle OPENSSL_sk_value$handle() { + return OPENSSL_sk_value.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void *OPENSSL_sk_value(const OPENSSL_STACK *, int) + * } + */ + public static MemorySegment OPENSSL_sk_value$address() { + return OPENSSL_sk_value.ADDR; + } + + /** + * {@snippet lang = c : * void *OPENSSL_sk_value(const OPENSSL_STACK *, int) * } */ public static MemorySegment OPENSSL_sk_value(MemorySegment x0, int x1) { - var mh$ = OPENSSL_sk_value$MH(); + var mh$ = OPENSSL_sk_value.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OPENSSL_sk_value", x0, x1); } return (MemorySegment) mh$.invokeExact(x0, x1); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OpenSSL_version_num$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG ); + private static class OpenSSL_version_num { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OpenSSL_version_num"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("OpenSSL_version_num"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * unsigned long OpenSSL_version_num(void) + * Function descriptor for: + * {@snippet lang = c : * unsigned long OpenSSL_version_num() + * } + */ + public static FunctionDescriptor OpenSSL_version_num$descriptor() { + return OpenSSL_version_num.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * unsigned long OpenSSL_version_num() + * } + */ + public static MethodHandle OpenSSL_version_num$handle() { + return OpenSSL_version_num.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * unsigned long OpenSSL_version_num() + * } + */ + public static MemorySegment OpenSSL_version_num$address() { + return OpenSSL_version_num.ADDR; + } + + /** + * {@snippet lang = c : * unsigned long OpenSSL_version_num() * } */ public static long OpenSSL_version_num() { - var mh$ = OpenSSL_version_num$MH(); + var mh$ = OpenSSL_version_num.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OpenSSL_version_num"); } return (long) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OpenSSL_version$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OpenSSL_version"), - DESC); - } - return Holder.MH; + private static class OpenSSL_version { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OpenSSL_version"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const char *OpenSSL_version(int type) + * Function descriptor for: + * {@snippet lang = c : * const char *OpenSSL_version(int type) + * } + */ + public static FunctionDescriptor OpenSSL_version$descriptor() { + return OpenSSL_version.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const char *OpenSSL_version(int type) + * } + */ + public static MethodHandle OpenSSL_version$handle() { + return OpenSSL_version.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const char *OpenSSL_version(int type) + * } + */ + public static MemorySegment OpenSSL_version$address() { + return OpenSSL_version.ADDR; + } + + /** + * {@snippet lang = c : * const char *OpenSSL_version(int type) * } */ public static MemorySegment OpenSSL_version(int type) { - var mh$ = OpenSSL_version$MH(); + var mh$ = OpenSSL_version.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OpenSSL_version", type); } return (MemorySegment) mh$.invokeExact(type); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle CRYPTO_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("CRYPTO_free"), - DESC); - } - return Holder.MH; + private static class CRYPTO_free { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("CRYPTO_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void CRYPTO_free(void *ptr, const char *file, int line) + * Function descriptor for: + * {@snippet lang = c : * void CRYPTO_free(void *ptr, const char *file, int line) + * } + */ + public static FunctionDescriptor CRYPTO_free$descriptor() { + return CRYPTO_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void CRYPTO_free(void *ptr, const char *file, int line) + * } + */ + public static MethodHandle CRYPTO_free$handle() { + return CRYPTO_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void CRYPTO_free(void *ptr, const char *file, int line) + * } + */ + public static MemorySegment CRYPTO_free$address() { + return CRYPTO_free.ADDR; + } + + /** + * {@snippet lang = c : * void CRYPTO_free(void *ptr, const char *file, int line) * } */ public static void CRYPTO_free(MemorySegment ptr, MemorySegment file, int line) { - var mh$ = CRYPTO_free$MH(); + var mh$ = CRYPTO_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("CRYPTO_free", ptr, file, line); } mh$.invokeExact(ptr, file, line); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_ctrl_pending$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_ctrl_pending"), - DESC); - } - return Holder.MH; + private static class BIO_ctrl_pending { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_ctrl_pending"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * size_t BIO_ctrl_pending(BIO *b) + * Function descriptor for: + * {@snippet lang = c : * size_t BIO_ctrl_pending(BIO *b) + * } + */ + public static FunctionDescriptor BIO_ctrl_pending$descriptor() { + return BIO_ctrl_pending.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * size_t BIO_ctrl_pending(BIO *b) + * } + */ + public static MethodHandle BIO_ctrl_pending$handle() { + return BIO_ctrl_pending.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * size_t BIO_ctrl_pending(BIO *b) + * } + */ + public static MemorySegment BIO_ctrl_pending$address() { + return BIO_ctrl_pending.ADDR; + } + + /** + * {@snippet lang = c : * size_t BIO_ctrl_pending(BIO *b) * } */ public static long BIO_ctrl_pending(MemorySegment b) { - var mh$ = BIO_ctrl_pending$MH(); + var mh$ = BIO_ctrl_pending.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_ctrl_pending", b); } return (long) mh$.invokeExact(b); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_s_file$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class BIO_s_file { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_s_file"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_s_file"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const BIO_METHOD *BIO_s_file(void) + * Function descriptor for: + * {@snippet lang = c : * const BIO_METHOD *BIO_s_file() + * } + */ + public static FunctionDescriptor BIO_s_file$descriptor() { + return BIO_s_file.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const BIO_METHOD *BIO_s_file() + * } + */ + public static MethodHandle BIO_s_file$handle() { + return BIO_s_file.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const BIO_METHOD *BIO_s_file() + * } + */ + public static MemorySegment BIO_s_file$address() { + return BIO_s_file.ADDR; + } + + /** + * {@snippet lang = c : * const BIO_METHOD *BIO_s_file() * } */ public static MemorySegment BIO_s_file() { - var mh$ = BIO_s_file$MH(); + var mh$ = BIO_s_file.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_s_file"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_new_file$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_new_file"), - DESC); - } - return Holder.MH; + private static class BIO_new_file { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_new_file"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIO *BIO_new_file(const char *filename, const char *mode) + * Function descriptor for: + * {@snippet lang = c : * BIO *BIO_new_file(const char *filename, const char *mode) + * } + */ + public static FunctionDescriptor BIO_new_file$descriptor() { + return BIO_new_file.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIO *BIO_new_file(const char *filename, const char *mode) + * } + */ + public static MethodHandle BIO_new_file$handle() { + return BIO_new_file.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIO *BIO_new_file(const char *filename, const char *mode) + * } + */ + public static MemorySegment BIO_new_file$address() { + return BIO_new_file.ADDR; + } + + /** + * {@snippet lang = c : * BIO *BIO_new_file(const char *filename, const char *mode) * } */ public static MemorySegment BIO_new_file(MemorySegment filename, MemorySegment mode) { - var mh$ = BIO_new_file$MH(); + var mh$ = BIO_new_file.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_new_file", filename, mode); } return (MemorySegment) mh$.invokeExact(filename, mode); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_new$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_new"), - DESC); - } - return Holder.MH; + private static class BIO_new { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_new"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIO *BIO_new(const BIO_METHOD *type) + * Function descriptor for: + * {@snippet lang = c : * BIO *BIO_new(const BIO_METHOD *type) + * } + */ + public static FunctionDescriptor BIO_new$descriptor() { + return BIO_new.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIO *BIO_new(const BIO_METHOD *type) + * } + */ + public static MethodHandle BIO_new$handle() { + return BIO_new.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIO *BIO_new(const BIO_METHOD *type) + * } + */ + public static MemorySegment BIO_new$address() { + return BIO_new.ADDR; + } + + /** + * {@snippet lang = c : * BIO *BIO_new(const BIO_METHOD *type) * } */ public static MemorySegment BIO_new(MemorySegment type) { - var mh$ = BIO_new$MH(); + var mh$ = BIO_new.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_new", type); } return (MemorySegment) mh$.invokeExact(type); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_free"), - DESC); - } - return Holder.MH; + private static class BIO_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int BIO_free(BIO *a) + * Function descriptor for: + * {@snippet lang = c : * int BIO_free(BIO *a) + * } + */ + public static FunctionDescriptor BIO_free$descriptor() { + return BIO_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int BIO_free(BIO *a) + * } + */ + public static MethodHandle BIO_free$handle() { + return BIO_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int BIO_free(BIO *a) + * } + */ + public static MemorySegment BIO_free$address() { + return BIO_free.ADDR; + } + + /** + * {@snippet lang = c : * int BIO_free(BIO *a) * } */ public static int BIO_free(MemorySegment a) { - var mh$ = BIO_free$MH(); + var mh$ = BIO_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_free", a); } return (int) mh$.invokeExact(a); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_read$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_read"), - DESC); - } - return Holder.MH; + private static class BIO_read { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_read"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int BIO_read(BIO *b, void *data, int dlen) + * Function descriptor for: + * {@snippet lang = c : * int BIO_read(BIO *b, void *data, int dlen) + * } + */ + public static FunctionDescriptor BIO_read$descriptor() { + return BIO_read.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int BIO_read(BIO *b, void *data, int dlen) + * } + */ + public static MethodHandle BIO_read$handle() { + return BIO_read.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int BIO_read(BIO *b, void *data, int dlen) + * } + */ + public static MemorySegment BIO_read$address() { + return BIO_read.ADDR; + } + + /** + * {@snippet lang = c : * int BIO_read(BIO *b, void *data, int dlen) * } */ public static int BIO_read(MemorySegment b, MemorySegment data, int dlen) { - var mh$ = BIO_read$MH(); + var mh$ = BIO_read.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_read", b, data, dlen); } return (int) mh$.invokeExact(b, data, dlen); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_write$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_write"), - DESC); - } - return Holder.MH; + private static class BIO_write { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_write"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int BIO_write(BIO *b, const void *data, int dlen) + * Function descriptor for: + * {@snippet lang = c : * int BIO_write(BIO *b, const void *data, int dlen) + * } + */ + public static FunctionDescriptor BIO_write$descriptor() { + return BIO_write.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int BIO_write(BIO *b, const void *data, int dlen) + * } + */ + public static MethodHandle BIO_write$handle() { + return BIO_write.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int BIO_write(BIO *b, const void *data, int dlen) + * } + */ + public static MemorySegment BIO_write$address() { + return BIO_write.ADDR; + } + + /** + * {@snippet lang = c : * int BIO_write(BIO *b, const void *data, int dlen) * } */ public static int BIO_write(MemorySegment b, MemorySegment data, int dlen) { - var mh$ = BIO_write$MH(); + var mh$ = BIO_write.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_write", b, data, dlen); } return (int) mh$.invokeExact(b, data, dlen); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_ctrl$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_ctrl"), - DESC); - } - return Holder.MH; + private static class BIO_ctrl { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, + openssl_h.C_INT, openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_ctrl"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg) + * Function descriptor for: + * {@snippet lang = c : * long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg) + * } + */ + public static FunctionDescriptor BIO_ctrl$descriptor() { + return BIO_ctrl.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg) + * } + */ + public static MethodHandle BIO_ctrl$handle() { + return BIO_ctrl.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg) + * } + */ + public static MemorySegment BIO_ctrl$address() { + return BIO_ctrl.ADDR; + } + + /** + * {@snippet lang = c : * long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg) * } */ public static long BIO_ctrl(MemorySegment bp, int cmd, long larg, MemorySegment parg) { - var mh$ = BIO_ctrl$MH(); + var mh$ = BIO_ctrl.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_ctrl", bp, cmd, larg, parg); } return (long) mh$.invokeExact(bp, cmd, larg, parg); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_s_mem$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class BIO_s_mem { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_s_mem"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_s_mem"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const BIO_METHOD *BIO_s_mem(void) + * Function descriptor for: + * {@snippet lang = c : * const BIO_METHOD *BIO_s_mem() + * } + */ + public static FunctionDescriptor BIO_s_mem$descriptor() { + return BIO_s_mem.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const BIO_METHOD *BIO_s_mem() + * } + */ + public static MethodHandle BIO_s_mem$handle() { + return BIO_s_mem.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const BIO_METHOD *BIO_s_mem() + * } + */ + public static MemorySegment BIO_s_mem$address() { + return BIO_s_mem.ADDR; + } + + /** + * {@snippet lang = c : * const BIO_METHOD *BIO_s_mem() * } */ public static MemorySegment BIO_s_mem() { - var mh$ = BIO_s_mem$MH(); + var mh$ = BIO_s_mem.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_s_mem"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_s_bio$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class BIO_s_bio { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_s_bio"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_s_bio"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const BIO_METHOD *BIO_s_bio(void) + * Function descriptor for: + * {@snippet lang = c : * const BIO_METHOD *BIO_s_bio() + * } + */ + public static FunctionDescriptor BIO_s_bio$descriptor() { + return BIO_s_bio.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const BIO_METHOD *BIO_s_bio() + * } + */ + public static MethodHandle BIO_s_bio$handle() { + return BIO_s_bio.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const BIO_METHOD *BIO_s_bio() + * } + */ + public static MemorySegment BIO_s_bio$address() { + return BIO_s_bio.ADDR; + } + + /** + * {@snippet lang = c : * const BIO_METHOD *BIO_s_bio() * } */ public static MemorySegment BIO_s_bio() { - var mh$ = BIO_s_bio$MH(); + var mh$ = BIO_s_bio.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_s_bio"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BIO_new_bio_pair$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_LONG, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BIO_new_bio_pair"), - DESC); - } - return Holder.MH; + private static class BIO_new_bio_pair { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, + openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BIO_new_bio_pair"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2) + * Function descriptor for: + * {@snippet lang = c : * int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2) + * } + */ + public static FunctionDescriptor BIO_new_bio_pair$descriptor() { + return BIO_new_bio_pair.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2) + * } + */ + public static MethodHandle BIO_new_bio_pair$handle() { + return BIO_new_bio_pair.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2) + * } + */ + public static MemorySegment BIO_new_bio_pair$address() { + return BIO_new_bio_pair.ADDR; + } + + /** + * {@snippet lang = c : * int BIO_new_bio_pair(BIO **bio1, size_t writebuf1, BIO **bio2, size_t writebuf2) * } */ public static int BIO_new_bio_pair(MemorySegment bio1, long writebuf1, MemorySegment bio2, long writebuf2) { - var mh$ = BIO_new_bio_pair$MH(); + var mh$ = BIO_new_bio_pair.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BIO_new_bio_pair", bio1, writebuf1, bio2, writebuf2); } return (int) mh$.invokeExact(bio1, writebuf1, bio2, writebuf2); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_new$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class BN_new { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_new"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_new"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIGNUM *BN_new(void) + * Function descriptor for: + * {@snippet lang = c : * BIGNUM *BN_new() + * } + */ + public static FunctionDescriptor BN_new$descriptor() { + return BN_new.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIGNUM *BN_new() + * } + */ + public static MethodHandle BN_new$handle() { + return BN_new.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIGNUM *BN_new() + * } + */ + public static MemorySegment BN_new$address() { + return BN_new.ADDR; + } + + /** + * {@snippet lang = c : * BIGNUM *BN_new() * } */ public static MemorySegment BN_new() { - var mh$ = BN_new$MH(); + var mh$ = BN_new.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_new"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_set_word$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_set_word"), - DESC); - } - return Holder.MH; + private static class BN_set_word { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_set_word"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int BN_set_word(BIGNUM *a, unsigned long w) + * Function descriptor for: + * {@snippet lang = c : * int BN_set_word(BIGNUM *a, unsigned long w) + * } + */ + public static FunctionDescriptor BN_set_word$descriptor() { + return BN_set_word.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int BN_set_word(BIGNUM *a, unsigned long w) + * } + */ + public static MethodHandle BN_set_word$handle() { + return BN_set_word.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int BN_set_word(BIGNUM *a, unsigned long w) + * } + */ + public static MemorySegment BN_set_word$address() { + return BN_set_word.ADDR; + } + + /** + * {@snippet lang = c : * int BN_set_word(BIGNUM *a, unsigned long w) * } */ public static int BN_set_word(MemorySegment a, long w) { - var mh$ = BN_set_word$MH(); + var mh$ = BN_set_word.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_set_word", a, w); } return (int) mh$.invokeExact(a, w); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_get_rfc2409_prime_768$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_get_rfc2409_prime_768"), - DESC); - } - return Holder.MH; + private static class BN_get_rfc2409_prime_768 { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_get_rfc2409_prime_768"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIGNUM *BN_get_rfc2409_prime_768(BIGNUM *bn) + * Function descriptor for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc2409_prime_768(BIGNUM *bn) + * } + */ + public static FunctionDescriptor BN_get_rfc2409_prime_768$descriptor() { + return BN_get_rfc2409_prime_768.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc2409_prime_768(BIGNUM *bn) + * } + */ + public static MethodHandle BN_get_rfc2409_prime_768$handle() { + return BN_get_rfc2409_prime_768.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc2409_prime_768(BIGNUM *bn) + * } + */ + public static MemorySegment BN_get_rfc2409_prime_768$address() { + return BN_get_rfc2409_prime_768.ADDR; + } + + /** + * {@snippet lang = c : * BIGNUM *BN_get_rfc2409_prime_768(BIGNUM *bn) * } */ public static MemorySegment BN_get_rfc2409_prime_768(MemorySegment bn) { - var mh$ = BN_get_rfc2409_prime_768$MH(); + var mh$ = BN_get_rfc2409_prime_768.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_get_rfc2409_prime_768", bn); } return (MemorySegment) mh$.invokeExact(bn); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_get_rfc2409_prime_1024$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_get_rfc2409_prime_1024"), - DESC); - } - return Holder.MH; + private static class BN_get_rfc2409_prime_1024 { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_get_rfc2409_prime_1024"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIGNUM *BN_get_rfc2409_prime_1024(BIGNUM *bn) + * Function descriptor for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc2409_prime_1024(BIGNUM *bn) + * } + */ + public static FunctionDescriptor BN_get_rfc2409_prime_1024$descriptor() { + return BN_get_rfc2409_prime_1024.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc2409_prime_1024(BIGNUM *bn) + * } + */ + public static MethodHandle BN_get_rfc2409_prime_1024$handle() { + return BN_get_rfc2409_prime_1024.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc2409_prime_1024(BIGNUM *bn) + * } + */ + public static MemorySegment BN_get_rfc2409_prime_1024$address() { + return BN_get_rfc2409_prime_1024.ADDR; + } + + /** + * {@snippet lang = c : * BIGNUM *BN_get_rfc2409_prime_1024(BIGNUM *bn) * } */ public static MemorySegment BN_get_rfc2409_prime_1024(MemorySegment bn) { - var mh$ = BN_get_rfc2409_prime_1024$MH(); + var mh$ = BN_get_rfc2409_prime_1024.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_get_rfc2409_prime_1024", bn); } return (MemorySegment) mh$.invokeExact(bn); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_get_rfc3526_prime_1536$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_get_rfc3526_prime_1536"), - DESC); - } - return Holder.MH; + private static class BN_get_rfc3526_prime_1536 { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_get_rfc3526_prime_1536"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIGNUM *BN_get_rfc3526_prime_1536(BIGNUM *bn) + * Function descriptor for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_1536(BIGNUM *bn) + * } + */ + public static FunctionDescriptor BN_get_rfc3526_prime_1536$descriptor() { + return BN_get_rfc3526_prime_1536.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_1536(BIGNUM *bn) + * } + */ + public static MethodHandle BN_get_rfc3526_prime_1536$handle() { + return BN_get_rfc3526_prime_1536.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_1536(BIGNUM *bn) + * } + */ + public static MemorySegment BN_get_rfc3526_prime_1536$address() { + return BN_get_rfc3526_prime_1536.ADDR; + } + + /** + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_1536(BIGNUM *bn) * } */ public static MemorySegment BN_get_rfc3526_prime_1536(MemorySegment bn) { - var mh$ = BN_get_rfc3526_prime_1536$MH(); + var mh$ = BN_get_rfc3526_prime_1536.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_get_rfc3526_prime_1536", bn); } return (MemorySegment) mh$.invokeExact(bn); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_get_rfc3526_prime_2048$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_get_rfc3526_prime_2048"), - DESC); - } - return Holder.MH; + private static class BN_get_rfc3526_prime_2048 { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_get_rfc3526_prime_2048"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIGNUM *BN_get_rfc3526_prime_2048(BIGNUM *bn) + * Function descriptor for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_2048(BIGNUM *bn) + * } + */ + public static FunctionDescriptor BN_get_rfc3526_prime_2048$descriptor() { + return BN_get_rfc3526_prime_2048.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_2048(BIGNUM *bn) + * } + */ + public static MethodHandle BN_get_rfc3526_prime_2048$handle() { + return BN_get_rfc3526_prime_2048.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_2048(BIGNUM *bn) + * } + */ + public static MemorySegment BN_get_rfc3526_prime_2048$address() { + return BN_get_rfc3526_prime_2048.ADDR; + } + + /** + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_2048(BIGNUM *bn) * } */ public static MemorySegment BN_get_rfc3526_prime_2048(MemorySegment bn) { - var mh$ = BN_get_rfc3526_prime_2048$MH(); + var mh$ = BN_get_rfc3526_prime_2048.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_get_rfc3526_prime_2048", bn); } return (MemorySegment) mh$.invokeExact(bn); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_get_rfc3526_prime_3072$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_get_rfc3526_prime_3072"), - DESC); - } - return Holder.MH; + private static class BN_get_rfc3526_prime_3072 { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_get_rfc3526_prime_3072"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIGNUM *BN_get_rfc3526_prime_3072(BIGNUM *bn) + * Function descriptor for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_3072(BIGNUM *bn) + * } + */ + public static FunctionDescriptor BN_get_rfc3526_prime_3072$descriptor() { + return BN_get_rfc3526_prime_3072.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_3072(BIGNUM *bn) + * } + */ + public static MethodHandle BN_get_rfc3526_prime_3072$handle() { + return BN_get_rfc3526_prime_3072.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_3072(BIGNUM *bn) + * } + */ + public static MemorySegment BN_get_rfc3526_prime_3072$address() { + return BN_get_rfc3526_prime_3072.ADDR; + } + + /** + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_3072(BIGNUM *bn) * } */ public static MemorySegment BN_get_rfc3526_prime_3072(MemorySegment bn) { - var mh$ = BN_get_rfc3526_prime_3072$MH(); + var mh$ = BN_get_rfc3526_prime_3072.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_get_rfc3526_prime_3072", bn); } return (MemorySegment) mh$.invokeExact(bn); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_get_rfc3526_prime_4096$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_get_rfc3526_prime_4096"), - DESC); - } - return Holder.MH; + private static class BN_get_rfc3526_prime_4096 { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_get_rfc3526_prime_4096"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIGNUM *BN_get_rfc3526_prime_4096(BIGNUM *bn) + * Function descriptor for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_4096(BIGNUM *bn) + * } + */ + public static FunctionDescriptor BN_get_rfc3526_prime_4096$descriptor() { + return BN_get_rfc3526_prime_4096.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_4096(BIGNUM *bn) + * } + */ + public static MethodHandle BN_get_rfc3526_prime_4096$handle() { + return BN_get_rfc3526_prime_4096.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_4096(BIGNUM *bn) + * } + */ + public static MemorySegment BN_get_rfc3526_prime_4096$address() { + return BN_get_rfc3526_prime_4096.ADDR; + } + + /** + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_4096(BIGNUM *bn) * } */ public static MemorySegment BN_get_rfc3526_prime_4096(MemorySegment bn) { - var mh$ = BN_get_rfc3526_prime_4096$MH(); + var mh$ = BN_get_rfc3526_prime_4096.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_get_rfc3526_prime_4096", bn); } return (MemorySegment) mh$.invokeExact(bn); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_get_rfc3526_prime_6144$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_get_rfc3526_prime_6144"), - DESC); - } - return Holder.MH; + private static class BN_get_rfc3526_prime_6144 { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_get_rfc3526_prime_6144"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIGNUM *BN_get_rfc3526_prime_6144(BIGNUM *bn) + * Function descriptor for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_6144(BIGNUM *bn) + * } + */ + public static FunctionDescriptor BN_get_rfc3526_prime_6144$descriptor() { + return BN_get_rfc3526_prime_6144.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_6144(BIGNUM *bn) + * } + */ + public static MethodHandle BN_get_rfc3526_prime_6144$handle() { + return BN_get_rfc3526_prime_6144.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_6144(BIGNUM *bn) + * } + */ + public static MemorySegment BN_get_rfc3526_prime_6144$address() { + return BN_get_rfc3526_prime_6144.ADDR; + } + + /** + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_6144(BIGNUM *bn) * } */ public static MemorySegment BN_get_rfc3526_prime_6144(MemorySegment bn) { - var mh$ = BN_get_rfc3526_prime_6144$MH(); + var mh$ = BN_get_rfc3526_prime_6144.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_get_rfc3526_prime_6144", bn); } return (MemorySegment) mh$.invokeExact(bn); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle BN_get_rfc3526_prime_8192$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("BN_get_rfc3526_prime_8192"), - DESC); - } - return Holder.MH; + private static class BN_get_rfc3526_prime_8192 { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("BN_get_rfc3526_prime_8192"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * BIGNUM *BN_get_rfc3526_prime_8192(BIGNUM *bn) + * Function descriptor for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_8192(BIGNUM *bn) + * } + */ + public static FunctionDescriptor BN_get_rfc3526_prime_8192$descriptor() { + return BN_get_rfc3526_prime_8192.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_8192(BIGNUM *bn) + * } + */ + public static MethodHandle BN_get_rfc3526_prime_8192$handle() { + return BN_get_rfc3526_prime_8192.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_8192(BIGNUM *bn) + * } + */ + public static MemorySegment BN_get_rfc3526_prime_8192$address() { + return BN_get_rfc3526_prime_8192.ADDR; + } + + /** + * {@snippet lang = c : * BIGNUM *BN_get_rfc3526_prime_8192(BIGNUM *bn) * } */ public static MemorySegment BN_get_rfc3526_prime_8192(MemorySegment bn) { - var mh$ = BN_get_rfc3526_prime_8192$MH(); + var mh$ = BN_get_rfc3526_prime_8192.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("BN_get_rfc3526_prime_8192", bn); } return (MemorySegment) mh$.invokeExact(bn); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ASN1_STRING_length$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ASN1_STRING_length"), - DESC); - } - return Holder.MH; + private static class ASN1_STRING_length { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("ASN1_STRING_length"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int ASN1_STRING_length(const ASN1_STRING *x) + * Function descriptor for: + * {@snippet lang = c : * int ASN1_STRING_length(const ASN1_STRING *x) + * } + */ + public static FunctionDescriptor ASN1_STRING_length$descriptor() { + return ASN1_STRING_length.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int ASN1_STRING_length(const ASN1_STRING *x) + * } + */ + public static MethodHandle ASN1_STRING_length$handle() { + return ASN1_STRING_length.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int ASN1_STRING_length(const ASN1_STRING *x) + * } + */ + public static MemorySegment ASN1_STRING_length$address() { + return ASN1_STRING_length.ADDR; + } + + /** + * {@snippet lang = c : * int ASN1_STRING_length(const ASN1_STRING *x) * } */ public static int ASN1_STRING_length(MemorySegment x) { - var mh$ = ASN1_STRING_length$MH(); + var mh$ = ASN1_STRING_length.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("ASN1_STRING_length", x); } return (int) mh$.invokeExact(x); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ASN1_STRING_get0_data$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ASN1_STRING_get0_data"), - DESC); - } - return Holder.MH; + private static class ASN1_STRING_get0_data { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("ASN1_STRING_get0_data"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x) + * Function descriptor for: + * {@snippet lang = c : * const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x) + * } + */ + public static FunctionDescriptor ASN1_STRING_get0_data$descriptor() { + return ASN1_STRING_get0_data.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x) + * } + */ + public static MethodHandle ASN1_STRING_get0_data$handle() { + return ASN1_STRING_get0_data.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x) + * } + */ + public static MemorySegment ASN1_STRING_get0_data$address() { + return ASN1_STRING_get0_data.ADDR; + } + + /** + * {@snippet lang = c : * const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x) * } */ public static MemorySegment ASN1_STRING_get0_data(MemorySegment x) { - var mh$ = ASN1_STRING_get0_data$MH(); + var mh$ = ASN1_STRING_get0_data.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("ASN1_STRING_get0_data", x); } return (MemorySegment) mh$.invokeExact(x); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EVP_MD_get0_provider$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EVP_MD_get0_provider"), - DESC); - } - return Holder.MH; + private static class EVP_MD_get0_provider { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EVP_MD_get0_provider"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const OSSL_PROVIDER *EVP_MD_get0_provider(const EVP_MD *md) + * Function descriptor for: + * {@snippet lang = c : * const OSSL_PROVIDER *EVP_MD_get0_provider(const EVP_MD *md) + * } + */ + public static FunctionDescriptor EVP_MD_get0_provider$descriptor() { + return EVP_MD_get0_provider.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const OSSL_PROVIDER *EVP_MD_get0_provider(const EVP_MD *md) + * } + */ + public static MethodHandle EVP_MD_get0_provider$handle() { + return EVP_MD_get0_provider.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const OSSL_PROVIDER *EVP_MD_get0_provider(const EVP_MD *md) + * } + */ + public static MemorySegment EVP_MD_get0_provider$address() { + return EVP_MD_get0_provider.ADDR; + } + + /** + * {@snippet lang = c : * const OSSL_PROVIDER *EVP_MD_get0_provider(const EVP_MD *md) * } */ public static MemorySegment EVP_MD_get0_provider(MemorySegment md) { - var mh$ = EVP_MD_get0_provider$MH(); + var mh$ = EVP_MD_get0_provider.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EVP_MD_get0_provider", md); } return (MemorySegment) mh$.invokeExact(md); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EVP_MD_fetch$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EVP_MD_fetch"), - DESC); - } - return Holder.MH; + private static class EVP_MD_fetch { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EVP_MD_fetch"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) + * Function descriptor for: + * {@snippet lang = c : * EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) + * } + */ + public static FunctionDescriptor EVP_MD_fetch$descriptor() { + return EVP_MD_fetch.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) + * } + */ + public static MethodHandle EVP_MD_fetch$handle() { + return EVP_MD_fetch.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) + * } + */ + public static MemorySegment EVP_MD_fetch$address() { + return EVP_MD_fetch.ADDR; + } + + /** + * {@snippet lang = c : * EVP_MD *EVP_MD_fetch(OSSL_LIB_CTX *ctx, const char *algorithm, const char *properties) * } */ public static MemorySegment EVP_MD_fetch(MemorySegment ctx, MemorySegment algorithm, MemorySegment properties) { - var mh$ = EVP_MD_fetch$MH(); + var mh$ = EVP_MD_fetch.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EVP_MD_fetch", ctx, algorithm, properties); } return (MemorySegment) mh$.invokeExact(ctx, algorithm, properties); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EVP_MD_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EVP_MD_free"), - DESC); - } - return Holder.MH; + private static class EVP_MD_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EVP_MD_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void EVP_MD_free(EVP_MD *md) + * Function descriptor for: + * {@snippet lang = c : * void EVP_MD_free(EVP_MD *md) + * } + */ + public static FunctionDescriptor EVP_MD_free$descriptor() { + return EVP_MD_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void EVP_MD_free(EVP_MD *md) + * } + */ + public static MethodHandle EVP_MD_free$handle() { + return EVP_MD_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void EVP_MD_free(EVP_MD *md) + * } + */ + public static MemorySegment EVP_MD_free$address() { + return EVP_MD_free.ADDR; + } + + /** + * {@snippet lang = c : * void EVP_MD_free(EVP_MD *md) * } */ public static void EVP_MD_free(MemorySegment md) { - var mh$ = EVP_MD_free$MH(); + var mh$ = EVP_MD_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EVP_MD_free", md); } mh$.invokeExact(md); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EVP_PKEY_get_base_id$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EVP_PKEY_get_base_id"), - DESC); - } - return Holder.MH; + private static class EVP_PKEY_get_base_id { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EVP_PKEY_get_base_id"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int EVP_PKEY_get_base_id(const EVP_PKEY *pkey) + * Function descriptor for: + * {@snippet lang = c : * int EVP_PKEY_get_base_id(const EVP_PKEY *pkey) + * } + */ + public static FunctionDescriptor EVP_PKEY_get_base_id$descriptor() { + return EVP_PKEY_get_base_id.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int EVP_PKEY_get_base_id(const EVP_PKEY *pkey) + * } + */ + public static MethodHandle EVP_PKEY_get_base_id$handle() { + return EVP_PKEY_get_base_id.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int EVP_PKEY_get_base_id(const EVP_PKEY *pkey) + * } + */ + public static MemorySegment EVP_PKEY_get_base_id$address() { + return EVP_PKEY_get_base_id.ADDR; + } + + /** + * {@snippet lang = c : * int EVP_PKEY_get_base_id(const EVP_PKEY *pkey) * } */ public static int EVP_PKEY_get_base_id(MemorySegment pkey) { - var mh$ = EVP_PKEY_get_base_id$MH(); + var mh$ = EVP_PKEY_get_base_id.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EVP_PKEY_get_base_id", pkey); } return (int) mh$.invokeExact(pkey); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EVP_PKEY_get_bits$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EVP_PKEY_get_bits"), - DESC); - } - return Holder.MH; + private static class EVP_PKEY_get_bits { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EVP_PKEY_get_bits"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int EVP_PKEY_get_bits(const EVP_PKEY *pkey) + * Function descriptor for: + * {@snippet lang = c : * int EVP_PKEY_get_bits(const EVP_PKEY *pkey) + * } + */ + public static FunctionDescriptor EVP_PKEY_get_bits$descriptor() { + return EVP_PKEY_get_bits.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int EVP_PKEY_get_bits(const EVP_PKEY *pkey) + * } + */ + public static MethodHandle EVP_PKEY_get_bits$handle() { + return EVP_PKEY_get_bits.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int EVP_PKEY_get_bits(const EVP_PKEY *pkey) + * } + */ + public static MemorySegment EVP_PKEY_get_bits$address() { + return EVP_PKEY_get_bits.ADDR; + } + + /** + * {@snippet lang = c : * int EVP_PKEY_get_bits(const EVP_PKEY *pkey) * } */ public static int EVP_PKEY_get_bits(MemorySegment pkey) { - var mh$ = EVP_PKEY_get_bits$MH(); + var mh$ = EVP_PKEY_get_bits.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EVP_PKEY_get_bits", pkey); } return (int) mh$.invokeExact(pkey); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EVP_PKEY_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EVP_PKEY_free"), - DESC); - } - return Holder.MH; + private static class EVP_PKEY_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EVP_PKEY_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void EVP_PKEY_free(EVP_PKEY *pkey) + * Function descriptor for: + * {@snippet lang = c : * void EVP_PKEY_free(EVP_PKEY *pkey) + * } + */ + public static FunctionDescriptor EVP_PKEY_free$descriptor() { + return EVP_PKEY_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void EVP_PKEY_free(EVP_PKEY *pkey) + * } + */ + public static MethodHandle EVP_PKEY_free$handle() { + return EVP_PKEY_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void EVP_PKEY_free(EVP_PKEY *pkey) + * } + */ + public static MemorySegment EVP_PKEY_free$address() { + return EVP_PKEY_free.ADDR; + } + + /** + * {@snippet lang = c : * void EVP_PKEY_free(EVP_PKEY *pkey) * } */ public static void EVP_PKEY_free(MemorySegment pkey) { - var mh$ = EVP_PKEY_free$MH(); + var mh$ = EVP_PKEY_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EVP_PKEY_free", pkey); } mh$.invokeExact(pkey); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EC_GROUP_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EC_GROUP_free"), - DESC); - } - return Holder.MH; + private static class EC_GROUP_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EC_GROUP_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void EC_GROUP_free(EC_GROUP *group) + * Function descriptor for: + * {@snippet lang = c : * void EC_GROUP_free(EC_GROUP *group) + * } + */ + public static FunctionDescriptor EC_GROUP_free$descriptor() { + return EC_GROUP_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void EC_GROUP_free(EC_GROUP *group) + * } + */ + public static MethodHandle EC_GROUP_free$handle() { + return EC_GROUP_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void EC_GROUP_free(EC_GROUP *group) + * } + */ + public static MemorySegment EC_GROUP_free$address() { + return EC_GROUP_free.ADDR; + } + + /** + * {@snippet lang = c : * void EC_GROUP_free(EC_GROUP *group) * } */ public static void EC_GROUP_free(MemorySegment group) { - var mh$ = EC_GROUP_free$MH(); + var mh$ = EC_GROUP_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EC_GROUP_free", group); } mh$.invokeExact(group); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EC_GROUP_get_curve_name$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EC_GROUP_get_curve_name"), - DESC); - } - return Holder.MH; + private static class EC_GROUP_get_curve_name { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EC_GROUP_get_curve_name"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int EC_GROUP_get_curve_name(const EC_GROUP *group) + * Function descriptor for: + * {@snippet lang = c : * int EC_GROUP_get_curve_name(const EC_GROUP *group) + * } + */ + public static FunctionDescriptor EC_GROUP_get_curve_name$descriptor() { + return EC_GROUP_get_curve_name.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int EC_GROUP_get_curve_name(const EC_GROUP *group) + * } + */ + public static MethodHandle EC_GROUP_get_curve_name$handle() { + return EC_GROUP_get_curve_name.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int EC_GROUP_get_curve_name(const EC_GROUP *group) + * } + */ + public static MemorySegment EC_GROUP_get_curve_name$address() { + return EC_GROUP_get_curve_name.ADDR; + } + + /** + * {@snippet lang = c : * int EC_GROUP_get_curve_name(const EC_GROUP *group) * } */ public static int EC_GROUP_get_curve_name(MemorySegment group) { - var mh$ = EC_GROUP_get_curve_name$MH(); + var mh$ = EC_GROUP_get_curve_name.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EC_GROUP_get_curve_name", group); } return (int) mh$.invokeExact(group); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle d2i_ECPKParameters$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("d2i_ECPKParameters"), - DESC); - } - return Holder.MH; + private static class d2i_ECPKParameters { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("d2i_ECPKParameters"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * EC_GROUP *d2i_ECPKParameters(EC_GROUP **, const unsigned char **in, long len) + * Function descriptor for: + * {@snippet lang = c : * EC_GROUP *d2i_ECPKParameters(EC_GROUP **, const unsigned char **in, long len) + * } + */ + public static FunctionDescriptor d2i_ECPKParameters$descriptor() { + return d2i_ECPKParameters.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * EC_GROUP *d2i_ECPKParameters(EC_GROUP **, const unsigned char **in, long len) + * } + */ + public static MethodHandle d2i_ECPKParameters$handle() { + return d2i_ECPKParameters.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * EC_GROUP *d2i_ECPKParameters(EC_GROUP **, const unsigned char **in, long len) + * } + */ + public static MemorySegment d2i_ECPKParameters$address() { + return d2i_ECPKParameters.ADDR; + } + + /** + * {@snippet lang = c : * EC_GROUP *d2i_ECPKParameters(EC_GROUP **, const unsigned char **in, long len) * } */ public static MemorySegment d2i_ECPKParameters(MemorySegment x0, MemorySegment in, long len) { - var mh$ = d2i_ECPKParameters$MH(); + var mh$ = d2i_ECPKParameters.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("d2i_ECPKParameters", x0, in, len); } return (MemorySegment) mh$.invokeExact(x0, in, len); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EC_KEY_new_by_curve_name$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EC_KEY_new_by_curve_name"), - DESC); - } - return Holder.MH; + private static class EC_KEY_new_by_curve_name { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EC_KEY_new_by_curve_name"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * EC_KEY *EC_KEY_new_by_curve_name(int nid) + * Function descriptor for: + * {@snippet lang = c : * EC_KEY *EC_KEY_new_by_curve_name(int nid) + * } + */ + public static FunctionDescriptor EC_KEY_new_by_curve_name$descriptor() { + return EC_KEY_new_by_curve_name.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * EC_KEY *EC_KEY_new_by_curve_name(int nid) + * } + */ + public static MethodHandle EC_KEY_new_by_curve_name$handle() { + return EC_KEY_new_by_curve_name.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * EC_KEY *EC_KEY_new_by_curve_name(int nid) + * } + */ + public static MemorySegment EC_KEY_new_by_curve_name$address() { + return EC_KEY_new_by_curve_name.ADDR; + } + + /** + * {@snippet lang = c : * EC_KEY *EC_KEY_new_by_curve_name(int nid) * } */ public static MemorySegment EC_KEY_new_by_curve_name(int nid) { - var mh$ = EC_KEY_new_by_curve_name$MH(); + var mh$ = EC_KEY_new_by_curve_name.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EC_KEY_new_by_curve_name", nid); } return (MemorySegment) mh$.invokeExact(nid); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle EC_KEY_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("EC_KEY_free"), - DESC); - } - return Holder.MH; + private static class EC_KEY_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("EC_KEY_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void EC_KEY_free(EC_KEY *key) + * Function descriptor for: + * {@snippet lang = c : * void EC_KEY_free(EC_KEY *key) + * } + */ + public static FunctionDescriptor EC_KEY_free$descriptor() { + return EC_KEY_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void EC_KEY_free(EC_KEY *key) + * } + */ + public static MethodHandle EC_KEY_free$handle() { + return EC_KEY_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void EC_KEY_free(EC_KEY *key) + * } + */ + public static MemorySegment EC_KEY_free$address() { + return EC_KEY_free.ADDR; + } + + /** + * {@snippet lang = c : * void EC_KEY_free(EC_KEY *key) * } */ public static void EC_KEY_free(MemorySegment key) { - var mh$ = EC_KEY_free$MH(); + var mh$ = EC_KEY_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("EC_KEY_free", key); } mh$.invokeExact(key); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle DH_new$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class DH_new { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("DH_new"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("DH_new"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * DH *DH_new(void) + * Function descriptor for: + * {@snippet lang = c : * DH *DH_new() + * } + */ + public static FunctionDescriptor DH_new$descriptor() { + return DH_new.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * DH *DH_new() + * } + */ + public static MethodHandle DH_new$handle() { + return DH_new.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * DH *DH_new() + * } + */ + public static MemorySegment DH_new$address() { + return DH_new.ADDR; + } + + /** + * {@snippet lang = c : * DH *DH_new() * } */ public static MemorySegment DH_new() { - var mh$ = DH_new$MH(); + var mh$ = DH_new.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("DH_new"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle DH_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("DH_free"), - DESC); - } - return Holder.MH; + private static class DH_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("DH_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void DH_free(DH *dh) + * Function descriptor for: + * {@snippet lang = c : * void DH_free(DH *dh) + * } + */ + public static FunctionDescriptor DH_free$descriptor() { + return DH_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void DH_free(DH *dh) + * } + */ + public static MethodHandle DH_free$handle() { + return DH_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void DH_free(DH *dh) + * } + */ + public static MemorySegment DH_free$address() { + return DH_free.ADDR; + } + + /** + * {@snippet lang = c : * void DH_free(DH *dh) * } */ public static void DH_free(MemorySegment dh) { - var mh$ = DH_free$MH(); + var mh$ = DH_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("DH_free", dh); } mh$.invokeExact(dh); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle DH_set0_pqg$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("DH_set0_pqg"), - DESC); - } - return Holder.MH; + private static class DH_set0_pqg { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("DH_set0_pqg"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) + * Function descriptor for: + * {@snippet lang = c : * int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) + * } + */ + public static FunctionDescriptor DH_set0_pqg$descriptor() { + return DH_set0_pqg.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) + * } + */ + public static MethodHandle DH_set0_pqg$handle() { + return DH_set0_pqg.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) + * } + */ + public static MemorySegment DH_set0_pqg$address() { + return DH_set0_pqg.ADDR; + } + + /** + * {@snippet lang = c : * int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) * } */ public static int DH_set0_pqg(MemorySegment dh, MemorySegment p, MemorySegment q, MemorySegment g) { - var mh$ = DH_set0_pqg$MH(); + var mh$ = DH_set0_pqg.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("DH_set0_pqg", dh, p, q, g); } return (int) mh$.invokeExact(dh, p, q, g); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_STORE_set_flags$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_STORE_set_flags"), - DESC); + private static class X509_STORE_set_flags { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_set_flags"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang = c : * int X509_STORE_set_flags(X509_STORE *xs, unsigned long flags) + * } + */ + public static FunctionDescriptor X509_STORE_set_flags$descriptor() { + return X509_STORE_set_flags.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int X509_STORE_set_flags(X509_STORE *xs, unsigned long flags) + * } + */ + public static MethodHandle X509_STORE_set_flags$handle() { + return X509_STORE_set_flags.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int X509_STORE_set_flags(X509_STORE *xs, unsigned long flags) + * } + */ + public static MemorySegment X509_STORE_set_flags$address() { + return X509_STORE_set_flags.ADDR; + } + + /** + * {@snippet lang = c : * int X509_STORE_set_flags(X509_STORE *xs, unsigned long flags) + * } + */ + public static int X509_STORE_set_flags(MemorySegment xs, long flags) { + var mh$ = X509_STORE_set_flags.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("X509_STORE_set_flags", xs, flags); + } + return (int) mh$.invokeExact(xs, flags); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); } - return Holder.MH; + } + + private static class X509_STORE_CTX_get0_store { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + openssl_h.C_POINTER, + openssl_h.C_POINTER + ); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get0_store"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** + * Function descriptor for: * {@snippet lang=c : - * int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags) + * X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx) * } */ - public static int X509_STORE_set_flags(MemorySegment ctx, long flags) { - var mh$ = X509_STORE_set_flags$MH(); + public static FunctionDescriptor X509_STORE_CTX_get0_store$descriptor() { + return X509_STORE_CTX_get0_store.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx) + * } + */ + public static MethodHandle X509_STORE_CTX_get0_store$handle() { + return X509_STORE_CTX_get0_store.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx) + * } + */ + public static MemorySegment X509_STORE_CTX_get0_store$address() { + return X509_STORE_CTX_get0_store.ADDR; + } + + /** + * {@snippet lang=c : + * X509_STORE *X509_STORE_CTX_get0_store(const X509_STORE_CTX *ctx) + * } + */ + public static MemorySegment X509_STORE_CTX_get0_store(MemorySegment ctx) { + var mh$ = X509_STORE_CTX_get0_store.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("X509_STORE_set_flags", ctx, flags); + traceDowncall("X509_STORE_CTX_get0_store", ctx); } - return (int) mh$.invokeExact(ctx, flags); + return (MemorySegment)mh$.invokeExact(ctx); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_STORE_CTX_get0_untrusted$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_STORE_CTX_get0_untrusted"), - DESC); - } - return Holder.MH; + private static class X509_STORE_CTX_get0_untrusted { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get0_untrusted"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * struct stack_st_X509 *X509_STORE_CTX_get0_untrusted(const X509_STORE_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * struct stack_st_X509 *X509_STORE_CTX_get0_untrusted(const X509_STORE_CTX *ctx) + * } + */ + public static FunctionDescriptor X509_STORE_CTX_get0_untrusted$descriptor() { + return X509_STORE_CTX_get0_untrusted.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * struct stack_st_X509 *X509_STORE_CTX_get0_untrusted(const X509_STORE_CTX *ctx) + * } + */ + public static MethodHandle X509_STORE_CTX_get0_untrusted$handle() { + return X509_STORE_CTX_get0_untrusted.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * struct stack_st_X509 *X509_STORE_CTX_get0_untrusted(const X509_STORE_CTX *ctx) + * } + */ + public static MemorySegment X509_STORE_CTX_get0_untrusted$address() { + return X509_STORE_CTX_get0_untrusted.ADDR; + } + + /** + * {@snippet lang = c : * struct stack_st_X509 *X509_STORE_CTX_get0_untrusted(const X509_STORE_CTX *ctx) * } */ public static MemorySegment X509_STORE_CTX_get0_untrusted(MemorySegment ctx) { - var mh$ = X509_STORE_CTX_get0_untrusted$MH(); + var mh$ = X509_STORE_CTX_get0_untrusted.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_STORE_CTX_get0_untrusted", ctx); } return (MemorySegment) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_STORE_add_lookup$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_STORE_add_lookup"), - DESC); - } - return Holder.MH; + private static class X509_STORE_add_lookup { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_add_lookup"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m) + * Function descriptor for: + * {@snippet lang = c : * X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *xs, X509_LOOKUP_METHOD *m) + * } + */ + public static FunctionDescriptor X509_STORE_add_lookup$descriptor() { + return X509_STORE_add_lookup.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *xs, X509_LOOKUP_METHOD *m) + * } + */ + public static MethodHandle X509_STORE_add_lookup$handle() { + return X509_STORE_add_lookup.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *xs, X509_LOOKUP_METHOD *m) + * } + */ + public static MemorySegment X509_STORE_add_lookup$address() { + return X509_STORE_add_lookup.ADDR; + } + + /** + * {@snippet lang = c : * X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *xs, X509_LOOKUP_METHOD *m) * } */ - public static MemorySegment X509_STORE_add_lookup(MemorySegment v, MemorySegment m) { - var mh$ = X509_STORE_add_lookup$MH(); + public static MemorySegment X509_STORE_add_lookup(MemorySegment xs, MemorySegment m) { + var mh$ = X509_STORE_add_lookup.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("X509_STORE_add_lookup", v, m); + traceDowncall("X509_STORE_add_lookup", xs, m); } - return (MemorySegment) mh$.invokeExact(v, m); + return (MemorySegment) mh$.invokeExact(xs, m); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_LOOKUP_hash_dir$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class X509_LOOKUP_hash_dir { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_LOOKUP_hash_dir"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_LOOKUP_hash_dir"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void) + * Function descriptor for: + * {@snippet lang = c : * X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir() + * } + */ + public static FunctionDescriptor X509_LOOKUP_hash_dir$descriptor() { + return X509_LOOKUP_hash_dir.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir() + * } + */ + public static MethodHandle X509_LOOKUP_hash_dir$handle() { + return X509_LOOKUP_hash_dir.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir() + * } + */ + public static MemorySegment X509_LOOKUP_hash_dir$address() { + return X509_LOOKUP_hash_dir.ADDR; + } + + /** + * {@snippet lang = c : * X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir() * } */ public static MemorySegment X509_LOOKUP_hash_dir() { - var mh$ = X509_LOOKUP_hash_dir$MH(); + var mh$ = X509_LOOKUP_hash_dir.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_LOOKUP_hash_dir"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_LOOKUP_file$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class X509_LOOKUP_file { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_LOOKUP_file"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_LOOKUP_file"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * X509_LOOKUP_METHOD *X509_LOOKUP_file(void) + * Function descriptor for: + * {@snippet lang = c : * X509_LOOKUP_METHOD *X509_LOOKUP_file() + * } + */ + public static FunctionDescriptor X509_LOOKUP_file$descriptor() { + return X509_LOOKUP_file.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * X509_LOOKUP_METHOD *X509_LOOKUP_file() + * } + */ + public static MethodHandle X509_LOOKUP_file$handle() { + return X509_LOOKUP_file.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * X509_LOOKUP_METHOD *X509_LOOKUP_file() + * } + */ + public static MemorySegment X509_LOOKUP_file$address() { + return X509_LOOKUP_file.ADDR; + } + + /** + * {@snippet lang = c : * X509_LOOKUP_METHOD *X509_LOOKUP_file() * } */ public static MemorySegment X509_LOOKUP_file() { - var mh$ = X509_LOOKUP_file$MH(); + var mh$ = X509_LOOKUP_file.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_LOOKUP_file"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_LOOKUP_ctrl$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_LOOKUP_ctrl"), - DESC); - } - return Holder.MH; + private static class X509_LOOKUP_ctrl { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, + openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_LOOKUP_ctrl"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, char **ret) + * Function descriptor for: + * {@snippet lang = c : * int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, char **ret) + * } + */ + public static FunctionDescriptor X509_LOOKUP_ctrl$descriptor() { + return X509_LOOKUP_ctrl.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, char **ret) + * } + */ + public static MethodHandle X509_LOOKUP_ctrl$handle() { + return X509_LOOKUP_ctrl.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, char **ret) + * } + */ + public static MemorySegment X509_LOOKUP_ctrl$address() { + return X509_LOOKUP_ctrl.ADDR; + } + + /** + * {@snippet lang = c : * int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc, long argl, char **ret) * } */ public static int X509_LOOKUP_ctrl(MemorySegment ctx, int cmd, MemorySegment argc, long argl, MemorySegment ret) { - var mh$ = X509_LOOKUP_ctrl$MH(); + var mh$ = X509_LOOKUP_ctrl.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_LOOKUP_ctrl", ctx, cmd, argc, argl, ret); } return (int) mh$.invokeExact(ctx, cmd, argc, argl, ret); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_STORE_CTX_get_ex_data$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_STORE_CTX_get_ex_data"), - DESC); - } - return Holder.MH; + private static class X509_STORE_CTX_get_ex_data { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get_ex_data"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void *X509_STORE_CTX_get_ex_data(const X509_STORE_CTX *ctx, int idx) + * Function descriptor for: + * {@snippet lang = c : * void *X509_STORE_CTX_get_ex_data(const X509_STORE_CTX *ctx, int idx) + * } + */ + public static FunctionDescriptor X509_STORE_CTX_get_ex_data$descriptor() { + return X509_STORE_CTX_get_ex_data.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void *X509_STORE_CTX_get_ex_data(const X509_STORE_CTX *ctx, int idx) + * } + */ + public static MethodHandle X509_STORE_CTX_get_ex_data$handle() { + return X509_STORE_CTX_get_ex_data.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void *X509_STORE_CTX_get_ex_data(const X509_STORE_CTX *ctx, int idx) + * } + */ + public static MemorySegment X509_STORE_CTX_get_ex_data$address() { + return X509_STORE_CTX_get_ex_data.ADDR; + } + + /** + * {@snippet lang = c : * void *X509_STORE_CTX_get_ex_data(const X509_STORE_CTX *ctx, int idx) * } */ public static MemorySegment X509_STORE_CTX_get_ex_data(MemorySegment ctx, int idx) { - var mh$ = X509_STORE_CTX_get_ex_data$MH(); + var mh$ = X509_STORE_CTX_get_ex_data.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_STORE_CTX_get_ex_data", ctx, idx); } return (MemorySegment) mh$.invokeExact(ctx, idx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_STORE_CTX_get_error$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_STORE_CTX_get_error"), - DESC); - } - return Holder.MH; + private static class X509_STORE_CTX_get_error { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get_error"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int X509_STORE_CTX_get_error(const X509_STORE_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * int X509_STORE_CTX_get_error(const X509_STORE_CTX *ctx) + * } + */ + public static FunctionDescriptor X509_STORE_CTX_get_error$descriptor() { + return X509_STORE_CTX_get_error.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int X509_STORE_CTX_get_error(const X509_STORE_CTX *ctx) + * } + */ + public static MethodHandle X509_STORE_CTX_get_error$handle() { + return X509_STORE_CTX_get_error.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int X509_STORE_CTX_get_error(const X509_STORE_CTX *ctx) + * } + */ + public static MemorySegment X509_STORE_CTX_get_error$address() { + return X509_STORE_CTX_get_error.ADDR; + } + + /** + * {@snippet lang = c : * int X509_STORE_CTX_get_error(const X509_STORE_CTX *ctx) * } */ public static int X509_STORE_CTX_get_error(MemorySegment ctx) { - var mh$ = X509_STORE_CTX_get_error$MH(); + var mh$ = X509_STORE_CTX_get_error.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_STORE_CTX_get_error", ctx); } return (int) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_STORE_CTX_set_error$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_STORE_CTX_set_error"), - DESC); - } - return Holder.MH; + private static class X509_STORE_CTX_set_error { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_set_error"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int s) + * Function descriptor for: + * {@snippet lang = c : * void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int s) + * } + */ + public static FunctionDescriptor X509_STORE_CTX_set_error$descriptor() { + return X509_STORE_CTX_set_error.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int s) + * } + */ + public static MethodHandle X509_STORE_CTX_set_error$handle() { + return X509_STORE_CTX_set_error.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int s) + * } + */ + public static MemorySegment X509_STORE_CTX_set_error$address() { + return X509_STORE_CTX_set_error.ADDR; + } + + /** + * {@snippet lang = c : * void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int s) * } */ public static void X509_STORE_CTX_set_error(MemorySegment ctx, int s) { - var mh$ = X509_STORE_CTX_set_error$MH(); + var mh$ = X509_STORE_CTX_set_error.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_STORE_CTX_set_error", ctx, s); } mh$.invokeExact(ctx, s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_STORE_CTX_get_error_depth$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_STORE_CTX_get_error_depth"), - DESC); - } - return Holder.MH; + private static class X509_STORE_CTX_get_error_depth { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get_error_depth"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int X509_STORE_CTX_get_error_depth(const X509_STORE_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * int X509_STORE_CTX_get_error_depth(const X509_STORE_CTX *ctx) + * } + */ + public static FunctionDescriptor X509_STORE_CTX_get_error_depth$descriptor() { + return X509_STORE_CTX_get_error_depth.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int X509_STORE_CTX_get_error_depth(const X509_STORE_CTX *ctx) + * } + */ + public static MethodHandle X509_STORE_CTX_get_error_depth$handle() { + return X509_STORE_CTX_get_error_depth.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int X509_STORE_CTX_get_error_depth(const X509_STORE_CTX *ctx) + * } + */ + public static MemorySegment X509_STORE_CTX_get_error_depth$address() { + return X509_STORE_CTX_get_error_depth.ADDR; + } + + /** + * {@snippet lang = c : * int X509_STORE_CTX_get_error_depth(const X509_STORE_CTX *ctx) * } */ public static int X509_STORE_CTX_get_error_depth(MemorySegment ctx) { - var mh$ = X509_STORE_CTX_get_error_depth$MH(); + var mh$ = X509_STORE_CTX_get_error_depth.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_STORE_CTX_get_error_depth", ctx); } return (int) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_STORE_CTX_get_current_cert$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_STORE_CTX_get_current_cert"), - DESC); - } - return Holder.MH; + private static class X509_STORE_CTX_get_current_cert { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get_current_cert"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * X509 *X509_STORE_CTX_get_current_cert(const X509_STORE_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * X509 *X509_STORE_CTX_get_current_cert(const X509_STORE_CTX *ctx) + * } + */ + public static FunctionDescriptor X509_STORE_CTX_get_current_cert$descriptor() { + return X509_STORE_CTX_get_current_cert.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * X509 *X509_STORE_CTX_get_current_cert(const X509_STORE_CTX *ctx) + * } + */ + public static MethodHandle X509_STORE_CTX_get_current_cert$handle() { + return X509_STORE_CTX_get_current_cert.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * X509 *X509_STORE_CTX_get_current_cert(const X509_STORE_CTX *ctx) + * } + */ + public static MemorySegment X509_STORE_CTX_get_current_cert$address() { + return X509_STORE_CTX_get_current_cert.ADDR; + } + + /** + * {@snippet lang = c : * X509 *X509_STORE_CTX_get_current_cert(const X509_STORE_CTX *ctx) * } */ public static MemorySegment X509_STORE_CTX_get_current_cert(MemorySegment ctx) { - var mh$ = X509_STORE_CTX_get_current_cert$MH(); + var mh$ = X509_STORE_CTX_get_current_cert.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_STORE_CTX_get_current_cert", ctx); } return (MemorySegment) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_STORE_CTX_get0_current_issuer$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_STORE_CTX_get0_current_issuer"), - DESC); - } - return Holder.MH; + private static class X509_STORE_CTX_get1_issuer { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get1_issuer"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) + * } + */ + public static FunctionDescriptor X509_STORE_CTX_get1_issuer$descriptor() { + return X509_STORE_CTX_get1_issuer.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) + * } + */ + public static MethodHandle X509_STORE_CTX_get1_issuer$handle() { + return X509_STORE_CTX_get1_issuer.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) * } */ - public static MemorySegment X509_STORE_CTX_get0_current_issuer(MemorySegment ctx) { - var mh$ = X509_STORE_CTX_get0_current_issuer$MH(); + public static MemorySegment X509_STORE_CTX_get1_issuer$address() { + return X509_STORE_CTX_get1_issuer.ADDR; + } + + /** + * {@snippet lang = c : * int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x) + * } + */ + public static int X509_STORE_CTX_get1_issuer(MemorySegment issuer, MemorySegment ctx, MemorySegment x) { + var mh$ = X509_STORE_CTX_get1_issuer.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("X509_STORE_CTX_get0_current_issuer", ctx); + traceDowncall("X509_STORE_CTX_get1_issuer", issuer, ctx, x); } - return (MemorySegment) mh$.invokeExact(ctx); + return (int) mh$.invokeExact(issuer, ctx, x); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle d2i_X509_bio$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("d2i_X509_bio"), - DESC); - } - return Holder.MH; + private static class d2i_X509_bio { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("d2i_X509_bio"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * X509 *d2i_X509_bio(BIO *bp, X509 **x509) + * Function descriptor for: + * {@snippet lang = c : * X509 *d2i_X509_bio(BIO *bp, X509 **x509) + * } + */ + public static FunctionDescriptor d2i_X509_bio$descriptor() { + return d2i_X509_bio.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * X509 *d2i_X509_bio(BIO *bp, X509 **x509) + * } + */ + public static MethodHandle d2i_X509_bio$handle() { + return d2i_X509_bio.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * X509 *d2i_X509_bio(BIO *bp, X509 **x509) + * } + */ + public static MemorySegment d2i_X509_bio$address() { + return d2i_X509_bio.ADDR; + } + + /** + * {@snippet lang = c : * X509 *d2i_X509_bio(BIO *bp, X509 **x509) * } */ public static MemorySegment d2i_X509_bio(MemorySegment bp, MemorySegment x509) { - var mh$ = d2i_X509_bio$MH(); + var mh$ = d2i_X509_bio.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("d2i_X509_bio", bp, x509); } return (MemorySegment) mh$.invokeExact(bp, x509); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_free"), - DESC); - } - return Holder.MH; + private static class X509_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern void X509_free(X509 *a) + * Function descriptor for: + * {@snippet lang = c : * extern void X509_free(X509 *a) + * } + */ + public static FunctionDescriptor X509_free$descriptor() { + return X509_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern void X509_free(X509 *a) + * } + */ + public static MethodHandle X509_free$handle() { + return X509_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern void X509_free(X509 *a) + * } + */ + public static MemorySegment X509_free$address() { + return X509_free.ADDR; + } + + /** + * {@snippet lang = c : * extern void X509_free(X509 *a) * } */ public static void X509_free(MemorySegment a) { - var mh$ = X509_free$MH(); + var mh$ = X509_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_free", a); } mh$.invokeExact(a); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle d2i_X509$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("d2i_X509"), - DESC); - } - return Holder.MH; + private static class d2i_X509 { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("d2i_X509"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern X509 *d2i_X509(X509 **a, const unsigned char **in, long len) + * Function descriptor for: + * {@snippet lang = c : * extern X509 *d2i_X509(X509 **a, const unsigned char **in, long len) + * } + */ + public static FunctionDescriptor d2i_X509$descriptor() { + return d2i_X509.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern X509 *d2i_X509(X509 **a, const unsigned char **in, long len) + * } + */ + public static MethodHandle d2i_X509$handle() { + return d2i_X509.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern X509 *d2i_X509(X509 **a, const unsigned char **in, long len) + * } + */ + public static MemorySegment d2i_X509$address() { + return d2i_X509.ADDR; + } + + /** + * {@snippet lang = c : * extern X509 *d2i_X509(X509 **a, const unsigned char **in, long len) * } */ public static MemorySegment d2i_X509(MemorySegment a, MemorySegment in, long len) { - var mh$ = d2i_X509$MH(); + var mh$ = d2i_X509.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("d2i_X509", a, in, len); } return (MemorySegment) mh$.invokeExact(a, in, len); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle i2d_X509$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("i2d_X509"), - DESC); - } - return Holder.MH; + private static class i2d_X509 { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("i2d_X509"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern int i2d_X509(const X509 *a, unsigned char **out) + * Function descriptor for: + * {@snippet lang = c : * extern int i2d_X509(const X509 *a, unsigned char **out) + * } + */ + public static FunctionDescriptor i2d_X509$descriptor() { + return i2d_X509.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern int i2d_X509(const X509 *a, unsigned char **out) + * } + */ + public static MethodHandle i2d_X509$handle() { + return i2d_X509.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern int i2d_X509(const X509 *a, unsigned char **out) + * } + */ + public static MemorySegment i2d_X509$address() { + return i2d_X509.ADDR; + } + + /** + * {@snippet lang = c : * extern int i2d_X509(const X509 *a, unsigned char **out) * } */ public static int i2d_X509(MemorySegment a, MemorySegment out) { - var mh$ = i2d_X509$MH(); + var mh$ = i2d_X509.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("i2d_X509", a, out); } return (int) mh$.invokeExact(a, out); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_get_ext_by_NID$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_get_ext_by_NID"), - DESC); - } - return Holder.MH; + private static class X509_get_ext_by_NID { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_INT, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_get_ext_by_NID"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos) + * Function descriptor for: + * {@snippet lang = c : * int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos) + * } + */ + public static FunctionDescriptor X509_get_ext_by_NID$descriptor() { + return X509_get_ext_by_NID.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos) + * } + */ + public static MethodHandle X509_get_ext_by_NID$handle() { + return X509_get_ext_by_NID.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos) + * } + */ + public static MemorySegment X509_get_ext_by_NID$address() { + return X509_get_ext_by_NID.ADDR; + } + + /** + * {@snippet lang = c : * int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos) * } */ public static int X509_get_ext_by_NID(MemorySegment x, int nid, int lastpos) { - var mh$ = X509_get_ext_by_NID$MH(); + var mh$ = X509_get_ext_by_NID.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_get_ext_by_NID", x, nid, lastpos); } return (int) mh$.invokeExact(x, nid, lastpos); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_get_ext$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_get_ext"), - DESC); - } - return Holder.MH; + private static class X509_get_ext { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_get_ext"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * X509_EXTENSION *X509_get_ext(const X509 *x, int loc) + * Function descriptor for: + * {@snippet lang = c : * X509_EXTENSION *X509_get_ext(const X509 *x, int loc) + * } + */ + public static FunctionDescriptor X509_get_ext$descriptor() { + return X509_get_ext.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * X509_EXTENSION *X509_get_ext(const X509 *x, int loc) + * } + */ + public static MethodHandle X509_get_ext$handle() { + return X509_get_ext.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * X509_EXTENSION *X509_get_ext(const X509 *x, int loc) + * } + */ + public static MemorySegment X509_get_ext$address() { + return X509_get_ext.ADDR; + } + + /** + * {@snippet lang = c : * X509_EXTENSION *X509_get_ext(const X509 *x, int loc) * } */ public static MemorySegment X509_get_ext(MemorySegment x, int loc) { - var mh$ = X509_get_ext$MH(); + var mh$ = X509_get_ext.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_get_ext", x, loc); } return (MemorySegment) mh$.invokeExact(x, loc); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_EXTENSION_get_data$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_EXTENSION_get_data"), - DESC); - } - return Holder.MH; + private static class X509_EXTENSION_get_data { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_EXTENSION_get_data"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne) + * Function descriptor for: + * {@snippet lang = c : * ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne) + * } + */ + public static FunctionDescriptor X509_EXTENSION_get_data$descriptor() { + return X509_EXTENSION_get_data.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne) + * } + */ + public static MethodHandle X509_EXTENSION_get_data$handle() { + return X509_EXTENSION_get_data.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne) + * } + */ + public static MemorySegment X509_EXTENSION_get_data$address() { + return X509_EXTENSION_get_data.ADDR; + } + + /** + * {@snippet lang = c : * ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne) * } */ public static MemorySegment X509_EXTENSION_get_data(MemorySegment ne) { - var mh$ = X509_EXTENSION_get_data$MH(); + var mh$ = X509_EXTENSION_get_data.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_EXTENSION_get_data", ne); } return (MemorySegment) mh$.invokeExact(ne); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle PEM_ASN1_read_bio$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("PEM_ASN1_read_bio"), - DESC); - } - return Holder.MH; + private static class PEM_ASN1_read_bio { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("PEM_ASN1_read_bio"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x, pem_password_cb *cb, void *u) + * Function descriptor for: + * {@snippet lang = c + * : * void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x, pem_password_cb *cb, void *u) + * } + */ + public static FunctionDescriptor PEM_ASN1_read_bio$descriptor() { + return PEM_ASN1_read_bio.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c + * : * void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x, pem_password_cb *cb, void *u) + * } + */ + public static MethodHandle PEM_ASN1_read_bio$handle() { + return PEM_ASN1_read_bio.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c + * : * void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x, pem_password_cb *cb, void *u) + * } + */ + public static MemorySegment PEM_ASN1_read_bio$address() { + return PEM_ASN1_read_bio.ADDR; + } + + /** + * {@snippet lang = c + * : * void *PEM_ASN1_read_bio(d2i_of_void *d2i, const char *name, BIO *bp, void **x, pem_password_cb *cb, void *u) * } */ - public static MemorySegment PEM_ASN1_read_bio(MemorySegment d2i, MemorySegment name, MemorySegment bp, MemorySegment x, MemorySegment cb, MemorySegment u) { - var mh$ = PEM_ASN1_read_bio$MH(); + public static MemorySegment PEM_ASN1_read_bio(MemorySegment d2i, MemorySegment name, MemorySegment bp, + MemorySegment x, MemorySegment cb, MemorySegment u) { + var mh$ = PEM_ASN1_read_bio.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("PEM_ASN1_read_bio", d2i, name, bp, x, cb, u); } return (MemorySegment) mh$.invokeExact(d2i, name, bp, x, cb, u); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle PEM_read_bio_X509_AUX$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("PEM_read_bio_X509_AUX"), - DESC); - } - return Holder.MH; + private static class PEM_read_bio_X509_AUX { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("PEM_read_bio_X509_AUX"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern X509 *PEM_read_bio_X509_AUX(BIO *out, X509 **x, pem_password_cb *cb, void *u) + * Function descriptor for: + * {@snippet lang = c : * extern X509 *PEM_read_bio_X509_AUX(BIO *out, X509 **x, pem_password_cb *cb, void *u) + * } + */ + public static FunctionDescriptor PEM_read_bio_X509_AUX$descriptor() { + return PEM_read_bio_X509_AUX.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern X509 *PEM_read_bio_X509_AUX(BIO *out, X509 **x, pem_password_cb *cb, void *u) + * } + */ + public static MethodHandle PEM_read_bio_X509_AUX$handle() { + return PEM_read_bio_X509_AUX.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern X509 *PEM_read_bio_X509_AUX(BIO *out, X509 **x, pem_password_cb *cb, void *u) + * } + */ + public static MemorySegment PEM_read_bio_X509_AUX$address() { + return PEM_read_bio_X509_AUX.ADDR; + } + + /** + * {@snippet lang = c : * extern X509 *PEM_read_bio_X509_AUX(BIO *out, X509 **x, pem_password_cb *cb, void *u) * } */ - public static MemorySegment PEM_read_bio_X509_AUX(MemorySegment out, MemorySegment x, MemorySegment cb, MemorySegment u) { - var mh$ = PEM_read_bio_X509_AUX$MH(); + public static MemorySegment PEM_read_bio_X509_AUX(MemorySegment out, MemorySegment x, MemorySegment cb, + MemorySegment u) { + var mh$ = PEM_read_bio_X509_AUX.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("PEM_read_bio_X509_AUX", out, x, cb, u); } return (MemorySegment) mh$.invokeExact(out, x, cb, u); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle PEM_read_bio_ECPKParameters$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("PEM_read_bio_ECPKParameters"), - DESC); - } - return Holder.MH; + private static class PEM_read_bio_ECPKParameters { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("PEM_read_bio_ECPKParameters"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * EC_GROUP *PEM_read_bio_ECPKParameters(BIO *out, EC_GROUP **x, pem_password_cb *cb, void *u) + * Function descriptor for: + * {@snippet lang = c + * : * EC_GROUP *PEM_read_bio_ECPKParameters(BIO *out, EC_GROUP **x, pem_password_cb *cb, void *u) * } */ - public static MemorySegment PEM_read_bio_ECPKParameters(MemorySegment out, MemorySegment x, MemorySegment cb, MemorySegment u) { - var mh$ = PEM_read_bio_ECPKParameters$MH(); + public static FunctionDescriptor PEM_read_bio_ECPKParameters$descriptor() { + return PEM_read_bio_ECPKParameters.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c + * : * EC_GROUP *PEM_read_bio_ECPKParameters(BIO *out, EC_GROUP **x, pem_password_cb *cb, void *u) + * } + */ + public static MethodHandle PEM_read_bio_ECPKParameters$handle() { + return PEM_read_bio_ECPKParameters.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c + * : * EC_GROUP *PEM_read_bio_ECPKParameters(BIO *out, EC_GROUP **x, pem_password_cb *cb, void *u) + * } + */ + public static MemorySegment PEM_read_bio_ECPKParameters$address() { + return PEM_read_bio_ECPKParameters.ADDR; + } + + /** + * {@snippet lang = c + * : * EC_GROUP *PEM_read_bio_ECPKParameters(BIO *out, EC_GROUP **x, pem_password_cb *cb, void *u) + * } + */ + public static MemorySegment PEM_read_bio_ECPKParameters(MemorySegment out, MemorySegment x, MemorySegment cb, + MemorySegment u) { + var mh$ = PEM_read_bio_ECPKParameters.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("PEM_read_bio_ECPKParameters", out, x, cb, u); } return (MemorySegment) mh$.invokeExact(out, x, cb, u); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle PEM_read_bio_DHparams$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("PEM_read_bio_DHparams"), - DESC); - } - return Holder.MH; + private static class PEM_read_bio_DHparams { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("PEM_read_bio_DHparams"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * DH *PEM_read_bio_DHparams(BIO *out, DH **x, pem_password_cb *cb, void *u) + * Function descriptor for: + * {@snippet lang = c : * DH *PEM_read_bio_DHparams(BIO *out, DH **x, pem_password_cb *cb, void *u) + * } + */ + public static FunctionDescriptor PEM_read_bio_DHparams$descriptor() { + return PEM_read_bio_DHparams.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * DH *PEM_read_bio_DHparams(BIO *out, DH **x, pem_password_cb *cb, void *u) + * } + */ + public static MethodHandle PEM_read_bio_DHparams$handle() { + return PEM_read_bio_DHparams.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * DH *PEM_read_bio_DHparams(BIO *out, DH **x, pem_password_cb *cb, void *u) + * } + */ + public static MemorySegment PEM_read_bio_DHparams$address() { + return PEM_read_bio_DHparams.ADDR; + } + + /** + * {@snippet lang = c : * DH *PEM_read_bio_DHparams(BIO *out, DH **x, pem_password_cb *cb, void *u) * } */ - public static MemorySegment PEM_read_bio_DHparams(MemorySegment out, MemorySegment x, MemorySegment cb, MemorySegment u) { - var mh$ = PEM_read_bio_DHparams$MH(); + public static MemorySegment PEM_read_bio_DHparams(MemorySegment out, MemorySegment x, MemorySegment cb, + MemorySegment u) { + var mh$ = PEM_read_bio_DHparams.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("PEM_read_bio_DHparams", out, x, cb, u); } return (MemorySegment) mh$.invokeExact(out, x, cb, u); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle PEM_read_bio_PrivateKey$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("PEM_read_bio_PrivateKey"), - DESC); - } - return Holder.MH; + private static class PEM_read_bio_PrivateKey { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("PEM_read_bio_PrivateKey"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern EVP_PKEY *PEM_read_bio_PrivateKey(BIO *out, EVP_PKEY **x, pem_password_cb *cb, void *u) + * Function descriptor for: + * {@snippet lang = c + * : * extern EVP_PKEY *PEM_read_bio_PrivateKey(BIO *out, EVP_PKEY **x, pem_password_cb *cb, void *u) + * } + */ + public static FunctionDescriptor PEM_read_bio_PrivateKey$descriptor() { + return PEM_read_bio_PrivateKey.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c + * : * extern EVP_PKEY *PEM_read_bio_PrivateKey(BIO *out, EVP_PKEY **x, pem_password_cb *cb, void *u) + * } + */ + public static MethodHandle PEM_read_bio_PrivateKey$handle() { + return PEM_read_bio_PrivateKey.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c + * : * extern EVP_PKEY *PEM_read_bio_PrivateKey(BIO *out, EVP_PKEY **x, pem_password_cb *cb, void *u) + * } + */ + public static MemorySegment PEM_read_bio_PrivateKey$address() { + return PEM_read_bio_PrivateKey.ADDR; + } + + /** + * {@snippet lang = c + * : * extern EVP_PKEY *PEM_read_bio_PrivateKey(BIO *out, EVP_PKEY **x, pem_password_cb *cb, void *u) * } */ - public static MemorySegment PEM_read_bio_PrivateKey(MemorySegment out, MemorySegment x, MemorySegment cb, MemorySegment u) { - var mh$ = PEM_read_bio_PrivateKey$MH(); + public static MemorySegment PEM_read_bio_PrivateKey(MemorySegment out, MemorySegment x, MemorySegment cb, + MemorySegment u) { + var mh$ = PEM_read_bio_PrivateKey.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("PEM_read_bio_PrivateKey", out, x, cb, u); } return (MemorySegment) mh$.invokeExact(out, x, cb, u); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle PEM_read_bio_Parameters$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("PEM_read_bio_Parameters"), - DESC); - } - return Holder.MH; + private static class PEM_read_bio_Parameters { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("PEM_read_bio_Parameters"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * EVP_PKEY *PEM_read_bio_Parameters(BIO *bp, EVP_PKEY **x) + * Function descriptor for: + * {@snippet lang = c : * EVP_PKEY *PEM_read_bio_Parameters(BIO *bp, EVP_PKEY **x) + * } + */ + public static FunctionDescriptor PEM_read_bio_Parameters$descriptor() { + return PEM_read_bio_Parameters.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * EVP_PKEY *PEM_read_bio_Parameters(BIO *bp, EVP_PKEY **x) + * } + */ + public static MethodHandle PEM_read_bio_Parameters$handle() { + return PEM_read_bio_Parameters.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * EVP_PKEY *PEM_read_bio_Parameters(BIO *bp, EVP_PKEY **x) + * } + */ + public static MemorySegment PEM_read_bio_Parameters$address() { + return PEM_read_bio_Parameters.ADDR; + } + + /** + * {@snippet lang = c : * EVP_PKEY *PEM_read_bio_Parameters(BIO *bp, EVP_PKEY **x) * } */ public static MemorySegment PEM_read_bio_Parameters(MemorySegment bp, MemorySegment x) { - var mh$ = PEM_read_bio_Parameters$MH(); + var mh$ = PEM_read_bio_Parameters.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("PEM_read_bio_Parameters", bp, x); } return (MemorySegment) mh$.invokeExact(bp, x); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_get_options$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_get_options"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_get_options { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_get_options"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * uint64_t SSL_CTX_get_options(const SSL_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * uint64_t SSL_CTX_get_options(const SSL_CTX *ctx) + * } + */ + public static FunctionDescriptor SSL_CTX_get_options$descriptor() { + return SSL_CTX_get_options.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * uint64_t SSL_CTX_get_options(const SSL_CTX *ctx) + * } + */ + public static MethodHandle SSL_CTX_get_options$handle() { + return SSL_CTX_get_options.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * uint64_t SSL_CTX_get_options(const SSL_CTX *ctx) + * } + */ + public static MemorySegment SSL_CTX_get_options$address() { + return SSL_CTX_get_options.ADDR; + } + + /** + * {@snippet lang = c : * uint64_t SSL_CTX_get_options(const SSL_CTX *ctx) * } */ public static long SSL_CTX_get_options(MemorySegment ctx) { - var mh$ = SSL_CTX_get_options$MH(); + var mh$ = SSL_CTX_get_options.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_get_options", ctx); } return (long) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get_options$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get_options"), - DESC); - } - return Holder.MH; + private static class SSL_get_options { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get_options"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * uint64_t SSL_get_options(const SSL *s) + * Function descriptor for: + * {@snippet lang = c : * uint64_t SSL_get_options(const SSL *s) + * } + */ + public static FunctionDescriptor SSL_get_options$descriptor() { + return SSL_get_options.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * uint64_t SSL_get_options(const SSL *s) + * } + */ + public static MethodHandle SSL_get_options$handle() { + return SSL_get_options.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * uint64_t SSL_get_options(const SSL *s) + * } + */ + public static MemorySegment SSL_get_options$address() { + return SSL_get_options.ADDR; + } + + /** + * {@snippet lang = c : * uint64_t SSL_get_options(const SSL *s) * } */ public static long SSL_get_options(MemorySegment s) { - var mh$ = SSL_get_options$MH(); + var mh$ = SSL_get_options.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get_options", s); } return (long) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_clear_options$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_clear_options"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_clear_options { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_clear_options"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * uint64_t SSL_CTX_clear_options(SSL_CTX *ctx, uint64_t op) + * Function descriptor for: + * {@snippet lang = c : * uint64_t SSL_CTX_clear_options(SSL_CTX *ctx, uint64_t op) + * } + */ + public static FunctionDescriptor SSL_CTX_clear_options$descriptor() { + return SSL_CTX_clear_options.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * uint64_t SSL_CTX_clear_options(SSL_CTX *ctx, uint64_t op) + * } + */ + public static MethodHandle SSL_CTX_clear_options$handle() { + return SSL_CTX_clear_options.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * uint64_t SSL_CTX_clear_options(SSL_CTX *ctx, uint64_t op) + * } + */ + public static MemorySegment SSL_CTX_clear_options$address() { + return SSL_CTX_clear_options.ADDR; + } + + /** + * {@snippet lang = c : * uint64_t SSL_CTX_clear_options(SSL_CTX *ctx, uint64_t op) * } */ public static long SSL_CTX_clear_options(MemorySegment ctx, long op) { - var mh$ = SSL_CTX_clear_options$MH(); + var mh$ = SSL_CTX_clear_options.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_clear_options", ctx, op); } return (long) mh$.invokeExact(ctx, op); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_options$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_options"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_options { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_options"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * uint64_t SSL_CTX_set_options(SSL_CTX *ctx, uint64_t op) + * Function descriptor for: + * {@snippet lang = c : * uint64_t SSL_CTX_set_options(SSL_CTX *ctx, uint64_t op) + * } + */ + public static FunctionDescriptor SSL_CTX_set_options$descriptor() { + return SSL_CTX_set_options.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * uint64_t SSL_CTX_set_options(SSL_CTX *ctx, uint64_t op) + * } + */ + public static MethodHandle SSL_CTX_set_options$handle() { + return SSL_CTX_set_options.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * uint64_t SSL_CTX_set_options(SSL_CTX *ctx, uint64_t op) + * } + */ + public static MemorySegment SSL_CTX_set_options$address() { + return SSL_CTX_set_options.ADDR; + } + + /** + * {@snippet lang = c : * uint64_t SSL_CTX_set_options(SSL_CTX *ctx, uint64_t op) * } */ public static long SSL_CTX_set_options(MemorySegment ctx, long op) { - var mh$ = SSL_CTX_set_options$MH(); + var mh$ = SSL_CTX_set_options.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_options", ctx, op); } return (long) mh$.invokeExact(ctx, op); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_set_options$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_set_options"), - DESC); - } - return Holder.MH; + private static class SSL_set_options { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_set_options"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * uint64_t SSL_set_options(SSL *s, uint64_t op) + * Function descriptor for: + * {@snippet lang = c : * uint64_t SSL_set_options(SSL *s, uint64_t op) + * } + */ + public static FunctionDescriptor SSL_set_options$descriptor() { + return SSL_set_options.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * uint64_t SSL_set_options(SSL *s, uint64_t op) + * } + */ + public static MethodHandle SSL_set_options$handle() { + return SSL_set_options.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * uint64_t SSL_set_options(SSL *s, uint64_t op) + * } + */ + public static MemorySegment SSL_set_options$address() { + return SSL_set_options.ADDR; + } + + /** + * {@snippet lang = c : * uint64_t SSL_set_options(SSL *s, uint64_t op) * } */ public static long SSL_set_options(MemorySegment s, long op) { - var mh$ = SSL_set_options$MH(); + var mh$ = SSL_set_options.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_set_options", s, op); } return (long) mh$.invokeExact(s, op); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_alpn_select_cb$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_alpn_select_cb"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_alpn_select_cb { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_alpn_select_cb"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, SSL_CTX_alpn_select_cb_func cb, void *arg) + * Function descriptor for: + * {@snippet lang = c : * void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, SSL_CTX_alpn_select_cb_func cb, void *arg) + * } + */ + public static FunctionDescriptor SSL_CTX_set_alpn_select_cb$descriptor() { + return SSL_CTX_set_alpn_select_cb.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, SSL_CTX_alpn_select_cb_func cb, void *arg) + * } + */ + public static MethodHandle SSL_CTX_set_alpn_select_cb$handle() { + return SSL_CTX_set_alpn_select_cb.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, SSL_CTX_alpn_select_cb_func cb, void *arg) + * } + */ + public static MemorySegment SSL_CTX_set_alpn_select_cb$address() { + return SSL_CTX_set_alpn_select_cb.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx, SSL_CTX_alpn_select_cb_func cb, void *arg) * } */ public static void SSL_CTX_set_alpn_select_cb(MemorySegment ctx, MemorySegment cb, MemorySegment arg) { - var mh$ = SSL_CTX_set_alpn_select_cb$MH(); + var mh$ = SSL_CTX_set_alpn_select_cb.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_alpn_select_cb", ctx, cb, arg); } mh$.invokeExact(ctx, cb, arg); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get0_alpn_selected$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get0_alpn_selected"), - DESC); - } - return Holder.MH; + private static class SSL_get0_alpn_selected { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get0_alpn_selected"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, unsigned int *len) + * Function descriptor for: + * {@snippet lang = c : * void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, unsigned int *len) + * } + */ + public static FunctionDescriptor SSL_get0_alpn_selected$descriptor() { + return SSL_get0_alpn_selected.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, unsigned int *len) + * } + */ + public static MethodHandle SSL_get0_alpn_selected$handle() { + return SSL_get0_alpn_selected.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, unsigned int *len) + * } + */ + public static MemorySegment SSL_get0_alpn_selected$address() { + return SSL_get0_alpn_selected.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data, unsigned int *len) * } */ public static void SSL_get0_alpn_selected(MemorySegment ssl, MemorySegment data, MemorySegment len) { - var mh$ = SSL_get0_alpn_selected$MH(); + var mh$ = SSL_get0_alpn_selected.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get0_alpn_selected", ssl, data, len); } mh$.invokeExact(ssl, data, len); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_in_init$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_in_init"), - DESC); - } - return Holder.MH; + private static class SSL_in_init { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_in_init"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_in_init(const SSL *s) + * Function descriptor for: + * {@snippet lang = c : * int SSL_in_init(const SSL *s) + * } + */ + public static FunctionDescriptor SSL_in_init$descriptor() { + return SSL_in_init.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_in_init(const SSL *s) + * } + */ + public static MethodHandle SSL_in_init$handle() { + return SSL_in_init.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_in_init(const SSL *s) + * } + */ + public static MemorySegment SSL_in_init$address() { + return SSL_in_init.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_in_init(const SSL *s) * } */ public static int SSL_in_init(MemorySegment s) { - var mh$ = SSL_in_init$MH(); + var mh$ = SSL_in_init.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_in_init", s); } return (int) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set0_tmp_dh_pkey$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set0_tmp_dh_pkey"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set0_tmp_dh_pkey { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set0_tmp_dh_pkey"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_set0_tmp_dh_pkey(SSL_CTX *ctx, EVP_PKEY *dhpkey) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_set0_tmp_dh_pkey(SSL_CTX *ctx, EVP_PKEY *dhpkey) + * } + */ + public static FunctionDescriptor SSL_CTX_set0_tmp_dh_pkey$descriptor() { + return SSL_CTX_set0_tmp_dh_pkey.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_set0_tmp_dh_pkey(SSL_CTX *ctx, EVP_PKEY *dhpkey) + * } + */ + public static MethodHandle SSL_CTX_set0_tmp_dh_pkey$handle() { + return SSL_CTX_set0_tmp_dh_pkey.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_set0_tmp_dh_pkey(SSL_CTX *ctx, EVP_PKEY *dhpkey) + * } + */ + public static MemorySegment SSL_CTX_set0_tmp_dh_pkey$address() { + return SSL_CTX_set0_tmp_dh_pkey.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_set0_tmp_dh_pkey(SSL_CTX *ctx, EVP_PKEY *dhpkey) * } */ public static int SSL_CTX_set0_tmp_dh_pkey(MemorySegment ctx, MemorySegment dhpkey) { - var mh$ = SSL_CTX_set0_tmp_dh_pkey$MH(); + var mh$ = SSL_CTX_set0_tmp_dh_pkey.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set0_tmp_dh_pkey", ctx, dhpkey); } return (int) mh$.invokeExact(ctx, dhpkey); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_cipher_list$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_cipher_list"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_cipher_list { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_cipher_list"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_set_cipher_list(SSL_CTX *, const char *str) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_set_cipher_list(SSL_CTX *, const char *str) + * } + */ + public static FunctionDescriptor SSL_CTX_set_cipher_list$descriptor() { + return SSL_CTX_set_cipher_list.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_set_cipher_list(SSL_CTX *, const char *str) + * } + */ + public static MethodHandle SSL_CTX_set_cipher_list$handle() { + return SSL_CTX_set_cipher_list.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_set_cipher_list(SSL_CTX *, const char *str) + * } + */ + public static MemorySegment SSL_CTX_set_cipher_list$address() { + return SSL_CTX_set_cipher_list.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_set_cipher_list(SSL_CTX *, const char *str) * } */ public static int SSL_CTX_set_cipher_list(MemorySegment x0, MemorySegment str) { - var mh$ = SSL_CTX_set_cipher_list$MH(); + var mh$ = SSL_CTX_set_cipher_list.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_cipher_list", x0, str); } return (int) mh$.invokeExact(x0, str); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_new$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_new"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_new { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_new"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) + * Function descriptor for: + * {@snippet lang = c : * SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) + * } + */ + public static FunctionDescriptor SSL_CTX_new$descriptor() { + return SSL_CTX_new.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) + * } + */ + public static MethodHandle SSL_CTX_new$handle() { + return SSL_CTX_new.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) + * } + */ + public static MemorySegment SSL_CTX_new$address() { + return SSL_CTX_new.ADDR; + } + + /** + * {@snippet lang = c : * SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth) * } */ public static MemorySegment SSL_CTX_new(MemorySegment meth) { - var mh$ = SSL_CTX_new$MH(); + var mh$ = SSL_CTX_new.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_new", meth); } return (MemorySegment) mh$.invokeExact(meth); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_free"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_CTX_free(SSL_CTX *) + * Function descriptor for: + * {@snippet lang = c : * void SSL_CTX_free(SSL_CTX *) + * } + */ + public static FunctionDescriptor SSL_CTX_free$descriptor() { + return SSL_CTX_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_CTX_free(SSL_CTX *) + * } + */ + public static MethodHandle SSL_CTX_free$handle() { + return SSL_CTX_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_CTX_free(SSL_CTX *) + * } + */ + public static MemorySegment SSL_CTX_free$address() { + return SSL_CTX_free.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_CTX_free(SSL_CTX *) * } */ public static void SSL_CTX_free(MemorySegment x0) { - var mh$ = SSL_CTX_free$MH(); + var mh$ = SSL_CTX_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_free", x0); } mh$.invokeExact(x0); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_timeout$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_timeout"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_timeout { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_timeout"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) + * Function descriptor for: + * {@snippet lang = c : * long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) + * } + */ + public static FunctionDescriptor SSL_CTX_set_timeout$descriptor() { + return SSL_CTX_set_timeout.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) + * } + */ + public static MethodHandle SSL_CTX_set_timeout$handle() { + return SSL_CTX_set_timeout.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) + * } + */ + public static MemorySegment SSL_CTX_set_timeout$address() { + return SSL_CTX_set_timeout.ADDR; + } + + /** + * {@snippet lang = c : * long SSL_CTX_set_timeout(SSL_CTX *ctx, long t) * } */ public static long SSL_CTX_set_timeout(MemorySegment ctx, long t) { - var mh$ = SSL_CTX_set_timeout$MH(); + var mh$ = SSL_CTX_set_timeout.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_timeout", ctx, t); } return (long) mh$.invokeExact(ctx, t); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_get_timeout$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_get_timeout"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_get_timeout { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_get_timeout"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * long SSL_CTX_get_timeout(const SSL_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * long SSL_CTX_get_timeout(const SSL_CTX *ctx) + * } + */ + public static FunctionDescriptor SSL_CTX_get_timeout$descriptor() { + return SSL_CTX_get_timeout.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * long SSL_CTX_get_timeout(const SSL_CTX *ctx) + * } + */ + public static MethodHandle SSL_CTX_get_timeout$handle() { + return SSL_CTX_get_timeout.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * long SSL_CTX_get_timeout(const SSL_CTX *ctx) + * } + */ + public static MemorySegment SSL_CTX_get_timeout$address() { + return SSL_CTX_get_timeout.ADDR; + } + + /** + * {@snippet lang = c : * long SSL_CTX_get_timeout(const SSL_CTX *ctx) * } */ public static long SSL_CTX_get_timeout(MemorySegment ctx) { - var mh$ = SSL_CTX_get_timeout$MH(); + var mh$ = SSL_CTX_get_timeout.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_get_timeout", ctx); } return (long) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_get_cert_store$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_get_cert_store"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_get_cert_store { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_get_cert_store"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *) + * Function descriptor for: + * {@snippet lang = c : * X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *) + * } + */ + public static FunctionDescriptor SSL_CTX_get_cert_store$descriptor() { + return SSL_CTX_get_cert_store.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *) + * } + */ + public static MethodHandle SSL_CTX_get_cert_store$handle() { + return SSL_CTX_get_cert_store.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *) + * } + */ + public static MemorySegment SSL_CTX_get_cert_store$address() { + return SSL_CTX_get_cert_store.ADDR; + } + + /** + * {@snippet lang = c : * X509_STORE *SSL_CTX_get_cert_store(const SSL_CTX *) * } */ public static MemorySegment SSL_CTX_get_cert_store(MemorySegment x0) { - var mh$ = SSL_CTX_get_cert_store$MH(); + var mh$ = SSL_CTX_get_cert_store.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_get_cert_store", x0); } return (MemorySegment) mh$.invokeExact(x0); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get_current_cipher$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get_current_cipher"), - DESC); - } - return Holder.MH; + private static class SSL_get_current_cipher { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get_current_cipher"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) + * Function descriptor for: + * {@snippet lang = c : * const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) + * } + */ + public static FunctionDescriptor SSL_get_current_cipher$descriptor() { + return SSL_get_current_cipher.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) + * } + */ + public static MethodHandle SSL_get_current_cipher$handle() { + return SSL_get_current_cipher.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) + * } + */ + public static MemorySegment SSL_get_current_cipher$address() { + return SSL_get_current_cipher.ADDR; + } + + /** + * {@snippet lang = c : * const SSL_CIPHER *SSL_get_current_cipher(const SSL *s) * } */ public static MemorySegment SSL_get_current_cipher(MemorySegment s) { - var mh$ = SSL_get_current_cipher$MH(); + var mh$ = SSL_get_current_cipher.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get_current_cipher", s); } return (MemorySegment) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CIPHER_get_name$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CIPHER_get_name"), - DESC); - } - return Holder.MH; + private static class SSL_CIPHER_get_name { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CIPHER_get_name"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const char *SSL_CIPHER_get_name(const SSL_CIPHER *c) + * Function descriptor for: + * {@snippet lang = c : * const char *SSL_CIPHER_get_name(const SSL_CIPHER *c) + * } + */ + public static FunctionDescriptor SSL_CIPHER_get_name$descriptor() { + return SSL_CIPHER_get_name.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const char *SSL_CIPHER_get_name(const SSL_CIPHER *c) + * } + */ + public static MethodHandle SSL_CIPHER_get_name$handle() { + return SSL_CIPHER_get_name.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const char *SSL_CIPHER_get_name(const SSL_CIPHER *c) + * } + */ + public static MemorySegment SSL_CIPHER_get_name$address() { + return SSL_CIPHER_get_name.ADDR; + } + + /** + * {@snippet lang = c : * const char *SSL_CIPHER_get_name(const SSL_CIPHER *c) * } */ public static MemorySegment SSL_CIPHER_get_name(MemorySegment c) { - var mh$ = SSL_CIPHER_get_name$MH(); + var mh$ = SSL_CIPHER_get_name.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CIPHER_get_name", c); } return (MemorySegment) mh$.invokeExact(c); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CIPHER_get_kx_nid$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CIPHER_get_kx_nid"), - DESC); - } - return Holder.MH; + private static class SSL_CIPHER_get_kx_nid { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CIPHER_get_kx_nid"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *c) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *c) + * } + */ + public static FunctionDescriptor SSL_CIPHER_get_kx_nid$descriptor() { + return SSL_CIPHER_get_kx_nid.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *c) + * } + */ + public static MethodHandle SSL_CIPHER_get_kx_nid$handle() { + return SSL_CIPHER_get_kx_nid.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *c) + * } + */ + public static MemorySegment SSL_CIPHER_get_kx_nid$address() { + return SSL_CIPHER_get_kx_nid.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CIPHER_get_kx_nid(const SSL_CIPHER *c) * } */ public static int SSL_CIPHER_get_kx_nid(MemorySegment c) { - var mh$ = SSL_CIPHER_get_kx_nid$MH(); + var mh$ = SSL_CIPHER_get_kx_nid.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CIPHER_get_kx_nid", c); } return (int) mh$.invokeExact(c); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CIPHER_get_auth_nid$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CIPHER_get_auth_nid"), - DESC); - } - return Holder.MH; + private static class SSL_CIPHER_get_auth_nid { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CIPHER_get_auth_nid"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *c) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *c) + * } + */ + public static FunctionDescriptor SSL_CIPHER_get_auth_nid$descriptor() { + return SSL_CIPHER_get_auth_nid.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *c) + * } + */ + public static MethodHandle SSL_CIPHER_get_auth_nid$handle() { + return SSL_CIPHER_get_auth_nid.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *c) + * } + */ + public static MemorySegment SSL_CIPHER_get_auth_nid$address() { + return SSL_CIPHER_get_auth_nid.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CIPHER_get_auth_nid(const SSL_CIPHER *c) * } */ public static int SSL_CIPHER_get_auth_nid(MemorySegment c) { - var mh$ = SSL_CIPHER_get_auth_nid$MH(); + var mh$ = SSL_CIPHER_get_auth_nid.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CIPHER_get_auth_nid", c); } return (int) mh$.invokeExact(c); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_pending$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_pending"), - DESC); - } - return Holder.MH; + private static class SSL_pending { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_pending"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_pending(const SSL *s) + * Function descriptor for: + * {@snippet lang = c : * int SSL_pending(const SSL *s) + * } + */ + public static FunctionDescriptor SSL_pending$descriptor() { + return SSL_pending.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_pending(const SSL *s) + * } + */ + public static MethodHandle SSL_pending$handle() { + return SSL_pending.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_pending(const SSL *s) + * } + */ + public static MemorySegment SSL_pending$address() { + return SSL_pending.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_pending(const SSL *s) * } */ public static int SSL_pending(MemorySegment s) { - var mh$ = SSL_pending$MH(); + var mh$ = SSL_pending.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_pending", s); } return (int) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_set_bio$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_set_bio"), - DESC); - } - return Holder.MH; + private static class SSL_set_bio { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_set_bio"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio) + * Function descriptor for: + * {@snippet lang = c : * void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio) + * } + */ + public static FunctionDescriptor SSL_set_bio$descriptor() { + return SSL_set_bio.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio) + * } + */ + public static MethodHandle SSL_set_bio$handle() { + return SSL_set_bio.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio) + * } + */ + public static MemorySegment SSL_set_bio$address() { + return SSL_set_bio.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_set_bio(SSL *s, BIO *rbio, BIO *wbio) * } */ public static void SSL_set_bio(MemorySegment s, MemorySegment rbio, MemorySegment wbio) { - var mh$ = SSL_set_bio$MH(); + var mh$ = SSL_set_bio.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_set_bio", s, rbio, wbio); } mh$.invokeExact(s, rbio, wbio); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_set_cipher_list$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_set_cipher_list"), - DESC); - } - return Holder.MH; + private static class SSL_set_cipher_list { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_set_cipher_list"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_set_cipher_list(SSL *s, const char *str) + * Function descriptor for: + * {@snippet lang = c : * int SSL_set_cipher_list(SSL *s, const char *str) + * } + */ + public static FunctionDescriptor SSL_set_cipher_list$descriptor() { + return SSL_set_cipher_list.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_set_cipher_list(SSL *s, const char *str) + * } + */ + public static MethodHandle SSL_set_cipher_list$handle() { + return SSL_set_cipher_list.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_set_cipher_list(SSL *s, const char *str) + * } + */ + public static MemorySegment SSL_set_cipher_list$address() { + return SSL_set_cipher_list.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_set_cipher_list(SSL *s, const char *str) * } */ public static int SSL_set_cipher_list(MemorySegment s, MemorySegment str) { - var mh$ = SSL_set_cipher_list$MH(); + var mh$ = SSL_set_cipher_list.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_set_cipher_list", s, str); } return (int) mh$.invokeExact(s, str); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_ciphersuites$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_ciphersuites"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_ciphersuites { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_ciphersuites"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str) + * } + */ + public static FunctionDescriptor SSL_CTX_set_ciphersuites$descriptor() { + return SSL_CTX_set_ciphersuites.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str) + * } + */ + public static MethodHandle SSL_CTX_set_ciphersuites$handle() { + return SSL_CTX_set_ciphersuites.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str) + * } + */ + public static MemorySegment SSL_CTX_set_ciphersuites$address() { + return SSL_CTX_set_ciphersuites.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_set_ciphersuites(SSL_CTX *ctx, const char *str) * } */ public static int SSL_CTX_set_ciphersuites(MemorySegment ctx, MemorySegment str) { - var mh$ = SSL_CTX_set_ciphersuites$MH(); + var mh$ = SSL_CTX_set_ciphersuites.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_ciphersuites", ctx, str); } return (int) mh$.invokeExact(ctx, str); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_set_verify$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_set_verify"), - DESC); - } - return Holder.MH; + private static class SSL_set_verify { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_set_verify"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_set_verify(SSL *s, int mode, SSL_verify_cb callback) + * Function descriptor for: + * {@snippet lang = c : * void SSL_set_verify(SSL *s, int mode, SSL_verify_cb callback) + * } + */ + public static FunctionDescriptor SSL_set_verify$descriptor() { + return SSL_set_verify.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_set_verify(SSL *s, int mode, SSL_verify_cb callback) + * } + */ + public static MethodHandle SSL_set_verify$handle() { + return SSL_set_verify.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_set_verify(SSL *s, int mode, SSL_verify_cb callback) + * } + */ + public static MemorySegment SSL_set_verify$address() { + return SSL_set_verify.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_set_verify(SSL *s, int mode, SSL_verify_cb callback) * } */ public static void SSL_set_verify(MemorySegment s, int mode, MemorySegment callback) { - var mh$ = SSL_set_verify$MH(); + var mh$ = SSL_set_verify.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_set_verify", s, mode, callback); } mh$.invokeExact(s, mode, callback); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_use_certificate_chain_file$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_use_certificate_chain_file"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_use_certificate_chain_file { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_use_certificate_chain_file"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) + * } + */ + public static FunctionDescriptor SSL_CTX_use_certificate_chain_file$descriptor() { + return SSL_CTX_use_certificate_chain_file.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) + * } + */ + public static MethodHandle SSL_CTX_use_certificate_chain_file$handle() { + return SSL_CTX_use_certificate_chain_file.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) + * } + */ + public static MemorySegment SSL_CTX_use_certificate_chain_file$address() { + return SSL_CTX_use_certificate_chain_file.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) * } */ public static int SSL_CTX_use_certificate_chain_file(MemorySegment ctx, MemorySegment file) { - var mh$ = SSL_CTX_use_certificate_chain_file$MH(); + var mh$ = SSL_CTX_use_certificate_chain_file.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_use_certificate_chain_file", ctx, file); } return (int) mh$.invokeExact(ctx, file); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_load_client_CA_file$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_load_client_CA_file"), - DESC); - } - return Holder.MH; + private static class SSL_load_client_CA_file { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_load_client_CA_file"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * struct stack_st_X509_NAME *SSL_load_client_CA_file(const char *file) + * Function descriptor for: + * {@snippet lang = c : * struct stack_st_X509_NAME *SSL_load_client_CA_file(const char *file) + * } + */ + public static FunctionDescriptor SSL_load_client_CA_file$descriptor() { + return SSL_load_client_CA_file.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * struct stack_st_X509_NAME *SSL_load_client_CA_file(const char *file) + * } + */ + public static MethodHandle SSL_load_client_CA_file$handle() { + return SSL_load_client_CA_file.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * struct stack_st_X509_NAME *SSL_load_client_CA_file(const char *file) + * } + */ + public static MemorySegment SSL_load_client_CA_file$address() { + return SSL_load_client_CA_file.ADDR; + } + + /** + * {@snippet lang = c : * struct stack_st_X509_NAME *SSL_load_client_CA_file(const char *file) * } */ public static MemorySegment SSL_load_client_CA_file(MemorySegment file) { - var mh$ = SSL_load_client_CA_file$MH(); + var mh$ = SSL_load_client_CA_file.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_load_client_CA_file", file); } return (MemorySegment) mh$.invokeExact(file); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_add_file_cert_subjects_to_stack$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_add_file_cert_subjects_to_stack"), - DESC); - } - return Holder.MH; + private static class SSL_add_file_cert_subjects_to_stack { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_add_file_cert_subjects_to_stack"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_add_file_cert_subjects_to_stack(struct stack_st_X509_NAME *stackCAs, const char *file) + * Function descriptor for: + * {@snippet lang = c + * : * int SSL_add_file_cert_subjects_to_stack(struct stack_st_X509_NAME *stackCAs, const char *file) + * } + */ + public static FunctionDescriptor SSL_add_file_cert_subjects_to_stack$descriptor() { + return SSL_add_file_cert_subjects_to_stack.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c + * : * int SSL_add_file_cert_subjects_to_stack(struct stack_st_X509_NAME *stackCAs, const char *file) + * } + */ + public static MethodHandle SSL_add_file_cert_subjects_to_stack$handle() { + return SSL_add_file_cert_subjects_to_stack.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c + * : * int SSL_add_file_cert_subjects_to_stack(struct stack_st_X509_NAME *stackCAs, const char *file) + * } + */ + public static MemorySegment SSL_add_file_cert_subjects_to_stack$address() { + return SSL_add_file_cert_subjects_to_stack.ADDR; + } + + /** + * {@snippet lang = c + * : * int SSL_add_file_cert_subjects_to_stack(struct stack_st_X509_NAME *stackCAs, const char *file) * } */ public static int SSL_add_file_cert_subjects_to_stack(MemorySegment stackCAs, MemorySegment file) { - var mh$ = SSL_add_file_cert_subjects_to_stack$MH(); + var mh$ = SSL_add_file_cert_subjects_to_stack.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_add_file_cert_subjects_to_stack", stackCAs, file); } return (int) mh$.invokeExact(stackCAs, file); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_SESSION_get_time$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_SESSION_get_time"), - DESC); - } - return Holder.MH; + private static class SSL_SESSION_get_time { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_SESSION_get_time"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * long SSL_SESSION_get_time(const SSL_SESSION *s) + * Function descriptor for: + * {@snippet lang = c : * long SSL_SESSION_get_time(const SSL_SESSION *s) + * } + */ + public static FunctionDescriptor SSL_SESSION_get_time$descriptor() { + return SSL_SESSION_get_time.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * long SSL_SESSION_get_time(const SSL_SESSION *s) + * } + */ + public static MethodHandle SSL_SESSION_get_time$handle() { + return SSL_SESSION_get_time.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * long SSL_SESSION_get_time(const SSL_SESSION *s) + * } + */ + public static MemorySegment SSL_SESSION_get_time$address() { + return SSL_SESSION_get_time.ADDR; + } + + /** + * {@snippet lang = c : * long SSL_SESSION_get_time(const SSL_SESSION *s) * } */ public static long SSL_SESSION_get_time(MemorySegment s) { - var mh$ = SSL_SESSION_get_time$MH(); + var mh$ = SSL_SESSION_get_time.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_SESSION_get_time", s); } return (long) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_SESSION_get_id$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_SESSION_get_id"), - DESC); - } - return Holder.MH; + private static class SSL_SESSION_get_id { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_SESSION_get_id"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len) + * Function descriptor for: + * {@snippet lang = c : * const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len) + * } + */ + public static FunctionDescriptor SSL_SESSION_get_id$descriptor() { + return SSL_SESSION_get_id.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len) + * } + */ + public static MethodHandle SSL_SESSION_get_id$handle() { + return SSL_SESSION_get_id.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len) + * } + */ + public static MemorySegment SSL_SESSION_get_id$address() { + return SSL_SESSION_get_id.ADDR; + } + + /** + * {@snippet lang = c : * const unsigned char *SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len) * } */ public static MemorySegment SSL_SESSION_get_id(MemorySegment s, MemorySegment len) { - var mh$ = SSL_SESSION_get_id$MH(); + var mh$ = SSL_SESSION_get_id.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_SESSION_get_id", s, len); } return (MemorySegment) mh$.invokeExact(s, len); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get1_peer_certificate$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get1_peer_certificate"), - DESC); - } - return Holder.MH; + private static class SSL_get1_peer_certificate { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get1_peer_certificate"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * X509 *SSL_get1_peer_certificate(const SSL *s) + * Function descriptor for: + * {@snippet lang = c : * X509 *SSL_get1_peer_certificate(const SSL *s) + * } + */ + public static FunctionDescriptor SSL_get1_peer_certificate$descriptor() { + return SSL_get1_peer_certificate.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * X509 *SSL_get1_peer_certificate(const SSL *s) + * } + */ + public static MethodHandle SSL_get1_peer_certificate$handle() { + return SSL_get1_peer_certificate.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * X509 *SSL_get1_peer_certificate(const SSL *s) + * } + */ + public static MemorySegment SSL_get1_peer_certificate$address() { + return SSL_get1_peer_certificate.ADDR; + } + + /** + * {@snippet lang = c : * X509 *SSL_get1_peer_certificate(const SSL *s) * } */ public static MemorySegment SSL_get1_peer_certificate(MemorySegment s) { - var mh$ = SSL_get1_peer_certificate$MH(); + var mh$ = SSL_get1_peer_certificate.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get1_peer_certificate", s); } return (MemorySegment) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get_peer_cert_chain$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get_peer_cert_chain"), - DESC); - } - return Holder.MH; + private static class SSL_get_peer_cert_chain { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get_peer_cert_chain"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * struct stack_st_X509 *SSL_get_peer_cert_chain(const SSL *s) + * Function descriptor for: + * {@snippet lang = c : * struct stack_st_X509 *SSL_get_peer_cert_chain(const SSL *s) + * } + */ + public static FunctionDescriptor SSL_get_peer_cert_chain$descriptor() { + return SSL_get_peer_cert_chain.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * struct stack_st_X509 *SSL_get_peer_cert_chain(const SSL *s) + * } + */ + public static MethodHandle SSL_get_peer_cert_chain$handle() { + return SSL_get_peer_cert_chain.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * struct stack_st_X509 *SSL_get_peer_cert_chain(const SSL *s) + * } + */ + public static MemorySegment SSL_get_peer_cert_chain$address() { + return SSL_get_peer_cert_chain.ADDR; + } + + /** + * {@snippet lang = c : * struct stack_st_X509 *SSL_get_peer_cert_chain(const SSL *s) * } */ public static MemorySegment SSL_get_peer_cert_chain(MemorySegment s) { - var mh$ = SSL_get_peer_cert_chain$MH(); + var mh$ = SSL_get_peer_cert_chain.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get_peer_cert_chain", s); } return (MemorySegment) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_verify$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_verify"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_verify { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_verify"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb callback) + * Function descriptor for: + * {@snippet lang = c : * void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb callback) + * } + */ + public static FunctionDescriptor SSL_CTX_set_verify$descriptor() { + return SSL_CTX_set_verify.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb callback) + * } + */ + public static MethodHandle SSL_CTX_set_verify$handle() { + return SSL_CTX_set_verify.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb callback) + * } + */ + public static MemorySegment SSL_CTX_set_verify$address() { + return SSL_CTX_set_verify.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_CTX_set_verify(SSL_CTX *ctx, int mode, SSL_verify_cb callback) * } */ public static void SSL_CTX_set_verify(MemorySegment ctx, int mode, MemorySegment callback) { - var mh$ = SSL_CTX_set_verify$MH(); + var mh$ = SSL_CTX_set_verify.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_verify", ctx, mode, callback); } mh$.invokeExact(ctx, mode, callback); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_cert_verify_callback$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_cert_verify_callback"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_cert_verify_callback { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_cert_verify_callback"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *, void *), void *arg) + * Function descriptor for: + * {@snippet lang = c + * : * void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *, void *), void *arg) + * } + */ + public static FunctionDescriptor SSL_CTX_set_cert_verify_callback$descriptor() { + return SSL_CTX_set_cert_verify_callback.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c + * : * void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *, void *), void *arg) + * } + */ + public static MethodHandle SSL_CTX_set_cert_verify_callback$handle() { + return SSL_CTX_set_cert_verify_callback.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c + * : * void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *, void *), void *arg) + * } + */ + public static MemorySegment SSL_CTX_set_cert_verify_callback$address() { + return SSL_CTX_set_cert_verify_callback.ADDR; + } + + /** + * {@snippet lang = c + * : * void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx, int (*cb)(X509_STORE_CTX *, void *), void *arg) * } */ public static void SSL_CTX_set_cert_verify_callback(MemorySegment ctx, MemorySegment cb, MemorySegment arg) { - var mh$ = SSL_CTX_set_cert_verify_callback$MH(); + var mh$ = SSL_CTX_set_cert_verify_callback.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_cert_verify_callback", ctx, cb, arg); } mh$.invokeExact(ctx, cb, arg); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_use_PrivateKey$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_use_PrivateKey"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_use_PrivateKey { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_use_PrivateKey"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) + * } + */ + public static FunctionDescriptor SSL_CTX_use_PrivateKey$descriptor() { + return SSL_CTX_use_PrivateKey.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) + * } + */ + public static MethodHandle SSL_CTX_use_PrivateKey$handle() { + return SSL_CTX_use_PrivateKey.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) + * } + */ + public static MemorySegment SSL_CTX_use_PrivateKey$address() { + return SSL_CTX_use_PrivateKey.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_use_PrivateKey(SSL_CTX *ctx, EVP_PKEY *pkey) * } */ public static int SSL_CTX_use_PrivateKey(MemorySegment ctx, MemorySegment pkey) { - var mh$ = SSL_CTX_use_PrivateKey$MH(); + var mh$ = SSL_CTX_use_PrivateKey.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_use_PrivateKey", ctx, pkey); } return (int) mh$.invokeExact(ctx, pkey); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_use_certificate$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_use_certificate"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_use_certificate { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_use_certificate"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) + * } + */ + public static FunctionDescriptor SSL_CTX_use_certificate$descriptor() { + return SSL_CTX_use_certificate.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) + * } + */ + public static MethodHandle SSL_CTX_use_certificate$handle() { + return SSL_CTX_use_certificate.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) + * } + */ + public static MemorySegment SSL_CTX_use_certificate$address() { + return SSL_CTX_use_certificate.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_use_certificate(SSL_CTX *ctx, X509 *x) * } */ public static int SSL_CTX_use_certificate(MemorySegment ctx, MemorySegment x) { - var mh$ = SSL_CTX_use_certificate$MH(); + var mh$ = SSL_CTX_use_certificate.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_use_certificate", ctx, x); } return (int) mh$.invokeExact(ctx, x); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_default_passwd_cb$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_default_passwd_cb"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_default_passwd_cb { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_default_passwd_cb"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) + * Function descriptor for: + * {@snippet lang = c : * void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) + * } + */ + public static FunctionDescriptor SSL_CTX_set_default_passwd_cb$descriptor() { + return SSL_CTX_set_default_passwd_cb.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) + * } + */ + public static MethodHandle SSL_CTX_set_default_passwd_cb$handle() { + return SSL_CTX_set_default_passwd_cb.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) + * } + */ + public static MemorySegment SSL_CTX_set_default_passwd_cb$address() { + return SSL_CTX_set_default_passwd_cb.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) * } */ public static void SSL_CTX_set_default_passwd_cb(MemorySegment ctx, MemorySegment cb) { - var mh$ = SSL_CTX_set_default_passwd_cb$MH(); + var mh$ = SSL_CTX_set_default_passwd_cb.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_default_passwd_cb", ctx, cb); } mh$.invokeExact(ctx, cb); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_check_private_key$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_check_private_key"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_check_private_key { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_check_private_key"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_check_private_key(const SSL_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_check_private_key(const SSL_CTX *ctx) + * } + */ + public static FunctionDescriptor SSL_CTX_check_private_key$descriptor() { + return SSL_CTX_check_private_key.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_check_private_key(const SSL_CTX *ctx) + * } + */ + public static MethodHandle SSL_CTX_check_private_key$handle() { + return SSL_CTX_check_private_key.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_check_private_key(const SSL_CTX *ctx) + * } + */ + public static MemorySegment SSL_CTX_check_private_key$address() { + return SSL_CTX_check_private_key.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_check_private_key(const SSL_CTX *ctx) * } */ public static int SSL_CTX_check_private_key(MemorySegment ctx) { - var mh$ = SSL_CTX_check_private_key$MH(); + var mh$ = SSL_CTX_check_private_key.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_check_private_key", ctx); } return (int) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_session_id_context$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_session_id_context"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_session_id_context { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_session_id_context"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx, unsigned int sid_ctx_len) + * Function descriptor for: + * {@snippet lang = c + * : * int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx, unsigned int sid_ctx_len) + * } + */ + public static FunctionDescriptor SSL_CTX_set_session_id_context$descriptor() { + return SSL_CTX_set_session_id_context.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c + * : * int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx, unsigned int sid_ctx_len) + * } + */ + public static MethodHandle SSL_CTX_set_session_id_context$handle() { + return SSL_CTX_set_session_id_context.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c + * : * int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx, unsigned int sid_ctx_len) + * } + */ + public static MemorySegment SSL_CTX_set_session_id_context$address() { + return SSL_CTX_set_session_id_context.ADDR; + } + + /** + * {@snippet lang = c + * : * int SSL_CTX_set_session_id_context(SSL_CTX *ctx, const unsigned char *sid_ctx, unsigned int sid_ctx_len) * } */ public static int SSL_CTX_set_session_id_context(MemorySegment ctx, MemorySegment sid_ctx, int sid_ctx_len) { - var mh$ = SSL_CTX_set_session_id_context$MH(); + var mh$ = SSL_CTX_set_session_id_context.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_session_id_context", ctx, sid_ctx, sid_ctx_len); } return (int) mh$.invokeExact(ctx, sid_ctx, sid_ctx_len); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_new$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_new"), - DESC); - } - return Holder.MH; + private static class SSL_new { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_new"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * SSL *SSL_new(SSL_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * SSL *SSL_new(SSL_CTX *ctx) + * } + */ + public static FunctionDescriptor SSL_new$descriptor() { + return SSL_new.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * SSL *SSL_new(SSL_CTX *ctx) + * } + */ + public static MethodHandle SSL_new$handle() { + return SSL_new.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * SSL *SSL_new(SSL_CTX *ctx) + * } + */ + public static MemorySegment SSL_new$address() { + return SSL_new.ADDR; + } + + /** + * {@snippet lang = c : * SSL *SSL_new(SSL_CTX *ctx) * } */ public static MemorySegment SSL_new(MemorySegment ctx) { - var mh$ = SSL_new$MH(); + var mh$ = SSL_new.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_new", ctx); } return (MemorySegment) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_free"), - DESC); - } - return Holder.MH; + private static class SSL_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_free(SSL *ssl) + * Function descriptor for: + * {@snippet lang = c : * void SSL_free(SSL *ssl) + * } + */ + public static FunctionDescriptor SSL_free$descriptor() { + return SSL_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_free(SSL *ssl) + * } + */ + public static MethodHandle SSL_free$handle() { + return SSL_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_free(SSL *ssl) + * } + */ + public static MemorySegment SSL_free$address() { + return SSL_free.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_free(SSL *ssl) * } */ public static void SSL_free(MemorySegment ssl) { - var mh$ = SSL_free$MH(); + var mh$ = SSL_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_free", ssl); } mh$.invokeExact(ssl); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_read$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_read"), - DESC); - } - return Holder.MH; + private static class SSL_read { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_read"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_read(SSL *ssl, void *buf, int num) + * Function descriptor for: + * {@snippet lang = c : * int SSL_read(SSL *ssl, void *buf, int num) + * } + */ + public static FunctionDescriptor SSL_read$descriptor() { + return SSL_read.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_read(SSL *ssl, void *buf, int num) + * } + */ + public static MethodHandle SSL_read$handle() { + return SSL_read.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_read(SSL *ssl, void *buf, int num) + * } + */ + public static MemorySegment SSL_read$address() { + return SSL_read.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_read(SSL *ssl, void *buf, int num) * } */ public static int SSL_read(MemorySegment ssl, MemorySegment buf, int num) { - var mh$ = SSL_read$MH(); + var mh$ = SSL_read.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_read", ssl, buf, num); } return (int) mh$.invokeExact(ssl, buf, num); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_write$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_write"), - DESC); - } - return Holder.MH; + private static class SSL_write { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_write"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_write(SSL *ssl, const void *buf, int num) + * Function descriptor for: + * {@snippet lang = c : * int SSL_write(SSL *ssl, const void *buf, int num) + * } + */ + public static FunctionDescriptor SSL_write$descriptor() { + return SSL_write.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_write(SSL *ssl, const void *buf, int num) + * } + */ + public static MethodHandle SSL_write$handle() { + return SSL_write.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_write(SSL *ssl, const void *buf, int num) + * } + */ + public static MemorySegment SSL_write$address() { + return SSL_write.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_write(SSL *ssl, const void *buf, int num) * } */ public static int SSL_write(MemorySegment ssl, MemorySegment buf, int num) { - var mh$ = SSL_write$MH(); + var mh$ = SSL_write.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_write", ssl, buf, num); } return (int) mh$.invokeExact(ssl, buf, num); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_ctrl$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_ctrl"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_ctrl { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, + openssl_h.C_INT, openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_ctrl"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) + * Function descriptor for: + * {@snippet lang = c : * long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) + * } + */ + public static FunctionDescriptor SSL_CTX_ctrl$descriptor() { + return SSL_CTX_ctrl.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) + * } + */ + public static MethodHandle SSL_CTX_ctrl$handle() { + return SSL_CTX_ctrl.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) + * } + */ + public static MemorySegment SSL_CTX_ctrl$address() { + return SSL_CTX_ctrl.ADDR; + } + + /** + * {@snippet lang = c : * long SSL_CTX_ctrl(SSL_CTX *ctx, int cmd, long larg, void *parg) * } */ public static long SSL_CTX_ctrl(MemorySegment ctx, int cmd, long larg, MemorySegment parg) { - var mh$ = SSL_CTX_ctrl$MH(); + var mh$ = SSL_CTX_ctrl.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_ctrl", ctx, cmd, larg, parg); } return (long) mh$.invokeExact(ctx, cmd, larg, parg); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_ctrl$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG, - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_ctrl"), - DESC); - } - return Holder.MH; + private static class SSL_get_version { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get_version"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg) + * Function descriptor for: + * {@snippet lang = c : * const char *SSL_get_version(const SSL *s) * } */ - public static long SSL_ctrl(MemorySegment ssl, int cmd, long larg, MemorySegment parg) { - var mh$ = SSL_ctrl$MH(); - try { - if (TRACE_DOWNCALLS) { - traceDowncall("SSL_ctrl", ssl, cmd, larg, parg); - } - return (long) mh$.invokeExact(ssl, cmd, larg, parg); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } + public static FunctionDescriptor SSL_get_version$descriptor() { + return SSL_get_version.DESC; } - private static MethodHandle SSL_get_version$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get_version"), - DESC); - } - return Holder.MH; + /** + * Downcall method handle for: + * {@snippet lang = c : * const char *SSL_get_version(const SSL *s) + * } + */ + public static MethodHandle SSL_get_version$handle() { + return SSL_get_version.HANDLE; } /** - * {@snippet lang=c : - * const char *SSL_get_version(const SSL *s) + * Address for: + * {@snippet lang = c : * const char *SSL_get_version(const SSL *s) + * } + */ + public static MemorySegment SSL_get_version$address() { + return SSL_get_version.ADDR; + } + + /** + * {@snippet lang = c : * const char *SSL_get_version(const SSL *s) * } */ public static MemorySegment SSL_get_version(MemorySegment s) { - var mh$ = SSL_get_version$MH(); + var mh$ = SSL_get_version.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get_version", s); } return (MemorySegment) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle TLS_server_method$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class TLS_server_method { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("TLS_server_method"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("TLS_server_method"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const SSL_METHOD *TLS_server_method(void) + * Function descriptor for: + * {@snippet lang = c : * const SSL_METHOD *TLS_server_method() + * } + */ + public static FunctionDescriptor TLS_server_method$descriptor() { + return TLS_server_method.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const SSL_METHOD *TLS_server_method() + * } + */ + public static MethodHandle TLS_server_method$handle() { + return TLS_server_method.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const SSL_METHOD *TLS_server_method() + * } + */ + public static MemorySegment TLS_server_method$address() { + return TLS_server_method.ADDR; + } + + /** + * {@snippet lang = c : * const SSL_METHOD *TLS_server_method() * } */ public static MemorySegment TLS_server_method() { - var mh$ = TLS_server_method$MH(); + var mh$ = TLS_server_method.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("TLS_server_method"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get_ciphers$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get_ciphers"), - DESC); - } - return Holder.MH; + private static class SSL_get_ciphers { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get_ciphers"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * struct stack_st_SSL_CIPHER *SSL_get_ciphers(const SSL *s) + * Function descriptor for: + * {@snippet lang = c : * struct stack_st_SSL_CIPHER *SSL_get_ciphers(const SSL *s) + * } + */ + public static FunctionDescriptor SSL_get_ciphers$descriptor() { + return SSL_get_ciphers.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * struct stack_st_SSL_CIPHER *SSL_get_ciphers(const SSL *s) + * } + */ + public static MethodHandle SSL_get_ciphers$handle() { + return SSL_get_ciphers.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * struct stack_st_SSL_CIPHER *SSL_get_ciphers(const SSL *s) + * } + */ + public static MemorySegment SSL_get_ciphers$address() { + return SSL_get_ciphers.ADDR; + } + + /** + * {@snippet lang = c : * struct stack_st_SSL_CIPHER *SSL_get_ciphers(const SSL *s) * } */ public static MemorySegment SSL_get_ciphers(MemorySegment s) { - var mh$ = SSL_get_ciphers$MH(); + var mh$ = SSL_get_ciphers.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get_ciphers", s); } return (MemorySegment) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_get_ciphers$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_get_ciphers"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_get_ciphers { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_get_ciphers"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * struct stack_st_SSL_CIPHER *SSL_CTX_get_ciphers(const SSL_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * struct stack_st_SSL_CIPHER *SSL_CTX_get_ciphers(const SSL_CTX *ctx) + * } + */ + public static FunctionDescriptor SSL_CTX_get_ciphers$descriptor() { + return SSL_CTX_get_ciphers.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * struct stack_st_SSL_CIPHER *SSL_CTX_get_ciphers(const SSL_CTX *ctx) + * } + */ + public static MethodHandle SSL_CTX_get_ciphers$handle() { + return SSL_CTX_get_ciphers.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * struct stack_st_SSL_CIPHER *SSL_CTX_get_ciphers(const SSL_CTX *ctx) + * } + */ + public static MemorySegment SSL_CTX_get_ciphers$address() { + return SSL_CTX_get_ciphers.ADDR; + } + + /** + * {@snippet lang = c : * struct stack_st_SSL_CIPHER *SSL_CTX_get_ciphers(const SSL_CTX *ctx) * } */ public static MemorySegment SSL_CTX_get_ciphers(MemorySegment ctx) { - var mh$ = SSL_CTX_get_ciphers$MH(); + var mh$ = SSL_CTX_get_ciphers.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_get_ciphers", ctx); } return (MemorySegment) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_do_handshake$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_do_handshake"), - DESC); - } - return Holder.MH; + private static class SSL_do_handshake { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_do_handshake"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_do_handshake(SSL *s) + * Function descriptor for: + * {@snippet lang = c : * int SSL_do_handshake(SSL *s) + * } + */ + public static FunctionDescriptor SSL_do_handshake$descriptor() { + return SSL_do_handshake.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_do_handshake(SSL *s) + * } + */ + public static MethodHandle SSL_do_handshake$handle() { + return SSL_do_handshake.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_do_handshake(SSL *s) + * } + */ + public static MemorySegment SSL_do_handshake$address() { + return SSL_do_handshake.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_do_handshake(SSL *s) * } */ public static int SSL_do_handshake(MemorySegment s) { - var mh$ = SSL_do_handshake$MH(); + var mh$ = SSL_do_handshake.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_do_handshake", s); } return (int) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_renegotiate$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_renegotiate"), - DESC); - } - return Holder.MH; + private static class SSL_renegotiate { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_renegotiate"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_renegotiate(SSL *s) + * Function descriptor for: + * {@snippet lang = c : * int SSL_renegotiate(SSL *s) + * } + */ + public static FunctionDescriptor SSL_renegotiate$descriptor() { + return SSL_renegotiate.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_renegotiate(SSL *s) + * } + */ + public static MethodHandle SSL_renegotiate$handle() { + return SSL_renegotiate.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_renegotiate(SSL *s) + * } + */ + public static MemorySegment SSL_renegotiate$address() { + return SSL_renegotiate.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_renegotiate(SSL *s) * } */ public static int SSL_renegotiate(MemorySegment s) { - var mh$ = SSL_renegotiate$MH(); + var mh$ = SSL_renegotiate.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_renegotiate", s); } return (int) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_renegotiate_pending$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_renegotiate_pending"), - DESC); - } - return Holder.MH; + private static class SSL_renegotiate_pending { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_renegotiate_pending"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_renegotiate_pending(const SSL *s) + * Function descriptor for: + * {@snippet lang = c : * int SSL_renegotiate_pending(const SSL *s) + * } + */ + public static FunctionDescriptor SSL_renegotiate_pending$descriptor() { + return SSL_renegotiate_pending.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_renegotiate_pending(const SSL *s) + * } + */ + public static MethodHandle SSL_renegotiate_pending$handle() { + return SSL_renegotiate_pending.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_renegotiate_pending(const SSL *s) + * } + */ + public static MemorySegment SSL_renegotiate_pending$address() { + return SSL_renegotiate_pending.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_renegotiate_pending(const SSL *s) * } */ public static int SSL_renegotiate_pending(MemorySegment s) { - var mh$ = SSL_renegotiate_pending$MH(); + var mh$ = SSL_renegotiate_pending.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_renegotiate_pending", s); } return (int) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_shutdown$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_shutdown"), - DESC); - } - return Holder.MH; + private static class SSL_shutdown { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_shutdown"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_shutdown(SSL *s) + * Function descriptor for: + * {@snippet lang = c : * int SSL_shutdown(SSL *s) + * } + */ + public static FunctionDescriptor SSL_shutdown$descriptor() { + return SSL_shutdown.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_shutdown(SSL *s) + * } + */ + public static MethodHandle SSL_shutdown$handle() { + return SSL_shutdown.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_shutdown(SSL *s) + * } + */ + public static MemorySegment SSL_shutdown$address() { + return SSL_shutdown.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_shutdown(SSL *s) * } */ public static int SSL_shutdown(MemorySegment s) { - var mh$ = SSL_shutdown$MH(); + var mh$ = SSL_shutdown.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_shutdown", s); } return (int) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_verify_client_post_handshake$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_verify_client_post_handshake"), - DESC); - } - return Holder.MH; + private static class SSL_verify_client_post_handshake { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_verify_client_post_handshake"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_verify_client_post_handshake(SSL *s) + * Function descriptor for: + * {@snippet lang = c : * int SSL_verify_client_post_handshake(SSL *s) + * } + */ + public static FunctionDescriptor SSL_verify_client_post_handshake$descriptor() { + return SSL_verify_client_post_handshake.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_verify_client_post_handshake(SSL *s) + * } + */ + public static MethodHandle SSL_verify_client_post_handshake$handle() { + return SSL_verify_client_post_handshake.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_verify_client_post_handshake(SSL *s) + * } + */ + public static MemorySegment SSL_verify_client_post_handshake$address() { + return SSL_verify_client_post_handshake.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_verify_client_post_handshake(SSL *s) * } */ public static int SSL_verify_client_post_handshake(MemorySegment s) { - var mh$ = SSL_verify_client_post_handshake$MH(); + var mh$ = SSL_verify_client_post_handshake.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_verify_client_post_handshake", s); } return (int) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_client_CA_list$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_client_CA_list"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_client_CA_list { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_client_CA_list"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, struct stack_st_X509_NAME *name_list) + * Function descriptor for: + * {@snippet lang = c : * void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, struct stack_st_X509_NAME *name_list) + * } + */ + public static FunctionDescriptor SSL_CTX_set_client_CA_list$descriptor() { + return SSL_CTX_set_client_CA_list.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, struct stack_st_X509_NAME *name_list) + * } + */ + public static MethodHandle SSL_CTX_set_client_CA_list$handle() { + return SSL_CTX_set_client_CA_list.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, struct stack_st_X509_NAME *name_list) + * } + */ + public static MemorySegment SSL_CTX_set_client_CA_list$address() { + return SSL_CTX_set_client_CA_list.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_CTX_set_client_CA_list(SSL_CTX *ctx, struct stack_st_X509_NAME *name_list) * } */ public static void SSL_CTX_set_client_CA_list(MemorySegment ctx, MemorySegment name_list) { - var mh$ = SSL_CTX_set_client_CA_list$MH(); + var mh$ = SSL_CTX_set_client_CA_list.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_client_CA_list", ctx, name_list); } mh$.invokeExact(ctx, name_list); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_get_client_CA_list$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_get_client_CA_list"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_get_client_CA_list { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_get_client_CA_list"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * struct stack_st_X509_NAME *SSL_CTX_get_client_CA_list(const SSL_CTX *s) + * Function descriptor for: + * {@snippet lang = c : * struct stack_st_X509_NAME *SSL_CTX_get_client_CA_list(const SSL_CTX *s) + * } + */ + public static FunctionDescriptor SSL_CTX_get_client_CA_list$descriptor() { + return SSL_CTX_get_client_CA_list.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * struct stack_st_X509_NAME *SSL_CTX_get_client_CA_list(const SSL_CTX *s) + * } + */ + public static MethodHandle SSL_CTX_get_client_CA_list$handle() { + return SSL_CTX_get_client_CA_list.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * struct stack_st_X509_NAME *SSL_CTX_get_client_CA_list(const SSL_CTX *s) + * } + */ + public static MemorySegment SSL_CTX_get_client_CA_list$address() { + return SSL_CTX_get_client_CA_list.ADDR; + } + + /** + * {@snippet lang = c : * struct stack_st_X509_NAME *SSL_CTX_get_client_CA_list(const SSL_CTX *s) * } */ public static MemorySegment SSL_CTX_get_client_CA_list(MemorySegment s) { - var mh$ = SSL_CTX_get_client_CA_list$MH(); + var mh$ = SSL_CTX_get_client_CA_list.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_get_client_CA_list", s); } return (MemorySegment) mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_add_client_CA$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_add_client_CA"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_add_client_CA { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_add_client_CA"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) + * } + */ + public static FunctionDescriptor SSL_CTX_add_client_CA$descriptor() { + return SSL_CTX_add_client_CA.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) + * } + */ + public static MethodHandle SSL_CTX_add_client_CA$handle() { + return SSL_CTX_add_client_CA.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) + * } + */ + public static MemorySegment SSL_CTX_add_client_CA$address() { + return SSL_CTX_add_client_CA.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_add_client_CA(SSL_CTX *ctx, X509 *x) * } */ public static int SSL_CTX_add_client_CA(MemorySegment ctx, MemorySegment x) { - var mh$ = SSL_CTX_add_client_CA$MH(); + var mh$ = SSL_CTX_add_client_CA.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_add_client_CA", ctx, x); } return (int) mh$.invokeExact(ctx, x); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_set_connect_state$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_set_connect_state"), - DESC); - } - return Holder.MH; + private static class SSL_set_connect_state { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_set_connect_state"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_set_connect_state(SSL *s) + * Function descriptor for: + * {@snippet lang = c : * void SSL_set_connect_state(SSL *s) + * } + */ + public static FunctionDescriptor SSL_set_connect_state$descriptor() { + return SSL_set_connect_state.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_set_connect_state(SSL *s) + * } + */ + public static MethodHandle SSL_set_connect_state$handle() { + return SSL_set_connect_state.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_set_connect_state(SSL *s) + * } + */ + public static MemorySegment SSL_set_connect_state$address() { + return SSL_set_connect_state.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_set_connect_state(SSL *s) * } */ public static void SSL_set_connect_state(MemorySegment s) { - var mh$ = SSL_set_connect_state$MH(); + var mh$ = SSL_set_connect_state.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_set_connect_state", s); } mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_set_accept_state$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_set_accept_state"), - DESC); - } - return Holder.MH; + private static class SSL_set_accept_state { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_set_accept_state"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_set_accept_state(SSL *s) + * Function descriptor for: + * {@snippet lang = c : * void SSL_set_accept_state(SSL *s) + * } + */ + public static FunctionDescriptor SSL_set_accept_state$descriptor() { + return SSL_set_accept_state.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_set_accept_state(SSL *s) + * } + */ + public static MethodHandle SSL_set_accept_state$handle() { + return SSL_set_accept_state.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_set_accept_state(SSL *s) + * } + */ + public static MemorySegment SSL_set_accept_state$address() { + return SSL_set_accept_state.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_set_accept_state(SSL *s) * } */ public static void SSL_set_accept_state(MemorySegment s) { - var mh$ = SSL_set_accept_state$MH(); + var mh$ = SSL_set_accept_state.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_set_accept_state", s); } mh$.invokeExact(s); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get_privatekey$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get_privatekey"), - DESC); - } - return Holder.MH; + private static class SSL_get_privatekey { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get_privatekey"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * struct evp_pkey_st *SSL_get_privatekey(const SSL *ssl) + * Function descriptor for: + * {@snippet lang = c : * struct evp_pkey_st *SSL_get_privatekey(const SSL *ssl) + * } + */ + public static FunctionDescriptor SSL_get_privatekey$descriptor() { + return SSL_get_privatekey.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * struct evp_pkey_st *SSL_get_privatekey(const SSL *ssl) + * } + */ + public static MethodHandle SSL_get_privatekey$handle() { + return SSL_get_privatekey.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * struct evp_pkey_st *SSL_get_privatekey(const SSL *ssl) + * } + */ + public static MemorySegment SSL_get_privatekey$address() { + return SSL_get_privatekey.ADDR; + } + + /** + * {@snippet lang = c : * struct evp_pkey_st *SSL_get_privatekey(const SSL *ssl) * } */ public static MemorySegment SSL_get_privatekey(MemorySegment ssl) { - var mh$ = SSL_get_privatekey$MH(); + var mh$ = SSL_get_privatekey.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get_privatekey", ssl); } return (MemorySegment) mh$.invokeExact(ssl); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get_shutdown$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get_shutdown"), - DESC); - } - return Holder.MH; + private static class SSL_get_shutdown { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get_shutdown"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_get_shutdown(const SSL *ssl) + * Function descriptor for: + * {@snippet lang = c : * int SSL_get_shutdown(const SSL *ssl) + * } + */ + public static FunctionDescriptor SSL_get_shutdown$descriptor() { + return SSL_get_shutdown.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_get_shutdown(const SSL *ssl) + * } + */ + public static MethodHandle SSL_get_shutdown$handle() { + return SSL_get_shutdown.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_get_shutdown(const SSL *ssl) + * } + */ + public static MemorySegment SSL_get_shutdown$address() { + return SSL_get_shutdown.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_get_shutdown(const SSL *ssl) * } */ public static int SSL_get_shutdown(MemorySegment ssl) { - var mh$ = SSL_get_shutdown$MH(); + var mh$ = SSL_get_shutdown.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get_shutdown", ssl); } return (int) mh$.invokeExact(ssl); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_default_verify_paths$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_default_verify_paths"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_default_verify_paths { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_default_verify_paths"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) + * } + */ + public static FunctionDescriptor SSL_CTX_set_default_verify_paths$descriptor() { + return SSL_CTX_set_default_verify_paths.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) + * } + */ + public static MethodHandle SSL_CTX_set_default_verify_paths$handle() { + return SSL_CTX_set_default_verify_paths.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) + * } + */ + public static MemorySegment SSL_CTX_set_default_verify_paths$address() { + return SSL_CTX_set_default_verify_paths.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) * } */ public static int SSL_CTX_set_default_verify_paths(MemorySegment ctx) { - var mh$ = SSL_CTX_set_default_verify_paths$MH(); + var mh$ = SSL_CTX_set_default_verify_paths.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_default_verify_paths", ctx); } return (int) mh$.invokeExact(ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_load_verify_locations$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_load_verify_locations"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_load_verify_locations { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_load_verify_locations"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath) + * } + */ + public static FunctionDescriptor SSL_CTX_load_verify_locations$descriptor() { + return SSL_CTX_load_verify_locations.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath) + * } + */ + public static MethodHandle SSL_CTX_load_verify_locations$handle() { + return SSL_CTX_load_verify_locations.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath) + * } + */ + public static MemorySegment SSL_CTX_load_verify_locations$address() { + return SSL_CTX_load_verify_locations.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *CAfile, const char *CApath) * } */ public static int SSL_CTX_load_verify_locations(MemorySegment ctx, MemorySegment CAfile, MemorySegment CApath) { - var mh$ = SSL_CTX_load_verify_locations$MH(); + var mh$ = SSL_CTX_load_verify_locations.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_load_verify_locations", ctx, CAfile, CApath); } return (int) mh$.invokeExact(ctx, CAfile, CApath); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get_session$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get_session"), - DESC); - } - return Holder.MH; + private static class SSL_get_session { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get_session"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * SSL_SESSION *SSL_get_session(const SSL *ssl) + * Function descriptor for: + * {@snippet lang = c : * SSL_SESSION *SSL_get_session(const SSL *ssl) + * } + */ + public static FunctionDescriptor SSL_get_session$descriptor() { + return SSL_get_session.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * SSL_SESSION *SSL_get_session(const SSL *ssl) + * } + */ + public static MethodHandle SSL_get_session$handle() { + return SSL_get_session.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * SSL_SESSION *SSL_get_session(const SSL *ssl) + * } + */ + public static MemorySegment SSL_get_session$address() { + return SSL_get_session.ADDR; + } + + /** + * {@snippet lang = c : * SSL_SESSION *SSL_get_session(const SSL *ssl) * } */ public static MemorySegment SSL_get_session(MemorySegment ssl) { - var mh$ = SSL_get_session$MH(); + var mh$ = SSL_get_session.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get_session", ssl); } return (MemorySegment) mh$.invokeExact(ssl); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_set_info_callback$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_set_info_callback"), - DESC); - } - return Holder.MH; + private static class SSL_set_info_callback { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_set_info_callback"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_set_info_callback(SSL *ssl, void (*cb)(const SSL *, int, int)) + * Function descriptor for: + * {@snippet lang = c : * void SSL_set_info_callback(SSL *ssl, void (*cb)(const SSL *, int, int)) + * } + */ + public static FunctionDescriptor SSL_set_info_callback$descriptor() { + return SSL_set_info_callback.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_set_info_callback(SSL *ssl, void (*cb)(const SSL *, int, int)) + * } + */ + public static MethodHandle SSL_set_info_callback$handle() { + return SSL_set_info_callback.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_set_info_callback(SSL *ssl, void (*cb)(const SSL *, int, int)) + * } + */ + public static MemorySegment SSL_set_info_callback$address() { + return SSL_set_info_callback.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_set_info_callback(SSL *ssl, void (*cb)(const SSL *, int, int)) * } */ public static void SSL_set_info_callback(MemorySegment ssl, MemorySegment cb) { - var mh$ = SSL_set_info_callback$MH(); + var mh$ = SSL_set_info_callback.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_set_info_callback", ssl, cb); } mh$.invokeExact(ssl, cb); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_set_verify_result$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_set_verify_result"), - DESC); - } - return Holder.MH; + private static class SSL_set_verify_result { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_set_verify_result"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_set_verify_result(SSL *ssl, long v) + * Function descriptor for: + * {@snippet lang = c : * void SSL_set_verify_result(SSL *ssl, long v) + * } + */ + public static FunctionDescriptor SSL_set_verify_result$descriptor() { + return SSL_set_verify_result.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_set_verify_result(SSL *ssl, long v) + * } + */ + public static MethodHandle SSL_set_verify_result$handle() { + return SSL_set_verify_result.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_set_verify_result(SSL *ssl, long v) + * } + */ + public static MemorySegment SSL_set_verify_result$address() { + return SSL_set_verify_result.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_set_verify_result(SSL *ssl, long v) * } */ public static void SSL_set_verify_result(MemorySegment ssl, long v) { - var mh$ = SSL_set_verify_result$MH(); + var mh$ = SSL_set_verify_result.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_set_verify_result", ssl, v); } mh$.invokeExact(ssl, v); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_get_ex_data_X509_STORE_CTX_idx$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT ); + private static class SSL_get_ex_data_X509_STORE_CTX_idx { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_get_ex_data_X509_STORE_CTX_idx"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_get_ex_data_X509_STORE_CTX_idx"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_get_ex_data_X509_STORE_CTX_idx(void) + * Function descriptor for: + * {@snippet lang = c : * int SSL_get_ex_data_X509_STORE_CTX_idx() + * } + */ + public static FunctionDescriptor SSL_get_ex_data_X509_STORE_CTX_idx$descriptor() { + return SSL_get_ex_data_X509_STORE_CTX_idx.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_get_ex_data_X509_STORE_CTX_idx() + * } + */ + public static MethodHandle SSL_get_ex_data_X509_STORE_CTX_idx$handle() { + return SSL_get_ex_data_X509_STORE_CTX_idx.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_get_ex_data_X509_STORE_CTX_idx() + * } + */ + public static MemorySegment SSL_get_ex_data_X509_STORE_CTX_idx$address() { + return SSL_get_ex_data_X509_STORE_CTX_idx.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_get_ex_data_X509_STORE_CTX_idx() * } */ public static int SSL_get_ex_data_X509_STORE_CTX_idx() { - var mh$ = SSL_get_ex_data_X509_STORE_CTX_idx$MH(); + var mh$ = SSL_get_ex_data_X509_STORE_CTX_idx.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_get_ex_data_X509_STORE_CTX_idx"); } return (int) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CTX_set_tmp_dh_callback$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CTX_set_tmp_dh_callback"), - DESC); - } - return Holder.MH; + private static class SSL_CTX_set_tmp_dh_callback { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CTX_set_tmp_dh_callback"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx, DH *(*dh)(SSL *, int, int)) + * Function descriptor for: + * {@snippet lang = c : * void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx, DH *(*dh)(SSL *, int, int)) + * } + */ + public static FunctionDescriptor SSL_CTX_set_tmp_dh_callback$descriptor() { + return SSL_CTX_set_tmp_dh_callback.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx, DH *(*dh)(SSL *, int, int)) + * } + */ + public static MethodHandle SSL_CTX_set_tmp_dh_callback$handle() { + return SSL_CTX_set_tmp_dh_callback.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx, DH *(*dh)(SSL *, int, int)) + * } + */ + public static MemorySegment SSL_CTX_set_tmp_dh_callback$address() { + return SSL_CTX_set_tmp_dh_callback.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_CTX_set_tmp_dh_callback(SSL_CTX *ctx, DH *(*dh)(SSL *, int, int)) * } */ public static void SSL_CTX_set_tmp_dh_callback(MemorySegment ctx, MemorySegment dh) { - var mh$ = SSL_CTX_set_tmp_dh_callback$MH(); + var mh$ = SSL_CTX_set_tmp_dh_callback.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CTX_set_tmp_dh_callback", ctx, dh); } mh$.invokeExact(ctx, dh); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CONF_CTX_new$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class SSL_CONF_CTX_new { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CONF_CTX_new"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CONF_CTX_new"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * SSL_CONF_CTX *SSL_CONF_CTX_new(void) + * Function descriptor for: + * {@snippet lang = c : * SSL_CONF_CTX *SSL_CONF_CTX_new() + * } + */ + public static FunctionDescriptor SSL_CONF_CTX_new$descriptor() { + return SSL_CONF_CTX_new.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * SSL_CONF_CTX *SSL_CONF_CTX_new() + * } + */ + public static MethodHandle SSL_CONF_CTX_new$handle() { + return SSL_CONF_CTX_new.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * SSL_CONF_CTX *SSL_CONF_CTX_new() + * } + */ + public static MemorySegment SSL_CONF_CTX_new$address() { + return SSL_CONF_CTX_new.ADDR; + } + + /** + * {@snippet lang = c : * SSL_CONF_CTX *SSL_CONF_CTX_new() * } */ public static MemorySegment SSL_CONF_CTX_new() { - var mh$ = SSL_CONF_CTX_new$MH(); + var mh$ = SSL_CONF_CTX_new.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CONF_CTX_new"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CONF_CTX_finish$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CONF_CTX_finish"), - DESC); - } - return Holder.MH; + private static class SSL_CONF_CTX_finish { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CONF_CTX_finish"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CONF_CTX_finish(SSL_CONF_CTX *cctx) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CONF_CTX_finish(SSL_CONF_CTX *cctx) + * } + */ + public static FunctionDescriptor SSL_CONF_CTX_finish$descriptor() { + return SSL_CONF_CTX_finish.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CONF_CTX_finish(SSL_CONF_CTX *cctx) + * } + */ + public static MethodHandle SSL_CONF_CTX_finish$handle() { + return SSL_CONF_CTX_finish.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CONF_CTX_finish(SSL_CONF_CTX *cctx) + * } + */ + public static MemorySegment SSL_CONF_CTX_finish$address() { + return SSL_CONF_CTX_finish.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CONF_CTX_finish(SSL_CONF_CTX *cctx) * } */ public static int SSL_CONF_CTX_finish(MemorySegment cctx) { - var mh$ = SSL_CONF_CTX_finish$MH(); + var mh$ = SSL_CONF_CTX_finish.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CONF_CTX_finish", cctx); } return (int) mh$.invokeExact(cctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CONF_CTX_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CONF_CTX_free"), - DESC); - } - return Holder.MH; + private static class SSL_CONF_CTX_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CONF_CTX_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_CONF_CTX_free(SSL_CONF_CTX *cctx) + * Function descriptor for: + * {@snippet lang = c : * void SSL_CONF_CTX_free(SSL_CONF_CTX *cctx) + * } + */ + public static FunctionDescriptor SSL_CONF_CTX_free$descriptor() { + return SSL_CONF_CTX_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_CONF_CTX_free(SSL_CONF_CTX *cctx) + * } + */ + public static MethodHandle SSL_CONF_CTX_free$handle() { + return SSL_CONF_CTX_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_CONF_CTX_free(SSL_CONF_CTX *cctx) + * } + */ + public static MemorySegment SSL_CONF_CTX_free$address() { + return SSL_CONF_CTX_free.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_CONF_CTX_free(SSL_CONF_CTX *cctx) * } */ public static void SSL_CONF_CTX_free(MemorySegment cctx) { - var mh$ = SSL_CONF_CTX_free$MH(); + var mh$ = SSL_CONF_CTX_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CONF_CTX_free", cctx); } mh$.invokeExact(cctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CONF_CTX_set_flags$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CONF_CTX_set_flags"), - DESC); - } - return Holder.MH; + private static class SSL_CONF_CTX_set_flags { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CONF_CTX_set_flags"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * unsigned int SSL_CONF_CTX_set_flags(SSL_CONF_CTX *cctx, unsigned int flags) + * Function descriptor for: + * {@snippet lang = c : * unsigned int SSL_CONF_CTX_set_flags(SSL_CONF_CTX *cctx, unsigned int flags) + * } + */ + public static FunctionDescriptor SSL_CONF_CTX_set_flags$descriptor() { + return SSL_CONF_CTX_set_flags.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * unsigned int SSL_CONF_CTX_set_flags(SSL_CONF_CTX *cctx, unsigned int flags) + * } + */ + public static MethodHandle SSL_CONF_CTX_set_flags$handle() { + return SSL_CONF_CTX_set_flags.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * unsigned int SSL_CONF_CTX_set_flags(SSL_CONF_CTX *cctx, unsigned int flags) + * } + */ + public static MemorySegment SSL_CONF_CTX_set_flags$address() { + return SSL_CONF_CTX_set_flags.ADDR; + } + + /** + * {@snippet lang = c : * unsigned int SSL_CONF_CTX_set_flags(SSL_CONF_CTX *cctx, unsigned int flags) * } */ public static int SSL_CONF_CTX_set_flags(MemorySegment cctx, int flags) { - var mh$ = SSL_CONF_CTX_set_flags$MH(); + var mh$ = SSL_CONF_CTX_set_flags.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CONF_CTX_set_flags", cctx, flags); } return (int) mh$.invokeExact(cctx, flags); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CONF_CTX_set_ssl_ctx$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CONF_CTX_set_ssl_ctx"), - DESC); - } - return Holder.MH; + private static class SSL_CONF_CTX_set_ssl_ctx { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CONF_CTX_set_ssl_ctx"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *cctx, SSL_CTX *ctx) + * Function descriptor for: + * {@snippet lang = c : * void SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *cctx, SSL_CTX *ctx) + * } + */ + public static FunctionDescriptor SSL_CONF_CTX_set_ssl_ctx$descriptor() { + return SSL_CONF_CTX_set_ssl_ctx.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *cctx, SSL_CTX *ctx) + * } + */ + public static MethodHandle SSL_CONF_CTX_set_ssl_ctx$handle() { + return SSL_CONF_CTX_set_ssl_ctx.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *cctx, SSL_CTX *ctx) + * } + */ + public static MemorySegment SSL_CONF_CTX_set_ssl_ctx$address() { + return SSL_CONF_CTX_set_ssl_ctx.ADDR; + } + + /** + * {@snippet lang = c : * void SSL_CONF_CTX_set_ssl_ctx(SSL_CONF_CTX *cctx, SSL_CTX *ctx) * } */ public static void SSL_CONF_CTX_set_ssl_ctx(MemorySegment cctx, MemorySegment ctx) { - var mh$ = SSL_CONF_CTX_set_ssl_ctx$MH(); + var mh$ = SSL_CONF_CTX_set_ssl_ctx.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CONF_CTX_set_ssl_ctx", cctx, ctx); } mh$.invokeExact(cctx, ctx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CONF_cmd$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CONF_cmd"), - DESC); - } - return Holder.MH; + private static class SSL_CONF_cmd { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CONF_cmd"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CONF_cmd(SSL_CONF_CTX *cctx, const char *cmd, const char *value) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CONF_cmd(SSL_CONF_CTX *cctx, const char *cmd, const char *value) + * } + */ + public static FunctionDescriptor SSL_CONF_cmd$descriptor() { + return SSL_CONF_cmd.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CONF_cmd(SSL_CONF_CTX *cctx, const char *cmd, const char *value) + * } + */ + public static MethodHandle SSL_CONF_cmd$handle() { + return SSL_CONF_cmd.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CONF_cmd(SSL_CONF_CTX *cctx, const char *cmd, const char *value) + * } + */ + public static MemorySegment SSL_CONF_cmd$address() { + return SSL_CONF_cmd.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CONF_cmd(SSL_CONF_CTX *cctx, const char *cmd, const char *value) * } */ public static int SSL_CONF_cmd(MemorySegment cctx, MemorySegment cmd, MemorySegment value) { - var mh$ = SSL_CONF_cmd$MH(); + var mh$ = SSL_CONF_cmd.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CONF_cmd", cctx, cmd, value); } return (int) mh$.invokeExact(cctx, cmd, value); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle SSL_CONF_cmd_value_type$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("SSL_CONF_cmd_value_type"), - DESC); - } - return Holder.MH; + private static class SSL_CONF_cmd_value_type { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("SSL_CONF_cmd_value_type"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int SSL_CONF_cmd_value_type(SSL_CONF_CTX *cctx, const char *cmd) + * Function descriptor for: + * {@snippet lang = c : * int SSL_CONF_cmd_value_type(SSL_CONF_CTX *cctx, const char *cmd) + * } + */ + public static FunctionDescriptor SSL_CONF_cmd_value_type$descriptor() { + return SSL_CONF_cmd_value_type.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int SSL_CONF_cmd_value_type(SSL_CONF_CTX *cctx, const char *cmd) + * } + */ + public static MethodHandle SSL_CONF_cmd_value_type$handle() { + return SSL_CONF_cmd_value_type.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int SSL_CONF_cmd_value_type(SSL_CONF_CTX *cctx, const char *cmd) + * } + */ + public static MemorySegment SSL_CONF_cmd_value_type$address() { + return SSL_CONF_cmd_value_type.ADDR; + } + + /** + * {@snippet lang = c : * int SSL_CONF_cmd_value_type(SSL_CONF_CTX *cctx, const char *cmd) * } */ public static int SSL_CONF_cmd_value_type(MemorySegment cctx, MemorySegment cmd) { - var mh$ = SSL_CONF_cmd_value_type$MH(); + var mh$ = SSL_CONF_cmd_value_type.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("SSL_CONF_cmd_value_type", cctx, cmd); } return (int) mh$.invokeExact(cctx, cmd); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OPENSSL_init_ssl$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OPENSSL_init_ssl"), - DESC); - } - return Holder.MH; + private static class OPENSSL_init_ssl { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OPENSSL_init_ssl"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) + * Function descriptor for: + * {@snippet lang = c : * int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) + * } + */ + public static FunctionDescriptor OPENSSL_init_ssl$descriptor() { + return OPENSSL_init_ssl.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) + * } + */ + public static MethodHandle OPENSSL_init_ssl$handle() { + return OPENSSL_init_ssl.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) + * } + */ + public static MemorySegment OPENSSL_init_ssl$address() { + return OPENSSL_init_ssl.ADDR; + } + + /** + * {@snippet lang = c : * int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings) * } */ public static int OPENSSL_init_ssl(long opts, MemorySegment settings) { - var mh$ = OPENSSL_init_ssl$MH(); + var mh$ = OPENSSL_init_ssl.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OPENSSL_init_ssl", opts, settings); } return (int) mh$.invokeExact(opts, settings); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ERR_get_error$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG ); + private static class ERR_get_error { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ERR_get_error"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("ERR_get_error"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * unsigned long ERR_get_error(void) + * Function descriptor for: + * {@snippet lang = c : * unsigned long ERR_get_error() + * } + */ + public static FunctionDescriptor ERR_get_error$descriptor() { + return ERR_get_error.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * unsigned long ERR_get_error() + * } + */ + public static MethodHandle ERR_get_error$handle() { + return ERR_get_error.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * unsigned long ERR_get_error() + * } + */ + public static MemorySegment ERR_get_error$address() { + return ERR_get_error.ADDR; + } + + /** + * {@snippet lang = c : * unsigned long ERR_get_error() * } */ public static long ERR_get_error() { - var mh$ = ERR_get_error$MH(); + var mh$ = ERR_get_error.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("ERR_get_error"); } return (long) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ERR_peek_last_error$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_LONG ); + private static class ERR_peek_last_error { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ERR_peek_last_error"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("ERR_peek_last_error"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * unsigned long ERR_peek_last_error(void) + * Function descriptor for: + * {@snippet lang = c : * unsigned long ERR_peek_last_error() + * } + */ + public static FunctionDescriptor ERR_peek_last_error$descriptor() { + return ERR_peek_last_error.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * unsigned long ERR_peek_last_error() + * } + */ + public static MethodHandle ERR_peek_last_error$handle() { + return ERR_peek_last_error.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * unsigned long ERR_peek_last_error() + * } + */ + public static MemorySegment ERR_peek_last_error$address() { + return ERR_peek_last_error.ADDR; + } + + /** + * {@snippet lang = c : * unsigned long ERR_peek_last_error() * } */ public static long ERR_peek_last_error() { - var mh$ = ERR_peek_last_error$MH(); + var mh$ = ERR_peek_last_error.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("ERR_peek_last_error"); } return (long) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ERR_clear_error$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( ); + private static class ERR_clear_error { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ERR_clear_error"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("ERR_clear_error"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void ERR_clear_error(void) + * Function descriptor for: + * {@snippet lang = c : * void ERR_clear_error() + * } + */ + public static FunctionDescriptor ERR_clear_error$descriptor() { + return ERR_clear_error.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void ERR_clear_error() + * } + */ + public static MethodHandle ERR_clear_error$handle() { + return ERR_clear_error.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void ERR_clear_error() + * } + */ + public static MemorySegment ERR_clear_error$address() { + return ERR_clear_error.ADDR; + } + + /** + * {@snippet lang = c : * void ERR_clear_error() * } */ public static void ERR_clear_error() { - var mh$ = ERR_clear_error$MH(); + var mh$ = ERR_clear_error.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("ERR_clear_error"); } mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ERR_error_string$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_LONG, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ERR_error_string"), - DESC); - } - return Holder.MH; + private static class ERR_error_string { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_LONG, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("ERR_error_string"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * char *ERR_error_string(unsigned long e, char *buf) + * Function descriptor for: + * {@snippet lang = c : * char *ERR_error_string(unsigned long e, char *buf) + * } + */ + public static FunctionDescriptor ERR_error_string$descriptor() { + return ERR_error_string.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * char *ERR_error_string(unsigned long e, char *buf) + * } + */ + public static MethodHandle ERR_error_string$handle() { + return ERR_error_string.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * char *ERR_error_string(unsigned long e, char *buf) + * } + */ + public static MemorySegment ERR_error_string$address() { + return ERR_error_string.ADDR; + } + + /** + * {@snippet lang = c : * char *ERR_error_string(unsigned long e, char *buf) * } */ public static MemorySegment ERR_error_string(long e, MemorySegment buf) { - var mh$ = ERR_error_string$MH(); + var mh$ = ERR_error_string.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("ERR_error_string", e, buf); } return (MemorySegment) mh$.invokeExact(e, buf); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ERR_error_string_n$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_LONG, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ERR_error_string_n"), - DESC); - } - return Holder.MH; + private static class ERR_error_string_n { + public static final FunctionDescriptor DESC = + FunctionDescriptor.ofVoid(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("ERR_error_string_n"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * char *ERR_error_string_n(unsigned long e, char *buf, size_t len) + * Function descriptor for: + * {@snippet lang = c : * void ERR_error_string_n(unsigned long e, char *buf, size_t len) * } */ - public static MemorySegment ERR_error_string_n(long e, MemorySegment buf, int len) { - var mh$ = ERR_error_string_n$MH(); + public static FunctionDescriptor ERR_error_string_n$descriptor() { + return ERR_error_string_n.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void ERR_error_string_n(unsigned long e, char *buf, size_t len) + * } + */ + public static MethodHandle ERR_error_string_n$handle() { + return ERR_error_string_n.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void ERR_error_string_n(unsigned long e, char *buf, size_t len) + * } + */ + public static MemorySegment ERR_error_string_n$address() { + return ERR_error_string_n.ADDR; + } + + /** + * {@snippet lang = c : * void ERR_error_string_n(unsigned long e, char *buf, size_t len) + * } + */ + public static void ERR_error_string_n(long e, MemorySegment buf, long len) { + var mh$ = ERR_error_string_n.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("ERR_error_string_n", e, buf, len); } - return (MemorySegment) mh$.invokeExact(e, buf, len); + mh$.invokeExact(e, buf, len); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle PKCS12_verify_mac$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("PKCS12_verify_mac"), - DESC); - } - return Holder.MH; + private static class PKCS12_verify_mac { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("PKCS12_verify_mac"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen) + * Function descriptor for: + * {@snippet lang = c : * int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen) + * } + */ + public static FunctionDescriptor PKCS12_verify_mac$descriptor() { + return PKCS12_verify_mac.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen) + * } + */ + public static MethodHandle PKCS12_verify_mac$handle() { + return PKCS12_verify_mac.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen) + * } + */ + public static MemorySegment PKCS12_verify_mac$address() { + return PKCS12_verify_mac.ADDR; + } + + /** + * {@snippet lang = c : * int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen) * } */ public static int PKCS12_verify_mac(MemorySegment p12, MemorySegment pass, int passlen) { - var mh$ = PKCS12_verify_mac$MH(); + var mh$ = PKCS12_verify_mac.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("PKCS12_verify_mac", p12, pass, passlen); } return (int) mh$.invokeExact(p12, pass, passlen); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle PKCS12_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("PKCS12_free"), - DESC); - } - return Holder.MH; + private static class PKCS12_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("PKCS12_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern void PKCS12_free(PKCS12 *a) + * Function descriptor for: + * {@snippet lang = c : * extern void PKCS12_free(PKCS12 *a) + * } + */ + public static FunctionDescriptor PKCS12_free$descriptor() { + return PKCS12_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern void PKCS12_free(PKCS12 *a) + * } + */ + public static MethodHandle PKCS12_free$handle() { + return PKCS12_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern void PKCS12_free(PKCS12 *a) + * } + */ + public static MemorySegment PKCS12_free$address() { + return PKCS12_free.ADDR; + } + + /** + * {@snippet lang = c : * extern void PKCS12_free(PKCS12 *a) * } */ public static void PKCS12_free(MemorySegment a) { - var mh$ = PKCS12_free$MH(); + var mh$ = PKCS12_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("PKCS12_free", a); } mh$.invokeExact(a); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle PKCS12_parse$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("PKCS12_parse"), - DESC); - } - return Holder.MH; + private static class PKCS12_parse { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("PKCS12_parse"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, struct stack_st_X509 **ca) + * Function descriptor for: + * {@snippet lang = c + * : * int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, struct stack_st_X509 **ca) * } */ - public static int PKCS12_parse(MemorySegment p12, MemorySegment pass, MemorySegment pkey, MemorySegment cert, MemorySegment ca) { - var mh$ = PKCS12_parse$MH(); + public static FunctionDescriptor PKCS12_parse$descriptor() { + return PKCS12_parse.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c + * : * int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, struct stack_st_X509 **ca) + * } + */ + public static MethodHandle PKCS12_parse$handle() { + return PKCS12_parse.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c + * : * int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, struct stack_st_X509 **ca) + * } + */ + public static MemorySegment PKCS12_parse$address() { + return PKCS12_parse.ADDR; + } + + /** + * {@snippet lang = c + * : * int PKCS12_parse(PKCS12 *p12, const char *pass, EVP_PKEY **pkey, X509 **cert, struct stack_st_X509 **ca) + * } + */ + public static int PKCS12_parse(MemorySegment p12, MemorySegment pass, MemorySegment pkey, MemorySegment cert, + MemorySegment ca) { + var mh$ = PKCS12_parse.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("PKCS12_parse", p12, pass, pkey, cert, ca); } return (int) mh$.invokeExact(p12, pass, pkey, cert, ca); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle d2i_PKCS12_bio$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("d2i_PKCS12_bio"), - DESC); - } - return Holder.MH; + private static class d2i_PKCS12_bio { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("d2i_PKCS12_bio"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **p12) + * Function descriptor for: + * {@snippet lang = c : * PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **p12) + * } + */ + public static FunctionDescriptor d2i_PKCS12_bio$descriptor() { + return d2i_PKCS12_bio.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **p12) + * } + */ + public static MethodHandle d2i_PKCS12_bio$handle() { + return d2i_PKCS12_bio.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **p12) + * } + */ + public static MemorySegment d2i_PKCS12_bio$address() { + return d2i_PKCS12_bio.ADDR; + } + + /** + * {@snippet lang = c : * PKCS12 *d2i_PKCS12_bio(BIO *bp, PKCS12 **p12) * } */ public static MemorySegment d2i_PKCS12_bio(MemorySegment bp, MemorySegment p12) { - var mh$ = d2i_PKCS12_bio$MH(); + var mh$ = d2i_PKCS12_bio.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("d2i_PKCS12_bio", bp, p12); } return (MemorySegment) mh$.invokeExact(bp, p12); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle RAND_seed$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("RAND_seed"), - DESC); - } - return Holder.MH; + private static class RAND_seed { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("RAND_seed"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * void RAND_seed(const void *buf, int num) + * Function descriptor for: + * {@snippet lang = c : * void RAND_seed(const void *buf, int num) + * } + */ + public static FunctionDescriptor RAND_seed$descriptor() { + return RAND_seed.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * void RAND_seed(const void *buf, int num) + * } + */ + public static MethodHandle RAND_seed$handle() { + return RAND_seed.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * void RAND_seed(const void *buf, int num) + * } + */ + public static MemorySegment RAND_seed$address() { + return RAND_seed.ADDR; + } + + /** + * {@snippet lang = c : * void RAND_seed(const void *buf, int num) * } */ public static void RAND_seed(MemorySegment buf, int num) { - var mh$ = RAND_seed$MH(); + var mh$ = RAND_seed.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("RAND_seed", buf, num); } mh$.invokeExact(buf, num); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle RAND_load_file$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("RAND_load_file"), - DESC); - } - return Holder.MH; + private static class RAND_load_file { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("RAND_load_file"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int RAND_load_file(const char *file, long max_bytes) + * Function descriptor for: + * {@snippet lang = c : * int RAND_load_file(const char *file, long max_bytes) + * } + */ + public static FunctionDescriptor RAND_load_file$descriptor() { + return RAND_load_file.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int RAND_load_file(const char *file, long max_bytes) + * } + */ + public static MethodHandle RAND_load_file$handle() { + return RAND_load_file.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int RAND_load_file(const char *file, long max_bytes) + * } + */ + public static MemorySegment RAND_load_file$address() { + return RAND_load_file.ADDR; + } + + /** + * {@snippet lang = c : * int RAND_load_file(const char *file, long max_bytes) * } */ public static int RAND_load_file(MemorySegment file, long max_bytes) { - var mh$ = RAND_load_file$MH(); + var mh$ = RAND_load_file.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("RAND_load_file", file, max_bytes); } return (int) mh$.invokeExact(file, max_bytes); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle X509_check_issued$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("X509_check_issued"), - DESC); - } - return Holder.MH; + private static class X509_check_issued { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_check_issued"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int X509_check_issued(X509 *issuer, X509 *subject) + * Function descriptor for: + * {@snippet lang = c : * int X509_check_issued(X509 *issuer, X509 *subject) + * } + */ + public static FunctionDescriptor X509_check_issued$descriptor() { + return X509_check_issued.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int X509_check_issued(X509 *issuer, X509 *subject) + * } + */ + public static MethodHandle X509_check_issued$handle() { + return X509_check_issued.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int X509_check_issued(X509 *issuer, X509 *subject) + * } + */ + public static MemorySegment X509_check_issued$address() { + return X509_check_issued.ADDR; + } + + /** + * {@snippet lang = c : * int X509_check_issued(X509 *issuer, X509 *subject) * } */ public static int X509_check_issued(MemorySegment issuer, MemorySegment subject) { - var mh$ = X509_check_issued$MH(); + var mh$ = X509_check_issued.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("X509_check_issued", issuer, subject); } return (int) mh$.invokeExact(issuer, subject); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ENGINE_by_id$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ENGINE_by_id"), - DESC); - } - return Holder.MH; + private static class OCSP_resp_get0_certs { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + openssl_h.C_POINTER, + openssl_h.C_POINTER + ); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_resp_get0_certs"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** + * Function descriptor for: * {@snippet lang=c : - * ENGINE *ENGINE_by_id(const char *id) + * const struct stack_st_X509 *OCSP_resp_get0_certs(const OCSP_BASICRESP *bs) * } */ - public static MemorySegment ENGINE_by_id(MemorySegment id) { - var mh$ = ENGINE_by_id$MH(); - try { - if (TRACE_DOWNCALLS) { - traceDowncall("ENGINE_by_id", id); - } - return (MemorySegment) mh$.invokeExact(id); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } + public static FunctionDescriptor OCSP_resp_get0_certs$descriptor() { + return OCSP_resp_get0_certs.DESC; } - private static MethodHandle ENGINE_register_all_complete$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT ); + /** + * Downcall method handle for: + * {@snippet lang=c : + * const struct stack_st_X509 *OCSP_resp_get0_certs(const OCSP_BASICRESP *bs) + * } + */ + public static MethodHandle OCSP_resp_get0_certs$handle() { + return OCSP_resp_get0_certs.HANDLE; + } - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ENGINE_register_all_complete"), - DESC); - } - return Holder.MH; + /** + * Address for: + * {@snippet lang=c : + * const struct stack_st_X509 *OCSP_resp_get0_certs(const OCSP_BASICRESP *bs) + * } + */ + public static MemorySegment OCSP_resp_get0_certs$address() { + return OCSP_resp_get0_certs.ADDR; } /** * {@snippet lang=c : - * int ENGINE_register_all_complete(void) + * const struct stack_st_X509 *OCSP_resp_get0_certs(const OCSP_BASICRESP *bs) * } */ - public static int ENGINE_register_all_complete() { - var mh$ = ENGINE_register_all_complete$MH(); + public static MemorySegment OCSP_resp_get0_certs(MemorySegment bs) { + var mh$ = OCSP_resp_get0_certs.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("ENGINE_register_all_complete"); + traceDowncall("OCSP_resp_get0_certs", bs); } - return (int) mh$.invokeExact(); + return (MemorySegment)mh$.invokeExact(bs); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ENGINE_ctrl_cmd_string$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ENGINE_ctrl_cmd_string"), - DESC); - } - return Holder.MH; + private static class OCSP_basic_verify { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + openssl_h.C_INT, + openssl_h.C_POINTER, + openssl_h.C_POINTER, + openssl_h.C_POINTER, + openssl_h.C_LONG + ); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_basic_verify"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int OCSP_basic_verify(OCSP_BASICRESP *bs, struct stack_st_X509 *certs, X509_STORE *st, unsigned long flags) + * } + */ + public static FunctionDescriptor OCSP_basic_verify$descriptor() { + return OCSP_basic_verify.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int OCSP_basic_verify(OCSP_BASICRESP *bs, struct stack_st_X509 *certs, X509_STORE *st, unsigned long flags) + * } + */ + public static MethodHandle OCSP_basic_verify$handle() { + return OCSP_basic_verify.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * int OCSP_basic_verify(OCSP_BASICRESP *bs, struct stack_st_X509 *certs, X509_STORE *st, unsigned long flags) + * } + */ + public static MemorySegment OCSP_basic_verify$address() { + return OCSP_basic_verify.ADDR; } /** * {@snippet lang=c : - * int ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg, int cmd_optional) + * int OCSP_basic_verify(OCSP_BASICRESP *bs, struct stack_st_X509 *certs, X509_STORE *st, unsigned long flags) * } */ - public static int ENGINE_ctrl_cmd_string(MemorySegment e, MemorySegment cmd_name, MemorySegment arg, int cmd_optional) { - var mh$ = ENGINE_ctrl_cmd_string$MH(); + public static int OCSP_basic_verify(MemorySegment bs, MemorySegment certs, MemorySegment st, long flags) { + var mh$ = OCSP_basic_verify.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("ENGINE_ctrl_cmd_string", e, cmd_name, arg, cmd_optional); + traceDowncall("OCSP_basic_verify", bs, certs, st, flags); } - return (int) mh$.invokeExact(e, cmd_name, arg, cmd_optional); + return (int)mh$.invokeExact(bs, certs, st, flags); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ENGINE_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ENGINE_free"), - DESC); - } - return Holder.MH; + private static class OCSP_check_validity { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + openssl_h.C_INT, + openssl_h.C_POINTER, + openssl_h.C_POINTER, + openssl_h.C_LONG, + openssl_h.C_LONG + ); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_check_validity"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long sec, long maxsec) + * } + */ + public static FunctionDescriptor OCSP_check_validity$descriptor() { + return OCSP_check_validity.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long sec, long maxsec) + * } + */ + public static MethodHandle OCSP_check_validity$handle() { + return OCSP_check_validity.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long sec, long maxsec) + * } + */ + public static MemorySegment OCSP_check_validity$address() { + return OCSP_check_validity.ADDR; } /** * {@snippet lang=c : - * int ENGINE_free(ENGINE *e) + * int OCSP_check_validity(ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd, long sec, long maxsec) * } */ - public static int ENGINE_free(MemorySegment e) { - var mh$ = ENGINE_free$MH(); + public static int OCSP_check_validity(MemorySegment thisupd, MemorySegment nextupd, long sec, long maxsec) { + var mh$ = OCSP_check_validity.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("ENGINE_free", e); + traceDowncall("OCSP_check_validity", thisupd, nextupd, sec, maxsec); } - return (int) mh$.invokeExact(e); + return (int)mh$.invokeExact(thisupd, nextupd, sec, maxsec); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ENGINE_load_private_key$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ENGINE_load_private_key"), - DESC); - } - return Holder.MH; + private static class OCSP_request_add1_nonce { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + openssl_h.C_INT, + openssl_h.C_POINTER, + ValueLayout.JAVA_CHAR, + openssl_h.C_INT + ); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_request_add1_nonce"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** + * Function descriptor for: * {@snippet lang=c : - * EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, void *callback_data) + * int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len) * } */ - public static MemorySegment ENGINE_load_private_key(MemorySegment e, MemorySegment key_id, MemorySegment ui_method, MemorySegment callback_data) { - var mh$ = ENGINE_load_private_key$MH(); + public static FunctionDescriptor OCSP_request_add1_nonce$descriptor() { + return OCSP_request_add1_nonce.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len) + * } + */ + public static MethodHandle OCSP_request_add1_nonce$handle() { + return OCSP_request_add1_nonce.HANDLE; + } + + /** + * Address for: + * {@snippet lang=c : + * int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len) + * } + */ + public static MemorySegment OCSP_request_add1_nonce$address() { + return OCSP_request_add1_nonce.ADDR; + } + + /** + * {@snippet lang=c : + * int OCSP_request_add1_nonce(OCSP_REQUEST *req, unsigned char *val, int len) + * } + */ + public static int OCSP_request_add1_nonce(MemorySegment req, char val, int len) { + var mh$ = OCSP_request_add1_nonce.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("ENGINE_load_private_key", e, key_id, ui_method, callback_data); + traceDowncall("OCSP_request_add1_nonce", req, val, len); } - return (MemorySegment) mh$.invokeExact(e, key_id, ui_method, callback_data); + return (int)mh$.invokeExact(req, val, len); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle ENGINE_set_default$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("ENGINE_set_default"), - DESC); - } - return Holder.MH; + private static class OCSP_check_nonce { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + openssl_h.C_INT, + openssl_h.C_POINTER, + openssl_h.C_POINTER + ); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_check_nonce"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Function descriptor for: + * {@snippet lang=c : + * int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs) + * } + */ + public static FunctionDescriptor OCSP_check_nonce$descriptor() { + return OCSP_check_nonce.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=c : + * int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs) + * } + */ + public static MethodHandle OCSP_check_nonce$handle() { + return OCSP_check_nonce.HANDLE; } /** + * Address for: * {@snippet lang=c : - * int ENGINE_set_default(ENGINE *e, unsigned int flags) + * int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs) * } */ - public static int ENGINE_set_default(MemorySegment e, int flags) { - var mh$ = ENGINE_set_default$MH(); + public static MemorySegment OCSP_check_nonce$address() { + return OCSP_check_nonce.ADDR; + } + + /** + * {@snippet lang=c : + * int OCSP_check_nonce(OCSP_REQUEST *req, OCSP_BASICRESP *bs) + * } + */ + public static int OCSP_check_nonce(MemorySegment req, MemorySegment bs) { + var mh$ = OCSP_check_nonce.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("ENGINE_set_default", e, flags); + traceDowncall("OCSP_check_nonce", req, bs); } - return (int) mh$.invokeExact(e, flags); + return (int)mh$.invokeExact(req, bs); + } catch (Error | RuntimeException ex) { + throw ex; } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_cert_to_id$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_cert_to_id"), - DESC); - } - return Holder.MH; + private static class OCSP_cert_to_id { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_cert_to_id"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject, const X509 *issuer) + * Function descriptor for: + * {@snippet lang = c : * OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject, const X509 *issuer) + * } + */ + public static FunctionDescriptor OCSP_cert_to_id$descriptor() { + return OCSP_cert_to_id.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject, const X509 *issuer) + * } + */ + public static MethodHandle OCSP_cert_to_id$handle() { + return OCSP_cert_to_id.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject, const X509 *issuer) + * } + */ + public static MemorySegment OCSP_cert_to_id$address() { + return OCSP_cert_to_id.ADDR; + } + + /** + * {@snippet lang = c : * OCSP_CERTID *OCSP_cert_to_id(const EVP_MD *dgst, const X509 *subject, const X509 *issuer) * } */ public static MemorySegment OCSP_cert_to_id(MemorySegment dgst, MemorySegment subject, MemorySegment issuer) { - var mh$ = OCSP_cert_to_id$MH(); + var mh$ = OCSP_cert_to_id.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_cert_to_id", dgst, subject, issuer); } return (MemorySegment) mh$.invokeExact(dgst, subject, issuer); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_request_add0_id$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_request_add0_id"), - DESC); - } - return Holder.MH; + private static class OCSP_request_add0_id { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_request_add0_id"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid) + * Function descriptor for: + * {@snippet lang = c : * OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid) + * } + */ + public static FunctionDescriptor OCSP_request_add0_id$descriptor() { + return OCSP_request_add0_id.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid) + * } + */ + public static MethodHandle OCSP_request_add0_id$handle() { + return OCSP_request_add0_id.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid) + * } + */ + public static MemorySegment OCSP_request_add0_id$address() { + return OCSP_request_add0_id.ADDR; + } + + /** + * {@snippet lang = c : * OCSP_ONEREQ *OCSP_request_add0_id(OCSP_REQUEST *req, OCSP_CERTID *cid) * } */ public static MemorySegment OCSP_request_add0_id(MemorySegment req, MemorySegment cid) { - var mh$ = OCSP_request_add0_id$MH(); + var mh$ = OCSP_request_add0_id.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_request_add0_id", req, cid); } return (MemorySegment) mh$.invokeExact(req, cid); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_response_status$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_response_status"), - DESC); - } - return Holder.MH; + private static class OCSP_response_status { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_response_status"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int OCSP_response_status(OCSP_RESPONSE *resp) + * Function descriptor for: + * {@snippet lang = c : * int OCSP_response_status(OCSP_RESPONSE *resp) + * } + */ + public static FunctionDescriptor OCSP_response_status$descriptor() { + return OCSP_response_status.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int OCSP_response_status(OCSP_RESPONSE *resp) + * } + */ + public static MethodHandle OCSP_response_status$handle() { + return OCSP_response_status.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int OCSP_response_status(OCSP_RESPONSE *resp) + * } + */ + public static MemorySegment OCSP_response_status$address() { + return OCSP_response_status.ADDR; + } + + /** + * {@snippet lang = c : * int OCSP_response_status(OCSP_RESPONSE *resp) * } */ public static int OCSP_response_status(MemorySegment resp) { - var mh$ = OCSP_response_status$MH(); + var mh$ = OCSP_response_status.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_response_status", resp); } return (int) mh$.invokeExact(resp); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_response_get1_basic$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_response_get1_basic"), - DESC); - } - return Holder.MH; + private static class OCSP_response_get1_basic { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_response_get1_basic"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp) + * Function descriptor for: + * {@snippet lang = c : * OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp) + * } + */ + public static FunctionDescriptor OCSP_response_get1_basic$descriptor() { + return OCSP_response_get1_basic.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp) + * } + */ + public static MethodHandle OCSP_response_get1_basic$handle() { + return OCSP_response_get1_basic.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp) + * } + */ + public static MemorySegment OCSP_response_get1_basic$address() { + return OCSP_response_get1_basic.ADDR; + } + + /** + * {@snippet lang = c : * OCSP_BASICRESP *OCSP_response_get1_basic(OCSP_RESPONSE *resp) * } */ public static MemorySegment OCSP_response_get1_basic(MemorySegment resp) { - var mh$ = OCSP_response_get1_basic$MH(); + var mh$ = OCSP_response_get1_basic.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_response_get1_basic", resp); } return (MemorySegment) mh$.invokeExact(resp); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_resp_get0$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_resp_get0"), - DESC); - } - return Holder.MH; + private static class OCSP_resp_get0 { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_resp_get0"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, int idx) + * Function descriptor for: + * {@snippet lang = c : * OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, int idx) + * } + */ + public static FunctionDescriptor OCSP_resp_get0$descriptor() { + return OCSP_resp_get0.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, int idx) + * } + */ + public static MethodHandle OCSP_resp_get0$handle() { + return OCSP_resp_get0.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, int idx) + * } + */ + public static MemorySegment OCSP_resp_get0$address() { + return OCSP_resp_get0.ADDR; + } + + /** + * {@snippet lang = c : * OCSP_SINGLERESP *OCSP_resp_get0(OCSP_BASICRESP *bs, int idx) * } */ public static MemorySegment OCSP_resp_get0(MemorySegment bs, int idx) { - var mh$ = OCSP_resp_get0$MH(); + var mh$ = OCSP_resp_get0.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_resp_get0", bs, idx); } return (MemorySegment) mh$.invokeExact(bs, idx); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_resp_find$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_INT - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_resp_find"), - DESC); - } - return Holder.MH; + private static class OCSP_resp_find { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_resp_find"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last) + * Function descriptor for: + * {@snippet lang = c : * int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last) + * } + */ + public static FunctionDescriptor OCSP_resp_find$descriptor() { + return OCSP_resp_find.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last) + * } + */ + public static MethodHandle OCSP_resp_find$handle() { + return OCSP_resp_find.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last) + * } + */ + public static MemorySegment OCSP_resp_find$address() { + return OCSP_resp_find.ADDR; + } + + /** + * {@snippet lang = c : * int OCSP_resp_find(OCSP_BASICRESP *bs, OCSP_CERTID *id, int last) * } */ public static int OCSP_resp_find(MemorySegment bs, MemorySegment id, int last) { - var mh$ = OCSP_resp_find$MH(); + var mh$ = OCSP_resp_find.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_resp_find", bs, id, last); } return (int) mh$.invokeExact(bs, id, last); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_single_get0_status$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_single_get0_status"), - DESC); - } - return Holder.MH; + private static class OCSP_single_get0_status { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_single_get0_status"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime, ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd) + * Function descriptor for: + * {@snippet lang = c + * : * int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime, ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd) + * } + */ + public static FunctionDescriptor OCSP_single_get0_status$descriptor() { + return OCSP_single_get0_status.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c + * : * int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime, ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd) * } */ - public static int OCSP_single_get0_status(MemorySegment single, MemorySegment reason, MemorySegment revtime, MemorySegment thisupd, MemorySegment nextupd) { - var mh$ = OCSP_single_get0_status$MH(); + public static MethodHandle OCSP_single_get0_status$handle() { + return OCSP_single_get0_status.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c + * : * int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime, ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd) + * } + */ + public static MemorySegment OCSP_single_get0_status$address() { + return OCSP_single_get0_status.ADDR; + } + + /** + * {@snippet lang = c + * : * int OCSP_single_get0_status(OCSP_SINGLERESP *single, int *reason, ASN1_GENERALIZEDTIME **revtime, ASN1_GENERALIZEDTIME **thisupd, ASN1_GENERALIZEDTIME **nextupd) + * } + */ + public static int OCSP_single_get0_status(MemorySegment single, MemorySegment reason, MemorySegment revtime, + MemorySegment thisupd, MemorySegment nextupd) { + var mh$ = OCSP_single_get0_status.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_single_get0_status", single, reason, revtime, thisupd, nextupd); } return (int) mh$.invokeExact(single, reason, revtime, thisupd, nextupd); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_BASICRESP_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_BASICRESP_free"), - DESC); - } - return Holder.MH; + private static class OCSP_BASICRESP_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_BASICRESP_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern void OCSP_BASICRESP_free(OCSP_BASICRESP *a) + * Function descriptor for: + * {@snippet lang = c : * extern void OCSP_BASICRESP_free(OCSP_BASICRESP *a) + * } + */ + public static FunctionDescriptor OCSP_BASICRESP_free$descriptor() { + return OCSP_BASICRESP_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern void OCSP_BASICRESP_free(OCSP_BASICRESP *a) + * } + */ + public static MethodHandle OCSP_BASICRESP_free$handle() { + return OCSP_BASICRESP_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern void OCSP_BASICRESP_free(OCSP_BASICRESP *a) + * } + */ + public static MemorySegment OCSP_BASICRESP_free$address() { + return OCSP_BASICRESP_free.ADDR; + } + + /** + * {@snippet lang = c : * extern void OCSP_BASICRESP_free(OCSP_BASICRESP *a) * } */ public static void OCSP_BASICRESP_free(MemorySegment a) { - var mh$ = OCSP_BASICRESP_free$MH(); + var mh$ = OCSP_BASICRESP_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_BASICRESP_free", a); } mh$.invokeExact(a); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_RESPONSE_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_RESPONSE_free"), - DESC); - } - return Holder.MH; + private static class OCSP_RESPONSE_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_RESPONSE_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern void OCSP_RESPONSE_free(OCSP_RESPONSE *a) + * Function descriptor for: + * {@snippet lang = c : * extern void OCSP_RESPONSE_free(OCSP_RESPONSE *a) + * } + */ + public static FunctionDescriptor OCSP_RESPONSE_free$descriptor() { + return OCSP_RESPONSE_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern void OCSP_RESPONSE_free(OCSP_RESPONSE *a) + * } + */ + public static MethodHandle OCSP_RESPONSE_free$handle() { + return OCSP_RESPONSE_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern void OCSP_RESPONSE_free(OCSP_RESPONSE *a) + * } + */ + public static MemorySegment OCSP_RESPONSE_free$address() { + return OCSP_RESPONSE_free.ADDR; + } + + /** + * {@snippet lang = c : * extern void OCSP_RESPONSE_free(OCSP_RESPONSE *a) * } */ public static void OCSP_RESPONSE_free(MemorySegment a) { - var mh$ = OCSP_RESPONSE_free$MH(); + var mh$ = OCSP_RESPONSE_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_RESPONSE_free", a); } mh$.invokeExact(a); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle d2i_OCSP_RESPONSE$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_POINTER, - openssl_h.C_LONG - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("d2i_OCSP_RESPONSE"), - DESC); - } - return Holder.MH; + private static class d2i_OCSP_RESPONSE { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_LONG); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("d2i_OCSP_RESPONSE"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern OCSP_RESPONSE *d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len) + * Function descriptor for: + * {@snippet lang = c + * : * extern OCSP_RESPONSE *d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len) + * } + */ + public static FunctionDescriptor d2i_OCSP_RESPONSE$descriptor() { + return d2i_OCSP_RESPONSE.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c + * : * extern OCSP_RESPONSE *d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len) + * } + */ + public static MethodHandle d2i_OCSP_RESPONSE$handle() { + return d2i_OCSP_RESPONSE.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c + * : * extern OCSP_RESPONSE *d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len) + * } + */ + public static MemorySegment d2i_OCSP_RESPONSE$address() { + return d2i_OCSP_RESPONSE.ADDR; + } + + /** + * {@snippet lang = c + * : * extern OCSP_RESPONSE *d2i_OCSP_RESPONSE(OCSP_RESPONSE **a, const unsigned char **in, long len) * } */ public static MemorySegment d2i_OCSP_RESPONSE(MemorySegment a, MemorySegment in, long len) { - var mh$ = d2i_OCSP_RESPONSE$MH(); + var mh$ = d2i_OCSP_RESPONSE.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("d2i_OCSP_RESPONSE", a, in, len); } return (MemorySegment) mh$.invokeExact(a, in, len); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_CERTID_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_CERTID_free"), - DESC); - } - return Holder.MH; + private static class OCSP_CERTID_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_CERTID_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern void OCSP_CERTID_free(OCSP_CERTID *a) + * Function descriptor for: + * {@snippet lang = c : * extern void OCSP_CERTID_free(OCSP_CERTID *a) + * } + */ + public static FunctionDescriptor OCSP_CERTID_free$descriptor() { + return OCSP_CERTID_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern void OCSP_CERTID_free(OCSP_CERTID *a) + * } + */ + public static MethodHandle OCSP_CERTID_free$handle() { + return OCSP_CERTID_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern void OCSP_CERTID_free(OCSP_CERTID *a) + * } + */ + public static MemorySegment OCSP_CERTID_free$address() { + return OCSP_CERTID_free.ADDR; + } + + /** + * {@snippet lang = c : * extern void OCSP_CERTID_free(OCSP_CERTID *a) * } */ public static void OCSP_CERTID_free(MemorySegment a) { - var mh$ = OCSP_CERTID_free$MH(); + var mh$ = OCSP_CERTID_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_CERTID_free", a); } mh$.invokeExact(a); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_REQUEST_new$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER ); + private static class OCSP_REQUEST_new { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER); - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_REQUEST_new"), - DESC); - } - return Holder.MH; + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_REQUEST_new"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern OCSP_REQUEST *OCSP_REQUEST_new(void) + * Function descriptor for: + * {@snippet lang = c : * extern OCSP_REQUEST *OCSP_REQUEST_new() + * } + */ + public static FunctionDescriptor OCSP_REQUEST_new$descriptor() { + return OCSP_REQUEST_new.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern OCSP_REQUEST *OCSP_REQUEST_new() + * } + */ + public static MethodHandle OCSP_REQUEST_new$handle() { + return OCSP_REQUEST_new.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern OCSP_REQUEST *OCSP_REQUEST_new() + * } + */ + public static MemorySegment OCSP_REQUEST_new$address() { + return OCSP_REQUEST_new.ADDR; + } + + /** + * {@snippet lang = c : * extern OCSP_REQUEST *OCSP_REQUEST_new() * } */ public static MemorySegment OCSP_REQUEST_new() { - var mh$ = OCSP_REQUEST_new$MH(); + var mh$ = OCSP_REQUEST_new.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_REQUEST_new"); } return (MemorySegment) mh$.invokeExact(); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OCSP_REQUEST_free$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OCSP_REQUEST_free"), - DESC); - } - return Holder.MH; + private static class OCSP_REQUEST_free { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OCSP_REQUEST_free"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern void OCSP_REQUEST_free(OCSP_REQUEST *a) + * Function descriptor for: + * {@snippet lang = c : * extern void OCSP_REQUEST_free(OCSP_REQUEST *a) + * } + */ + public static FunctionDescriptor OCSP_REQUEST_free$descriptor() { + return OCSP_REQUEST_free.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern void OCSP_REQUEST_free(OCSP_REQUEST *a) + * } + */ + public static MethodHandle OCSP_REQUEST_free$handle() { + return OCSP_REQUEST_free.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern void OCSP_REQUEST_free(OCSP_REQUEST *a) + * } + */ + public static MemorySegment OCSP_REQUEST_free$address() { + return OCSP_REQUEST_free.ADDR; + } + + /** + * {@snippet lang = c : * extern void OCSP_REQUEST_free(OCSP_REQUEST *a) * } */ public static void OCSP_REQUEST_free(MemorySegment a) { - var mh$ = OCSP_REQUEST_free$MH(); + var mh$ = OCSP_REQUEST_free.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OCSP_REQUEST_free", a); } mh$.invokeExact(a); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle i2d_OCSP_REQUEST$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("i2d_OCSP_REQUEST"), - DESC); - } - return Holder.MH; + private static class i2d_OCSP_REQUEST { + public static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("i2d_OCSP_REQUEST"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * extern int i2d_OCSP_REQUEST(const OCSP_REQUEST *a, unsigned char **out) + * Function descriptor for: + * {@snippet lang = c : * extern int i2d_OCSP_REQUEST(const OCSP_REQUEST *a, unsigned char **out) + * } + */ + public static FunctionDescriptor i2d_OCSP_REQUEST$descriptor() { + return i2d_OCSP_REQUEST.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * extern int i2d_OCSP_REQUEST(const OCSP_REQUEST *a, unsigned char **out) + * } + */ + public static MethodHandle i2d_OCSP_REQUEST$handle() { + return i2d_OCSP_REQUEST.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * extern int i2d_OCSP_REQUEST(const OCSP_REQUEST *a, unsigned char **out) + * } + */ + public static MemorySegment i2d_OCSP_REQUEST$address() { + return i2d_OCSP_REQUEST.ADDR; + } + + /** + * {@snippet lang = c : * extern int i2d_OCSP_REQUEST(const OCSP_REQUEST *a, unsigned char **out) * } */ public static int i2d_OCSP_REQUEST(MemorySegment a, MemorySegment out) { - var mh$ = i2d_OCSP_REQUEST$MH(); + var mh$ = i2d_OCSP_REQUEST.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("i2d_OCSP_REQUEST", a, out); } return (int) mh$.invokeExact(a, out); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } - private static MethodHandle OSSL_PROVIDER_get0_name$MH() { - class Holder { - static final FunctionDescriptor DESC = FunctionDescriptor.of( - openssl_h.C_POINTER, - openssl_h.C_POINTER - ); - - static final MethodHandle MH = Linker.nativeLinker().downcallHandle( - openssl_h.findOrThrow("OSSL_PROVIDER_get0_name"), - DESC); - } - return Holder.MH; + private static class OSSL_PROVIDER_get0_name { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("OSSL_PROVIDER_get0_name"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } /** - * {@snippet lang=c : - * const char *OSSL_PROVIDER_get0_name(const OSSL_PROVIDER *prov) + * Function descriptor for: + * {@snippet lang = c : * const char *OSSL_PROVIDER_get0_name(const OSSL_PROVIDER *prov) + * } + */ + public static FunctionDescriptor OSSL_PROVIDER_get0_name$descriptor() { + return OSSL_PROVIDER_get0_name.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * const char *OSSL_PROVIDER_get0_name(const OSSL_PROVIDER *prov) + * } + */ + public static MethodHandle OSSL_PROVIDER_get0_name$handle() { + return OSSL_PROVIDER_get0_name.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * const char *OSSL_PROVIDER_get0_name(const OSSL_PROVIDER *prov) + * } + */ + public static MemorySegment OSSL_PROVIDER_get0_name$address() { + return OSSL_PROVIDER_get0_name.ADDR; + } + + /** + * {@snippet lang = c : * const char *OSSL_PROVIDER_get0_name(const OSSL_PROVIDER *prov) * } */ public static MemorySegment OSSL_PROVIDER_get0_name(MemorySegment prov) { - var mh$ = OSSL_PROVIDER_get0_name$MH(); + var mh$ = OSSL_PROVIDER_get0_name.HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall("OSSL_PROVIDER_get0_name", prov); } return (MemorySegment) mh$.invokeExact(prov); } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); + throw new AssertionError("should not reach here", ex$); } } + /** - * {@snippet lang=c : - * #define OPENSSL_FILE "/tmp/jextract$7967504926277577155.h" + * {@snippet lang = c : * #define OPENSSL_FILE "jextract$macro.h" * } */ public static MemorySegment OPENSSL_FILE() { class Holder { - static final MemorySegment OPENSSL_FILE - = openssl_h.LIBRARY_ARENA.allocateFrom("/tmp/jextract$7967504926277577155.h"); + static final MemorySegment OPENSSL_FILE = openssl_h.LIBRARY_ARENA.allocateFrom("jextract$macro.h"); } return Holder.OPENSSL_FILE; } - private static final int OPENSSL_LINE = (int)50L; + + private static final int OPENSSL_LINE = (int) 58L; + /** - * {@snippet lang=c : - * #define OPENSSL_LINE 50 + * {@snippet lang = c : * #define OPENSSL_LINE 58 * } */ public static int OPENSSL_LINE() { return OPENSSL_LINE; } + private static final long OPENSSL_INIT_ENGINE_ALL_BUILTIN = 30208L; + /** - * {@snippet lang=c : - * #define OPENSSL_INIT_ENGINE_ALL_BUILTIN 30208 + * {@snippet lang = c : * #define OPENSSL_INIT_ENGINE_ALL_BUILTIN 30208 * } */ public static long OPENSSL_INIT_ENGINE_ALL_BUILTIN() { return OPENSSL_INIT_ENGINE_ALL_BUILTIN; } - private static final int EVP_PKEY_NONE = (int)0L; + + private static final int EVP_PKEY_NONE = (int) 0L; + /** - * {@snippet lang=c : - * #define EVP_PKEY_NONE 0 + * {@snippet lang = c : * #define EVP_PKEY_NONE 0 * } */ public static int EVP_PKEY_NONE() { return EVP_PKEY_NONE; } - private static final int EVP_PKEY_RSA = (int)6L; + + private static final int EVP_PKEY_RSA = (int) 6L; + /** - * {@snippet lang=c : - * #define EVP_PKEY_RSA 6 + * {@snippet lang = c : * #define EVP_PKEY_RSA 6 * } */ public static int EVP_PKEY_RSA() { return EVP_PKEY_RSA; } - private static final int EVP_PKEY_DSA = (int)116L; + + private static final int EVP_PKEY_DSA = (int) 116L; + /** - * {@snippet lang=c : - * #define EVP_PKEY_DSA 116 + * {@snippet lang = c : * #define EVP_PKEY_DSA 116 * } */ public static int EVP_PKEY_DSA() { return EVP_PKEY_DSA; } + /** - * {@snippet lang=c : - * #define PEM_STRING_ECPARAMETERS "EC PARAMETERS" + * {@snippet lang = c : * #define PEM_STRING_ECPARAMETERS "EC PARAMETERS" * } */ public static MemorySegment PEM_STRING_ECPARAMETERS() { class Holder { - static final MemorySegment PEM_STRING_ECPARAMETERS - = openssl_h.LIBRARY_ARENA.allocateFrom("EC PARAMETERS"); + static final MemorySegment PEM_STRING_ECPARAMETERS = openssl_h.LIBRARY_ARENA.allocateFrom("EC PARAMETERS"); } return Holder.PEM_STRING_ECPARAMETERS; } + private static final long SSL_OP_NO_TICKET = 16384L; + /** - * {@snippet lang=c : - * #define SSL_OP_NO_TICKET 16384 + * {@snippet lang = c : * #define SSL_OP_NO_TICKET 16384 * } */ public static long SSL_OP_NO_TICKET() { return SSL_OP_NO_TICKET; } + private static final long SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION = 65536L; + /** - * {@snippet lang=c : - * #define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 65536 + * {@snippet lang = c : * #define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 65536 * } */ public static long SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION() { return SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; } + private static final long SSL_OP_NO_COMPRESSION = 131072L; + /** - * {@snippet lang=c : - * #define SSL_OP_NO_COMPRESSION 131072 + * {@snippet lang = c : * #define SSL_OP_NO_COMPRESSION 131072 * } */ public static long SSL_OP_NO_COMPRESSION() { return SSL_OP_NO_COMPRESSION; } + private static final long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION = 262144L; + /** - * {@snippet lang=c : - * #define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 262144 + * {@snippet lang = c : * #define SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 262144 * } */ public static long SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION() { return SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION; } + private static final long SSL_OP_CIPHER_SERVER_PREFERENCE = 4194304L; + /** - * {@snippet lang=c : - * #define SSL_OP_CIPHER_SERVER_PREFERENCE 4194304 + * {@snippet lang = c : * #define SSL_OP_CIPHER_SERVER_PREFERENCE 4194304 * } */ public static long SSL_OP_CIPHER_SERVER_PREFERENCE() { return SSL_OP_CIPHER_SERVER_PREFERENCE; } + private static final long SSL_OP_NO_SSLv3 = 33554432L; + /** - * {@snippet lang=c : - * #define SSL_OP_NO_SSLv3 33554432 + * {@snippet lang = c : * #define SSL_OP_NO_SSLv3 33554432 * } */ public static long SSL_OP_NO_SSLv3() { return SSL_OP_NO_SSLv3; } + private static final long SSL_OP_NO_TLSv1 = 67108864L; + /** - * {@snippet lang=c : - * #define SSL_OP_NO_TLSv1 67108864 + * {@snippet lang = c : * #define SSL_OP_NO_TLSv1 67108864 * } */ public static long SSL_OP_NO_TLSv1() { return SSL_OP_NO_TLSv1; } + private static final long SSL_OP_NO_TLSv1_2 = 134217728L; + /** - * {@snippet lang=c : - * #define SSL_OP_NO_TLSv1_2 134217728 + * {@snippet lang = c : * #define SSL_OP_NO_TLSv1_2 134217728 * } */ public static long SSL_OP_NO_TLSv1_2() { return SSL_OP_NO_TLSv1_2; } + private static final long SSL_OP_NO_TLSv1_1 = 268435456L; + /** - * {@snippet lang=c : - * #define SSL_OP_NO_TLSv1_1 268435456 + * {@snippet lang = c : * #define SSL_OP_NO_TLSv1_1 268435456 * } */ public static long SSL_OP_NO_TLSv1_1() { return SSL_OP_NO_TLSv1_1; } + private static final long SSL_OP_NO_TLSv1_3 = 536870912L; + /** - * {@snippet lang=c : - * #define SSL_OP_NO_TLSv1_3 536870912 + * {@snippet lang = c : * #define SSL_OP_NO_TLSv1_3 536870912 * } */ public static long SSL_OP_NO_TLSv1_3() { return SSL_OP_NO_TLSv1_3; } + private static final long SSL_OP_ALL = 2147485776L; + /** - * {@snippet lang=c : - * #define SSL_OP_ALL 2147485776 + * {@snippet lang = c : * #define SSL_OP_ALL 2147485776 * } */ public static long SSL_OP_ALL() { return SSL_OP_ALL; } - private static final int ENGINE_METHOD_ALL = (int)65535L; - /** - * {@snippet lang=c : - * #define ENGINE_METHOD_ALL 65535 - * } - */ - public static int ENGINE_METHOD_ALL() { - return ENGINE_METHOD_ALL; - } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/openssl_h_Compatibility.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/openssl_h_Compatibility.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/openssl_h_Compatibility.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/openssl_h_Compatibility.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,16 +33,45 @@ public static final boolean OPENSSL3; public static final boolean BORINGSSL; public static final boolean LIBRESSL; + + public static final int MAJOR; + public static final int MINOR; + static { String versionString = OpenSSL_version(0).getString(0); OPENSSL = versionString.contains("OpenSSL"); OPENSSL3 = OPENSSL && OpenSSL_version_num() >= 0x3000000fL; BORINGSSL = versionString.contains("BoringSSL"); LIBRESSL = versionString.contains("LibreSSL"); + int majorVersion = 0; + int minorVersion = 0; + try { + String[] blocks = versionString.split("\\s"); + if (blocks.length >= 2) { + versionString = blocks[1]; + } + String[] versionNumberStrings = versionString.split("\\."); + if (versionNumberStrings.length >= 2) { + majorVersion = Integer.parseInt(versionNumberStrings[0]); + minorVersion = Integer.parseInt(versionNumberStrings[1]); + } + } catch (Exception e) { + // Ignore, default to 0 + } finally { + MAJOR = majorVersion; + MINOR = minorVersion; + } + } + + public static boolean isLibreSSLPre35() { + return LIBRESSL && ((MAJOR == 3 && MINOR < 5) || MAJOR < 3); } // OpenSSL 1.1 FIPS_mode public static int FIPS_mode() { + if (isLibreSSLPre35()) { + return 0; + } class Holder { static final String NAME = "FIPS_mode"; static final FunctionDescriptor DESC = FunctionDescriptor.of(JAVA_INT); @@ -61,6 +90,9 @@ // OpenSSL 1.1 FIPS_mode_set public static int FIPS_mode_set(int r) { + if (isLibreSSLPre35()) { + return 0; + } class Holder { static final String NAME = "FIPS_mode_set"; static final FunctionDescriptor DESC = FunctionDescriptor.of(JAVA_INT, JAVA_INT); @@ -135,6 +167,58 @@ } } + private static class X509_STORE_CTX_get0_current_issuer { + public static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + public static final MemorySegment ADDR = openssl_h.findOrThrow("X509_STORE_CTX_get0_current_issuer"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * OpenSSL 1.1 X509_STORE_CTX_get0_current_issuer + * Function descriptor for: + * {@snippet lang = c : * X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) + * } + */ + public static FunctionDescriptor X509_STORE_CTX_get0_current_issuer$descriptor() { + return X509_STORE_CTX_get0_current_issuer.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang = c : * X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) + * } + */ + public static MethodHandle X509_STORE_CTX_get0_current_issuer$handle() { + return X509_STORE_CTX_get0_current_issuer.HANDLE; + } + + /** + * Address for: + * {@snippet lang = c : * X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) + * } + */ + public static MemorySegment X509_STORE_CTX_get0_current_issuer$address() { + return X509_STORE_CTX_get0_current_issuer.ADDR; + } + + /** + * {@snippet lang = c : * X509 *X509_STORE_CTX_get0_current_issuer(const X509_STORE_CTX *ctx) + * } + */ + public static MemorySegment X509_STORE_CTX_get0_current_issuer(MemorySegment ctx) { + var mh$ = X509_STORE_CTX_get0_current_issuer.HANDLE; + try { + if (openssl_h.TRACE_DOWNCALLS) { + openssl_h.traceDowncall("X509_STORE_CTX_get0_current_issuer", ctx); + } + return (MemorySegment) mh$.invokeExact(ctx); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + // LibreSSL SSL_CTRL_OPTIONS public static final int SSL_CTRL_OPTIONS = 32; @@ -159,7 +243,7 @@ // LibreSSL SSL_get_options public static long SSL_get_options(MemorySegment s) { if (LIBRESSL) { - return openssl_h.SSL_ctrl(s, SSL_CTRL_OPTIONS, 0, MemorySegment.NULL); + return SSL_ctrl(s, SSL_CTRL_OPTIONS, 0, MemorySegment.NULL); } else { return openssl_h.SSL_get_options(s); } @@ -168,7 +252,7 @@ // LibreSSL SSL_set_options public static long SSL_set_options(MemorySegment s, long op) { if (LIBRESSL) { - return openssl_h.SSL_ctrl(s, SSL_CTRL_OPTIONS, op, MemorySegment.NULL); + return SSL_ctrl(s, SSL_CTRL_OPTIONS, op, MemorySegment.NULL); } else { return openssl_h.SSL_set_options(s, op); } @@ -213,7 +297,8 @@ if (LIBRESSL) { class Holder { static final String NAME = "sk_value"; - static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); } var mh$ = Holder.MH; @@ -237,4 +322,151 @@ } } + /** + * {@snippet lang = c : * long SSL_ctrl(SSL *ssl, int cmd, long larg, void *parg) + * } + */ + public static long SSL_ctrl(MemorySegment ssl, int cmd, long larg, MemorySegment parg) { + class Holder { + static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, + openssl_h.C_INT, openssl_h.C_LONG, openssl_h.C_POINTER); + + static final MethodHandle MH = + Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow("SSL_ctrl"), DESC); + } + var mh$ = Holder.MH; + try { + return (long) mh$.invokeExact(ssl, cmd, larg, parg); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + // OpenSSL 1.x engine APIs + + /** + * {@snippet lang = c : * ENGINE *ENGINE_by_id(const char *id) + * } + */ + public static MemorySegment ENGINE_by_id(MemorySegment id) { + class Holder { + static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER); + + static final MethodHandle MH = + Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow("ENGINE_by_id"), DESC); + } + var mh$ = Holder.MH; + try { + return (MemorySegment) mh$.invokeExact(id); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * {@snippet lang = c : * int ENGINE_register_all_complete(void) + * } + */ + public static int ENGINE_register_all_complete() { + try { + return (int) Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow("ENGINE_register_all_complete"), + FunctionDescriptor.of(JAVA_INT)).invokeExact(); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * {@snippet lang = c + * : * int ENGINE_ctrl_cmd_string(ENGINE *e, const char *cmd_name, const char *arg, int cmd_optional) + * } + */ + public static int ENGINE_ctrl_cmd_string(MemorySegment e, MemorySegment cmd_name, MemorySegment arg, + int cmd_optional) { + class Holder { + static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + + static final MethodHandle MH = + Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow("ENGINE_ctrl_cmd_string"), DESC); + } + var mh$ = Holder.MH; + try { + return (int) mh$.invokeExact(e, cmd_name, arg, cmd_optional); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * {@snippet lang = c : * int ENGINE_free(ENGINE *e) + * } + */ + public static int ENGINE_free(MemorySegment e) { + class Holder { + static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER); + + static final MethodHandle MH = + Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow("ENGINE_free"), DESC); + } + var mh$ = Holder.MH; + try { + return (int) mh$.invokeExact(e); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * {@snippet lang = c + * : * EVP_PKEY *ENGINE_load_private_key(ENGINE *e, const char *key_id, UI_METHOD *ui_method, void *callback_data) + * } + */ + public static MemorySegment ENGINE_load_private_key(MemorySegment e, MemorySegment key_id, MemorySegment ui_method, + MemorySegment callback_data) { + class Holder { + static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_POINTER, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_POINTER); + + static final MethodHandle MH = + Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow("ENGINE_load_private_key"), DESC); + } + var mh$ = Holder.MH; + try { + return (MemorySegment) mh$.invokeExact(e, key_id, ui_method, callback_data); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * {@snippet lang = c : * int ENGINE_set_default(ENGINE *e, unsigned int flags) + * } + */ + public static int ENGINE_set_default(MemorySegment e, int flags) { + class Holder { + static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, openssl_h.C_INT); + + static final MethodHandle MH = + Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow("ENGINE_set_default"), DESC); + } + var mh$ = Holder.MH; + try { + return (int) mh$.invokeExact(e, flags); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + private static final int ENGINE_METHOD_ALL = (int) 65535L; + + /** + * {@snippet lang = c : * #define ENGINE_METHOD_ALL 65535 + * } + */ + public static int ENGINE_METHOD_ALL() { + return ENGINE_METHOD_ALL; + } + } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/openssl_h_Macros.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/openssl_h_Macros.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/openssl_h_Macros.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/openssl_h_Macros.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,7 +46,8 @@ if (openssl_h_Compatibility.BORINGSSL) { class Holder { static final String NAME = "SSL_CTX_set_max_proto_version"; - static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); } var mh$ = Holder.MH; @@ -79,7 +80,8 @@ if (openssl_h_Compatibility.BORINGSSL) { class Holder { static final String NAME = "SSL_CTX_set_min_proto_version"; - static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); } var mh$ = Holder.MH; @@ -144,7 +146,8 @@ if (openssl_h_Compatibility.BORINGSSL) { class Holder { static final String NAME = "SSL_CTX_sess_set_cache_size"; - static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); } var mh$ = Holder.MH; @@ -209,7 +212,8 @@ if (openssl_h_Compatibility.BORINGSSL) { class Holder { static final String NAME = "SSL_CTX_set_session_cache_mode"; - static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); + static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_LONG); static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); } var mh$ = Holder.MH; @@ -242,7 +246,8 @@ if (openssl_h_Compatibility.BORINGSSL) { class Holder { static final String NAME = "SSL_CTX_add0_chain_cert"; - static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_POINTER); + static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_POINTER); static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); } var mh$ = Holder.MH; @@ -276,7 +281,8 @@ if (openssl_h_Compatibility.BORINGSSL) { class Holder { static final String NAME = "SSL_CTX_set_tlsext_ticket_keys"; - static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_LONG); + static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_LONG); static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); } var mh$ = Holder.MH; @@ -345,7 +351,8 @@ if (openssl_h_Compatibility.BORINGSSL) { class Holder { static final String NAME = "SSL_CTX_set_tmp_ecdh"; - static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_POINTER); + static final FunctionDescriptor DESC = + FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_POINTER); static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); } var mh$ = Holder.MH; @@ -364,6 +371,24 @@ /** + * Set automatic dh. + * {@snippet lang = c : # define SSL_CTX_set_dh_auto(ctx, onoff) \ + * SSL_CTX_ctrl(ctx,SSL_CTRL_SET_DH_AUTO,onoff,NULL) + * } + * @param sslCtx the SSL context + * @param onoff 1 to enable + * @return > 0 if successful + */ + public static long SSL_CTX_set_dh_auto(MemorySegment sslCtx, int onoff) { + if (openssl_h_Compatibility.BORINGSSL) { + return 1; + } else { + return SSL_CTX_ctrl(sslCtx, SSL_CTRL_SET_DH_AUTO(), 1, MemorySegment.NULL); + } + } + + + /** * Free memory. * {@snippet lang = c : # define OPENSSL_free(addr) CRYPTO_free(addr, OPENSSL_FILE, OPENSSL_LINE) * } @@ -406,7 +431,8 @@ if (openssl_h_Compatibility.BORINGSSL) { class Holder { static final String NAME = "SSL_CTX_set1_groups"; - static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, openssl_h.C_POINTER, openssl_h.C_INT); + static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, + openssl_h.C_POINTER, openssl_h.C_INT); static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); } var mh$ = Holder.MH; @@ -425,6 +451,41 @@ /** + * Set list of groups in preference order. + * {@snippet lang = c : + * # define SSL_set1_groups_list(s, str) \ + * SSL_ctrl(s,SSL_CTRL_SET_GROUPS_LIST,0,(char *)(str)) + * } + * + * @param sslCtx the SSL context + * @param groupsList the groups list as a String + * + * @return > 0 if successful + */ + public static long SSL_CTX_set1_groups_list(MemorySegment sslCtx, MemorySegment groupsList) { + if (openssl_h_Compatibility.BORINGSSL) { + class Holder { + static final String NAME = "SSL_CTX_set1_groups_list"; + static final FunctionDescriptor DESC = FunctionDescriptor.of(openssl_h.C_LONG, openssl_h.C_POINTER, + openssl_h.C_POINTER); + static final MethodHandle MH = Linker.nativeLinker().downcallHandle(openssl_h.findOrThrow(NAME), DESC); + } + var mh$ = Holder.MH; + try { + if (openssl_h.TRACE_DOWNCALLS) { + openssl_h.traceDowncall(Holder.NAME, sslCtx, groupsList); + } + return (long) mh$.invokeExact(sslCtx, groupsList); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } else { + return SSL_CTX_ctrl(sslCtx, SSL_CTRL_SET_GROUPS_LIST(), 0, groupsList); + } + } + + + /** * Pass a path from which certificates are loaded into the store. * {@snippet lang = c : # define X509_LOOKUP_add_dir(x,name,type) \ * X509_LOOKUP_ctrl((x),X509_L_ADD_DIR,(name),(long)(type),NULL) @@ -460,6 +521,7 @@ /** * Return the d2i_ECPKParameters symbol. + * * @return the symbol */ public static MemorySegment d2i_ECPKParameters$SYMBOL() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/pem_password_cb.java tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/pem_password_cb.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/openssl/pem_password_cb.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/openssl/pem_password_cb.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,7 @@ import java.lang.invoke.MethodHandle; /** - * {@snippet lang=c : - * typedef int (pem_password_cb)(char *, int, int, void *) + * {@snippet lang = c : * typedef int (pem_password_cb)(char *, int, int, void *) * } */ @SuppressWarnings("javadoc") @@ -40,13 +39,8 @@ int apply(MemorySegment buf, int size, int rwflag, MemorySegment userdata); } - private static final FunctionDescriptor $DESC = FunctionDescriptor.of( - openssl_h.C_INT, - openssl_h.C_POINTER, - openssl_h.C_INT, - openssl_h.C_INT, - openssl_h.C_POINTER - ); + private static final FunctionDescriptor $DESC = FunctionDescriptor.of(openssl_h.C_INT, openssl_h.C_POINTER, + openssl_h.C_INT, openssl_h.C_INT, openssl_h.C_POINTER); /** * The descriptor of this function pointer @@ -58,8 +52,8 @@ private static final MethodHandle UP$MH = openssl_h.upcallHandle(pem_password_cb.Function.class, "apply", $DESC); /** - * Allocates a new upcall stub, whose implementation is defined by {@code fi}. - * The lifetime of the returned segment is managed by {@code arena} + * Allocates a new upcall stub, whose implementation is defined by {@code fi}. The lifetime of the returned segment + * is managed by {@code arena} */ public static MemorySegment allocate(pem_password_cb.Function fi, Arena arena) { return Linker.nativeLinker().upcallStub(UP$MH.bindTo(fi), $DESC, arena); @@ -70,7 +64,7 @@ /** * Invoke the upcall stub {@code funcPtr}, with given parameters */ - public static int invoke(MemorySegment funcPtr,MemorySegment buf, int size, int rwflag, MemorySegment userdata) { + public static int invoke(MemorySegment funcPtr, MemorySegment buf, int size, int rwflag, MemorySegment userdata) { try { return (int) DOWN$MH.invokeExact(funcPtr, buf, size, rwflag, userdata); } catch (Throwable ex$) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/res/StringManager.java tomcat10-10.1.52/java/org/apache/tomcat/util/res/StringManager.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/res/StringManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/res/StringManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,15 +38,11 @@ *

    * Please see the documentation for java.util.ResourceBundle for more information. * - * @author James Duncan Davidson [duncan@eng.sun.com] - * @author James Todd [gonzo@eng.sun.com] - * @author Mel Martinez [mmartinez@g1440.com] - * * @see java.util.ResourceBundle */ public class StringManager { - private static int LOCALE_CACHE_SIZE = 10; + private static final int LOCALE_CACHE_SIZE = 10; /** * The ResourceBundle for this StringManager. @@ -81,7 +77,7 @@ if (cl != null) { try { bnd = ResourceBundle.getBundle(bundleName, locale, cl); - } catch (MissingResourceException ex2) { + } catch (MissingResourceException ignore) { // Ignore } } @@ -112,8 +108,7 @@ */ public String getString(String key) { if (key == null) { - String msg = "key may not have a null value"; - throw new IllegalArgumentException(msg); + throw new IllegalArgumentException("key may not have a null value"); } String str = null; @@ -123,7 +118,7 @@ if (bundle != null) { str = bundle.getString(key); } - } catch (MissingResourceException mre) { + } catch (MissingResourceException ignore) { // bad: shouldn't mask an exception the following way: // str = "[cannot find message associated with key '" + key + // "' due to " + mre + "]"; @@ -135,7 +130,6 @@ // better: consistent with container pattern to // simply return null. Calling code can then do // a null check. - str = null; } return str; @@ -176,7 +170,7 @@ // STATIC SUPPORT METHODS // -------------------------------------------------------------- - private static final Map> managers = new HashMap<>(); + private static final Map> managers = new HashMap<>(); /** @@ -188,7 +182,7 @@ * * @return The instance associated with the package of the provide class */ - public static final StringManager getManager(Class clazz) { + public static StringManager getManager(Class clazz) { return getManager(clazz.getPackage().getName()); } @@ -201,7 +195,7 @@ * * @return The instance associated with the given package and the default Locale */ - public static final StringManager getManager(String packageName) { + public static StringManager getManager(String packageName) { return getManager(packageName, Locale.getDefault()); } @@ -215,24 +209,21 @@ * * @return The instance associated with the given package and Locale */ - public static final synchronized StringManager getManager(String packageName, Locale locale) { + public static synchronized StringManager getManager(String packageName, Locale locale) { - Map map = managers.get(packageName); + Map map = managers.get(packageName); if (map == null) { /* * Don't want the HashMap size to exceed LOCALE_CACHE_SIZE. Expansion occurs when size() exceeds capacity. - * Therefore keep size at or below capacity. removeEldestEntry() executes after insertion therefore the test - * for removal needs to use one less than the maximum desired size. Note this is an LRU cache. + * Therefore, keep size at or below capacity. removeEldestEntry() executes after insertion therefore the + * test for removal needs to use one less than the maximum desired size. Note this is an LRU cache. */ map = new LinkedHashMap<>(LOCALE_CACHE_SIZE, 0.75f, true) { private static final long serialVersionUID = 1L; @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - if (size() > (LOCALE_CACHE_SIZE - 1)) { - return true; - } - return false; + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > (LOCALE_CACHE_SIZE - 1); } }; managers.put(packageName, map); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,7 @@ import org.apache.tomcat.Jar; /** - * Base implementation of Jar for implementations that use a JarInputStream to - * access the JAR file. + * Base implementation of Jar for implementations that use a JarInputStream to access the JAR file. */ public abstract class AbstractInputStreamJar implements Jar { @@ -56,7 +55,7 @@ if (jarInputStream == null) { try { reset(); - } catch (IOException e) { + } catch (IOException ioe) { entry = null; return; } @@ -66,10 +65,8 @@ if (multiRelease.booleanValue()) { // Skip base entries where there is a multi-release entry // Skip multi-release entries that are not being used - while (entry != null && - (mrMap.containsKey(entry.getName()) || - entry.getName().startsWith("META-INF/versions/") && - !mrMap.containsValue(entry.getName()))) { + while (entry != null && (mrMap.containsKey(entry.getName()) || + entry.getName().startsWith("META-INF/versions/") && !mrMap.containsValue(entry.getName()))) { entry = jarInputStream.getNextJarEntry(); } } else { @@ -137,12 +134,7 @@ @Override public String getURL(String entry) { - StringBuilder result = new StringBuilder("jar:"); - result.append(getJarFileURL().toExternalForm()); - result.append("!/"); - result.append(entry); - - return result.toString(); + return "jar:" + getJarFileURL().toExternalForm() + "!/" + entry; } @@ -273,8 +265,8 @@ mrMap = new HashMap<>(); for (Entry mrVersion : mrVersions.entrySet()) { - mrMap.put(mrVersion.getKey() , "META-INF/versions/" + mrVersion.getValue().toString() + - "/" + mrVersion.getKey()); + mrMap.put(mrVersion.getKey(), + "META-INF/versions/" + mrVersion.getValue().toString() + "/" + mrVersion.getKey()); } // Reset stream back to the beginning of the JAR diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/Constants.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/Constants.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,10 +24,8 @@ public static final String Package = "org.apache.tomcat.util.scan"; /* System properties */ - public static final String SKIP_JARS_PROPERTY = - "tomcat.util.scan.StandardJarScanFilter.jarsToSkip"; - public static final String SCAN_JARS_PROPERTY = - "tomcat.util.scan.StandardJarScanFilter.jarsToScan"; + public static final String SKIP_JARS_PROPERTY = "tomcat.util.scan.StandardJarScanFilter.jarsToSkip"; + public static final String SCAN_JARS_PROPERTY = "tomcat.util.scan.StandardJarScanFilter.jarsToScan"; /* Commons strings */ public static final String JAR_EXT = ".jar"; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/JarFactory.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/JarFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/JarFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/JarFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,8 +53,7 @@ } - public static URL getJarEntryURL(URL baseUrl, String entryName) - throws MalformedURLException { + public static URL getJarEntryURL(URL baseUrl, String entryName) throws MalformedURLException { String baseExternal = baseUrl.toExternalForm(); @@ -62,8 +61,7 @@ // Assume this is pointing to a JAR file within a WAR. Java doesn't // support jar:jar:file:... so switch to Tomcat's war:file:... baseExternal = baseExternal.replaceFirst("^jar:", "war:"); - baseExternal = baseExternal.replaceFirst("!/", - Matcher.quoteReplacement(UriUtil.getWarSeparator())); + baseExternal = baseExternal.replaceFirst("!/", Matcher.quoteReplacement(UriUtil.getWarSeparator())); } return new URL("jar:" + baseExternal + "!/" + entryName); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/JarFileUrlJar.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/JarFileUrlJar.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/JarFileUrlJar.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/JarFileUrlJar.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,9 +34,8 @@ import org.apache.tomcat.Jar; /** - * Implementation of {@link Jar} that is optimised for file based JAR URLs that - * refer directly to a JAR file (e.g URLs of the form jar:file: ... .jar!/ or - * file:... .jar). + * Implementation of {@link Jar} that is optimised for file based JAR URLs that refer directly to a JAR file (e.g. URLs + * of the form jar:file: ... .jar!/ or file:... .jar). */ public class JarFileUrlJar implements Jar { @@ -68,11 +67,14 @@ boolean multiReleaseValue = false; try { multiReleaseValue = jarFile.isMultiRelease(); - } catch (IllegalStateException e) { - // ISE can be thrown if the JAR URL is bad, for example: - // https://github.com/spring-projects/spring-boot/issues/33633 - // The Javadoc does not document that ISE and given what it does for a vanilla IOE, - // this looks like a Java bug, it should return false instead. + } catch (IllegalStateException ignore) { + /* + * ISE can be thrown if the JAR URL is bad, for example: + * https://github.com/spring-projects/spring-boot/issues/33633 + * + * The Javadoc does not document that ISE and given what it does for a vanilla IOE, this looks like a Java + * bug, it should return false instead. + */ } multiRelease = multiReleaseValue; } @@ -115,12 +117,7 @@ @Override public String getURL(String entry) { - StringBuilder result = new StringBuilder("jar:"); - result.append(getJarFileURL().toExternalForm()); - result.append("!/"); - result.append(entry); - - return result.toString(); + return "jar:" + getJarFileURL().toExternalForm() + "!/" + entry; } @Override @@ -128,7 +125,7 @@ if (jarFile != null) { try { jarFile.close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } @@ -147,15 +144,15 @@ if (multiRelease) { // Need to ensure that: // - the one, correct entry is returned where multiple versions - // are available + // are available // - that the order of entries in the JAR doesn't prevent the - // correct entries being returned + // correct entries being returned // - the case where an entry appears in the versions location - // but not in the the base location is handled correctly + // but not in the base location is handled correctly // Enumerate the entries until one is reached that represents an // entry that has not been seen before. - String name = null; + String name; while (true) { if (entries.hasMoreElements()) { entry = entries.nextElement(); @@ -168,7 +165,7 @@ } name = name.substring(i + 1); } - if (name.length() == 0 || entryNamesSeen.contains(name)) { + if (name.isEmpty() || entryNamesSeen.contains(name)) { continue; } @@ -176,11 +173,10 @@ // JarFile.getJarEntry is version aware so use it entry = jarFile.getJarEntry(entry.getName()); - break; } else { entry = null; - break; } + break; } } else { if (entries.hasMoreElements()) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/JarFileUrlNestedJar.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/JarFileUrlNestedJar.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/JarFileUrlNestedJar.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/JarFileUrlNestedJar.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,8 @@ import java.util.jar.JarFile; /** - * Implementation of {@link org.apache.tomcat.Jar} that is optimised for file - * based JAR URLs that refer to a JAR file nested inside a WAR - * (e.g URLs of the form jar:file: ... .war!/ ... .jar). + * Implementation of {@link org.apache.tomcat.Jar} that is optimised for file based JAR URLs that refer to a JAR file + * nested inside a WAR (e.g. URLs of the form jar:file: ... .war!/ ... .jar). */ public class JarFileUrlNestedJar extends AbstractInputStreamJar { @@ -51,7 +50,7 @@ if (warFile != null) { try { warFile.close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/NonClosingJarInputStream.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/NonClosingJarInputStream.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/NonClosingJarInputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/NonClosingJarInputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,15 +21,13 @@ import java.util.jar.JarInputStream; /** - * When using a {@link JarInputStream} with an XML parser, the stream will be - * closed by the parser. This causes problems if multiple entries from the JAR - * need to be parsed. This implementation makes {{@link #close()} a NO-OP and - * adds {@link #reallyClose()} that will close the stream. + * When using a {@link JarInputStream} with an XML parser, the stream will be closed by the parser. This causes problems + * if multiple entries from the JAR need to be parsed. This implementation makes {{@link #close()} a NO-OP and adds + * {@link #reallyClose()} that will close the stream. */ public class NonClosingJarInputStream extends JarInputStream { - public NonClosingJarInputStream(InputStream in, boolean verify) - throws IOException { + public NonClosingJarInputStream(InputStream in, boolean verify) throws IOException { super(in, verify); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/ReferenceCountedJar.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/ReferenceCountedJar.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/ReferenceCountedJar.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/ReferenceCountedJar.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,8 @@ import org.apache.tomcat.Jar; /** - * This class provides a wrapper around {@link Jar} that uses reference counting - * to close and re-create the wrapped {@link Jar} instance as required. + * This class provides a wrapper around {@link Jar} that uses reference counting to close and re-create the wrapped + * {@link Jar} instance as required. */ public class ReferenceCountedJar implements Jar { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/StandardJarScanFilter.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/StandardJarScanFilter.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/StandardJarScanFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/StandardJarScanFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,7 @@ public class StandardJarScanFilter implements JarScanFilter { - private final ReadWriteLock configurationLock = - new ReentrantReadWriteLock(); + private final ReadWriteLock configurationLock = new ReentrantReadWriteLock(); private static final String defaultSkip; private static final String defaultScan; @@ -60,32 +59,22 @@ private boolean defaultPluggabilityScan = true; /** - * This is the standard implementation of {@link JarScanFilter}. By default, - * the following filtering rules are used: + * This is the standard implementation of {@link JarScanFilter}. By default, the following filtering rules are used: *

      - *
    • JARs that match neither the skip nor the scan list will be included - * in scan results.
    • - *
    • JARs that match the skip list but not the scan list will be excluded - * from scan results.
    • - *
    • JARs that match the scan list will be included from scan results. - *
    • + *
    • JARs that match neither the skip nor the scan list will be included in scan results.
    • + *
    • JARs that match the skip list but not the scan list will be excluded from scan results.
    • + *
    • JARs that match the scan list will be included from scan results.
    • *
    - * The default skip list and default scan list are obtained from the system - * properties {@link Constants#SKIP_JARS_PROPERTY} and - * {@link Constants#SCAN_JARS_PROPERTY} respectively. These default values - * may be over-ridden for the {@link JarScanType#TLD} and - * {@link JarScanType#PLUGGABILITY} scans. The filtering rules may also be - * modified for these scan types using {@link #setDefaultTldScan(boolean)} - * and {@link #setDefaultPluggabilityScan(boolean)}. If set to - * false, the following filtering rules are used for associated - * type: + * The default skip list and default scan list are obtained from the system properties + * {@link Constants#SKIP_JARS_PROPERTY} and {@link Constants#SCAN_JARS_PROPERTY} respectively. These default values + * may be over-ridden for the {@link JarScanType#TLD} and {@link JarScanType#PLUGGABILITY} scans. The filtering + * rules may also be modified for these scan types using {@link #setDefaultTldScan(boolean)} and + * {@link #setDefaultPluggabilityScan(boolean)}. If set to false, the following filtering rules are + * used for associated type: *
      - *
    • JARs that match neither the skip nor the scan list will be excluded - * from scan results.
    • - *
    • JARs that match the scan list but not the skip list will be included - * in scan results.
    • - *
    • JARs that match the skip list will be excluded from scan results. - *
    • + *
    • JARs that match neither the skip nor the scan list will be excluded from scan results.
    • + *
    • JARs that match the scan list but not the skip list will be included in scan results.
    • + *
    • JARs that match the skip list will be excluded from scan results.
    • *
    */ public StandardJarScanFilter() { @@ -224,20 +213,12 @@ } if (defaultScan) { if (Matcher.matchName(toSkip, jarName)) { - if (Matcher.matchName(toScan, jarName)) { - return true; - } else { - return false; - } + return Matcher.matchName(toScan, jarName); } return true; } else { if (Matcher.matchName(toScan, jarName)) { - if (Matcher.matchName(toSkip, jarName)) { - return false; - } else { - return true; - } + return !Matcher.matchName(toSkip, jarName); } return false; } @@ -252,7 +233,7 @@ StringTokenizer tokenizer = new StringTokenizer(attribute, ","); while (tokenizer.hasMoreElements()) { String token = tokenizer.nextToken().trim(); - if (token.length() > 0) { + if (!token.isEmpty()) { set.add(token); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/StandardJarScanner.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/StandardJarScanner.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/StandardJarScanner.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/StandardJarScanner.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,16 +47,13 @@ import org.apache.tomcat.util.res.StringManager; /** - * The default {@link JarScanner} implementation scans the WEB-INF/lib directory - * followed by the provided classloader and then works up the classloader - * hierarchy. This implementation is sufficient to meet the requirements of the - * Servlet 3.0 specification as well as to provide a number of Tomcat specific - * extensions. The extensions are: + * The default {@link JarScanner} implementation scans the WEB-INF/lib directory followed by the provided classloader + * and then works up the classloader hierarchy. This implementation is sufficient to meet the requirements of the + * Servlet 3.0 specification as well as to provide a number of Tomcat specific extensions. The extensions are: *
      - *
    • Scanning the classloader hierarchy (enabled by default)
    • - *
    • Testing all files to see if they are JARs (disabled by default)
    • - *
    • Testing all directories to see if they are exploded JARs - * (disabled by default)
    • + *
    • Scanning the classloader hierarchy (enabled by default)
    • + *
    • Testing all files to see if they are JARs (disabled by default)
    • + *
    • Testing all directories to see if they are exploded JARs (disabled by default)
    • *
    * All of the extensions may be controlled via configuration. */ @@ -87,9 +84,11 @@ * Controls the classpath scanning extension. */ private boolean scanClassPath = true; + public boolean isScanClassPath() { return scanClassPath; } + public void setScanClassPath(boolean scanClassPath) { this.scanClassPath = scanClassPath; } @@ -98,9 +97,11 @@ * Controls the JAR file Manifest scanning extension. */ private boolean scanManifest = true; + public boolean isScanManifest() { return scanManifest; } + public void setScanManifest(boolean scanManifest) { this.scanManifest = scanManifest; } @@ -109,33 +110,38 @@ * Controls the testing all files to see of they are JAR files extension. */ private boolean scanAllFiles = false; + public boolean isScanAllFiles() { return scanAllFiles; } + public void setScanAllFiles(boolean scanAllFiles) { this.scanAllFiles = scanAllFiles; } /** - * Controls the testing all directories to see of they are exploded JAR - * files extension. + * Controls the testing all directories to see of they are exploded JAR files extension. */ private boolean scanAllDirectories = true; + public boolean isScanAllDirectories() { return scanAllDirectories; } + public void setScanAllDirectories(boolean scanAllDirectories) { this.scanAllDirectories = scanAllDirectories; } /** - * Controls the testing of the bootstrap classpath which consists of the - * runtime classes provided by the JVM and any installed system extensions. + * Controls the testing of the bootstrap classpath which consists of the runtime classes provided by the JVM and any + * installed system extensions. */ private boolean scanBootstrapClassPath = false; + public boolean isScanBootstrapClassPath() { return scanBootstrapClassPath; } + public void setScanBootstrapClassPath(boolean scanBootstrapClassPath) { this.scanBootstrapClassPath = scanBootstrapClassPath; } @@ -144,29 +150,28 @@ * Controls the filtering of the results from the scan for JARs */ private JarScanFilter jarScanFilter = new StandardJarScanFilter(); + @Override public JarScanFilter getJarScanFilter() { return jarScanFilter; } + @Override public void setJarScanFilter(JarScanFilter jarScanFilter) { this.jarScanFilter = jarScanFilter; } /** - * Scan the provided ServletContext and class loader for JAR files. Each JAR - * file found will be passed to the callback handler to be processed. + * Scan the provided ServletContext and class loader for JAR files. Each JAR file found will be passed to the + * callback handler to be processed. * - * @param scanType The type of JAR scan to perform. This is passed to - * the filter which uses it to determine how to - * filter the results - * @param context The ServletContext - used to locate and access - * WEB-INF/lib - * @param callback The handler to process any JARs found + * @param scanType The type of JAR scan to perform. This is passed to the filter which uses it to determine how to + * filter the results + * @param context The ServletContext - used to locate and access WEB-INF/lib + * @param callback The handler to process any JARs found */ @Override - public void scan(JarScanType scanType, ServletContext context, - JarScannerCallback callback) { + public void scan(JarScanType scanType, ServletContext context, JarScannerCallback callback) { if (log.isTraceEnabled()) { log.trace(sm.getString("jarScan.webinflibStart")); @@ -183,8 +188,7 @@ if (dirList != null) { for (String path : dirList) { if (path.endsWith(Constants.JAR_EXT) && - getJarScanFilter().check(scanType, - path.substring(path.lastIndexOf('/')+1))) { + getJarScanFilter().check(scanType, path.substring(path.lastIndexOf('/') + 1))) { // Need to scan this JAR if (log.isDebugEnabled()) { log.debug(sm.getString("jarScan.webinflibJarScan", path)); @@ -198,8 +202,8 @@ } else { log.warn(sm.getString("jarScan.webinflibFail", path)); } - } catch (IOException e) { - log.warn(sm.getString("jarScan.webinflibFail", url), e); + } catch (IOException ioe) { + log.warn(sm.getString("jarScan.webinflibFail", url), ioe); } } else { if (log.isTraceEnabled()) { @@ -223,8 +227,8 @@ if (url != null) { try { callback.scanWebInfClasses(); - } catch (IOException e) { - log.warn(sm.getString("jarScan.webinfclassesFail"), e); + } catch (IOException ioe) { + log.warn(sm.getString("jarScan.webinfclassesFail"), ioe); } } } @@ -240,8 +244,8 @@ } - protected void doScanClassPath(JarScanType scanType, ServletContext context, - JarScannerCallback callback, Set processedURLs) { + protected void doScanClassPath(JarScanType scanType, ServletContext context, JarScannerCallback callback, + Set processedURLs) { if (log.isTraceEnabled()) { log.trace(sm.getString("jarScan.classloaderStart")); } @@ -269,8 +273,7 @@ isWebapp = isWebappClassLoader(classLoader); } - classPathUrlsToProcess.addAll( - Arrays.asList(((URLClassLoader) classLoader).getURLs())); + classPathUrlsToProcess.addAll(Arrays.asList(((URLClassLoader) classLoader).getURLs())); processURLs(scanType, callback, processedURLs, isWebapp, classPathUrlsToProcess); } @@ -298,8 +301,8 @@ } - protected void processURLs(JarScanType scanType, JarScannerCallback callback, - Set processedURLs, boolean isWebapp, Deque classPathUrlsToProcess) { + protected void processURLs(JarScanType scanType, JarScannerCallback callback, Set processedURLs, + boolean isWebapp, Deque classPathUrlsToProcess) { if (jarScanFilter.isSkipAll()) { return; @@ -319,11 +322,8 @@ // Directories are scanned for pluggability scans or // if scanAllDirectories is enabled unless the // filter says not to. - if ((cpe.isJar() || - scanType == JarScanType.PLUGGABILITY || - isScanAllDirectories()) && - getJarScanFilter().check(scanType, - cpe.getName())) { + if ((cpe.isJar() || scanType == JarScanType.PLUGGABILITY || isScanAllDirectories()) && + getJarScanFilter().check(scanType, cpe.getName())) { if (log.isDebugEnabled()) { log.debug(sm.getString("jarScan.classloaderJarScan", url)); } @@ -346,7 +346,7 @@ protected void addClassPath(Deque classPathUrlsToProcess) { String classPath = System.getProperty("java.class.path"); - if (classPath == null || classPath.length() == 0) { + if (classPath == null || classPath.isEmpty()) { return; } @@ -363,18 +363,14 @@ /* - * Since class loader hierarchies can get complicated, this method attempts - * to apply the following rule: A class loader is a web application class - * loader unless it loaded this class (StandardJarScanner) or is a parent - * of the class loader that loaded this class. + * Since class loader hierarchies can get complicated, this method attempts to apply the following rule: A class + * loader is a web application class loader unless it loaded this class (StandardJarScanner) or is a parent of the + * class loader that loaded this class. * - * This should mean: - * the webapp class loader is an application class loader - * the shared class loader is an application class loader - * the server class loader is not an application class loader - * the common class loader is not an application class loader - * the system class loader is not an application class loader - * the bootstrap class loader is not an application class loader + * This should mean: the webapp class loader is an application class loader the shared class loader is an + * application class loader the server class loader is not an application class loader the common class loader is + * not an application class loader the system class loader is not an application class loader the bootstrap class + * loader is not an application class loader */ private static boolean isWebappClassLoader(ClassLoader classLoader) { return !CLASSLOADER_HIERARCHY.contains(classLoader); @@ -382,12 +378,10 @@ /* - * Scan a URL for JARs with the optional extensions to look at all files - * and all directories. + * Scan a URL for JARs with the optional extensions to look at all files and all directories. */ - protected void process(JarScanType scanType, JarScannerCallback callback, - URL url, String webappPath, boolean isWebapp, Deque classPathUrlsToProcess) - throws IOException { + protected void process(JarScanType scanType, JarScannerCallback callback, URL url, String webappPath, + boolean isWebapp, Deque classPathUrlsToProcess) throws IOException { if (log.isTraceEnabled()) { log.trace(sm.getString("jarScan.jarUrlStart", url)); @@ -432,8 +426,7 @@ } - private void processManifest(Jar jar, boolean isWebapp, - Deque classPathUrlsToProcess) throws IOException { + private void processManifest(Jar jar, boolean isWebapp, Deque classPathUrlsToProcess) throws IOException { // Not processed for web application JARs nor if the caller did not // provide a Deque of URLs to append to. @@ -451,7 +444,7 @@ String[] classPathEntries = classPathAttribute.split(" "); for (String classPathEntry : classPathEntries) { classPathEntry = classPathEntry.trim(); - if (classPathEntry.length() == 0) { + if (classPathEntry.isEmpty()) { continue; } URL jarURL = jar.getJarFileURL(); @@ -459,14 +452,11 @@ try { URI jarURI = jarURL.toURI(); /* - * Note: Resolving the relative URLs from the manifest has the - * potential to introduce security concerns. However, since - * only JARs provided by the container and NOT those provided - * by web applications are processed, there should be no - * issues. - * If this feature is ever extended to include JARs provided - * by web applications, checks should be added to ensure that - * any relative URL does not step outside the web application. + * Note: Resolving the relative URLs from the manifest has the potential to introduce security + * concerns. However, since only JARs provided by the container and NOT those provided by web + * applications are processed, there should be no issues. If this feature is ever extended to + * include JARs provided by web applications, checks should be added to ensure that any relative URL + * does not step outside the web application. */ URI classPathEntryURI = jarURI.resolve(classPathEntry); classPathEntryURL = classPathEntryURI.toURL(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/scan/UrlJar.java tomcat10-10.1.52/java/org/apache/tomcat/util/scan/UrlJar.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/scan/UrlJar.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/scan/UrlJar.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,7 @@ import java.net.URLConnection; /** - * Implementation of {@link org.apache.tomcat.Jar} that is optimised for - * non-file based JAR URLs. + * Implementation of {@link org.apache.tomcat.Jar} that is optimised for non-file based JAR URLs. */ public class UrlJar extends AbstractInputStreamJar { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/security/ConcurrentMessageDigest.java tomcat10-10.1.52/java/org/apache/tomcat/util/security/ConcurrentMessageDigest.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/security/ConcurrentMessageDigest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/security/ConcurrentMessageDigest.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,22 +23,24 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.res.StringManager; /** - * A thread safe wrapper around {@link MessageDigest} that does not make use - * of ThreadLocal and - broadly - only creates enough MessageDigest objects - * to satisfy the concurrency requirements. + * A thread safe wrapper around {@link MessageDigest} that does not make use of ThreadLocal and - broadly - only creates + * enough MessageDigest objects to satisfy the concurrency requirements. */ public class ConcurrentMessageDigest { private static final StringManager sm = StringManager.getManager(ConcurrentMessageDigest.class); + private static final Log log = LogFactory.getLog(ConcurrentMessageDigest.class); private static final String MD5 = "MD5"; private static final String SHA1 = "SHA-1"; + private static final String SHA256 = "SHA-256"; - private static final Map> queues = - new ConcurrentHashMap<>(); + private static final Map> queues = new ConcurrentHashMap<>(); private ConcurrentMessageDigest() { @@ -46,13 +48,22 @@ } static { + // Init commonly used algorithms try { - // Init commonly used algorithms init(MD5); + } catch (NoSuchAlgorithmException e) { + log.warn(sm.getString("concurrentMessageDigest.noDigest"), e); + } + try { init(SHA1); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(sm.getString("concurrentMessageDigest.noDigest"), e); } + try { + init(SHA256); + } catch (NoSuchAlgorithmException e) { + throw new IllegalArgumentException(sm.getString("concurrentMessageDigest.noDigest"), e); + } } public static byte[] digestMD5(byte[]... input) { @@ -63,6 +74,10 @@ return digest(SHA1, input); } + public static byte[] digestSHA256(byte[]... input) { + return digest(SHA256, input); + } + public static byte[] digest(String algorithm, byte[]... input) { return digest(algorithm, 1, input); } @@ -107,14 +122,12 @@ /** - * Ensures that {@link #digest(String, byte[][])} will support the specified - * algorithm. This method must be called and return successfully - * before using {@link #digest(String, byte[][])}. + * Ensures that {@link #digest(String, byte[][])} will support the specified algorithm. This method must be + * called and return successfully before using {@link #digest(String, byte[][])}. * * @param algorithm The message digest algorithm to be supported * - * @throws NoSuchAlgorithmException If the algorithm is not supported by the - * JVM + * @throws NoSuchAlgorithmException If the algorithm is not supported by the JVM */ public static void init(String algorithm) throws NoSuchAlgorithmException { if (!queues.containsKey(algorithm)) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/security/Escape.java tomcat10-10.1.52/java/org/apache/tomcat/util/security/Escape.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/security/Escape.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/security/Escape.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,9 +17,8 @@ package org.apache.tomcat.util.security; /** - * Provides utility methods to escape content for different contexts. It is - * critical that the escaping used is correct for the context in which the data - * is to be used. + * Provides utility methods to escape content for different contexts. It is critical that the escaping used is correct + * for the context in which the data is to be used. */ public class Escape { @@ -29,19 +28,16 @@ /** - * Escape content for use in HTML. This escaping is suitable for the - * following uses: + * Escape content for use in HTML. This escaping is suitable for the following uses: *
      - *
    • Element content when the escaped data will be placed directly inside - * tags such as <p>, <td> etc.
    • - *
    • Attribute values when the attribute value is quoted with " or - * '.
    • + *
    • Element content when the escaped data will be placed directly inside tags such as <p>, <td> + * etc.
    • + *
    • Attribute values when the attribute value is quoted with " or '.
    • *
    * - * @param content The content to escape + * @param content The content to escape * - * @return The escaped content or {@code null} if the content was - * {@code null} + * @return The escaped content or {@code null} if the content was {@code null} */ public static String htmlElementContent(String content) { if (content == null) { @@ -74,34 +70,27 @@ /** - * Convert the object to a string via {@link Object#toString()} and HTML - * escape the resulting string for use in HTML content. + * Convert the object to a string via {@link Object#toString()} and HTML escape the resulting string for use in HTML + * content. * - * @param obj The object to convert to String and then escape + * @param obj The object to convert to String and then escape * - * @return The escaped content or "?" if obj is - * {@code null} + * @return The escaped content or "?" if obj is {@code null} */ public static String htmlElementContent(Object obj) { if (obj == null) { return "?"; } - - try { - return htmlElementContent(obj.toString()); - } catch (Exception e) { - return null; - } + return htmlElementContent(obj.toString()); } /** * Escape content for use in XML. * - * @param content The content to escape + * @param content The content to escape * - * @return The escaped content or {@code null} if the content was - * {@code null} + * @return The escaped content or {@code null} if the content was {@code null} */ public static String xml(String content) { return xml(null, content); @@ -111,11 +100,10 @@ /** * Escape content for use in XML. * - * @param ifNull The value to return if content is {@code null} - * @param content The content to escape + * @param ifNull The value to return if content is {@code null} + * @param content The content to escape * - * @return The escaped content or the value of {@code ifNull} if the - * content was {@code null} + * @return The escaped content or the value of {@code ifNull} if the content was {@code null} */ public static String xml(String ifNull, String content) { return xml(ifNull, false, content); @@ -125,12 +113,11 @@ /** * Escape content for use in XML. * - * @param ifNull The value to return if content is {@code null} - * @param escapeCRLF Should CR and LF also be escaped? - * @param content The content to escape + * @param ifNull The value to return if content is {@code null} + * @param escapeCRLF Should CR and LF also be escaped? + * @param content The content to escape * - * @return The escaped content or the value of ifNull if the content was - * {@code null} + * @return The escaped content or the value of ifNull if the content was {@code null} */ public static String xml(String ifNull, boolean escapeCRLF, String content) { if (content == null) { @@ -160,6 +147,6 @@ } } - return (sb.length() > content.length()) ? sb.toString(): content; + return (sb.length() > content.length()) ? sb.toString() : content; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/security/KeyStoreUtil.java tomcat10-10.1.52/java/org/apache/tomcat/util/security/KeyStoreUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/security/KeyStoreUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/security/KeyStoreUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,23 +32,17 @@ /** * Loads a KeyStore from an InputStream working around the known JDK bug - * https://bugs.openjdk.java.net/browse/JDK-8157404. + * https://bugs.openjdk.java.net/browse/JDK-8157404. This code can be removed once the minimum Java version for + * Tomcat is 13. * - * This code can be removed once the minimum Java version for Tomcat is 13. - * - * - * @param keystore The KeyStore to load from the InputStream - * @param is The InputStream to use to populate the KeyStore + * @param keystore The KeyStore to load from the InputStream + * @param is The InputStream to use to populate the KeyStore * @param storePass The password to access the KeyStore * - * @throws IOException - * If an I/O occurs reading from the given InputStream - * @throws CertificateException - * If one or more certificates can't be loaded into the - * KeyStore - * @throws NoSuchAlgorithmException - * If the algorithm specified to validate the integrity of the - * KeyStore cannot be found + * @throws IOException If an I/O occurs reading from the given InputStream + * @throws CertificateException If one or more certificates can't be loaded into the KeyStore + * @throws NoSuchAlgorithmException If the algorithm specified to validate the integrity of the KeyStore cannot be + * found */ public static void load(KeyStore keystore, InputStream is, char[] storePass) throws NoSuchAlgorithmException, CertificateException, IOException { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/security/MD5Encoder.java tomcat10-10.1.52/java/org/apache/tomcat/util/security/MD5Encoder.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/security/MD5Encoder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/security/MD5Encoder.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,11 +21,8 @@ /** * Encode an MD5 digest into a String. *

    - * The 128 bit MD5 hash is converted into a 32 character long String. - * Each character of the String is the hexadecimal representation of 4 bits - * of the digest. - * - * @author Remy Maucherat + * The 128 bit MD5 hash is converted into a 32 character long String. Each character of the String is the hexadecimal + * representation of 4 bits of the digest. * * @deprecated Unused. Use {@link HexUtils} instead. Will be removed in Tomcat 11. */ @@ -38,8 +35,8 @@ } - private static final char[] hexadecimal = {'0', '1', '2', '3', '4', '5', - '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + private static final char[] hexadecimal = + { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** @@ -57,11 +54,11 @@ char[] buffer = new char[32]; - for (int i=0; i<16; i++) { + for (int i = 0; i < 16; i++) { int low = binaryData[i] & 0x0f; int high = (binaryData[i] & 0xf0) >> 4; - buffer[i*2] = hexadecimal[high]; - buffer[i*2 + 1] = hexadecimal[low]; + buffer[i * 2] = hexadecimal[high]; + buffer[i * 2 + 1] = hexadecimal[low]; } return new String(buffer); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/security/PermissionCheck.java tomcat10-10.1.52/java/org/apache/tomcat/util/security/PermissionCheck.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/security/PermissionCheck.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/security/PermissionCheck.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,15 +19,12 @@ import java.security.Permission; /** - * This interface is implemented by components to enable privileged code to - * check whether the component has a given permission. - * This is typically used when a privileged component (e.g. the container) is - * performing an action on behalf of an untrusted component (e.g. a web - * application) without the current thread having passed through a code source - * provided by the untrusted component. Because the current thread has not - * passed through a code source provided by the untrusted component the - * SecurityManager assumes the code is trusted so the standard checking - * mechanisms can't be used. + * This interface is implemented by components to enable privileged code to check whether the component has a given + * permission. This is typically used when a privileged component (e.g. the container) is performing an action on behalf + * of an untrusted component (e.g. a web application) without the current thread having passed through a code source + * provided by the untrusted component. Because the current thread has not passed through a code source provided by the + * untrusted component the SecurityManager assumes the code is trusted so the standard checking mechanisms can't be + * used. */ public interface PermissionCheck { @@ -36,8 +33,8 @@ * * @param permission The permission to test * - * @return {@code false} if a SecurityManager is enabled and the component - * does not have the given permission, otherwise {@code true} + * @return {@code false} if a SecurityManager is enabled and the component does not have the given permission, + * otherwise {@code true} */ boolean check(Permission permission); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/security/PrivilegedSetAccessControlContext.java tomcat10-10.1.52/java/org/apache/tomcat/util/security/PrivilegedSetAccessControlContext.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/security/PrivilegedSetAccessControlContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/security/PrivilegedSetAccessControlContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -57,7 +57,7 @@ public Void run() { try { if (field != null) { - field.set(t, acc); + field.set(t, acc); } } catch (IllegalArgumentException | IllegalAccessException e) { log.warn(sm.getString("privilegedSetAccessControlContext.setFailed"), e); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/InlineExecutorService.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/InlineExecutorService.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/InlineExecutorService.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/InlineExecutorService.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,7 +40,7 @@ @Override public List shutdownNow() { shutdown(); - return null; + return List.of(); } @Override @@ -55,11 +55,17 @@ @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { + long timeoutExpiry = System.nanoTime() + unit.toNanos(timeout); + long timeoutMillis = unit.toMillis(timeout); synchronized (lock) { - if (terminated) { - return true; + /* + * Spurious wake-ups are possible. Keep waiting until the service has been terminated or the timeout has + * expired. + */ + while (!terminated && timeoutMillis > 0) { + lock.wait(timeoutMillis); + timeoutMillis = (timeoutExpiry - System.nanoTime()) / 1_000_000; } - lock.wait(unit.toMillis(timeout)); return terminated; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/LimitLatch.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/LimitLatch.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/LimitLatch.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/LimitLatch.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,9 +25,8 @@ import org.apache.tomcat.util.res.StringManager; /** - * Shared latch that allows the latch to be acquired a limited number of times - * after which all subsequent requests to acquire the latch will be placed in a - * FIFO queue until one of the shares is returned. + * Shared latch that allows the latch to be acquired a limited number of times after which all subsequent requests to + * acquire the latch will be placed in a FIFO queue until one of the shares is returned. */ public class LimitLatch { @@ -69,6 +68,7 @@ /** * Instantiates a LimitLatch object with an initial limit. + * * @param limit - maximum number of concurrent acquisitions of this latch */ public LimitLatch(long limit) { @@ -79,6 +79,7 @@ /** * Returns the current count for the latch + * * @return the current count for latch */ public long getCount() { @@ -87,6 +88,7 @@ /** * Obtain the current limit. + * * @return the limit */ public long getLimit() { @@ -95,13 +97,11 @@ /** - * Sets a new limit. If the limit is decreased there may be a period where - * more shares of the latch are acquired than the limit. In this case no - * more shares of the latch will be issued until sufficient shares have been - * returned to reduce the number of acquired shares of the latch to below - * the new limit. If the limit is increased, threads currently in the queue - * may not be issued one of the newly available shares until the next - * request is made for a latch. + * Sets a new limit. If the limit is decreased there may be a period where more shares of the latch are acquired + * than the limit. In this case no more shares of the latch will be issued until sufficient shares have been + * returned to reduce the number of acquired shares of the latch to below the new limit. If the limit is increased, + * threads currently in the queue may not be issued one of the newly available shares until the next request is made + * for a latch. * * @param limit The new limit */ @@ -111,33 +111,34 @@ /** - * Acquires a shared latch if one is available or waits for one if no shared - * latch is current available. + * Acquires a shared latch if one is available or waits for one if no shared latch is current available. + * * @throws InterruptedException If the current thread is interrupted */ public void countUpOrAwait() throws InterruptedException { if (log.isTraceEnabled()) { - log.trace("Counting up["+Thread.currentThread().getName()+"] latch="+getCount()); + log.trace("Counting up[" + Thread.currentThread().getName() + "] latch=" + getCount()); } sync.acquireSharedInterruptibly(1); } /** * Releases a shared latch, making it available for another thread to use. + * * @return the previous counter value */ public long countDown() { sync.releaseShared(0); long result = getCount(); if (log.isTraceEnabled()) { - log.trace("Counting down["+Thread.currentThread().getName()+"] latch="+result); + log.trace("Counting down[" + Thread.currentThread().getName() + "] latch=" + result); } return result; } /** - * Releases all waiting threads and causes the {@link #limit} to be ignored - * until {@link #reset()} is called. + * Releases all waiting threads and causes the {@link #limit} to be ignored until {@link #reset()} is called. + * * @return true if release was done */ public boolean releaseAll() { @@ -147,6 +148,7 @@ /** * Resets the latch and initializes the shared acquisition counter to zero. + * * @see #releaseAll() */ public void reset() { @@ -155,8 +157,9 @@ } /** - * Returns true if there is at least one thread waiting to - * acquire the shared lock, otherwise returns false. + * Returns true if there is at least one thread waiting to acquire the shared lock, otherwise returns + * false. + * * @return true if threads are waiting */ public boolean hasQueuedThreads() { @@ -164,8 +167,8 @@ } /** - * Provide access to the list of threads waiting to acquire this limited - * shared latch. + * Provide access to the list of threads waiting to acquire this limited shared latch. + * * @return a collection of threads */ public Collection getQueuedThreads() { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/tomcat/util/threads/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -18,5 +18,9 @@ taskQueue.notRunning=执行器未运行,无法强制命令进入队列 +threadPoolExecutor.invalidKeepAlive=核心线程必须有正数的存活时间 threadPoolExecutor.queueFull=队列容量已满 +threadPoolExecutor.taskRejected=任务[{0}]被[{1}]拒绝 threadPoolExecutor.threadStoppedToAvoidPotentialLeak=停止线程[{0}],从而避免context停止后的潜在的内存泄漏 + +virtualThreadExecutor.taskRejected=任务[{0}]被[{1}]拒绝 diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/ResizableExecutor.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/ResizableExecutor.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/ResizableExecutor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/ResizableExecutor.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,8 +30,7 @@ int getMaxThreads(); /** - * Returns the approximate number of threads that are actively executing - * tasks. + * Returns the approximate number of threads that are actively executing tasks. * * @return the number of threads */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/RetryableQueue.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/RetryableQueue.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/RetryableQueue.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/RetryableQueue.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * 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.threads; + +import java.util.concurrent.BlockingQueue; + +public interface RetryableQueue extends BlockingQueue { + + /** + * Used to add a task to the queue if the task has been rejected by the Executor. + * + * @param o The task to add to the queue + * + * @return {@code true} if the task was added to the queue, otherwise {@code false} + */ + boolean force(T o); +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/ScheduledThreadPoolExecutor.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,7 @@ import java.util.concurrent.TimeoutException; /** - * Class which wraps a ScheduledExecutorService, while preventing - * lifecycle and configuration operations. + * Class which wraps a ScheduledExecutorService, while preventing lifecycle and configuration operations. */ public class ScheduledThreadPoolExecutor implements ScheduledExecutorService { @@ -37,6 +36,7 @@ /** * Builds a wrapper for the given executor. + * * @param executor the wrapped executor */ public ScheduledThreadPoolExecutor(ScheduledExecutorService executor) { @@ -65,8 +65,7 @@ } @Override - public boolean awaitTermination(long timeout, TimeUnit unit) - throws InterruptedException { + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return executor.awaitTermination(timeout, unit); } @@ -86,26 +85,23 @@ } @Override - public List> invokeAll(Collection> tasks) - throws InterruptedException { + public List> invokeAll(Collection> tasks) throws InterruptedException { return executor.invokeAll(tasks); } @Override - public List> invokeAll(Collection> tasks, long timeout, - TimeUnit unit) throws InterruptedException { + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { return executor.invokeAll(tasks, timeout, unit); } @Override - public T invokeAny(Collection> tasks) - throws InterruptedException, ExecutionException { + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { return executor.invokeAny(tasks); } @Override - public T invokeAny(Collection> tasks, - long timeout, TimeUnit unit) + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return executor.invokeAny(tasks, timeout, unit); } @@ -116,26 +112,22 @@ } @Override - public ScheduledFuture schedule(Runnable command, long delay, - TimeUnit unit) { + public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { return executor.schedule(command, delay, unit); } @Override - public ScheduledFuture schedule(Callable callable, long delay, - TimeUnit unit) { + public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { return executor.schedule(callable, delay, unit); } @Override - public ScheduledFuture scheduleAtFixedRate(Runnable command, - long initialDelay, long period, TimeUnit unit) { + public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { return executor.scheduleAtFixedRate(command, initialDelay, period, unit); } @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable command, - long initialDelay, long delay, TimeUnit unit) { + public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { return executor.scheduleWithFixedDelay(command, initialDelay, delay, unit); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/StopPooledThreadException.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/StopPooledThreadException.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/StopPooledThreadException.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/StopPooledThreadException.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,8 +18,8 @@ /** - * A custom {@link RuntimeException} thrown by the {@link ThreadPoolExecutor} - * to signal that the thread should be disposed of. + * A custom {@link RuntimeException} thrown by the {@link ThreadPoolExecutor} to signal that the thread should be + * disposed of. */ public class StopPooledThreadException extends RuntimeException { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/TaskQueue.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/TaskQueue.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/TaskQueue.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/TaskQueue.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,13 +24,11 @@ import org.apache.tomcat.util.res.StringManager; /** - * As task queue specifically designed to run with a thread pool executor. The - * task queue is optimised to properly utilize threads within a thread pool - * executor. If you use a normal queue, the executor will spawn threads when - * there are idle threads and you won't be able to force items onto the queue - * itself. + * As task queue specifically designed to run with a thread pool executor. The task queue is optimised to properly + * utilize threads within a thread pool executor. If you use a normal queue, the executor will spawn threads when there + * are idle threads and you won't be able to force items onto the queue itself. */ -public class TaskQueue extends LinkedBlockingQueue { +public class TaskQueue extends LinkedBlockingQueue implements RetryableQueue { private static final long serialVersionUID = 1L; protected static final StringManager sm = StringManager.getManager(TaskQueue.class); @@ -54,48 +52,40 @@ } - /** - * Used to add a task to the queue if the task has been rejected by the Executor. - * - * @param o The task to add to the queue - * - * @return {@code true} if the task was added to the queue, - * otherwise {@code false} - */ + @Override public boolean force(Runnable o) { if (parent == null || parent.isShutdown()) { throw new RejectedExecutionException(sm.getString("taskQueue.notRunning")); } - return super.offer(o); //forces the item onto the queue, to be used if the task is rejected + return super.offer(o); // forces the item onto the queue, to be used if the task is rejected } @Override public boolean offer(Runnable o) { - //we can't do any checks - if (parent==null) { + // we can't do any checks + if (parent == null) { return super.offer(o); } - //we are maxed out on threads, simply queue the object + // we are maxed out on threads, simply queue the object if (parent.getPoolSizeNoLock() == parent.getMaximumPoolSize()) { return super.offer(o); } - //we have idle threads, just add it to the queue + // we have idle threads, just add it to the queue if (parent.getSubmittedCount() <= parent.getPoolSizeNoLock()) { return super.offer(o); } - //if we have less threads than maximum force creation of a new thread + // if we have less threads than maximum force creation of a new thread if (parent.getPoolSizeNoLock() < parent.getMaximumPoolSize()) { return false; } - //if we reached here, we need to add it to the queue + // if we reached here, we need to add it to the queue return super.offer(o); } @Override - public Runnable poll(long timeout, TimeUnit unit) - throws InterruptedException { + public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException { Runnable runnable = super.poll(timeout, unit); if (runnable == null && parent != null) { // the poll timed out, it gives an opportunity to stop the current @@ -108,8 +98,7 @@ @Override public Runnable take() throws InterruptedException { if (parent != null && parent.currentThreadShouldBeStopped()) { - return poll(parent.getKeepAliveTime(TimeUnit.MILLISECONDS), - TimeUnit.MILLISECONDS); + return poll(parent.getKeepAliveTime(TimeUnit.MILLISECONDS), TimeUnit.MILLISECONDS); // yes, this may return null (in case of timeout) which normally // does not occur with take() // but the ThreadPoolExecutor implementation allows this diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/TaskThread.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/TaskThread.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/TaskThread.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/TaskThread.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,7 +22,6 @@ /** * A Thread implementation that records the time at which it was created. - * */ public class TaskThread extends Thread { @@ -35,8 +34,7 @@ this.creationTime = System.currentTimeMillis(); } - public TaskThread(ThreadGroup group, Runnable target, String name, - long stackSize) { + public TaskThread(ThreadGroup group, Runnable target, String name, long stackSize) { super(group, new WrappingRunnable(target), name, stackSize); this.creationTime = System.currentTimeMillis(); } @@ -49,25 +47,27 @@ } /** - * Wraps a {@link Runnable} to swallow any {@link StopPooledThreadException} - * instead of letting it go and potentially trigger a break in a debugger. + * Wraps a {@link Runnable} to swallow any {@link StopPooledThreadException} instead of letting it go and + * potentially trigger a break in a debugger. */ private static class WrappingRunnable implements Runnable { - private Runnable wrappedRunnable; + private final Runnable wrappedRunnable; + WrappingRunnable(Runnable wrappedRunnable) { this.wrappedRunnable = wrappedRunnable; } + @Override public void run() { try { wrappedRunnable.run(); - } catch(StopPooledThreadException exc) { - //expected : we just swallow the exception to avoid disturbing - //debuggers like eclipse's - log.debug(sm.getString("taskThread.exiting"), exc); + } catch (StopPooledThreadException exc) { + // expected : we just swallow the exception to avoid disturbing debuggers like eclipse's + if (log.isDebugEnabled()) { + log.debug(sm.getString("taskThread.exiting"), exc); + } } } - } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/TaskThreadFactory.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/TaskThreadFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/TaskThreadFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/TaskThreadFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,7 @@ import org.apache.tomcat.util.security.PrivilegedSetTccl; /** - * Simple task thread factory to use to create threads for an executor - * implementation. + * Simple task thread factory to use to create threads for an executor implementation. */ public class TaskThreadFactory implements ThreadFactory { @@ -54,8 +53,7 @@ // Set the context class loader of newly created threads to be the // class loader that loaded this factory. This avoids retaining // references to web application class loaders and similar. - PrivilegedAction pa = new PrivilegedSetTccl( - t, getClass().getClassLoader()); + PrivilegedAction pa = new PrivilegedSetTccl(t, getClass().getClassLoader()); AccessController.doPrivileged(pa); // This method may be triggered from an InnocuousThread. Ensure that diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/ThreadPoolExecutor.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.ConcurrentModificationException; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.BlockingQueue; @@ -45,244 +44,134 @@ import org.apache.tomcat.util.res.StringManager; /** - * An {@link java.util.concurrent.ExecutorService} - * that executes each submitted task using - * one of possibly several pooled threads, normally configured - * using {@link Executors} factory methods. - * - *

    Thread pools address two different problems: they usually - * provide improved performance when executing large numbers of - * asynchronous tasks, due to reduced per-task invocation overhead, - * and they provide a means of bounding and managing the resources, - * including threads, consumed when executing a collection of tasks. - * Each {@code ThreadPoolExecutor} also maintains some basic - * statistics, such as the number of completed tasks. - * - *

    To be useful across a wide range of contexts, this class - * provides many adjustable parameters and extensibility - * hooks. However, programmers are urged to use the more convenient - * {@link Executors} factory methods {@link - * Executors#newCachedThreadPool} (unbounded thread pool, with - * automatic thread reclamation), {@link Executors#newFixedThreadPool} - * (fixed size thread pool) and {@link - * Executors#newSingleThreadExecutor} (single background thread), that - * preconfigure settings for the most common usage - * scenarios. Otherwise, use the following guide when manually - * configuring and tuning this class: - * + * An {@link java.util.concurrent.ExecutorService} that executes each submitted task using one of possibly several + * pooled threads, normally configured using {@link Executors} factory methods. + *

    + * Thread pools address two different problems: they usually provide improved performance when executing large numbers + * of asynchronous tasks, due to reduced per-task invocation overhead, and they provide a means of bounding and managing + * the resources, including threads, consumed when executing a collection of tasks. Each {@code ThreadPoolExecutor} also + * maintains some basic statistics, such as the number of completed tasks. + *

    + * To be useful across a wide range of contexts, this class provides many adjustable parameters and extensibility hooks. + * However, programmers are urged to use the more convenient {@link Executors} factory methods + * {@link Executors#newCachedThreadPool} (unbounded thread pool, with automatic thread reclamation), + * {@link Executors#newFixedThreadPool} (fixed size thread pool) and {@link Executors#newSingleThreadExecutor} (single + * background thread), that preconfigure settings for the most common usage scenarios. Otherwise, use the following + * guide when manually configuring and tuning this class: *

    - * *
    Core and maximum pool sizes
    - * - *
    A {@code ThreadPoolExecutor} will automatically adjust the - * pool size (see {@link #getPoolSize}) - * according to the bounds set by - * corePoolSize (see {@link #getCorePoolSize}) and - * maximumPoolSize (see {@link #getMaximumPoolSize}). - * - * When a new task is submitted in method {@link #execute(Runnable)}, - * if fewer than corePoolSize threads are running, a new thread is - * created to handle the request, even if other worker threads are - * idle. Else if fewer than maximumPoolSize threads are running, a - * new thread will be created to handle the request only if the queue - * is full. By setting corePoolSize and maximumPoolSize the same, you - * create a fixed-size thread pool. By setting maximumPoolSize to an - * essentially unbounded value such as {@code Integer.MAX_VALUE}, you - * allow the pool to accommodate an arbitrary number of concurrent - * tasks. Most typically, core and maximum pool sizes are set only - * upon construction, but they may also be changed dynamically using - * {@link #setCorePoolSize} and {@link #setMaximumPoolSize}.
    - * + *
    A {@code ThreadPoolExecutor} will automatically adjust the pool size (see {@link #getPoolSize}) according to the + * bounds set by corePoolSize (see {@link #getCorePoolSize}) and maximumPoolSize (see {@link #getMaximumPoolSize}). When + * a new task is submitted in method {@link #execute(Runnable)}, if fewer than corePoolSize threads are running, a new + * thread is created to handle the request, even if other worker threads are idle. Else if fewer than maximumPoolSize + * threads are running, a new thread will be created to handle the request only if the queue is full. By setting + * corePoolSize and maximumPoolSize the same, you create a fixed-size thread pool. By setting maximumPoolSize to an + * essentially unbounded value such as {@code Integer.MAX_VALUE}, you allow the pool to accommodate an arbitrary number + * of concurrent tasks. Most typically, core and maximum pool sizes are set only upon construction, but they may also be + * changed dynamically using {@link #setCorePoolSize} and {@link #setMaximumPoolSize}.
    *
    On-demand construction
    - * - *
    By default, even core threads are initially created and - * started only when new tasks arrive, but this can be overridden - * dynamically using method {@link #prestartCoreThread} or {@link - * #prestartAllCoreThreads}. You probably want to prestart threads if - * you construct the pool with a non-empty queue.
    - * + *
    By default, even core threads are initially created and started only when new tasks arrive, but this can be + * overridden dynamically using method {@link #prestartCoreThread} or {@link #prestartAllCoreThreads}. You probably want + * to prestart threads if you construct the pool with a non-empty queue.
    *
    Creating new threads
    - * - *
    New threads are created using a {@link ThreadFactory}. If not - * otherwise specified, a {@link Executors#defaultThreadFactory} is - * used, that creates threads to all be in the same {@link - * ThreadGroup} and with the same {@code NORM_PRIORITY} priority and - * non-daemon status. By supplying a different ThreadFactory, you can - * alter the thread's name, thread group, priority, daemon status, - * etc. If a {@code ThreadFactory} fails to create a thread when asked - * by returning null from {@code newThread}, the executor will - * continue, but might not be able to execute any tasks. Threads - * should possess the "modifyThread" {@code RuntimePermission}. If - * worker threads or other threads using the pool do not possess this - * permission, service may be degraded: configuration changes may not - * take effect in a timely manner, and a shutdown pool may remain in a - * state in which termination is possible but not completed.
    - * + *
    New threads are created using a {@link ThreadFactory}. If not otherwise specified, a + * {@link Executors#defaultThreadFactory} is used, that creates threads to all be in the same {@link ThreadGroup} and + * with the same {@code NORM_PRIORITY} priority and non-daemon status. By supplying a different ThreadFactory, you can + * alter the thread's name, thread group, priority, daemon status, etc. If a {@code ThreadFactory} fails to create a + * thread when asked by returning null from {@code newThread}, the executor will continue, but might not be able to + * execute any tasks. Threads should possess the "modifyThread" {@code RuntimePermission}. If worker threads or other + * threads using the pool do not possess this permission, service may be degraded: configuration changes may not take + * effect in a timely manner, and a shutdown pool may remain in a state in which termination is possible but not + * completed.
    *
    Keep-alive times
    - * - *
    If the pool currently has more than corePoolSize threads, - * excess threads will be terminated if they have been idle for more - * than the keepAliveTime (see {@link #getKeepAliveTime(TimeUnit)}). - * This provides a means of reducing resource consumption when the - * pool is not being actively used. If the pool becomes more active - * later, new threads will be constructed. This parameter can also be - * changed dynamically using method {@link #setKeepAliveTime(long, - * TimeUnit)}. Using a value of {@code Long.MAX_VALUE} {@link - * TimeUnit#NANOSECONDS} effectively disables idle threads from ever - * terminating prior to shut down. By default, the keep-alive policy - * applies only when there are more than corePoolSize threads, but - * method {@link #allowCoreThreadTimeOut(boolean)} can be used to - * apply this time-out policy to core threads as well, so long as the - * keepAliveTime value is non-zero.
    - * + *
    If the pool currently has more than corePoolSize threads, excess threads will be terminated if they have been + * idle for more than the keepAliveTime (see {@link #getKeepAliveTime(TimeUnit)}). This provides a means of reducing + * resource consumption when the pool is not being actively used. If the pool becomes more active later, new threads + * will be constructed. This parameter can also be changed dynamically using method + * {@link #setKeepAliveTime(long, TimeUnit)}. Using a value of {@code Long.MAX_VALUE} {@link TimeUnit#NANOSECONDS} + * effectively disables idle threads from ever terminating prior to shut down. By default, the keep-alive policy applies + * only when there are more than corePoolSize threads, but method {@link #allowCoreThreadTimeOut(boolean)} can be used + * to apply this time-out policy to core threads as well, so long as the keepAliveTime value is non-zero.
    *
    Queuing
    - * - *
    Any {@link BlockingQueue} may be used to transfer and hold - * submitted tasks. The use of this queue interacts with pool sizing: - * + *
    Any {@link BlockingQueue} may be used to transfer and hold submitted tasks. The use of this queue interacts with + * pool sizing: *
      - * - *
    • If fewer than corePoolSize threads are running, the Executor - * always prefers adding a new thread - * rather than queuing. - * - *
    • If corePoolSize or more threads are running, the Executor - * always prefers queuing a request rather than adding a new - * thread. - * - *
    • If a request cannot be queued, a new thread is created unless - * this would exceed maximumPoolSize, in which case, the task will be - * rejected. - * + *
    • If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than + * queuing. + *
    • If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a + * new thread. + *
    • If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, + * the task will be rejected. *
    - * * There are three general strategies for queuing: *
      - * - *
    1. Direct handoffs. A good default choice for a work - * queue is a {@link java.util.concurrent.SynchronousQueue} - * that hands off tasks to threads - * without otherwise holding them. Here, an attempt to queue a task - * will fail if no threads are immediately available to run it, so a - * new thread will be constructed. This policy avoids lockups when - * handling sets of requests that might have internal dependencies. - * Direct handoffs generally require unbounded maximumPoolSizes to - * avoid rejection of new submitted tasks. This in turn admits the - * possibility of unbounded thread growth when commands continue to - * arrive on average faster than they can be processed. - * - *
    2. Unbounded queues. Using an unbounded queue (for - * example a {@link java.util.concurrent.LinkedBlockingQueue} - * without a predefined - * capacity) will cause new tasks to wait in the queue when all - * corePoolSize threads are busy. Thus, no more than corePoolSize - * threads will ever be created. (And the value of the maximumPoolSize - * therefore doesn't have any effect.) This may be appropriate when - * each task is completely independent of others, so tasks cannot - * affect each others execution; for example, in a web page server. - * While this style of queuing can be useful in smoothing out - * transient bursts of requests, it admits the possibility of - * unbounded work queue growth when commands continue to arrive on - * average faster than they can be processed. - * - *
    3. Bounded queues. A bounded queue (for example, an - * {@link java.util.concurrent.ArrayBlockingQueue}) - * helps prevent resource exhaustion when - * used with finite maximumPoolSizes, but can be more difficult to - * tune and control. Queue sizes and maximum pool sizes may be traded - * off for each other: Using large queues and small pools minimizes - * CPU usage, OS resources, and context-switching overhead, but can - * lead to artificially low throughput. If tasks frequently block (for - * example if they are I/O bound), a system may be able to schedule - * time for more threads than you otherwise allow. Use of small queues - * generally requires larger pool sizes, which keeps CPUs busier but - * may encounter unacceptable scheduling overhead, which also - * decreases throughput. - * + *
    4. Direct handoffs. A good default choice for a work queue is a + * {@link java.util.concurrent.SynchronousQueue} that hands off tasks to threads without otherwise holding them. Here, + * an attempt to queue a task will fail if no threads are immediately available to run it, so a new thread will be + * constructed. This policy avoids lockups when handling sets of requests that might have internal dependencies. Direct + * handoffs generally require unbounded maximumPoolSizes to avoid rejection of new submitted tasks. This in turn admits + * the possibility of unbounded thread growth when commands continue to arrive faster on average than they can be + * processed. + *
    5. Unbounded queues. Using an unbounded queue (for example a + * {@link java.util.concurrent.LinkedBlockingQueue} without a predefined capacity) will cause new tasks to wait in the + * queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the + * value of the maximumPoolSize therefore doesn't have any effect.) This may be appropriate when each task is completely + * independent of others, so tasks cannot affect each others execution; for example, in a web page server. While this + * style of queuing can be useful in smoothing out transient bursts of requests, it admits the possibility of unbounded + * work queue growth when commands continue to arrive faster on average than they can be processed. + *
    6. Bounded queues. A bounded queue (for example, an {@link java.util.concurrent.ArrayBlockingQueue}) helps + * prevent resource exhaustion when used with finite maximumPoolSizes, but can be more difficult to tune and control. + * Queue sizes and maximum pool sizes may be traded off for each other: Using large queues and small pools minimizes CPU + * usage, OS resources, and context-switching overhead, but can lead to artificially low throughput. If tasks frequently + * block (for example if they are I/O bound), a system may be able to schedule time for more threads than you otherwise + * allow. Use of small queues generally requires larger pool sizes, which keeps CPUs busier but may encounter + * unacceptable scheduling overhead, which also decreases throughput. *
    - * *
    - * *
    Rejected tasks
    - * - *
    New tasks submitted in method {@link #execute(Runnable)} will be - * rejected when the Executor has been shut down, and also when - * the Executor uses finite bounds for both maximum threads and work queue - * capacity, and is saturated. In either case, the {@code execute} method - * invokes the {@link - * RejectedExecutionHandler#rejectedExecution(Runnable, ThreadPoolExecutor)} - * method of its {@link RejectedExecutionHandler}. Four predefined handler - * policies are provided: - * + *
    New tasks submitted in method {@link #execute(Runnable)} will be rejected when the Executor has been + * shut down, and also when the Executor uses finite bounds for both maximum threads and work queue capacity, and is + * saturated. In either case, the {@code execute} method invokes the + * {@link RejectedExecutionHandler#rejectedExecution(Runnable, ThreadPoolExecutor)} method of its + * {@link RejectedExecutionHandler}. Four predefined handler policies are provided: *
      - * - *
    1. In the default {@link ThreadPoolExecutor.AbortPolicy}, the handler - * throws a runtime {@link RejectedExecutionException} upon rejection. - * - *
    2. In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread - * that invokes {@code execute} itself runs the task. This provides a - * simple feedback control mechanism that will slow down the rate that - * new tasks are submitted. - * - *
    3. In {@link ThreadPoolExecutor.DiscardPolicy}, a task that cannot - * be executed is simply dropped. This policy is designed only for - * those rare cases in which task completion is never relied upon. - * - *
    4. In {@link ThreadPoolExecutor.DiscardOldestPolicy}, if the - * executor is not shut down, the task at the head of the work queue - * is dropped, and then execution is retried (which can fail again, - * causing this to be repeated.) This policy is rarely acceptable. In - * nearly all cases, you should also cancel the task to cause an - * exception in any component waiting for its completion, and/or log - * the failure, as illustrated in {@link - * ThreadPoolExecutor.DiscardOldestPolicy} documentation. - * + *
    5. In the default {@link ThreadPoolExecutor.AbortPolicy}, the handler throws a runtime + * {@link RejectedExecutionException} upon rejection. + *
    6. In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread that invokes {@code execute} itself runs the task. + * This provides a simple feedback control mechanism that will slow down the rate that new tasks are submitted. + *
    7. In {@link ThreadPoolExecutor.DiscardPolicy}, a task that cannot be executed is simply dropped. This policy is + * designed only for those rare cases in which task completion is never relied upon. + *
    8. In {@link ThreadPoolExecutor.DiscardOldestPolicy}, if the executor is not shut down, the task at the head of the + * work queue is dropped, and then execution is retried (which can fail again, causing this to be repeated.) This policy + * is rarely acceptable. In nearly all cases, you should also cancel the task to cause an exception in any component + * waiting for its completion, and/or log the failure, as illustrated in {@link ThreadPoolExecutor.DiscardOldestPolicy} + * documentation. *
    - * - * It is possible to define and use other kinds of {@link - * RejectedExecutionHandler} classes. Doing so requires some care - * especially when policies are designed to work only under particular - * capacity or queuing policies.
    - * + * It is possible to define and use other kinds of {@link RejectedExecutionHandler} classes. Doing so requires some care + * especially when policies are designed to work only under particular capacity or queuing policies. *
    Hook methods
    - * - *
    This class provides {@code protected} overridable - * {@link #beforeExecute(Thread, Runnable)} and - * {@link #afterExecute(Runnable, Throwable)} methods that are called - * before and after execution of each task. These can be used to - * manipulate the execution environment; for example, reinitializing - * ThreadLocals, gathering statistics, or adding log entries. - * Additionally, method {@link #terminated} can be overridden to perform - * any special processing that needs to be done once the Executor has - * fully terminated. - * - *

    If hook, callback, or BlockingQueue methods throw exceptions, - * internal worker threads may in turn fail, abruptly terminate, and - * possibly be replaced.

    - * + *
    This class provides {@code protected} overridable {@link #beforeExecute(Thread, Runnable)} and + * {@link #afterExecute(Runnable, Throwable)} methods that are called before and after execution of each task. These can + * be used to manipulate the execution environment; for example, reinitializing ThreadLocals, gathering statistics, or + * adding log entries. Additionally, method {@link #terminated} can be overridden to perform any special processing that + * needs to be done once the Executor has fully terminated. + *

    + * If hook, callback, or BlockingQueue methods throw exceptions, internal worker threads may in turn fail, abruptly + * terminate, and possibly be replaced.

    *
    Queue maintenance
    - * - *
    Method {@link #getQueue()} allows access to the work queue - * for purposes of monitoring and debugging. Use of this method for - * any other purpose is strongly discouraged. Two supplied methods, - * {@link #remove(Runnable)} and {@link #purge} are available to - * assist in storage reclamation when large numbers of queued tasks - * become cancelled.
    - * + *
    Method {@link #getQueue()} allows access to the work queue for purposes of monitoring and debugging. Use of this + * method for any other purpose is strongly discouraged. Two supplied methods, {@link #remove(Runnable)} and + * {@link #purge} are available to assist in storage reclamation when large numbers of queued tasks become + * cancelled.
    *
    Reclamation
    - * - *
    A pool that is no longer referenced in a program AND - * has no remaining threads may be reclaimed (garbage collected) - * without being explicitly shutdown. You can configure a pool to - * allow all unused threads to eventually die by setting appropriate - * keep-alive times, using a lower bound of zero core threads and/or - * setting {@link #allowCoreThreadTimeOut(boolean)}.
    - * + *
    A pool that is no longer referenced in a program AND has no remaining threads may be reclaimed (garbage + * collected) without being explicitly shutdown. You can configure a pool to allow all unused threads to eventually die + * by setting appropriate keep-alive times, using a lower bound of zero core threads and/or setting + * {@link #allowCoreThreadTimeOut(boolean)}.
    *
    - * - *

    Extension example. Most extensions of this class - * override one or more of the protected hook methods. For example, - * here is a subclass that adds a simple pause/resume feature: + *

    + * Extension example. Most extensions of this class override one or more of the protected hook methods. For + * example, here is a subclass that adds a simple pause/resume feature: * *

     {@code
      * class PausableThreadPoolExecutor extends ThreadPoolExecutor {
    @@ -325,87 +214,74 @@
      * }}
    * * @since 1.5 - * @author Doug Lea */ public class ThreadPoolExecutor extends AbstractExecutorService { protected static final StringManager sm = StringManager.getManager(ThreadPoolExecutor.class); /** - * The main pool control state, ctl, is an atomic integer packing - * two conceptual fields - * workerCount, indicating the effective number of threads - * runState, indicating whether running, shutting down etc - * - * In order to pack them into one int, we limit workerCount to - * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2 - * billion) otherwise representable. If this is ever an issue in - * the future, the variable can be changed to be an AtomicLong, - * and the shift/mask constants below adjusted. But until the need - * arises, this code is a bit faster and simpler using an int. - * - * The workerCount is the number of workers that have been - * permitted to start and not permitted to stop. The value may be - * transiently different from the actual number of live threads, - * for example when a ThreadFactory fails to create a thread when - * asked, and when exiting threads are still performing - * bookkeeping before terminating. The user-visible pool size is - * reported as the current size of the workers set. - * + * The main pool control state, ctl, is an atomic integer packing two conceptual fields: + *
      + *
    • workerCount, indicating the effective number of threads
    • + *
    • runState, indicating whether running, shutting down etc
    • + *
    + * In order to pack them into one int, we limit workerCount to (2^29)-1 (about 500 million) threads rather than + * (2^31)-1 (2 billion) otherwise representable. If this is ever an issue in the future, the variable can be changed + * to be an AtomicLong, and the shift/mask constants below adjusted. But until the need arises, this code is a bit + * faster and simpler using an int. + *

    + * The workerCount is the number of workers that have been permitted to start and not permitted to stop. The value + * may be transiently different from the actual number of live threads, for example when a ThreadFactory fails to + * create a thread when asked, and when exiting threads are still performing bookkeeping before terminating. The + * user-visible pool size is reported as the current size of the workers set. + *

    * The runState provides the main lifecycle control, taking on values: - * - * RUNNING: Accept new tasks and process queued tasks - * SHUTDOWN: Don't accept new tasks, but process queued tasks - * STOP: Don't accept new tasks, don't process queued tasks, - * and interrupt in-progress tasks - * TIDYING: All tasks have terminated, workerCount is zero, - * the thread transitioning to state TIDYING - * will run the terminated() hook method - * TERMINATED: terminated() has completed - * - * The numerical order among these values matters, to allow - * ordered comparisons. The runState monotonically increases over - * time, but need not hit each state. The transitions are: - * - * RUNNING -> SHUTDOWN - * On invocation of shutdown() - * (RUNNING or SHUTDOWN) -> STOP - * On invocation of shutdownNow() - * SHUTDOWN -> TIDYING - * When both queue and pool are empty - * STOP -> TIDYING - * When pool is empty - * TIDYING -> TERMINATED - * When the terminated() hook method has completed - * - * Threads waiting in awaitTermination() will return when the - * state reaches TERMINATED. - * - * Detecting the transition from SHUTDOWN to TIDYING is less - * straightforward than you'd like because the queue may become - * empty after non-empty and vice versa during SHUTDOWN state, but - * we can only terminate if, after seeing that it is empty, we see - * that workerCount is 0 (which sometimes entails a recheck -- see - * below). + *

      + *
    • RUNNING: Accept new tasks and process queued tasks
    • + *
    • SHUTDOWN: Don't accept new tasks, but process queued tasks
    • + *
    • STOP: Don't accept new tasks, don't process queued tasks, and interrupt in-progress tasks
    • + *
    • TIDYING: All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run + * the terminated() hook method
    • + *
    • TERMINATED: terminated() has completed
    • + *
    + * The numerical order among these values matters, to allow ordered comparisons. The runState monotonically + * increases over time, but need not hit each state. The transitions are: + *
      + *
    • RUNNING -> SHUTDOWN On invocation of shutdown()
    • + *
    • (RUNNING or SHUTDOWN) -> STOP On invocation of shutdownNow()
    • + *
    • SHUTDOWN -> TIDYING When both queue and pool are empty
    • + *
    • STOP -> TIDYING When pool is empty
    • + *
    • TIDYING -> TERMINATED When the terminated() hook method has completed
    • + *
    + * Threads waiting in awaitTermination() will return when the state reaches TERMINATED. + *

    + * Detecting the transition from SHUTDOWN to TIDYING is less straightforward than you'd like because the queue may + * become empty after non-empty and vice versa during SHUTDOWN state, but we can only terminate if, after seeing + * that it is empty, we see that workerCount is 0 (which sometimes entails a recheck -- see below). */ private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3; private static final int COUNT_MASK = (1 << COUNT_BITS) - 1; // runState is stored in the high-order bits - private static final int RUNNING = -1 << COUNT_BITS; - private static final int SHUTDOWN = 0 << COUNT_BITS; - private static final int STOP = 1 << COUNT_BITS; - private static final int TIDYING = 2 << COUNT_BITS; - private static final int TERMINATED = 3 << COUNT_BITS; + private static final int RUNNING = -1 << COUNT_BITS; + private static final int SHUTDOWN = 0; + private static final int STOP = 1 << COUNT_BITS; + private static final int TIDYING = 2 << COUNT_BITS; + private static final int TERMINATED = 3 << COUNT_BITS; // Packing and unpacking ctl - private static int workerCountOf(int c) { return c & COUNT_MASK; } - private static int ctlOf(int rs, int wc) { return rs | wc; } + private static int workerCountOf(int c) { + return c & COUNT_MASK; + } + + private static int ctlOf(int rs, int wc) { + return rs | wc; + } /* - * Bit field accessors that don't require unpacking ctl. - * These depend on the bit layout and on workerCount being never negative. + * Bit field accessors that don't require unpacking ctl. These depend on the bit layout and on workerCount being + * never negative. */ private static boolean runStateLessThan(int c, int s) { @@ -435,45 +311,35 @@ } /** - * Decrements the workerCount field of ctl. This is called only on - * abrupt termination of a thread (see processWorkerExit). Other - * decrements are performed within getTask. + * Decrements the workerCount field of ctl. This is called only on abrupt termination of a thread (see + * processWorkerExit). Other decrements are performed within getTask. */ private void decrementWorkerCount() { ctl.addAndGet(-1); } /** - * The queue used for holding tasks and handing off to worker - * threads. We do not require that workQueue.poll() returning - * null necessarily means that workQueue.isEmpty(), so rely - * solely on isEmpty to see if the queue is empty (which we must - * do for example when deciding whether to transition from - * SHUTDOWN to TIDYING). This accommodates special-purpose - * queues such as DelayQueues for which poll() is allowed to - * return null even if it may later return non-null when delays - * expire. + * The queue used for holding tasks and handing off to worker threads. We do not require that workQueue.poll() + * returning null necessarily means that workQueue.isEmpty(), so rely solely on isEmpty to see if the queue is empty + * (which we must do for example when deciding whether to transition from SHUTDOWN to TIDYING). This accommodates + * special-purpose queues such as DelayQueues for which poll() is allowed to return null even if it may later return + * non-null when delays expire. */ private final BlockingQueue workQueue; /** - * Lock held on access to workers set and related bookkeeping. - * While we could use a concurrent set of some sort, it turns out - * to be generally preferable to use a lock. Among the reasons is - * that this serializes interruptIdleWorkers, which avoids - * unnecessary interrupt storms, especially during shutdown. - * Otherwise exiting threads would concurrently interrupt those - * that have not yet interrupted. It also simplifies some of the - * associated statistics bookkeeping of largestPoolSize etc. We - * also hold mainLock on shutdown and shutdownNow, for the sake of - * ensuring workers set is stable while separately checking - * permission to interrupt and actually interrupting. + * Lock held on access to workers set and related bookkeeping. While we could use a concurrent set of some sort, it + * turns out to be generally preferable to use a lock. Among the reasons is that this serializes + * interruptIdleWorkers, which avoids unnecessary interrupt storms, especially during shutdown. Otherwise, exiting + * threads would concurrently interrupt those that have not yet interrupted. It also simplifies some of the + * associated statistics bookkeeping of largestPoolSize etc. We also hold mainLock on shutdown and shutdownNow, for + * the sake of ensuring workers set is stable while separately checking permission to interrupt and actually + * interrupting. */ private final ReentrantLock mainLock = new ReentrantLock(); /** - * Set containing all worker threads in pool. Accessed only when - * holding mainLock. + * Set containing all worker threads in pool. Accessed only when holding mainLock. */ private final HashSet workers = new HashSet<>(); @@ -483,38 +349,33 @@ private final Condition termination = mainLock.newCondition(); /** - * Tracks largest attained pool size. Accessed only under - * mainLock. + * Tracks largest attained pool size. Accessed only under mainLock. */ private int largestPoolSize; /** - * Counter for completed tasks. Updated only on termination of - * worker threads. Accessed only under mainLock. + * Counter for completed tasks. Updated only on termination of worker threads. Accessed only under mainLock. */ private long completedTaskCount; /** - * The number of tasks submitted but not yet finished. This includes tasks - * in the queue and tasks that have been handed to a worker thread but the - * latter did not start executing the task yet. - * This number is always greater or equal to {@link #getActiveCount()}. + * The number of tasks submitted but not yet finished. This includes tasks in the queue and tasks that have been + * handed to a worker thread but the latter did not start executing the task yet. This number is always greater or + * equal to {@link #getActiveCount()}. */ private final AtomicInteger submittedCount = new AtomicInteger(0); private final AtomicLong lastContextStoppedTime = new AtomicLong(0L); /** - * Most recent time in ms when a thread decided to kill itself to avoid - * potential memory leaks. Useful to throttle the rate of renewals of - * threads. + * Most recent time in ms when a thread decided to kill itself to avoid potential memory leaks. Useful to throttle + * the rate of renewals of threads. */ private final AtomicLong lastTimeThreadKilledItself = new AtomicLong(0L); /* - * All user control parameters are declared as volatiles so that - * ongoing actions are based on freshest values, but without need - * for locking, since no internal invariants depend on them - * changing synchronously with respect to other actions. + * All user control parameters are declared as volatiles so that ongoing actions are based on freshest values, but + * without need for locking, since no internal invariants depend on them changing synchronously with respect to + * other actions. */ /** @@ -523,21 +384,15 @@ private volatile long threadRenewalDelay = Constants.DEFAULT_THREAD_RENEWAL_DELAY; /** - * Factory for new threads. All threads are created using this - * factory (via method addWorker). All callers must be prepared - * for addWorker to fail, which may reflect a system or user's - * policy limiting the number of threads. Even though it is not - * treated as an error, failure to create threads may result in - * new tasks being rejected or existing ones remaining stuck in - * the queue. - * - * We go further and preserve pool invariants even in the face of - * errors such as OutOfMemoryError, that might be thrown while - * trying to create threads. Such errors are rather common due to - * the need to allocate a native stack in Thread.start, and users - * will want to perform clean pool shutdown to clean up. There - * will likely be enough memory available for the cleanup code to - * complete without encountering yet another OutOfMemoryError. + * Factory for new threads. All threads are created using this factory (via method addWorker). All callers must be + * prepared for addWorker to fail, which may reflect a system or user's policy limiting the number of threads. Even + * though it is not treated as an error, failure to create threads may result in new tasks being rejected or + * existing ones remaining stuck in the queue. + *

    + * We go further and preserve pool invariants even in the face of errors such as OutOfMemoryError, that might be + * thrown while trying to create threads. Such errors are rather common due to the need to allocate a native stack + * in Thread.start, and users will want to perform clean pool shutdown to clean up. There will likely be enough + * memory available for the cleanup code to complete without encountering yet another OutOfMemoryError. */ private volatile ThreadFactory threadFactory; @@ -547,35 +402,31 @@ private volatile RejectedExecutionHandler handler; /** - * Timeout in nanoseconds for idle threads waiting for work. - * Threads use this timeout when there are more than corePoolSize - * present or if allowCoreThreadTimeOut. Otherwise they wait - * forever for new work. + * Timeout in nanoseconds for idle threads waiting for work. Threads use this timeout when there are more than + * corePoolSize present or if allowCoreThreadTimeOut. Otherwise, they wait forever for new work. */ private volatile long keepAliveTime; /** - * If false (default), core threads stay alive even when idle. - * If true, core threads use keepAliveTime to time out waiting - * for work. + * If false (default), core threads stay alive even when idle. If true, core threads use keepAliveTime to time out + * waiting for work. */ private volatile boolean allowCoreThreadTimeOut; /** - * Core pool size is the minimum number of workers to keep alive - * (and not allow to time out etc) unless allowCoreThreadTimeOut - * is set, in which case the minimum is zero. - * - * Since the worker count is actually stored in COUNT_BITS bits, - * the effective limit is {@code corePoolSize & COUNT_MASK}. + * Core pool size is the minimum number of workers to keep alive (and not allow to time out etc) unless + * allowCoreThreadTimeOut is set, in which case the minimum is zero. + *

    + * Since the worker count is actually stored in COUNT_BITS bits, the effective limit is + * {@code corePoolSize & COUNT_MASK}. */ private volatile int corePoolSize; /** * Maximum pool size. - * - * Since the worker count is actually stored in COUNT_BITS bits, - * the effective limit is {@code maximumPoolSize & COUNT_MASK}. + *

    + * Since the worker count is actually stored in COUNT_BITS bits, the effective limit is + * {@code maximumPoolSize & COUNT_MASK}. */ private volatile int maximumPoolSize; @@ -585,57 +436,37 @@ private static final RejectedExecutionHandler defaultHandler = new RejectPolicy(); /** - * Permission required for callers of shutdown and shutdownNow. - * We additionally require (see checkShutdownAccess) that callers - * have permission to actually interrupt threads in the worker set - * (as governed by Thread.interrupt, which relies on - * ThreadGroup.checkAccess, which in turn relies on - * SecurityManager.checkAccess). Shutdowns are attempted only if - * these checks pass. - * - * All actual invocations of Thread.interrupt (see - * interruptIdleWorkers and interruptWorkers) ignore - * SecurityExceptions, meaning that the attempted interrupts - * silently fail. In the case of shutdown, they should not fail - * unless the SecurityManager has inconsistent policies, sometimes - * allowing access to a thread and sometimes not. In such cases, - * failure to actually interrupt threads may disable or delay full - * termination. Other uses of interruptIdleWorkers are advisory, - * and failure to actually interrupt will merely delay response to - * configuration changes so is not handled exceptionally. - */ - private static final RuntimePermission shutdownPerm = - new RuntimePermission("modifyThread"); - - /** - * Class Worker mainly maintains interrupt control state for - * threads running tasks, along with other minor bookkeeping. - * This class opportunistically extends AbstractQueuedSynchronizer - * to simplify acquiring and releasing a lock surrounding each - * task execution. This protects against interrupts that are - * intended to wake up a worker thread waiting for a task from - * instead interrupting a task being run. We implement a simple - * non-reentrant mutual exclusion lock rather than use - * ReentrantLock because we do not want worker tasks to be able to - * reacquire the lock when they invoke pool control methods like - * setCorePoolSize. Additionally, to suppress interrupts until - * the thread actually starts running tasks, we initialize lock - * state to a negative value, and clear it upon start (in + * Permission required for callers of shutdown and shutdownNow. We additionally require (see checkShutdownAccess) + * that callers have permission to actually interrupt threads in the worker set (as governed by Thread.interrupt, + * which relies on ThreadGroup.checkAccess, which in turn relies on SecurityManager.checkAccess). Shutdowns are + * attempted only if these checks pass. All actual invocations of Thread.interrupt (see interruptIdleWorkers and + * interruptWorkers) ignore SecurityExceptions, meaning that the attempted interrupts silently fail. In the case of + * shutdown, they should not fail unless the SecurityManager has inconsistent policies, sometimes allowing access to + * a thread and sometimes not. In such cases, failure to actually interrupt threads may disable or delay full + * termination. Other uses of interruptIdleWorkers are advisory, and failure to actually interrupt will merely delay + * response to configuration changes so is not handled exceptionally. + */ + private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread"); + + /** + * Class Worker mainly maintains interrupt control state for threads running tasks, along with other minor + * bookkeeping. This class opportunistically extends AbstractQueuedSynchronizer to simplify acquiring and releasing + * a lock surrounding each task execution. This protects against interrupts that are intended to wake up a worker + * thread waiting for a task from instead interrupting a task being run. We implement a simple non-reentrant mutual + * exclusion lock rather than use ReentrantLock because we do not want worker tasks to be able to reacquire the lock + * when they invoke pool control methods like setCorePoolSize. Additionally, to suppress interrupts until the thread + * actually starts running tasks, we initialize lock state to a negative value, and clear it upon start (in * runWorker). */ - private final class Worker - extends AbstractQueuedSynchronizer - implements Runnable - { + private final class Worker extends AbstractQueuedSynchronizer implements Runnable { /** - * This class will never be serialized, but we provide a - * serialVersionUID to suppress a javac warning. + * This class will never be serialized, but we provide a serialVersionUID to suppress a javac warning. */ private static final long serialVersionUID = 6138294804551838833L; - /** Thread this worker is running in. Null if factory fails. */ + /** Thread this worker is running in. Null if factory fails. */ final Thread thread; - /** Initial task to run. Possibly null. */ + /** Initial task to run. Possibly null. */ Runnable firstTask; /** Per-thread task counter */ volatile long completedTasks; @@ -645,6 +476,7 @@ /** * Creates with given first task and thread from ThreadFactory. + * * @param firstTask the first task (null if none) */ Worker(Runnable firstTask) { @@ -685,10 +517,21 @@ return true; } - public void lock() { acquire(1); } - public boolean tryLock() { return tryAcquire(1); } - public void unlock() { release(1); } - public boolean isLocked() { return isHeldExclusively(); } + public void lock() { + acquire(1); + } + + public boolean tryLock() { + return tryAcquire(1); + } + + public void unlock() { + release(1); + } + + public boolean isLocked() { + return isHeldExclusively(); + } void interruptIfStarted() { Thread t; @@ -706,39 +549,32 @@ */ /** - * Transitions runState to given target, or leaves it alone if - * already at least the given target. + * Transitions runState to given target, or leaves it alone if already at least the given target. * - * @param targetState the desired state, either SHUTDOWN or STOP - * (but not TIDYING or TERMINATED -- use tryTerminate for that) + * @param targetState the desired state, either SHUTDOWN or STOP (but not TIDYING or TERMINATED -- use tryTerminate + * for that) */ private void advanceRunState(int targetState) { // assert targetState == SHUTDOWN || targetState == STOP; for (;;) { int c = ctl.get(); - if (runStateAtLeast(c, targetState) || - ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) { + if (runStateAtLeast(c, targetState) || ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) { break; } } } /** - * Transitions to TERMINATED state if either (SHUTDOWN and pool - * and queue empty) or (STOP and pool empty). If otherwise - * eligible to terminate but workerCount is nonzero, interrupts an - * idle worker to ensure that shutdown signals propagate. This - * method must be called following any action that might make - * termination possible -- reducing worker count or removing tasks - * from the queue during shutdown. The method is non-private to - * allow access from ScheduledThreadPoolExecutor. + * Transitions to TERMINATED state if either (SHUTDOWN and pool and queue empty) or (STOP and pool empty). If + * otherwise eligible to terminate but workerCount is nonzero, interrupts an idle worker to ensure that shutdown + * signals propagate. This method must be called following any action that might make termination possible -- + * reducing worker count or removing tasks from the queue during shutdown. The method is non-private to allow access + * from ScheduledThreadPoolExecutor. */ final void tryTerminate() { for (;;) { int c = ctl.get(); - if (isRunning(c) || - runStateAtLeast(c, TIDYING) || - (runStateLessThan(c, STOP) && ! workQueue.isEmpty())) { + if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateLessThan(c, STOP) && !workQueue.isEmpty())) { return; } if (workerCountOf(c) != 0) { // Eligible to terminate @@ -770,12 +606,9 @@ */ /** - * If there is a security manager, makes sure caller has - * permission to shut down threads in general (see shutdownPerm). - * If this passes, additionally makes sure the caller is allowed - * to interrupt each worker thread. This might not be true even if - * first check passed, if the SecurityManager treats some threads - * specially. + * If there is a security manager, makes sure caller has permission to shut down threads in general (see + * shutdownPerm). If this passes, additionally makes sure the caller is allowed to interrupt each worker thread. + * This might not be true even if first check passed, if the SecurityManager treats some threads specially. */ private void checkShutdownAccess() { // assert mainLock.isHeldByCurrentThread(); @@ -789,8 +622,8 @@ } /** - * Interrupts all threads, even if active. Ignores SecurityExceptions - * (in which case some threads may remain uninterrupted). + * Interrupts all threads, even if active. Ignores SecurityExceptions (in which case some threads may remain + * uninterrupted). */ private void interruptWorkers() { // assert mainLock.isHeldByCurrentThread(); @@ -800,23 +633,17 @@ } /** - * Interrupts threads that might be waiting for tasks (as - * indicated by not being locked) so they can check for - * termination or configuration changes. Ignores - * SecurityExceptions (in which case some threads may remain + * Interrupts threads that might be waiting for tasks (as indicated by not being locked) so they can check for + * termination or configuration changes. Ignores SecurityExceptions (in which case some threads may remain * uninterrupted). * - * @param onlyOne If true, interrupt at most one worker. This is - * called only from tryTerminate when termination is otherwise - * enabled but there are still other workers. In this case, at - * most one waiting worker is interrupted to propagate shutdown - * signals in case all threads are currently waiting. - * Interrupting any arbitrary thread ensures that newly arriving - * workers since shutdown began will also eventually exit. - * To guarantee eventual termination, it suffices to always - * interrupt only one idle worker, but shutdown() interrupts all - * idle workers so that redundant workers exit promptly, not - * waiting for a straggler task to finish. + * @param onlyOne If true, interrupt at most one worker. This is called only from tryTerminate when termination is + * otherwise enabled but there are still other workers. In this case, at most one waiting worker + * is interrupted to propagate shutdown signals in case all threads are currently waiting. + * Interrupting any arbitrary thread ensures that newly arriving workers since shutdown began + * will also eventually exit. To guarantee eventual termination, it suffices to always interrupt + * only one idle worker, but shutdown() interrupts all idle workers so that redundant workers + * exit promptly, not waiting for a straggler task to finish. */ private void interruptIdleWorkers(boolean onlyOne) { final ReentrantLock mainLock = this.mainLock; @@ -842,8 +669,7 @@ } /** - * Common form of interruptIdleWorkers, to avoid having to - * remember what the boolean argument means. + * Common form of interruptIdleWorkers, to avoid having to remember what the boolean argument means. */ private void interruptIdleWorkers() { interruptIdleWorkers(false); @@ -852,31 +678,27 @@ private static final boolean ONLY_ONE = true; /* - * Misc utilities, most of which are also exported to - * ScheduledThreadPoolExecutor + * Misc utilities, most of which are also exported to ScheduledThreadPoolExecutor */ /** - * Invokes the rejected execution handler for the given command. - * Package-protected for use by ScheduledThreadPoolExecutor. + * Invokes the rejected execution handler for the given command. Package-protected for use by + * ScheduledThreadPoolExecutor. */ final void reject(Runnable command) { handler.rejectedExecution(command, this); } /** - * Performs any further cleanup following run state transition on - * invocation of shutdown. A no-op here, but used by + * Performs any further cleanup following run state transition on invocation of shutdown. A no-op here, but used by * ScheduledThreadPoolExecutor to cancel delayed tasks. */ void onShutdown() { } /** - * Drains the task queue into a new list, normally using - * drainTo. But if the queue is a DelayQueue or any other kind of - * queue for which poll or drainTo may fail to remove some - * elements, it deletes them one by one. + * Drains the task queue into a new list, normally using drainTo. But if the queue is a DelayQueue or any other kind + * of queue for which poll or drainTo may fail to remove some elements, it deletes them one by one. */ private List drainQueue() { BlockingQueue q = workQueue; @@ -897,55 +719,43 @@ */ /** - * Checks if a new worker can be added with respect to current - * pool state and the given bound (either core or maximum). If so, - * the worker count is adjusted accordingly, and, if possible, a - * new worker is created and started, running firstTask as its - * first task. This method returns false if the pool is stopped or - * eligible to shut down. It also returns false if the thread - * factory fails to create a thread when asked. If the thread - * creation fails, either due to the thread factory returning - * null, or due to an exception (typically OutOfMemoryError in + * Checks if a new worker can be added with respect to current pool state and the given bound (either core or + * maximum). If so, the worker count is adjusted accordingly, and, if possible, a new worker is created and started, + * running firstTask as its first task. This method returns false if the pool is stopped or eligible to shut down. + * It also returns false if the thread factory fails to create a thread when asked. If the thread creation fails, + * either due to the thread factory returning null, or due to an exception (typically OutOfMemoryError in * Thread.start()), we roll back cleanly. * - * @param firstTask the task the new thread should run first (or - * null if none). Workers are created with an initial first task - * (in method execute()) to bypass queuing when there are fewer - * than corePoolSize threads (in which case we always start one), - * or when the queue is full (in which case we must bypass queue). - * Initially idle threads are usually created via - * prestartCoreThread or to replace other dying workers. - * - * @param core if true use corePoolSize as bound, else - * maximumPoolSize. (A boolean indicator is used here rather than a - * value to ensure reads of fresh values after checking other pool - * state). + * @param firstTask the task the new thread should run first (or null if none). Workers are created with an initial + * first task (in method execute()) to bypass queuing when there are fewer than corePoolSize + * threads (in which case we always start one), or when the queue is full (in which case we + * must bypass queue). Initially idle threads are usually created via prestartCoreThread or to + * replace other dying workers. + * @param core if true use corePoolSize as bound, else maximumPoolSize. (A boolean indicator is used here + * rather than a value to ensure reads of fresh values after checking other pool state). + * * @return true if successful */ private boolean addWorker(Runnable firstTask, boolean core) { retry: for (int c = ctl.get();;) { // Check if queue empty only if necessary. - if (runStateAtLeast(c, SHUTDOWN) - && (runStateAtLeast(c, STOP) - || firstTask != null - || workQueue.isEmpty())) { + if (runStateAtLeast(c, SHUTDOWN) && + (runStateAtLeast(c, STOP) || firstTask != null || workQueue.isEmpty())) { return false; } for (;;) { - if (workerCountOf(c) - >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK)) { + if (workerCountOf(c) >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK)) { return false; } if (compareAndIncrementWorkerCount(c)) { break retry; } - c = ctl.get(); // Re-read ctl - if (runStateAtLeast(c, SHUTDOWN)) - { + c = ctl.get(); // Re-read ctl + if (runStateAtLeast(c, SHUTDOWN)) { continue retry; - // else CAS failed due to workerCount change; retry inner loop + // else CAS failed due to workerCount change; retry inner loop } } } @@ -965,8 +775,7 @@ // shut down before lock acquired. int c = ctl.get(); - if (isRunning(c) || - (runStateLessThan(c, STOP) && firstTask == null)) { + if (isRunning(c) || (runStateLessThan(c, STOP) && firstTask == null)) { if (t.getState() != Thread.State.NEW) { throw new IllegalThreadStateException(); } @@ -986,7 +795,7 @@ } } } finally { - if (! workerStarted) { + if (!workerStarted) { addWorkerFailed(w); } } @@ -995,10 +804,11 @@ /** * Rolls back the worker thread creation. - * - removes worker from workers, if present - * - decrements worker count - * - rechecks for termination, in case the existence of this - * worker was holding up termination + *

      + *
    • removes worker from workers, if present
    • + *
    • decrements worker count
    • + *
    • rechecks for termination, in case the existence of this worker was holding up termination
    • + *
    */ private void addWorkerFailed(Worker w) { final ReentrantLock mainLock = this.mainLock; @@ -1015,16 +825,12 @@ } /** - * Performs cleanup and bookkeeping for a dying worker. Called - * only from worker threads. Unless completedAbruptly is set, - * assumes that workerCount has already been adjusted to account - * for exit. This method removes thread from worker set, and - * possibly terminates the pool or replaces the worker if either - * it exited due to user task exception or if fewer than - * corePoolSize workers are running or queue is non-empty but - * there are no workers. + * Performs cleanup and bookkeeping for a dying worker. Called only from worker threads. Unless completedAbruptly is + * set, assumes that workerCount has already been adjusted to account for exit. This method removes thread from + * worker set, and possibly terminates the pool or replaces the worker if either it exited due to user task + * exception or if fewer than corePoolSize workers are running or queue is non-empty but there are no workers. * - * @param w the worker + * @param w the worker * @param completedAbruptly if the worker died due to user exception */ private void processWorkerExit(Worker w, boolean completedAbruptly) { @@ -1047,7 +853,7 @@ if (runStateLessThan(c, STOP)) { if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; - if (min == 0 && ! workQueue.isEmpty()) { + if (min == 0 && !workQueue.isEmpty()) { min = 1; } // https://bz.apache.org/bugzilla/show_bug.cgi?id=65454 @@ -1066,21 +872,18 @@ } /** - * Performs blocking or timed wait for a task, depending on - * current configuration settings, or returns null if this worker - * must exit because of any of: - * 1. There are more than maximumPoolSize workers (due to - * a call to setMaximumPoolSize). - * 2. The pool is stopped. - * 3. The pool is shutdown and the queue is empty. - * 4. This worker timed out waiting for a task, and timed-out - * workers are subject to termination (that is, - * {@code allowCoreThreadTimeOut || workerCount > corePoolSize}) - * both before and after the timed wait, and if the queue is - * non-empty, this worker is not the last thread in the pool. + * Performs blocking or timed wait for a task, depending on current configuration settings, or returns null if this + * worker must exit because of any of: + *
      + *
    1. There are more than maximumPoolSize workers (due to a call to setMaximumPoolSize).
    2. + *
    3. The pool is stopped.
    4. + *
    5. The pool is shutdown and the queue is empty.
    6. + *
    7. This worker timed out waiting for a task, and timed-out workers are subject to termination (that is, + * {@code allowCoreThreadTimeOut || workerCount > corePoolSize}) both before and after the timed wait, and if the + * queue is non-empty, this worker is not the last thread in the pool.
    8. + *
    * - * @return task, or null if the worker must exit, in which case - * workerCount is decremented + * @return task, or null if the worker must exit, in which case workerCount is decremented */ private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? @@ -1089,8 +892,7 @@ int c = ctl.get(); // Check if queue empty only if necessary. - if (runStateAtLeast(c, SHUTDOWN) - && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) { + if (runStateAtLeast(c, SHUTDOWN) && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) { decrementWorkerCount(); return null; } @@ -1100,8 +902,7 @@ // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; - if ((wc > maximumPoolSize || (timed && timedOut)) - && (wc > 1 || workQueue.isEmpty())) { + if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) { return null; } @@ -1109,9 +910,7 @@ } try { - Runnable r = timed ? - workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : - workQueue.take(); + Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) { return r; } @@ -1123,45 +922,31 @@ } /** - * Main worker run loop. Repeatedly gets tasks from queue and - * executes them, while coping with a number of issues: - * - * 1. We may start out with an initial task, in which case we - * don't need to get the first one. Otherwise, as long as pool is - * running, we get tasks from getTask. If it returns null then the - * worker exits due to changed pool state or configuration - * parameters. Other exits result from exception throws in - * external code, in which case completedAbruptly holds, which - * usually leads processWorkerExit to replace this thread. - * - * 2. Before running any task, the lock is acquired to prevent - * other pool interrupts while the task is executing, and then we - * ensure that unless pool is stopping, this thread does not have - * its interrupt set. - * - * 3. Each task run is preceded by a call to beforeExecute, which - * might throw an exception, in which case we cause thread to die - * (breaking loop with completedAbruptly true) without processing - * the task. - * - * 4. Assuming beforeExecute completes normally, we run the task, - * gathering any of its thrown exceptions to send to afterExecute. - * We separately handle RuntimeException, Error (both of which the - * specs guarantee that we trap) and arbitrary Throwables. - * Because we cannot rethrow Throwables within Runnable.run, we - * wrap them within Errors on the way out (to the thread's - * UncaughtExceptionHandler). Any thrown exception also - * conservatively causes thread to die. - * - * 5. After task.run completes, we call afterExecute, which may - * also throw an exception, which will also cause thread to - * die. According to JLS Sec 14.20, this exception is the one that - * will be in effect even if task.run throws. - * - * The net effect of the exception mechanics is that afterExecute - * and the thread's UncaughtExceptionHandler have as accurate - * information as we can provide about any problems encountered by - * user code. + * Main worker run loop. Repeatedly gets tasks from queue and executes them, while coping with a number of issues: + *

    + * 1. We may start out with an initial task, in which case we don't need to get the first one. Otherwise, as long as + * pool is running, we get tasks from getTask. If it returns null then the worker exits due to changed pool state or + * configuration parameters. Other exits result from exception throws in external code, in which case + * completedAbruptly holds, which usually leads processWorkerExit to replace this thread. + *

    + * 2. Before running any task, the lock is acquired to prevent other pool interrupts while the task is executing, + * and then we ensure that unless pool is stopping, this thread does not have its interrupt set. + *

    + * 3. Each task run is preceded by a call to beforeExecute, which might throw an exception, in which case we cause + * thread to die (breaking loop with completedAbruptly true) without processing the task. + *

    + * 4. Assuming beforeExecute completes normally, we run the task, gathering any of its thrown exceptions to send to + * afterExecute. We separately handle RuntimeException, Error (both of which the specs guarantee that we trap) and + * arbitrary Throwables. Because we cannot rethrow Throwables within Runnable.run, we wrap them within Errors on the + * way out (to the thread's UncaughtExceptionHandler). Any thrown exception also conservatively causes thread to + * die. + *

    + * 5. After task.run completes, we call afterExecute, which may also throw an exception, which will also cause + * thread to die. According to JLS Sec 14.20, this exception is the one that will be in effect even if task.run + * throws. + *

    + * The net effect of the exception mechanics is that afterExecute and the thread's UncaughtExceptionHandler have as + * accurate information as we can provide about any problems encountered by user code. * * @param w the worker */ @@ -1175,13 +960,11 @@ while (task != null || (task = getTask()) != null) { w.lock(); // If pool is stopping, ensure thread is interrupted; - // if not, ensure thread is not interrupted. This + // if not, ensure thread is not interrupted. This // requires a recheck in second case to deal with // shutdownNow race while clearing interrupt - if ((runStateAtLeast(ctl.get(), STOP) || - (Thread.interrupted() && - runStateAtLeast(ctl.get(), STOP))) && - !wt.isInterrupted()) { + if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && + !wt.isInterrupted()) { wt.interrupt(); } try { @@ -1208,152 +991,113 @@ // Public constructors and methods /** - * Creates a new {@code ThreadPoolExecutor} with the given initial - * parameters, the - * {@linkplain Executors#defaultThreadFactory default thread factory} - * and the {@linkplain ThreadPoolExecutor.RejectPolicy - * default rejected execution handler}. - * - *

    It may be more convenient to use one of the {@link Executors} - * factory methods instead of this general purpose constructor. - * - * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle, unless {@code allowCoreThreadTimeOut} is set - * @param maximumPoolSize the maximum number of threads to allow in the - * pool - * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the {@code keepAliveTime} argument - * @param workQueue the queue to use for holding tasks before they are - * executed. This queue will hold only the {@code Runnable} - * tasks submitted by the {@code execute} method. + * Creates a new {@code ThreadPoolExecutor} with the given initial parameters, the + * {@linkplain Executors#defaultThreadFactory default thread factory} and the + * {@linkplain ThreadPoolExecutor.RejectPolicy default rejected execution handler}. + *

    + * It may be more convenient to use one of the {@link Executors} factory methods instead of this general purpose + * constructor. + * + * @param corePoolSize the number of threads to keep in the pool, even if they are idle, unless + * {@code allowCoreThreadTimeOut} is set + * @param maximumPoolSize the maximum number of threads to allow in the pool + * @param keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess + * idle threads will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are executed. This queue will hold only the + * {@code Runnable} tasks submitted by the {@code execute} method. + * * @throws IllegalArgumentException if one of the following holds:
    - * {@code corePoolSize < 0}
    - * {@code keepAliveTime < 0}
    - * {@code maximumPoolSize <= 0}
    - * {@code maximumPoolSize < corePoolSize} - * @throws NullPointerException if {@code workQueue} is null - */ - public ThreadPoolExecutor(int corePoolSize, - int maximumPoolSize, - long keepAliveTime, - TimeUnit unit, - BlockingQueue workQueue) { - this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, - Executors.defaultThreadFactory(), defaultHandler); + * {@code corePoolSize < 0}
    + * {@code keepAliveTime < 0}
    + * {@code maximumPoolSize <= 0}
    + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} is null + */ + public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), + defaultHandler); } /** - * Creates a new {@code ThreadPoolExecutor} with the given initial - * parameters and the {@linkplain ThreadPoolExecutor.RejectPolicy - * default rejected execution handler}. - * - * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle, unless {@code allowCoreThreadTimeOut} is set - * @param maximumPoolSize the maximum number of threads to allow in the - * pool - * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the {@code keepAliveTime} argument - * @param workQueue the queue to use for holding tasks before they are - * executed. This queue will hold only the {@code Runnable} - * tasks submitted by the {@code execute} method. - * @param threadFactory the factory to use when the executor - * creates a new thread + * Creates a new {@code ThreadPoolExecutor} with the given initial parameters and the + * {@linkplain ThreadPoolExecutor.RejectPolicy default rejected execution handler}. + * + * @param corePoolSize the number of threads to keep in the pool, even if they are idle, unless + * {@code allowCoreThreadTimeOut} is set + * @param maximumPoolSize the maximum number of threads to allow in the pool + * @param keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess + * idle threads will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are executed. This queue will hold only the + * {@code Runnable} tasks submitted by the {@code execute} method. + * @param threadFactory the factory to use when the executor creates a new thread + * * @throws IllegalArgumentException if one of the following holds:
    - * {@code corePoolSize < 0}
    - * {@code keepAliveTime < 0}
    - * {@code maximumPoolSize <= 0}
    - * {@code maximumPoolSize < corePoolSize} - * @throws NullPointerException if {@code workQueue} - * or {@code threadFactory} is null - */ - public ThreadPoolExecutor(int corePoolSize, - int maximumPoolSize, - long keepAliveTime, - TimeUnit unit, - BlockingQueue workQueue, - ThreadFactory threadFactory) { - this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, - threadFactory, defaultHandler); + * {@code corePoolSize < 0}
    + * {@code keepAliveTime < 0}
    + * {@code maximumPoolSize <= 0}
    + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} or {@code threadFactory} is null + */ + public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, ThreadFactory threadFactory) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); } /** - * Creates a new {@code ThreadPoolExecutor} with the given initial - * parameters and the + * Creates a new {@code ThreadPoolExecutor} with the given initial parameters and the * {@linkplain Executors#defaultThreadFactory default thread factory}. * - * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle, unless {@code allowCoreThreadTimeOut} is set - * @param maximumPoolSize the maximum number of threads to allow in the - * pool - * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the {@code keepAliveTime} argument - * @param workQueue the queue to use for holding tasks before they are - * executed. This queue will hold only the {@code Runnable} - * tasks submitted by the {@code execute} method. - * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached + * @param corePoolSize the number of threads to keep in the pool, even if they are idle, unless + * {@code allowCoreThreadTimeOut} is set + * @param maximumPoolSize the maximum number of threads to allow in the pool + * @param keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess + * idle threads will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are executed. This queue will hold only the + * {@code Runnable} tasks submitted by the {@code execute} method. + * @param handler the handler to use when execution is blocked because the thread bounds and queue + * capacities are reached + * * @throws IllegalArgumentException if one of the following holds:
    - * {@code corePoolSize < 0}
    - * {@code keepAliveTime < 0}
    - * {@code maximumPoolSize <= 0}
    - * {@code maximumPoolSize < corePoolSize} - * @throws NullPointerException if {@code workQueue} - * or {@code handler} is null - */ - public ThreadPoolExecutor(int corePoolSize, - int maximumPoolSize, - long keepAliveTime, - TimeUnit unit, - BlockingQueue workQueue, - RejectedExecutionHandler handler) { - this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, - Executors.defaultThreadFactory(), handler); + * {@code corePoolSize < 0}
    + * {@code keepAliveTime < 0}
    + * {@code maximumPoolSize <= 0}
    + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} or {@code handler} is null + */ + public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, RejectedExecutionHandler handler) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler); } /** - * Creates a new {@code ThreadPoolExecutor} with the given initial - * parameters. - * - * @param corePoolSize the number of threads to keep in the pool, even - * if they are idle, unless {@code allowCoreThreadTimeOut} is set - * @param maximumPoolSize the maximum number of threads to allow in the - * pool - * @param keepAliveTime when the number of threads is greater than - * the core, this is the maximum time that excess idle threads - * will wait for new tasks before terminating. - * @param unit the time unit for the {@code keepAliveTime} argument - * @param workQueue the queue to use for holding tasks before they are - * executed. This queue will hold only the {@code Runnable} - * tasks submitted by the {@code execute} method. - * @param threadFactory the factory to use when the executor - * creates a new thread - * @param handler the handler to use when execution is blocked - * because the thread bounds and queue capacities are reached + * Creates a new {@code ThreadPoolExecutor} with the given initial parameters. + * + * @param corePoolSize the number of threads to keep in the pool, even if they are idle, unless + * {@code allowCoreThreadTimeOut} is set + * @param maximumPoolSize the maximum number of threads to allow in the pool + * @param keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess + * idle threads will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are executed. This queue will hold only the + * {@code Runnable} tasks submitted by the {@code execute} method. + * @param threadFactory the factory to use when the executor creates a new thread + * @param handler the handler to use when execution is blocked because the thread bounds and queue + * capacities are reached + * * @throws IllegalArgumentException if one of the following holds:
    - * {@code corePoolSize < 0}
    - * {@code keepAliveTime < 0}
    - * {@code maximumPoolSize <= 0}
    - * {@code maximumPoolSize < corePoolSize} - * @throws NullPointerException if {@code workQueue} - * or {@code threadFactory} or {@code handler} is null - */ - public ThreadPoolExecutor(int corePoolSize, - int maximumPoolSize, - long keepAliveTime, - TimeUnit unit, - BlockingQueue workQueue, - ThreadFactory threadFactory, - RejectedExecutionHandler handler) { - if (corePoolSize < 0 || - maximumPoolSize <= 0 || - maximumPoolSize < corePoolSize || - keepAliveTime < 0) { + * {@code corePoolSize < 0}
    + * {@code keepAliveTime < 0}
    + * {@code maximumPoolSize <= 0}
    + * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} or {@code threadFactory} or {@code handler} is null + */ + public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, + BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { + if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) { throw new IllegalArgumentException(); } if (workQueue == null || threadFactory == null || handler == null) { @@ -1376,12 +1120,12 @@ try { executeInternal(command); } catch (RejectedExecutionException rx) { - if (getQueue() instanceof TaskQueue) { + if (getQueue() instanceof RetryableQueue) { // If the Executor is close to maximum pool size, concurrent // calls to execute() may result (due to Tomcat's use of // TaskQueue) in some tasks being rejected rather than queued. // If this happens, add them to the queue. - final TaskQueue queue = (TaskQueue) getQueue(); + final RetryableQueue queue = (RetryableQueue) getQueue(); if (!queue.force(command)) { submittedCount.decrementAndGet(); throw new RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull")); @@ -1395,18 +1139,15 @@ /** - * Executes the given task sometime in the future. The task - * may execute in a new thread or in an existing pooled thread. - * - * If the task cannot be submitted for execution, either because this - * executor has been shutdown or because its capacity has been reached, - * the task is handled by the current {@link RejectedExecutionHandler}. + * Executes the given task sometime in the future. The task may execute in a new thread or in an existing pooled + * thread. If the task cannot be submitted for execution, either because this executor has been shutdown or because + * its capacity has been reached, the task is handled by the current {@link RejectedExecutionHandler}. * * @param command the task to execute - * @throws RejectedExecutionException at discretion of - * {@code RejectedExecutionHandler}, if the task - * cannot be accepted for execution - * @throws NullPointerException if {@code command} is null + * + * @throws RejectedExecutionException at discretion of {@code RejectedExecutionHandler}, if the task cannot be + * accepted for execution + * @throws NullPointerException if {@code command} is null */ private void executeInternal(Runnable command) { if (command == null) { @@ -1415,22 +1156,17 @@ /* * Proceed in 3 steps: * - * 1. If fewer than corePoolSize threads are running, try to - * start a new thread with the given command as its first - * task. The call to addWorker atomically checks runState and - * workerCount, and so prevents false alarms that would add - * threads when it shouldn't, by returning false. + * 1. If fewer than corePoolSize threads are running, try to start a new thread with the given command as its + * first task. The call to addWorker atomically checks runState and workerCount, and so prevents false alarms + * that would add threads when it shouldn't, by returning false. * - * 2. If a task can be successfully queued, then we still need - * to double-check whether we should have added a thread - * (because existing ones died since last checking) or that - * the pool shut down since entry into this method. So we - * recheck state and if necessary roll back the enqueuing if - * stopped, or start a new thread if there are none. + * 2. If a task can be successfully queued, then we still need to double-check whether we should have added a + * thread (because existing ones died since last checking) or that the pool shut down since entry into this + * method. So we recheck state and if necessary roll back the enqueuing if stopped, or start a new thread if + * there are none. * - * 3. If we cannot queue task, then we try to add a new - * thread. If it fails, we know we are shut down or saturated - * and so reject the task. + * 3. If we cannot queue task, then we try to add a new thread. If it fails, we know we are shut down or + * saturated and so reject the task. */ int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { @@ -1441,25 +1177,22 @@ } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); - if (! isRunning(recheck) && remove(command)) { + if (!isRunning(recheck) && remove(command)) { reject(command); } else if (workerCountOf(recheck) == 0) { addWorker(null, false); } - } - else if (!addWorker(command, false)) { + } else if (!addWorker(command, false)) { reject(command); } } /** - * Initiates an orderly shutdown in which previously submitted - * tasks are executed, but no new tasks will be accepted. - * Invocation has no additional effect if already shut down. - * - *

    This method does not wait for previously submitted tasks to - * complete execution. Use {@link #awaitTermination awaitTermination} - * to do that. + * Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be + * accepted. Invocation has no additional effect if already shut down. + *

    + * This method does not wait for previously submitted tasks to complete execution. Use {@link #awaitTermination + * awaitTermination} to do that. * * @throws SecurityException {@inheritDoc} */ @@ -1479,19 +1212,16 @@ } /** - * Attempts to stop all actively executing tasks, halts the - * processing of waiting tasks, and returns a list of the tasks - * that were awaiting execution. These tasks are drained (removed) - * from the task queue upon return from this method. - * - *

    This method does not wait for actively executing tasks to - * terminate. Use {@link #awaitTermination awaitTermination} to - * do that. - * - *

    There are no guarantees beyond best-effort attempts to stop - * processing actively executing tasks. This implementation - * interrupts tasks via {@link Thread#interrupt}; any task that - * fails to respond to interrupts may never terminate. + * Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the + * tasks that were awaiting execution. These tasks are drained (removed) from the task queue upon return from this + * method. + *

    + * This method does not wait for actively executing tasks to terminate. Use {@link #awaitTermination + * awaitTermination} to do that. + *

    + * There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. This + * implementation interrupts tasks via {@link Thread#interrupt}; any task that fails to respond to interrupts may + * never terminate. * * @throws SecurityException {@inheritDoc} */ @@ -1523,13 +1253,10 @@ } /** - * Returns true if this executor is in the process of terminating - * after {@link #shutdown} or {@link #shutdownNow} but has not - * completely terminated. This method may be useful for - * debugging. A return of {@code true} reported a sufficient - * period after shutdown may indicate that submitted tasks have - * ignored or suppressed interruption, causing this executor not - * to properly terminate. + * Returns true if this executor is in the process of terminating after {@link #shutdown} or {@link #shutdownNow} + * but has not completely terminated. This method may be useful for debugging. A return of {@code true} reported a + * sufficient period after shutdown may indicate that submitted tasks have ignored or suppressed interruption, + * causing this executor not to properly terminate. * * @return {@code true} if terminating but not yet terminated */ @@ -1544,8 +1271,7 @@ } @Override - public boolean awaitTermination(long timeout, TimeUnit unit) - throws InterruptedException { + public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { long nanos = unit.toNanos(timeout); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); @@ -1566,7 +1292,9 @@ * Sets the thread factory used to create new threads. * * @param threadFactory the new thread factory + * * @throws NullPointerException if threadFactory is null + * * @see #getThreadFactory */ public void setThreadFactory(ThreadFactory threadFactory) { @@ -1580,6 +1308,7 @@ * Returns the thread factory used to create new threads. * * @return the current thread factory + * * @see #setThreadFactory(ThreadFactory) */ public ThreadFactory getThreadFactory() { @@ -1590,7 +1319,9 @@ * Sets a new handler for unexecutable tasks. * * @param handler the new handler + * * @throws NullPointerException if handler is null + * * @see #getRejectedExecutionHandler */ public void setRejectedExecutionHandler(RejectedExecutionHandler handler) { @@ -1604,6 +1335,7 @@ * Returns the current handler for unexecutable tasks. * * @return the current handler + * * @see #setRejectedExecutionHandler(RejectedExecutionHandler) */ public RejectedExecutionHandler getRejectedExecutionHandler() { @@ -1611,16 +1343,15 @@ } /** - * Sets the core number of threads. This overrides any value set - * in the constructor. If the new value is smaller than the - * current value, excess existing threads will be terminated when - * they next become idle. If larger, new threads will, if needed, - * be started to execute any queued tasks. + * Sets the core number of threads. This overrides any value set in the constructor. If the new value is smaller + * than the current value, excess existing threads will be terminated when they next become idle. If larger, new + * threads will, if needed, be started to execute any queued tasks. * * @param corePoolSize the new core size - * @throws IllegalArgumentException if {@code corePoolSize < 0} - * or {@code corePoolSize} is greater than the {@linkplain - * #getMaximumPoolSize() maximum pool size} + * + * @throws IllegalArgumentException if {@code corePoolSize < 0} or {@code corePoolSize} is greater than the + * {@linkplain #getMaximumPoolSize() maximum pool size} + * * @see #getCorePoolSize */ public void setCorePoolSize(int corePoolSize) { @@ -1649,6 +1380,7 @@ * Returns the core number of threads. * * @return the core number of threads + * * @see #setCorePoolSize */ public int getCorePoolSize() { @@ -1656,21 +1388,18 @@ } /** - * Starts a core thread, causing it to idly wait for work. This - * overrides the default policy of starting core threads only when - * new tasks are executed. This method will return {@code false} - * if all core threads have already been started. + * Starts a core thread, causing it to idly wait for work. This overrides the default policy of starting core + * threads only when new tasks are executed. This method will return {@code false} if all core threads have already + * been started. * * @return {@code true} if a thread was started */ public boolean prestartCoreThread() { - return workerCountOf(ctl.get()) < corePoolSize && - addWorker(null, true); + return workerCountOf(ctl.get()) < corePoolSize && addWorker(null, true); } /** - * Same as prestartCoreThread except arranges that at least one - * thread is started even if corePoolSize is 0. + * Same as prestartCoreThread except arranges that at least one thread is started even if corePoolSize is 0. */ void ensurePrestart() { int wc = workerCountOf(ctl.get()); @@ -1682,9 +1411,8 @@ } /** - * Starts all core threads, causing them to idly wait for work. This - * overrides the default policy of starting core threads only when - * new tasks are executed. + * Starts all core threads, causing them to idly wait for work. This overrides the default policy of starting core + * threads only when new tasks are executed. * * @return the number of threads started */ @@ -1697,15 +1425,12 @@ } /** - * Returns true if this pool allows core threads to time out and - * terminate if no tasks arrive within the keepAlive time, being - * replaced if needed when new tasks arrive. When true, the same - * keep-alive policy applying to non-core threads applies also to - * core threads. When false (the default), core threads are never - * terminated due to lack of incoming tasks. + * Returns true if this pool allows core threads to time out and terminate if no tasks arrive within the keepAlive + * time, being replaced if needed when new tasks arrive. When true, the same keep-alive policy applying to non-core + * threads applies also to core threads. When false (the default), core threads are never terminated due to lack of + * incoming tasks. * - * @return {@code true} if core threads are allowed to time out, - * else {@code false} + * @return {@code true} if core threads are allowed to time out, else {@code false} * * @since 1.6 */ @@ -1714,19 +1439,16 @@ } /** - * Sets the policy governing whether core threads may time out and - * terminate if no tasks arrive within the keep-alive time, being - * replaced if needed when new tasks arrive. When false, core - * threads are never terminated due to lack of incoming - * tasks. When true, the same keep-alive policy applying to - * non-core threads applies also to core threads. To avoid - * continual thread replacement, the keep-alive time must be - * greater than zero when setting {@code true}. This method - * should in general be called before the pool is actively used. + * Sets the policy governing whether core threads may time out and terminate if no tasks arrive within the + * keep-alive time, being replaced if needed when new tasks arrive. When false, core threads are never terminated + * due to lack of incoming tasks. When true, the same keep-alive policy applying to non-core threads applies also to + * core threads. To avoid continual thread replacement, the keep-alive time must be greater than zero when setting + * {@code true}. This method should in general be called before the pool is actively used. * * @param value {@code true} if should time out, else {@code false} - * @throws IllegalArgumentException if value is {@code true} - * and the current keep-alive time is not greater than zero + * + * @throws IllegalArgumentException if value is {@code true} and the current keep-alive time is not greater than + * zero * * @since 1.6 */ @@ -1743,15 +1465,14 @@ } /** - * Sets the maximum allowed number of threads. This overrides any - * value set in the constructor. If the new value is smaller than - * the current value, excess existing threads will be - * terminated when they next become idle. + * Sets the maximum allowed number of threads. This overrides any value set in the constructor. If the new value is + * smaller than the current value, excess existing threads will be terminated when they next become idle. * * @param maximumPoolSize the new maximum - * @throws IllegalArgumentException if the new maximum is - * less than or equal to zero, or - * less than the {@linkplain #getCorePoolSize core pool size} + * + * @throws IllegalArgumentException if the new maximum is less than or equal to zero, or less than the + * {@linkplain #getCorePoolSize core pool size} + * * @see #getMaximumPoolSize */ public void setMaximumPoolSize(int maximumPoolSize) { @@ -1768,6 +1489,7 @@ * Returns the maximum allowed number of threads. * * @return the maximum allowed number of threads + * * @see #setMaximumPoolSize */ public int getMaximumPoolSize() { @@ -1775,19 +1497,18 @@ } /** - * Sets the thread keep-alive time, which is the amount of time - * that threads may remain idle before being terminated. - * Threads that wait this amount of time without processing a - * task will be terminated if there are more than the core - * number of threads currently in the pool, or if this pool - * {@linkplain #allowsCoreThreadTimeOut() allows core thread timeout}. - * This overrides any value set in the constructor. + * Sets the thread keep-alive time, which is the amount of time that threads may remain idle before being + * terminated. Threads that wait this amount of time without processing a task will be terminated if there are more + * than the core number of threads currently in the pool, or if this pool {@linkplain #allowsCoreThreadTimeOut() + * allows core thread timeout}. This overrides any value set in the constructor. * - * @param time the time to wait. A time value of zero will cause - * excess threads to terminate immediately after executing tasks. + * @param time the time to wait. A time value of zero will cause excess threads to terminate immediately after + * executing tasks. * @param unit the time unit of the {@code time} argument - * @throws IllegalArgumentException if {@code time} less than zero or - * if {@code time} is zero and {@code allowsCoreThreadTimeOut} + * + * @throws IllegalArgumentException if {@code time} less than zero or if {@code time} is zero and + * {@code allowsCoreThreadTimeOut} + * * @see #getKeepAliveTime(TimeUnit) */ public void setKeepAliveTime(long time, TimeUnit unit) { @@ -1806,15 +1527,15 @@ } /** - * Returns the thread keep-alive time, which is the amount of time - * that threads may remain idle before being terminated. - * Threads that wait this amount of time without processing a - * task will be terminated if there are more than the core - * number of threads currently in the pool, or if this pool - * {@linkplain #allowsCoreThreadTimeOut() allows core thread timeout}. + * Returns the thread keep-alive time, which is the amount of time that threads may remain idle before being + * terminated. Threads that wait this amount of time without processing a task will be terminated if there are more + * than the core number of threads currently in the pool, or if this pool {@linkplain #allowsCoreThreadTimeOut() + * allows core thread timeout}. * * @param unit the desired time unit of the result + * * @return the time limit + * * @see #setKeepAliveTime(long, TimeUnit) */ public long getKeepAliveTime(TimeUnit unit) { @@ -1835,10 +1556,9 @@ /* User-level queue utilities */ /** - * Returns the task queue used by this executor. Access to the - * task queue is intended primarily for debugging and monitoring. - * This queue may be in active use. Retrieving the task queue - * does not prevent queued tasks from executing. + * Returns the task queue used by this executor. Access to the task queue is intended primarily for debugging and + * monitoring. This queue may be in active use. Retrieving the task queue does not prevent queued tasks from + * executing. * * @return the task queue */ @@ -1847,19 +1567,16 @@ } /** - * Removes this task from the executor's internal queue if it is - * present, thus causing it not to be run if it has not already - * started. - * - *

    This method may be useful as one part of a cancellation - * scheme. It may fail to remove tasks that have been converted - * into other forms before being placed on the internal queue. - * For example, a task entered using {@code submit} might be - * converted into a form that maintains {@code Future} status. - * However, in such cases, method {@link #purge} may be used to - * remove those Futures that have been cancelled. + * Removes this task from the executor's internal queue if it is present, thus causing it not to be run if it has + * not already started. + *

    + * This method may be useful as one part of a cancellation scheme. It may fail to remove tasks that have been + * converted into other forms before being placed on the internal queue. For example, a task entered using + * {@code submit} might be converted into a form that maintains {@code Future} status. However, in such cases, + * method {@link #purge} may be used to remove those Futures that have been cancelled. * * @param task the task to remove + * * @return {@code true} if the task was removed */ public boolean remove(Runnable task) { @@ -1869,31 +1586,22 @@ } /** - * Tries to remove from the work queue all {@link Future} - * tasks that have been cancelled. This method can be useful as a - * storage reclamation operation, that has no other impact on - * functionality. Cancelled tasks are never executed, but may - * accumulate in work queues until worker threads can actively - * remove them. Invoking this method instead tries to remove them now. - * However, this method may fail to remove tasks in - * the presence of interference by other threads. + * Tries to remove from the work queue all {@link Future} tasks that have been cancelled. This method can be useful + * as a storage reclamation operation, that has no other impact on functionality. Cancelled tasks are never + * executed, but may accumulate in work queues until worker threads can actively remove them. Invoking this method + * instead tries to remove them now. However, this method may fail to remove tasks in the presence of interference + * by other threads. */ public void purge() { final BlockingQueue q = workQueue; try { - Iterator it = q.iterator(); - while (it.hasNext()) { - Runnable r = it.next(); - if (r instanceof Future && ((Future)r).isCancelled()) { - it.remove(); - } - } + q.removeIf(r -> r instanceof Future && ((Future) r).isCancelled()); } catch (ConcurrentModificationException fallThrough) { // Take slow path if we encounter interference during traversal. // Make copy for traversal and call remove for cancelled entries. // The slow path is more likely to be O(N*N). for (Object r : q.toArray()) { - if (r instanceof Future && ((Future)r).isCancelled()) { + if (r instanceof Future && ((Future) r).isCancelled()) { q.remove(r); } } @@ -1933,28 +1641,25 @@ try { // Remove rare and surprising possibility of // isTerminated() && getPoolSize() > 0 - return runStateAtLeast(ctl.get(), TIDYING) ? 0 - : workers.size(); + return runStateAtLeast(ctl.get(), TIDYING) ? 0 : workers.size(); } finally { mainLock.unlock(); } } /** - * Returns the current number of threads in the pool. - *
    NOTE: this method only used in {@link TaskQueue#offer(Runnable)}, - * where operations are frequent, can greatly reduce unnecessary - * performance overhead by a lock-free way. + * Returns the current number of threads in the pool.
    + * NOTE: this method only used in {@link TaskQueue#offer(Runnable)}, where operations are frequent, can + * greatly reduce unnecessary performance overhead by a lock-free way. + * * @return the number of threads */ protected int getPoolSizeNoLock() { - return runStateAtLeast(ctl.get(), TIDYING) ? 0 - : workers.size(); + return runStateAtLeast(ctl.get(), TIDYING) ? 0 : workers.size(); } /** - * Returns the approximate number of threads that are actively - * executing tasks. + * Returns the approximate number of threads that are actively executing tasks. * * @return the number of threads */ @@ -1975,8 +1680,7 @@ } /** - * Returns the largest number of threads that have ever - * simultaneously been in the pool. + * Returns the largest number of threads that have ever simultaneously been in the pool. * * @return the number of threads */ @@ -1991,10 +1695,8 @@ } /** - * Returns the approximate total number of tasks that have ever been - * scheduled for execution. Because the states of tasks and - * threads may change dynamically during computation, the returned - * value is only an approximation. + * Returns the approximate total number of tasks that have ever been scheduled for execution. Because the states of + * tasks and threads may change dynamically during computation, the returned value is only an approximation. * * @return the number of tasks */ @@ -2016,11 +1718,9 @@ } /** - * Returns the approximate total number of tasks that have - * completed execution. Because the states of tasks and threads - * may change dynamically during computation, the returned value - * is only an approximation, but one that does not ever decrease - * across successive calls. + * Returns the approximate total number of tasks that have completed execution. Because the states of tasks and + * threads may change dynamically during computation, the returned value is only an approximation, but one that does + * not ever decrease across successive calls. * * @return the number of tasks */ @@ -2045,9 +1745,8 @@ /** - * Returns a string identifying this pool, as well as its state, - * including indications of run state and estimated worker and - * task counts. + * Returns a string identifying this pool, as well as its state, including indications of run state and estimated + * worker and task counts. * * @return a string identifying this pool, as well as its state */ @@ -2071,87 +1770,66 @@ mainLock.unlock(); } int c = ctl.get(); - String runState = - isRunning(c) ? "Running" : - runStateAtLeast(c, TERMINATED) ? "Terminated" : - "Shutting down"; - return super.toString() + - "[" + runState + - ", pool size = " + nworkers + - ", active threads = " + nactive + - ", queued tasks = " + workQueue.size() + - ", completed tasks = " + ncompleted + - "]"; + String runState = isRunning(c) ? "Running" : runStateAtLeast(c, TERMINATED) ? "Terminated" : "Shutting down"; + return super.toString() + "[" + runState + ", pool size = " + nworkers + ", active threads = " + nactive + + ", queued tasks = " + workQueue.size() + ", completed tasks = " + ncompleted + "]"; } /* Extension hooks */ /** - * Method invoked prior to executing the given Runnable in the - * given thread. This method is invoked by thread {@code t} that - * will execute task {@code r}, and may be used to re-initialize - * ThreadLocals, or to perform logging. - * - *

    This implementation does nothing, but may be customized in - * subclasses. Note: To properly nest multiple overridings, subclasses - * should generally invoke {@code super.beforeExecute} at the end of - * this method. + * Method invoked prior to executing the given Runnable in the given thread. This method is invoked by thread + * {@code t} that will execute task {@code r}, and may be used to re-initialize ThreadLocals, or to perform logging. + *

    + * This implementation does nothing, but may be customized in subclasses. Note: To properly nest multiple + * overridings, subclasses should generally invoke {@code super.beforeExecute} at the end of this method. * * @param t the thread that will run task {@code r} * @param r the task that will be executed */ - protected void beforeExecute(Thread t, Runnable r) { } + protected void beforeExecute(Thread t, Runnable r) { + } /** - * Method invoked upon completion of execution of the given Runnable. - * This method is invoked by the thread that executed the task. If - * non-null, the Throwable is the uncaught {@code RuntimeException} - * or {@code Error} that caused execution to terminate abruptly. - * - *

    This implementation does nothing, but may be customized in - * subclasses. Note: To properly nest multiple overridings, subclasses - * should generally invoke {@code super.afterExecute} at the - * beginning of this method. - * - *

    Note: When actions are enclosed in tasks (such as - * {@link java.util.concurrent.FutureTask}) - * either explicitly or via methods such as - * {@code submit}, these task objects catch and maintain - * computational exceptions, and so they do not cause abrupt - * termination, and the internal exceptions are not - * passed to this method. If you would like to trap both kinds of - * failures in this method, you can further probe for such cases, - * as in this sample subclass that prints either the direct cause - * or the underlying exception if a task has been aborted: + * Method invoked upon completion of execution of the given Runnable. This method is invoked by the thread that + * executed the task. If non-null, the Throwable is the uncaught {@code RuntimeException} or {@code Error} that + * caused execution to terminate abruptly. + *

    + * This implementation does nothing, but may be customized in subclasses. Note: To properly nest multiple + * overridings, subclasses should generally invoke {@code super.afterExecute} at the beginning of this method. + *

    + * Note: When actions are enclosed in tasks (such as {@link java.util.concurrent.FutureTask}) either + * explicitly or via methods such as {@code submit}, these task objects catch and maintain computational exceptions, + * and so they do not cause abrupt termination, and the internal exceptions are not passed to this method. + * If you would like to trap both kinds of failures in this method, you can further probe for such cases, as in this + * sample subclass that prints either the direct cause or the underlying exception if a task has been aborted: * *

     {@code
          * class ExtendedExecutor extends ThreadPoolExecutor {
    -     *   // ...
    -     *   protected void afterExecute(Runnable r, Throwable t) {
    -     *     super.afterExecute(r, t);
    -     *     if (t == null
    -     *         && r instanceof Future
    -     *         && ((Future)r).isDone()) {
    -     *       try {
    -     *         Object result = ((Future) r).get();
    -     *       } catch (CancellationException ce) {
    -     *         t = ce;
    -     *       } catch (ExecutionException ee) {
    -     *         t = ee.getCause();
    -     *       } catch (InterruptedException ie) {
    -     *         // ignore/reset
    -     *         Thread.currentThread().interrupt();
    -     *       }
    +     *     // ...
    +     *     protected void afterExecute(Runnable r, Throwable t) {
    +     *         super.afterExecute(r, t);
    +     *         if (t == null && r instanceof Future && ((Future) r).isDone()) {
    +     *             try {
    +     *                 Object result = ((Future) r).get();
    +     *             } catch (CancellationException ce) {
    +     *                 t = ce;
    +     *             } catch (ExecutionException ee) {
    +     *                 t = ee.getCause();
    +     *             } catch (InterruptedException ie) {
    +     *                 // ignore/reset
    +     *                 Thread.currentThread().interrupt();
    +     *             }
    +     *         }
    +     *         if (t != null)
    +     *             System.out.println(t);
          *     }
    -     *     if (t != null)
    -     *       System.out.println(t);
    -     *   }
    -     * }}
    + * } + * } * * @param r the runnable that has completed - * @param t the exception that caused termination, or null if - * execution completed normally + * @param t the exception that caused termination, or null if execution completed normally */ protected void afterExecute(Runnable r, Throwable t) { // Throwing StopPooledThreadException is likely to cause this method to @@ -2169,20 +1847,18 @@ /** - * If the current thread was started before the last time when a context was - * stopped, an exception is thrown so that the current thread is stopped. + * If the current thread was started before the last time when a context was stopped, an exception is thrown so that + * the current thread is stopped. */ protected void stopCurrentThreadIfNeeded() { if (currentThreadShouldBeStopped()) { long lastTime = lastTimeThreadKilledItself.longValue(); if (lastTime + threadRenewalDelay < System.currentTimeMillis()) { - if (lastTimeThreadKilledItself.compareAndSet(lastTime, - System.currentTimeMillis() + 1)) { + if (lastTimeThreadKilledItself.compareAndSet(lastTime, System.currentTimeMillis() + 1)) { // OK, it's really time to dispose of this thread - final String msg = sm.getString( - "threadPoolExecutor.threadStoppedToAvoidPotentialLeak", - Thread.currentThread().getName()); + final String msg = sm.getString("threadPoolExecutor.threadStoppedToAvoidPotentialLeak", + Thread.currentThread().getName()); throw new StopPooledThreadException(msg); } @@ -2195,40 +1871,35 @@ Thread currentThread = Thread.currentThread(); if (threadRenewalDelay >= 0 && currentThread instanceof TaskThread) { TaskThread currentTaskThread = (TaskThread) currentThread; - if (currentTaskThread.getCreationTime() < - this.lastContextStoppedTime.longValue()) { - return true; - } + return currentTaskThread.getCreationTime() < this.lastContextStoppedTime.longValue(); } return false; } /** - * Method invoked when the Executor has terminated. Default - * implementation does nothing. Note: To properly nest multiple - * overridings, subclasses should generally invoke - * {@code super.terminated} within this method. + * Method invoked when the Executor has terminated. Default implementation does nothing. Note: To properly nest + * multiple overridings, subclasses should generally invoke {@code super.terminated} within this method. */ - protected void terminated() { } + protected void terminated() { + } /* Predefined RejectedExecutionHandlers */ /** - * A handler for rejected tasks that runs the rejected task - * directly in the calling thread of the {@code execute} method, - * unless the executor has been shut down, in which case the task - * is discarded. + * A handler for rejected tasks that runs the rejected task directly in the calling thread of the {@code execute} + * method, unless the executor has been shut down, in which case the task is discarded. */ public static class CallerRunsPolicy implements RejectedExecutionHandler { /** * Creates a {@code CallerRunsPolicy}. */ - public CallerRunsPolicy() { } + public CallerRunsPolicy() { + } /** - * Executes task r in the caller's thread, unless the executor - * has been shut down, in which case the task is discarded. + * Executes task r in the caller's thread, unless the executor has been shut down, in which case the task is + * discarded. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task @@ -2242,20 +1913,21 @@ } /** - * A handler for rejected tasks that throws a - * {@link RejectedExecutionException}. + * A handler for rejected tasks that throws a {@link RejectedExecutionException}. */ public static class AbortPolicy implements RejectedExecutionHandler { /** * Creates an {@code AbortPolicy}. */ - public AbortPolicy() { } + public AbortPolicy() { + } /** * Always throws RejectedExecutionException. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task + * * @throws RejectedExecutionException always */ @Override @@ -2266,14 +1938,14 @@ } /** - * A handler for rejected tasks that silently discards the - * rejected task. + * A handler for rejected tasks that silently discards the rejected task. */ public static class DiscardPolicy implements RejectedExecutionHandler { /** * Creates a {@code DiscardPolicy}. */ - public DiscardPolicy() { } + public DiscardPolicy() { + } /** * Does nothing, which has the effect of discarding task r. @@ -2287,34 +1959,35 @@ } /** - * A handler for rejected tasks that discards the oldest unhandled - * request and then retries {@code execute}, unless the executor - * is shut down, in which case the task is discarded. This policy is - * rarely useful in cases where other threads may be waiting for - * tasks to terminate, or failures must be recorded. Instead consider - * using a handler of the form: + * A handler for rejected tasks that discards the oldest unhandled request and then retries {@code execute}, unless + * the executor is shut down, in which case the task is discarded. This policy is rarely useful in cases where other + * threads may be waiting for tasks to terminate, or failures must be recorded. Instead, consider using a handler of + * the form: + * *
     {@code
          * new RejectedExecutionHandler() {
    -     *   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    -     *     Runnable dropped = e.getQueue().poll();
    -     *     if (dropped instanceof Future) {
    -     *       ((Future)dropped).cancel(false);
    -     *       // also consider logging the failure
    +     *     public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    +     *         Runnable dropped = e.getQueue().poll();
    +     *         if (dropped instanceof Future) {
    +     *             ((Future) dropped).cancel(false);
    +     *             // also consider logging the failure
    +     *         }
    +     *         e.execute(r); // retry
          *     }
    -     *     e.execute(r);  // retry
    -     * }}}
    + * } + * } */ public static class DiscardOldestPolicy implements RejectedExecutionHandler { /** * Creates a {@code DiscardOldestPolicy} for the given executor. */ - public DiscardOldestPolicy() { } + public DiscardOldestPolicy() { + } /** - * Obtains and ignores the next task that the executor - * would otherwise execute, if one is immediately available, - * and then retries execution of task r, unless the executor - * is shut down, in which case task r is instead discarded. + * Obtains and ignores the next task that the executor would otherwise execute, if one is immediately available, + * and then retries execution of task r, unless the executor is shut down, in which case task r is instead + * discarded. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task @@ -2339,18 +2012,16 @@ public interface RejectedExecutionHandler { /** - * Method that may be invoked by a {@link ThreadPoolExecutor} when - * {@link ThreadPoolExecutor#execute execute} cannot accept a - * task. This may occur when no more threads or queue slots are - * available because their bounds would be exceeded, or upon - * shutdown of the Executor. + * Method that may be invoked by a {@link ThreadPoolExecutor} when {@link ThreadPoolExecutor#execute execute} + * cannot accept a task. This may occur when no more threads or queue slots are available because their bounds + * would be exceeded, or upon shutdown of the Executor. + *

    + * In the absence of other alternatives, the method may throw an unchecked {@link RejectedExecutionException}, + * which will be propagated to the caller of {@code execute}. * - *

    In the absence of other alternatives, the method may throw - * an unchecked {@link RejectedExecutionException}, which will be - * propagated to the caller of {@code execute}. - * - * @param r the runnable task requested to be executed + * @param r the runnable task requested to be executed * @param executor the executor attempting to execute this task + * * @throws RejectedExecutionException if there is no remedy */ void rejectedExecution(Runnable r, ThreadPoolExecutor executor); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/threads/VirtualThreadExecutor.java tomcat10-10.1.52/java/org/apache/tomcat/util/threads/VirtualThreadExecutor.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/threads/VirtualThreadExecutor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/threads/VirtualThreadExecutor.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,7 +33,7 @@ private static final StringManager sm = StringManager.getManager(VirtualThreadExecutor.class); - private CountDownLatch shutdown = new CountDownLatch(1); + private final CountDownLatch shutdown = new CountDownLatch(1); private final JreCompat jreCompat = JreCompat.getInstance(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/ObjectReflectionPropertyInspector.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,90 +36,71 @@ public static void main(String... args) throws Exception { if (args.length == 0) { - System.err.println("Usage:\n\t"+ - "org.apache.tomcat.util.xreflection.ObjectReflectionPropertyInspector" + - " " - ); + System.err.println("Usage:\n\t" + "org.apache.tomcat.util.xreflection.ObjectReflectionPropertyInspector" + + " "); System.exit(1); } File outputDir = new File(args[0]); if (!outputDir.exists() || !outputDir.isDirectory()) { - System.err.println("Invalid output directory: "+ outputDir.getAbsolutePath()); + System.err.println("Invalid output directory: " + outputDir.getAbsolutePath()); System.exit(1); } - Set baseClasses = getKnownClasses() - .stream() - .map(ObjectReflectionPropertyInspector::processClass) - .collect(Collectors.toCollection(LinkedHashSet::new)); - generateCode( - baseClasses, - "org.apache.tomcat.util", - outputDir, - "XReflectionIntrospectionUtils" - ); + Set baseClasses = + getKnownClasses().stream().map(ObjectReflectionPropertyInspector::processClass) + .collect(Collectors.toCollection(LinkedHashSet::new)); + generateCode(baseClasses, "org.apache.tomcat.util", outputDir, "XReflectionIntrospectionUtils"); } private static Set> getKnownClasses() throws ClassNotFoundException { - return - Collections.unmodifiableSet(new LinkedHashSet<>( - Arrays.asList( - Class.forName("org.apache.catalina.authenticator.jaspic.SimpleAuthConfigProvider"), - Class.forName("org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations$Property"), - Class.forName("org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations$Provider"), - Class.forName("org.apache.catalina.connector.Connector"), - Class.forName("org.apache.catalina.core.ContainerBase"), - Class.forName("org.apache.catalina.core.StandardContext"), - Class.forName("org.apache.catalina.core.StandardEngine"), - Class.forName("org.apache.catalina.core.StandardHost"), - Class.forName("org.apache.catalina.core.StandardServer"), - Class.forName("org.apache.catalina.core.StandardService"), - Class.forName("org.apache.catalina.filters.AddDefaultCharsetFilter"), - Class.forName("org.apache.catalina.filters.RestCsrfPreventionFilter"), - Class.forName("org.apache.catalina.loader.ParallelWebappClassLoader"), - Class.forName("org.apache.catalina.loader.WebappClassLoaderBase"), - Class.forName("org.apache.catalina.realm.UserDatabaseRealm"), - Class.forName("org.apache.catalina.valves.AccessLogValve"), - Class.forName("org.apache.coyote.AbstractProtocol"), - Class.forName("org.apache.coyote.ajp.AbstractAjpProtocol"), - Class.forName("org.apache.coyote.ajp.AjpNio2Protocol"), - Class.forName("org.apache.coyote.ajp.AjpNioProtocol"), - Class.forName("org.apache.coyote.http11.AbstractHttp11JsseProtocol"), - Class.forName("org.apache.coyote.http11.AbstractHttp11Protocol"), - Class.forName("org.apache.coyote.http11.Http11Nio2Protocol"), - Class.forName("org.apache.coyote.http11.Http11NioProtocol"), - Class.forName("org.apache.tomcat.util.descriptor.web.ContextResource"), - Class.forName("org.apache.tomcat.util.descriptor.web.ResourceBase"), - Class.forName("org.apache.tomcat.util.modeler.AttributeInfo"), - Class.forName("org.apache.tomcat.util.modeler.FeatureInfo"), - Class.forName("org.apache.tomcat.util.modeler.ManagedBean"), - Class.forName("org.apache.tomcat.util.modeler.OperationInfo"), - Class.forName("org.apache.tomcat.util.modeler.ParameterInfo"), - Class.forName("org.apache.tomcat.util.net.AbstractEndpoint"), - Class.forName("org.apache.tomcat.util.net.Nio2Endpoint"), - Class.forName("org.apache.tomcat.util.net.NioEndpoint"), - Class.forName("org.apache.tomcat.util.net.SocketProperties") - ) - ) - ); - } - - //types of properties that IntrospectionUtils.setProperty supports - private static final Set> ALLOWED_TYPES = Collections.unmodifiableSet(new LinkedHashSet<>( - Arrays.asList( - Boolean.TYPE, - Integer.TYPE, - Long.TYPE, - String.class, - InetAddress.class - ) - )); - private static Map, SetPropertyClass> classes = new LinkedHashMap<>(); - - public static void generateCode(Set baseClasses, String packageName, File location, String className) throws Exception { - String packageDirectory = packageName.replace('.','/'); + return Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList( + Class.forName("org.apache.catalina.authenticator.jaspic.SimpleAuthConfigProvider"), + Class.forName("org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations$Property"), + Class.forName("org.apache.catalina.authenticator.jaspic.PersistentProviderRegistrations$Provider"), + Class.forName("org.apache.catalina.connector.Connector"), + Class.forName("org.apache.catalina.core.ContainerBase"), + Class.forName("org.apache.catalina.core.StandardContext"), + Class.forName("org.apache.catalina.core.StandardEngine"), + Class.forName("org.apache.catalina.core.StandardHost"), + Class.forName("org.apache.catalina.core.StandardServer"), + Class.forName("org.apache.catalina.core.StandardService"), + Class.forName("org.apache.catalina.filters.AddDefaultCharsetFilter"), + Class.forName("org.apache.catalina.filters.RestCsrfPreventionFilter"), + Class.forName("org.apache.catalina.loader.ParallelWebappClassLoader"), + Class.forName("org.apache.catalina.loader.WebappClassLoaderBase"), + Class.forName("org.apache.catalina.realm.UserDatabaseRealm"), + Class.forName("org.apache.catalina.valves.AccessLogValve"), + Class.forName("org.apache.coyote.AbstractProtocol"), + Class.forName("org.apache.coyote.ajp.AbstractAjpProtocol"), + Class.forName("org.apache.coyote.ajp.AjpNio2Protocol"), + Class.forName("org.apache.coyote.ajp.AjpNioProtocol"), + Class.forName("org.apache.coyote.http11.AbstractHttp11JsseProtocol"), + Class.forName("org.apache.coyote.http11.AbstractHttp11Protocol"), + Class.forName("org.apache.coyote.http11.Http11Nio2Protocol"), + Class.forName("org.apache.coyote.http11.Http11NioProtocol"), + Class.forName("org.apache.tomcat.util.descriptor.web.ContextResource"), + Class.forName("org.apache.tomcat.util.descriptor.web.ResourceBase"), + Class.forName("org.apache.tomcat.util.modeler.AttributeInfo"), + Class.forName("org.apache.tomcat.util.modeler.FeatureInfo"), + Class.forName("org.apache.tomcat.util.modeler.ManagedBean"), + Class.forName("org.apache.tomcat.util.modeler.OperationInfo"), + Class.forName("org.apache.tomcat.util.modeler.ParameterInfo"), + Class.forName("org.apache.tomcat.util.net.AbstractEndpoint"), + Class.forName("org.apache.tomcat.util.net.Nio2Endpoint"), + Class.forName("org.apache.tomcat.util.net.NioEndpoint"), + Class.forName("org.apache.tomcat.util.net.SocketProperties")))); + } + + // types of properties that IntrospectionUtils.setProperty supports + private static final Set> ALLOWED_TYPES = Collections.unmodifiableSet( + new LinkedHashSet<>(Arrays.asList(Boolean.TYPE, Integer.TYPE, Long.TYPE, String.class, InetAddress.class))); + private static final Map,SetPropertyClass> classes = new LinkedHashMap<>(); + + public static void generateCode(Set baseClasses, String packageName, File location, + String className) throws Exception { + String packageDirectory = packageName.replace('.', '/'); File sourceFileLocation = new File(location, packageDirectory); ReflectionLessCodeGenerator.generateCode(sourceFileLocation, className, packageName, baseClasses); } @@ -130,19 +111,14 @@ } private static boolean isAllowedSetMethod(Method method) { - return method.getName().startsWith("set") && - method.getParameterTypes() != null && - method.getParameterTypes().length == 1 && - ALLOWED_TYPES.contains(method.getParameterTypes()[0]) && - !Modifier.isPrivate(method.getModifiers()); + return method.getName().startsWith("set") && method.getParameterTypes().length == 1 && + ALLOWED_TYPES.contains(method.getParameterTypes()[0]) && !Modifier.isPrivate(method.getModifiers()); } private static boolean isAllowedGetMethod(Method method) { return (method.getName().startsWith("get") || method.getName().startsWith("is")) && - (method.getParameterTypes() == null || - method.getParameterTypes().length == 0) && - ALLOWED_TYPES.contains(method.getReturnType()) && - !Modifier.isPrivate(method.getModifiers()); + method.getParameterTypes().length == 0 && ALLOWED_TYPES.contains(method.getReturnType()) && + !Modifier.isPrivate(method.getModifiers()); } @@ -157,13 +133,15 @@ } static Method findGetter(Class declaringClass, String propertyName) { - for (String getterName : Arrays.asList("get" + IntrospectionUtils.capitalize(propertyName), "is" + propertyName)) { + for (String getterName : Arrays.asList("get" + IntrospectionUtils.capitalize(propertyName), + "is" + propertyName)) { try { Method method = declaringClass.getMethod(getterName); if (!Modifier.isPrivate(method.getModifiers())) { return method; } } catch (NoSuchMethodException e) { + // Ignore } } try { @@ -172,6 +150,7 @@ return method; } } catch (NoSuchMethodException e) { + // Ignore } return null; @@ -184,6 +163,7 @@ return method; } } catch (NoSuchMethodException e) { + // Ignore } try { Method method = declaringClass.getMethod("setProperty", String.class, String.class); @@ -191,19 +171,19 @@ return method; } } catch (NoSuchMethodException e) { + // Ignore } return null; } static String decapitalize(String name) { - if (name == null || name.length() == 0) { + if (name == null || name.isEmpty()) { return name; } - if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && - Character.isUpperCase(name.charAt(0))) { + if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))) { return name; } - char chars[] = name.toCharArray(); + char[] chars = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); } @@ -218,13 +198,8 @@ Class propertyType = method.getParameterTypes()[0]; Method getter = findGetter(clazz, propertyName); Method setter = findSetter(clazz, propertyName, propertyType); - ReflectionProperty property = new ReflectionProperty( - spc.getClazz().getName(), - propertyName, - propertyType, - setter, - getter - ); + ReflectionProperty property = + new ReflectionProperty(spc.getClazz().getName(), propertyName, propertyType, setter, getter); spc.addProperty(property); } else if (isAllowedGetMethod(method)) { boolean startsWithIs = method.getName().startsWith("is"); @@ -232,13 +207,8 @@ Class propertyType = method.getReturnType(); Method getter = findGetter(clazz, propertyName); Method setter = findSetter(clazz, propertyName, propertyType); - ReflectionProperty property = new ReflectionProperty( - spc.getClazz().getName(), - propertyName, - propertyType, - setter, - getter - ); + ReflectionProperty property = + new ReflectionProperty(spc.getClazz().getName(), propertyName, propertyType, setter, getter); spc.addProperty(property); } } @@ -246,22 +216,11 @@ final Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (isAllowedField(field)) { - Method getter = findGetter( - field.getDeclaringClass(), - IntrospectionUtils.capitalize(field.getName()) - ); - Method setter = findSetter( - field.getDeclaringClass(), - IntrospectionUtils.capitalize(field.getName()), - field.getType() - ); - ReflectionProperty property = new ReflectionProperty( - spc.getClazz().getName(), - field.getName(), - field.getType(), - setter, - getter - ); + Method getter = findGetter(field.getDeclaringClass(), IntrospectionUtils.capitalize(field.getName())); + Method setter = findSetter(field.getDeclaringClass(), IntrospectionUtils.capitalize(field.getName()), + field.getType()); + ReflectionProperty property = new ReflectionProperty(spc.getClazz().getName(), field.getName(), + field.getType(), setter, getter); spc.addProperty(property); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/xreflection/ReflectionLessCodeGenerator.java tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/ReflectionLessCodeGenerator.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/xreflection/ReflectionLessCodeGenerator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/ReflectionLessCodeGenerator.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,13 +33,10 @@ return indent; } - static void generateCode( - File directory, - String className, - String packageName, - Set baseClasses - ) throws IOException { - //begin - class + static void generateCode(File directory, String className, String packageName, Set baseClasses) + throws IOException { + //@formatter:off + // begin - class StringBuilder code = new StringBuilder(AL20_HEADER) .append("package ") .append(packageName) @@ -52,7 +49,7 @@ .append(System.lineSeparator()) .append(System.lineSeparator()); - //begin - isEnabled method + // begin - isEnabled method code.append(getIndent(1)) .append("static boolean isEnabled() {") .append(System.lineSeparator()) @@ -64,9 +61,9 @@ .append(System.lineSeparator()) .append(System.lineSeparator()) ; - //end - isEnabled method + // end - isEnabled method - //begin - getInetAddress method + // begin - getInetAddress method code.append(getIndent(1)) .append("private static java.net.InetAddress getInetAddress(String value) {") .append(System.lineSeparator()) @@ -84,9 +81,9 @@ .append(System.lineSeparator()) .append(System.lineSeparator()) ; - //end - getInetAddress method + // end - getInetAddress method - //begin - getPropertyInternal method + // begin - getPropertyInternal method code.append(getIndent(1)) .append("static Object getPropertyInternal(Object ") .append(SetPropertyClass.OBJECT_VAR_NAME) @@ -107,7 +104,7 @@ .append("switch (checkThisClass.getName()) {") .append(System.lineSeparator()); - //generate case statements for getPropertyInternal + // generate case statements for getPropertyInternal generateCaseStatementsForGetPropertyInternal(baseClasses, code); @@ -127,13 +124,13 @@ .append(getIndent(1)) .append('}') .append(System.lineSeparator()); - //end - getPropertyInternal method + // end - getPropertyInternal method - //begin - getPropertyForXXX methods + // begin - getPropertyForXXX methods generateGetPropertyForMethods(baseClasses, code); - //end - getPropertyForXXX methods + // end - getPropertyForXXX methods - //begin - setPropertyInternal method + // begin - setPropertyInternal method code.append(getIndent(1)) .append("static boolean setPropertyInternal(Object ") .append(SetPropertyClass.OBJECT_VAR_NAME) @@ -155,7 +152,7 @@ .append("switch (checkThisClass.getName()) {") .append(System.lineSeparator()); - //generate case statements for setPropertyInternal + // generate case statements for setPropertyInternal generateCaseStatementsForSetPropertyInternal(baseClasses, code); @@ -175,16 +172,17 @@ .append(getIndent(1)) .append('}') .append(System.lineSeparator()); - //end - setPropertyInternal method + // end - setPropertyInternal method - //begin - setPropertyForXXX methods + // begin - setPropertyForXXX methods generateSetPropertyForMethods(baseClasses, code); - //end - setPropertyForXXX methods + // end - setPropertyForXXX methods code.append('}') .append(System.lineSeparator()); - //end - class - File destination = new File(directory, className+".java"); + // end - class + //@formatter:on + File destination = new File(directory, className + ".java"); try (BufferedWriter writer = new BufferedWriter(new FileWriter(destination, false))) { writer.write(code.toString()); writer.flush(); @@ -201,7 +199,8 @@ } } - private static void generateCaseStatementsForSetPropertyInternal(Set baseClasses, StringBuilder code) { + private static void generateCaseStatementsForSetPropertyInternal(Set baseClasses, + StringBuilder code) { for (SetPropertyClass clazz : baseClasses) { generateCaseStatementForSetPropertyInternal(clazz, code); } @@ -211,9 +210,7 @@ for (SetPropertyClass child : clazz.getChildren()) { generateSetPropertyForMethod(child, code); } - code.append(clazz.generateSetPropertyForMethod()) - .append(System.lineSeparator()) - .append(System.lineSeparator()); + code.append(clazz.generateSetPropertyForMethod()).append(System.lineSeparator()).append(System.lineSeparator()); } private static void generateSetPropertyForMethods(Set baseClasses, StringBuilder code) { @@ -223,7 +220,6 @@ } - private static void generateCaseStatementForGetPropertyInternal(SetPropertyClass clazz, StringBuilder code) { for (SetPropertyClass child : clazz.getChildren()) { generateCaseStatementForGetPropertyInternal(child, code); @@ -233,7 +229,8 @@ } } - private static void generateCaseStatementsForGetPropertyInternal(Set baseClasses, StringBuilder code) { + private static void generateCaseStatementsForGetPropertyInternal(Set baseClasses, + StringBuilder code) { for (SetPropertyClass clazz : baseClasses) { generateCaseStatementForGetPropertyInternal(clazz, code); } @@ -243,9 +240,7 @@ for (SetPropertyClass child : clazz.getChildren()) { generateGetPropertyForMethod(child, code); } - code.append(clazz.generateGetPropertyForMethod()) - .append(System.lineSeparator()) - .append(System.lineSeparator()); + code.append(clazz.generateGetPropertyForMethod()).append(System.lineSeparator()).append(System.lineSeparator()); } private static void generateGetPropertyForMethods(Set baseClasses, StringBuilder code) { @@ -254,20 +249,17 @@ } } - private static final String AL20_HEADER = "/*\n" + - " * Licensed to the Apache Software Foundation (ASF) under one or more\n" + - " * contributor license agreements. See the NOTICE file distributed with\n" + - " * this work for additional information regarding copyright ownership.\n" + - " * The ASF licenses this file to You under the Apache License, Version 2.0\n" + - " * (the \"License\"); you may not use this file except in compliance with\n" + - " * the License. You may obtain a copy of the License at\n" + - " *\n" + - " * http://www.apache.org/licenses/LICENSE-2.0\n" + - " *\n" + - " * Unless required by applicable law or agreed to in writing, software\n" + - " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + - " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + - " * See the License for the specific language governing permissions and\n" + - " * limitations under the License.\n" + - " */\n"; + private static final String AL20_HEADER = + "/*\n" + " * Licensed to the Apache Software Foundation (ASF) under one or more\n" + + " * contributor license agreements. See the NOTICE file distributed with\n" + + " * this work for additional information regarding copyright ownership.\n" + + " * The ASF licenses this file to You under the Apache License, Version 2.0\n" + + " * (the \"License\"); you may not use this file except in compliance with\n" + + " * the License. You may obtain a copy of the License at\n" + " *\n" + + " * http://www.apache.org/licenses/LICENSE-2.0\n" + " *\n" + + " * Unless required by applicable law or agreed to in writing, software\n" + + " * distributed under the License is distributed on an \"AS IS\" BASIS,\n" + + " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" + + " * See the License for the specific language governing permissions and\n" + + " * limitations under the License.\n" + " */\n"; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/ReflectionProperty.java 2026-01-23 19:33:36.000000000 +0000 @@ -116,9 +116,7 @@ @Override public String toString() { - return "ReflectionProperty{" + "name='" + propertyName + '\'' + - ", type=" + propertyType + - '}'; + return "ReflectionProperty{" + "name='" + propertyName + '\'' + ", type=" + propertyType + '}'; } @Override diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java --- tomcat10-10.1.34/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/util/xreflection/SetPropertyClass.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,7 @@ import org.apache.tomcat.util.IntrospectionUtils; -final class SetPropertyClass implements Comparable { +public final class SetPropertyClass implements Comparable { static final String OBJECT_VAR_NAME = "o"; static final String NAME_VAR_NAME = "name"; @@ -32,8 +32,8 @@ private final SetPropertyClass parent; private final Class clazz; - private Set children = new TreeSet<>(); - private Set properties = new TreeSet<>(); + private final Set children = new TreeSet<>(); + private final Set properties = new TreeSet<>(); private final boolean isAbstract; private final Method genericSetPropertyMethod; private final Method genericGetPropertyMethod; @@ -118,8 +118,7 @@ @Override public String toString() { - return "SetPropertyClass{" + "clazz=" + clazz.getName() + - '}'; + return "SetPropertyClass{" + "clazz=" + clazz.getName() + '}'; } public void addProperty(ReflectionProperty property) { @@ -127,38 +126,38 @@ } - public String generateSetPropertyMethod(ReflectionProperty property) { - //this property has a setProperty method + // this property has a setProperty method if (property.hasSetPropertySetter()) { - return "((" + this.getClazz().getName().replace('$','.') + ")" + OBJECT_VAR_NAME + ")." + - property.getSetMethod().getName() + "(" + NAME_VAR_NAME + ", " + VALUE_VAR_NAME + ");"; + return "((" + this.getClazz().getName().replace('$', '.') + ")" + OBJECT_VAR_NAME + ")." + + property.getSetMethod().getName() + "(" + NAME_VAR_NAME + ", " + VALUE_VAR_NAME + ");"; } - //direct setter + // direct setter if (property.hasSetter()) { - return "((" + this.getClazz().getName().replace('$','.') + ")" + OBJECT_VAR_NAME + ")." + - property.getSetMethod().getName() + "(" + property.getConversion(VALUE_VAR_NAME) + ");"; + return "((" + this.getClazz().getName().replace('$', '.') + ")" + OBJECT_VAR_NAME + ")." + + property.getSetMethod().getName() + "(" + property.getConversion(VALUE_VAR_NAME) + ");"; } return null; } public String generateGetPropertyMethod(ReflectionProperty property) { - //this property has a getProperty method + // this property has a getProperty method if (property.hasGetPropertyGetter()) { - return "result = ((" + this.getClazz().getName().replace('$','.') + ")" + OBJECT_VAR_NAME + ")." + - property.getGetMethod().getName() + "(" + NAME_VAR_NAME + ");"; + return "result = ((" + this.getClazz().getName().replace('$', '.') + ")" + OBJECT_VAR_NAME + ")." + + property.getGetMethod().getName() + "(" + NAME_VAR_NAME + ");"; } - //direct getter + // direct getter if (property.hasGetter()) { - return "result = ((" + this.getClazz().getName().replace('$','.') + ")" + OBJECT_VAR_NAME + ")." + - property.getGetMethod().getName() + "();"; + return "result = ((" + this.getClazz().getName().replace('$', '.') + ")" + OBJECT_VAR_NAME + ")." + + property.getGetMethod().getName() + "();"; } return null; } public String generateSetPropertyForMethod() { + //@formatter:off StringBuilder code = new StringBuilder(ReflectionLessCodeGenerator.getIndent(1)) .append(generatesSetPropertyForMethodHeader()) .append(System.lineSeparator()) @@ -168,7 +167,7 @@ .append(") {") .append(System.lineSeparator()); - //case statements for each property + // case statements for each property for (ReflectionProperty property : getProperties()) { String invocation = generateSetPropertyMethod(property); if (invocation != null) { @@ -183,25 +182,22 @@ .append(System.lineSeparator()) .append(ReflectionLessCodeGenerator.getIndent(4)) .append("return true;") - .append(System.lineSeparator()) - ; + .append(System.lineSeparator()); } else { - code.append(ReflectionLessCodeGenerator.getIndent(3)) - .append("//no set" + IntrospectionUtils.capitalize(property.getPropertyName())+ " method found on this class") - .append(System.lineSeparator()) - ; + code.append(ReflectionLessCodeGenerator.getIndent(3)).append("//no set") + .append(IntrospectionUtils.capitalize( + property.getPropertyName())).append(" method found on this class") + .append(System.lineSeparator()); } } - - - //end switch statement + // end switch statement code.append(ReflectionLessCodeGenerator.getIndent(2)) .append('}') .append(System.lineSeparator()); - //we have a generic setProperty(String, String) method, invoke it + // we have a generic setProperty(String, String) method, invoke it if (getGenericSetPropertyMethod() != null) { ReflectionProperty p = new ReflectionProperty( clazz.getName(), @@ -226,7 +222,7 @@ .append(System.lineSeparator()); } - //invoke parent or return false + // invoke parent or return false code.append(ReflectionLessCodeGenerator.getIndent(2)) .append("return ") .append(getSetPropertyForExitStatement()) @@ -235,18 +231,20 @@ .append('}'); return code.toString(); + //@formatter:on } private String getSetPropertyForExitStatement() { return (getParent() != null) ? - //invoke the parent if we have one - getParent().generateParentSetPropertyForMethodInvocation() : - //if we invoke setProperty, return true, return false otherwise - getGenericSetPropertyMethod() != null ? "true;" : "false;"; + // invoke the parent if we have one + getParent().generateParentSetPropertyForMethodInvocation() : + // if we invoke setProperty, return true, return false otherwise + getGenericSetPropertyMethod() != null ? "true;" : "false;"; } public String generateInvocationSetForPropertyCaseStatement(int level) { + //@formatter:off StringBuilder code = new StringBuilder(ReflectionLessCodeGenerator.getIndent(level)) .append("case \"") .append(getClazz().getName()) @@ -257,6 +255,7 @@ .append(generateParentSetPropertyForMethodInvocation()) .append(System.lineSeparator()); return code.toString(); + //@formatter:on } public String generateParentSetPropertyForMethodInvocation() { @@ -265,8 +264,9 @@ for (String s : classParts) { methodInvocation.append(IntrospectionUtils.capitalize(s)); } + //@formatter:off methodInvocation.append('(') - .append(OBJECT_VAR_NAME) + .append(OBJECT_VAR_NAME) .append(", ") .append(NAME_VAR_NAME) .append(", ") @@ -275,6 +275,7 @@ .append(SETP_VAR_NAME) .append(");"); return methodInvocation.toString(); + //@formatter:on } public String generatesSetPropertyForMethodHeader() { @@ -283,6 +284,7 @@ for (String s : classParts) { methodInvocation.append(IntrospectionUtils.capitalize(s)); } + //@formatter:off methodInvocation.append("(Object ") .append(OBJECT_VAR_NAME) .append(", String ") @@ -293,9 +295,11 @@ .append(SETP_VAR_NAME) .append(") {"); return methodInvocation.toString(); + //@formatter:on } public String generateInvocationGetForPropertyCaseStatement(int level) { + //@formatter:off StringBuilder code = new StringBuilder(ReflectionLessCodeGenerator.getIndent(level)) .append("case \"") .append(getClazz().getName()) @@ -307,9 +311,9 @@ .append(System.lineSeparator()) .append(ReflectionLessCodeGenerator.getIndent(level+1)) .append("break;") - .append(System.lineSeparator()) - ; + .append(System.lineSeparator()); return code.toString(); + //@formatter:on } public String generateParentGetPropertyForMethodInvocation() { @@ -318,12 +322,14 @@ for (String s : classParts) { methodInvocation.append(IntrospectionUtils.capitalize(s)); } + //@formatter:off methodInvocation.append('(') .append(OBJECT_VAR_NAME) .append(", ") .append(NAME_VAR_NAME) .append(");"); return methodInvocation.toString(); + //@formatter:on } public String generatesGetPropertyForMethodHeader() { @@ -332,12 +338,14 @@ for (String s : classParts) { methodInvocation.append(IntrospectionUtils.capitalize(s)); } + //@formatter:off methodInvocation.append("(Object ") .append(OBJECT_VAR_NAME) .append(", String ") .append(NAME_VAR_NAME) .append(") {"); return methodInvocation.toString(); + //@formatter:on } private String getGetPropertyForExitStatement() { @@ -349,6 +357,7 @@ public String generateGetPropertyForMethod() { + //@formatter:off StringBuilder code = new StringBuilder(ReflectionLessCodeGenerator.getIndent(1)) .append(generatesGetPropertyForMethodHeader()) .append(System.lineSeparator()) @@ -361,7 +370,7 @@ .append(") {") .append(System.lineSeparator()); - //case statements for each property + // case statements for each property for (ReflectionProperty property : getProperties()) { String invocation = generateGetPropertyMethod(property); if (invocation != null) { @@ -376,23 +385,20 @@ .append(System.lineSeparator()) .append(ReflectionLessCodeGenerator.getIndent(4)) .append("break;") - .append(System.lineSeparator()) - ; - + .append(System.lineSeparator()); } else { - code.append(ReflectionLessCodeGenerator.getIndent(3)) - .append("//no get" + IntrospectionUtils.capitalize(property.getPropertyName())+ " method found on this class") - .append(System.lineSeparator()) - ; + code.append(ReflectionLessCodeGenerator.getIndent(3)).append("//no get") + .append(IntrospectionUtils.capitalize(property.getPropertyName())).append(" method found on this class") + .append(System.lineSeparator()); } } - //end switch statement + // end switch statement code.append(ReflectionLessCodeGenerator.getIndent(2)) .append('}') .append(System.lineSeparator()); - //invoke parent or return null + // invoke parent or return null code.append(ReflectionLessCodeGenerator.getIndent(2)) .append("if (result == null) {") .append(System.lineSeparator()) @@ -402,10 +408,9 @@ .append(System.lineSeparator()) .append(ReflectionLessCodeGenerator.getIndent(2)) .append('}') - .append(System.lineSeparator()) - ; + .append(System.lineSeparator()); - //we have a generic getProperty(String, String) method, invoke it + // we have a generic getProperty(String, String) method, invoke it if (getGenericGetPropertyMethod() != null) { ReflectionProperty p = new ReflectionProperty( clazz.getName(), @@ -431,9 +436,8 @@ .append('}') .append(System.lineSeparator()); - - return code.toString(); + //@formatter:on } @Override diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/AsyncChannelGroupUtil.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelGroupUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/AsyncChannelGroupUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelGroupUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -81,12 +81,12 @@ // These are the same settings as the default // AsynchronousChannelGroup int initialSize = Runtime.getRuntime().availableProcessors(); - ExecutorService executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, - TimeUnit.SECONDS, new SynchronousQueue<>(), new AsyncIOThreadFactory()); + ExecutorService executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, + new SynchronousQueue<>(), new AsyncIOThreadFactory()); try { return AsynchronousChannelGroup.withCachedThreadPool(executorService, initialSize); - } catch (IOException e) { + } catch (IOException ioe) { // No good reason for this to happen. throw new IllegalStateException(sm.getString("asyncChannelGroup.createFail")); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/AsyncChannelWrapper.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelWrapper.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/AsyncChannelWrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelWrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,12 +33,12 @@ Future read(ByteBuffer dst); - void read(ByteBuffer dst, A attachment, CompletionHandler handler); + void read(ByteBuffer dst, A attachment, CompletionHandler handler); Future write(ByteBuffer src); void write(ByteBuffer[] srcs, int offset, int length, long timeout, TimeUnit unit, A attachment, - CompletionHandler handler); + CompletionHandler handler); void close(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/AsyncChannelWrapperNonSecure.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelWrapperNonSecure.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/AsyncChannelWrapperNonSecure.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelWrapperNonSecure.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,7 +46,7 @@ } @Override - public void read(ByteBuffer dst, A attachment, CompletionHandler handler) { + public void read(ByteBuffer dst, A attachment, CompletionHandler handler) { socketChannel.read(dst, attachment, handler); } @@ -57,7 +57,7 @@ @Override public void write(ByteBuffer[] srcs, int offset, int length, long timeout, TimeUnit unit, - A attachment, CompletionHandler handler) { + A attachment, CompletionHandler handler) { socketChannel.write(srcs, offset, length, timeout, unit, attachment, handler); } @@ -65,7 +65,7 @@ public void close() { try { socketChannel.close(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/AsyncChannelWrapperSecure.java 2026-01-23 19:33:36.000000000 +0000 @@ -59,8 +59,8 @@ private final ByteBuffer socketWriteBuffer; // One thread for read, one for write private final ExecutorService executor = Executors.newFixedThreadPool(2, new SecureIOThreadFactory()); - private AtomicBoolean writing = new AtomicBoolean(false); - private AtomicBoolean reading = new AtomicBoolean(false); + private final AtomicBoolean writing = new AtomicBoolean(false); + private final AtomicBoolean reading = new AtomicBoolean(false); public AsyncChannelWrapperSecure(AsynchronousSocketChannel socketChannel, SSLEngine sslEngine) { this.socketChannel = socketChannel; @@ -73,7 +73,7 @@ @Override public Future read(ByteBuffer dst) { - WrapperFuture future = new WrapperFuture<>(); + WrapperFuture future = new WrapperFuture<>(); if (!reading.compareAndSet(false, true)) { throw new IllegalStateException(sm.getString("asyncChannelWrapperSecure.concurrentRead")); @@ -87,9 +87,9 @@ } @Override - public void read(ByteBuffer dst, A attachment, CompletionHandler handler) { + public void read(ByteBuffer dst, A attachment, CompletionHandler handler) { - WrapperFuture future = new WrapperFuture<>(handler, attachment); + WrapperFuture future = new WrapperFuture<>(handler, attachment); if (!reading.compareAndSet(false, true)) { throw new IllegalStateException(sm.getString("asyncChannelWrapperSecure.concurrentRead")); @@ -103,7 +103,7 @@ @Override public Future write(ByteBuffer src) { - WrapperFuture inner = new WrapperFuture<>(); + WrapperFuture inner = new WrapperFuture<>(); if (!writing.compareAndSet(false, true)) { throw new IllegalStateException(sm.getString("asyncChannelWrapperSecure.concurrentWrite")); @@ -113,15 +113,14 @@ executor.execute(writeTask); - Future future = new LongToIntegerFuture(inner); - return future; + return new LongToIntegerFuture(inner); } @Override public void write(ByteBuffer[] srcs, int offset, int length, long timeout, TimeUnit unit, - A attachment, CompletionHandler handler) { + A attachment, CompletionHandler handler) { - WrapperFuture future = new WrapperFuture<>(handler, attachment); + WrapperFuture future = new WrapperFuture<>(handler, attachment); if (!writing.compareAndSet(false, true)) { throw new IllegalStateException(sm.getString("asyncChannelWrapperSecure.concurrentWrite")); @@ -136,8 +135,12 @@ public void close() { try { socketChannel.close(); - } catch (IOException e) { - log.info(sm.getString("asyncChannelWrapperSecure.closeFail")); + } catch (IOException ioe) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("asyncChannelWrapperSecure.closeFail"), ioe); + } else { + log.info(sm.getString("asyncChannelWrapperSecure.closeFail")); + } } executor.shutdownNow(); } @@ -145,7 +148,7 @@ @Override public Future handshake() throws SSLException { - WrapperFuture wFuture = new WrapperFuture<>(); + WrapperFuture wFuture = new WrapperFuture<>(); Thread t = new WebSocketSslHandshakeThread(wFuture); t.start(); @@ -165,9 +168,9 @@ private final ByteBuffer[] srcs; private final int offset; private final int length; - private final WrapperFuture future; + private final WrapperFuture future; - WriteTask(ByteBuffer[] srcs, int offset, int length, WrapperFuture future) { + WriteTask(ByteBuffer[] srcs, int offset, int length, WrapperFuture future) { this.srcs = srcs; this.future = future; this.offset = offset; @@ -236,9 +239,9 @@ private class ReadTask implements Runnable { private final ByteBuffer dest; - private final WrapperFuture future; + private final WrapperFuture future; - ReadTask(ByteBuffer dest, WrapperFuture future) { + ReadTask(ByteBuffer dest, WrapperFuture future) { this.dest = dest; this.future = future; } @@ -321,8 +324,8 @@ } else { future.fail(new IllegalStateException(sm.getString("asyncChannelWrapperSecure.wrongStateRead"))); } - } catch (RuntimeException | ReadBufferOverflowException | SSLException | EOFException | ExecutionException - | InterruptedException e) { + } catch (RuntimeException | ReadBufferOverflowException | SSLException | EOFException | ExecutionException | + InterruptedException e) { reading.set(false); future.fail(e); } @@ -332,12 +335,12 @@ private class WebSocketSslHandshakeThread extends Thread { - private final WrapperFuture hFuture; + private final WrapperFuture hFuture; private HandshakeStatus handshakeStatus; private Status resultStatus; - WebSocketSslHandshakeThread(WrapperFuture hFuture) { + WebSocketSslHandshakeThread(WrapperFuture hFuture) { this.hFuture = hFuture; } @@ -376,7 +379,7 @@ break; } case NEED_TASK: { - Runnable r = null; + Runnable r; while ((r = sslEngine.getDelegatedTask()) != null) { r.run(); } @@ -424,18 +427,18 @@ private static class WrapperFuture implements Future { - private final CompletionHandler handler; + private final CompletionHandler handler; private final A attachment; private volatile T result = null; private volatile Throwable throwable = null; - private CountDownLatch completionLatch = new CountDownLatch(1); + private final CountDownLatch completionLatch = new CountDownLatch(1); WrapperFuture() { this(null, null); } - WrapperFuture(CompletionHandler handler, A attachment) { + WrapperFuture(CompletionHandler handler, A attachment) { this.handler = handler; this.attachment = attachment; } @@ -485,7 +488,7 @@ @Override public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { boolean latchResult = completionLatch.await(timeout, unit); - if (latchResult == false) { + if (!latchResult) { throw new TimeoutException(); } if (throwable != null) { @@ -541,7 +544,7 @@ private static class SecureIOThreadFactory implements ThreadFactory { - private AtomicInteger count = new AtomicInteger(0); + private final AtomicInteger count = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/Authenticator.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/Authenticator.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/Authenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/Authenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,9 +38,9 @@ * * @param requestUri The request URI * @param authenticateHeader The server authentication header received - * @param userName The user name + * @param userName The username * @param userPassword The user password - * @param userRealm The realm for which the provided user name and password are valid. {@code null} to + * @param userRealm The realm for which the provided username and password are valid. {@code null} to * indicate all realms. * * @return The generated authorization header value @@ -66,10 +66,10 @@ * * @return a map of authentication parameter names and values */ - public Map parseAuthenticateHeader(String authenticateHeader) { + public Map parseAuthenticateHeader(String authenticateHeader) { Matcher m = pattern.matcher(authenticateHeader); - Map parameterMap = new HashMap<>(); + Map parameterMap = new HashMap<>(); while (m.find()) { String key = m.group(1); @@ -104,7 +104,7 @@ } userRealm = userRealm.trim(); - if (userRealm.length() == 0) { + if (userRealm.isEmpty()) { return; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/BackgroundProcessManager.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/BackgroundProcessManager.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/BackgroundProcessManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/BackgroundProcessManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -55,7 +55,7 @@ public void register(BackgroundProcess process) { synchronized (processesLock) { - if (processes.size() == 0) { + if (processes.isEmpty()) { wsBackgroundThread = new WsBackgroundThread(this); wsBackgroundThread.setContextClassLoader(this.getClass().getClassLoader()); wsBackgroundThread.setDaemon(true); @@ -69,7 +69,7 @@ public void unregister(BackgroundProcess process) { synchronized (processesLock) { processes.remove(process); - if (wsBackgroundThread != null && processes.size() == 0) { + if (wsBackgroundThread != null && processes.isEmpty()) { wsBackgroundThread.halt(); wsBackgroundThread = null; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/BasicAuthenticator.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/BasicAuthenticator.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/BasicAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/BasicAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,7 +36,7 @@ validateUsername(userName); validatePassword(userPassword); - Map parameterMap = parseAuthenticateHeader(authenticateHeader); + Map parameterMap = parseAuthenticateHeader(authenticateHeader); String realm = parameterMap.get("realm"); validateRealm(userRealm, realm); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/Constants.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/Constants.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,8 +16,6 @@ */ package org.apache.tomcat.websocket; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -43,8 +41,8 @@ static final byte INTERNAL_OPCODE_FLUSH = 0x18; // Buffers - static final int DEFAULT_BUFFER_SIZE = Integer - .getInteger("org.apache.tomcat.websocket.DEFAULT_BUFFER_SIZE", 8 * 1024).intValue(); + static final int DEFAULT_BUFFER_SIZE = + Integer.getInteger("org.apache.tomcat.websocket.DEFAULT_BUFFER_SIZE", 8 * 1024).intValue(); // Client connection /** @@ -110,8 +108,8 @@ public static final int PROXY_AUTHENTICATION_REQUIRED = 407; // Configuration for Origin header in client - static final String DEFAULT_ORIGIN_HEADER_VALUE = System - .getProperty("org.apache.tomcat.websocket.DEFAULT_ORIGIN_HEADER_VALUE"); + static final String DEFAULT_ORIGIN_HEADER_VALUE = + System.getProperty("org.apache.tomcat.websocket.DEFAULT_ORIGIN_HEADER_VALUE"); // Configuration for blocking sends public static final String BLOCKING_SEND_TIMEOUT_PROPERTY = "org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT"; @@ -124,7 +122,8 @@ public static final long DEFAULT_SESSION_CLOSE_TIMEOUT = TimeUnit.SECONDS.toMillis(30); // Configuration for session close timeout - public static final String ABNORMAL_SESSION_CLOSE_SEND_TIMEOUT_PROPERTY = "org.apache.tomcat.websocket.ABNORMAL_SESSION_CLOSE_SEND_TIMEOUT"; + public static final String ABNORMAL_SESSION_CLOSE_SEND_TIMEOUT_PROPERTY = + "org.apache.tomcat.websocket.ABNORMAL_SESSION_CLOSE_SEND_TIMEOUT"; // Default is 50 milliseconds - setting is in milliseconds public static final long DEFAULT_ABNORMAL_SESSION_CLOSE_SEND_TIMEOUT = 50; @@ -135,24 +134,21 @@ public static final String WRITE_IDLE_TIMEOUT_MS = "org.apache.tomcat.websocket.WRITE_IDLE_TIMEOUT_MS"; // Configuration for background processing checks intervals - static final int DEFAULT_PROCESS_PERIOD = Integer - .getInteger("org.apache.tomcat.websocket.DEFAULT_PROCESS_PERIOD", 10).intValue(); + static final int DEFAULT_PROCESS_PERIOD = + Integer.getInteger("org.apache.tomcat.websocket.DEFAULT_PROCESS_PERIOD", 10).intValue(); public static final String WS_AUTHENTICATION_USER_NAME = "org.apache.tomcat.websocket.WS_AUTHENTICATION_USER_NAME"; public static final String WS_AUTHENTICATION_PASSWORD = "org.apache.tomcat.websocket.WS_AUTHENTICATION_PASSWORD"; public static final String WS_AUTHENTICATION_REALM = "org.apache.tomcat.websocket.WS_AUTHENTICATION_REALM"; - public static final String WS_AUTHENTICATION_PROXY_USER_NAME = "org.apache.tomcat.websocket.WS_AUTHENTICATION_PROXY_USER_NAME"; - public static final String WS_AUTHENTICATION_PROXY_PASSWORD = "org.apache.tomcat.websocket.WS_AUTHENTICATION_PROXY_PASSWORD"; - public static final String WS_AUTHENTICATION_PROXY_REALM = "org.apache.tomcat.websocket.WS_AUTHENTICATION_PROXY_REALM"; - - public static final List INSTALLED_EXTENSIONS; - - static { - List installed = new ArrayList<>(1); - installed.add(new WsExtension("permessage-deflate")); - INSTALLED_EXTENSIONS = Collections.unmodifiableList(installed); - } + public static final String WS_AUTHENTICATION_PROXY_USER_NAME = + "org.apache.tomcat.websocket.WS_AUTHENTICATION_PROXY_USER_NAME"; + public static final String WS_AUTHENTICATION_PROXY_PASSWORD = + "org.apache.tomcat.websocket.WS_AUTHENTICATION_PROXY_PASSWORD"; + public static final String WS_AUTHENTICATION_PROXY_REALM = + "org.apache.tomcat.websocket.WS_AUTHENTICATION_PROXY_REALM"; + + public static final List INSTALLED_EXTENSIONS = List.of(new WsExtension("permessage-deflate")); private Constants() { // Hide default constructor diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/DigestAuthenticator.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/DigestAuthenticator.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/DigestAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/DigestAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,7 +46,7 @@ validateUsername(userName); validatePassword(userPassword); - Map parameterMap = parseAuthenticateHeader(authenticateHeader); + Map parameterMap = parseAuthenticateHeader(authenticateHeader); String realm = parameterMap.get("realm"); validateRealm(userRealm, realm); @@ -72,28 +72,29 @@ } challenge.append("Digest "); - challenge.append("username =\"" + userName + "\","); - challenge.append("realm=\"" + realm + "\","); - challenge.append("nonce=\"" + nonce + "\","); - challenge.append("uri=\"" + requestUri + "\","); + challenge.append("username =\"").append(userName).append("\","); + challenge.append("realm=\"").append(realm).append("\","); + challenge.append("nonce=\"").append(nonce).append("\","); + challenge.append("uri=\"").append(requestUri).append("\","); try { - challenge.append("response=\"" + - calculateRequestDigest(requestUri, userName, userPassword, realm, nonce, messageQop, algorithm) + - "\","); + challenge.append("response=\""); + challenge.append( + calculateRequestDigest(requestUri, userName, userPassword, realm, nonce, messageQop, algorithm)); + challenge.append("\","); } catch (NoSuchAlgorithmException e) { throw new AuthenticationException(sm.getString("digestAuthenticator.algorithm", e.getMessage())); } - challenge.append("algorithm=" + algorithm + ","); - challenge.append("opaque=\"" + opaque + "\","); + challenge.append("algorithm=").append(algorithm).append(","); + challenge.append("opaque=\"").append(opaque).append("\","); if (!messageQop.isEmpty()) { - challenge.append("qop=\"" + messageQop + "\""); - challenge.append(",cnonce=\"" + cNonce + "\","); - challenge.append("nc=" + String.format("%08X", Integer.valueOf(nonceCount))); + challenge.append("qop=\"").append(messageQop).append("\""); + challenge.append(",cnonce=\"").append(cNonce).append("\","); + challenge.append("nc=").append(String.format("%08X", Integer.valueOf(nonceCount))); } return challenge.toString(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/FutureToSendHandler.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/FutureToSendHandler.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/FutureToSendHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/FutureToSendHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,7 +39,7 @@ private final CountDownLatch latch = new CountDownLatch(1); private final WsSession wsSession; - private volatile AtomicReference result = new AtomicReference<>(null); + private final AtomicReference result = new AtomicReference<>(null); FutureToSendHandler(WsSession wsSession) { this.wsSession = wsSession; @@ -90,7 +90,7 @@ @Override public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - boolean retval = false; + boolean retval; try { wsSession.registerFuture(this); retval = latch.await(timeout, unit); @@ -98,7 +98,7 @@ wsSession.unregisterFuture(this); } - if (retval == false) { + if (!retval) { throw new TimeoutException(sm.getString("futureToSendHandler.timeout", Long.valueOf(timeout), unit.toString().toLowerCase(Locale.ENGLISH))); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -73,6 +73,7 @@ wsFrame.invalidUtf8=A WebSocket text frame was received that could not be decoded to UTF-8 because it contained invalid byte sequences wsFrame.invalidUtf8Close=A WebSocket close frame was received with a close reason that contained invalid UTF-8 byte sequences wsFrame.ioeTriggeredClose=An unrecoverable IOException occurred so the connection was closed +wsFrame.masked=The server frame was masked but server frames must not be masked wsFrame.messageTooBig=The message was [{0}] bytes long but the MessageHandler has a limit of [{1}] bytes wsFrame.noContinuation=A new message was started when a continuation frame was expected wsFrame.notMasked=The client frame was not masked but all client frames must be masked @@ -96,7 +97,7 @@ wsRemoteEndpoint.closedDuringMessage=The remainder of the message will not be sent because the WebSocket session has been closed wsRemoteEndpoint.closedOutputStream=This method may not be called as the OutputStream has been closed wsRemoteEndpoint.closedWriter=This method may not be called as the Writer has been closed -wsRemoteEndpoint.encoderDestoryFailed=Failed to destroy the encoder of type [{0}] +wsRemoteEndpoint.encoderDestroyFailed=Failed to destroy the encoder of type [{0}] wsRemoteEndpoint.encoderError=Encoding error [{0}] wsRemoteEndpoint.flushOnCloseFailed=Batched messages still enabled after session has been closed. Unable to flush remaining batched message. wsRemoteEndpoint.invalidEncoder=The specified encoder of type [{0}] could not be instantiated diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_es.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_es.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_es.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_es.properties 2026-01-23 19:33:36.000000000 +0000 @@ -25,6 +25,7 @@ util.unknownDecoderType=No se reconoce el decodificador tipo [{0}] wsFrame.closed=Nuevo cuadro recibido luego de cerrar el cuadro de control +wsFrame.controlPayloadTooBig=Se envió una trama de control con una carga útil de tamaño [{0}], que es mayor que el máximo permitido de 125 bytes wsFrame.illegalReadState=Estado de lectura inesperado [{0}] wsFrame.notMasked=El cuadro del cliente no fue enmascarado, pero todos los cuadros de clientes deben ser enmascarados wsFrame.wrongRsv=El rango del cliente fija los bits reservados a [{0}] para un mensaje con opCode [{1}] el cual no fue soportado por este endpoint\n @@ -44,6 +45,8 @@ wsSession.duplicateHandlerText=Un manejador de mensaje de texto ya ha sido configurado wsSession.instanceNew=Falló la registración de la instancia del dispoitivo final +wsWebSocketContainer.asynchronousSocketChannelFail=No se pudo abrir una conexión con el servidor +wsWebSocketContainer.failedAuthentication=Error al manejar el código de respuesta HTTP [{0}]. El encabezado [{1}] no fue aceptado por el servidor.\n wsWebSocketContainer.missingAuthenticateHeader=Fallo al manejar el código de respuesta HTTP [{0}]. No existe la cabecera [{1}] en la respuesta wsWebSocketContainer.pathNoHost=No se especificó ningún host en URI wsWebSocketContainer.sessionCloseFail=La sesión con ID [{0}] no se cerró correctamente diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -73,6 +73,7 @@ wsFrame.invalidUtf8=Une trame texte Websocket a été reçue et n'a pu 6etre traitée car elle contenait des séquence d'octets UTF-8 invalides wsFrame.invalidUtf8Close=Une trame de fermeture Websocket a été reçue avec une cause qui contenait des séquences UTF-8 invalides wsFrame.ioeTriggeredClose=Une IOException non récupérable est survenue donc la connection a été fermée +wsFrame.masked=La trame du serveur a été masquée mais les trames ne serveur ne doivent pas l'être wsFrame.messageTooBig=Le message fait [{0}] octets mais le MessageHandler a une limite de [{1}] octets wsFrame.noContinuation=Un nouveau message a été démarré quand une trame de continuation était attendue wsFrame.notMasked=La trame du client n'a pas de masque alors que toutes les trames des clients doivent en avoir un @@ -96,7 +97,7 @@ wsRemoteEndpoint.closedDuringMessage=Le reste du message ne sera pas envoyé parce que la session WebSocket est déjà fermée. wsRemoteEndpoint.closedOutputStream=La méthode ne peut pas être appelée alors que l'OutputStream a été fermée wsRemoteEndpoint.closedWriter=Cette méthode ne doit pas être appelée car le Writer a été fermé -wsRemoteEndpoint.encoderDestoryFailed=Echec de la destruction de l''encodeur de type [{0}] +wsRemoteEndpoint.encoderDestroyFailed=Echec de la destruction de l''encodeur de type [{0}] wsRemoteEndpoint.encoderError=Erreur d''encodage [{0}] wsRemoteEndpoint.flushOnCloseFailed=Le groupement de messages est toujours actif après fermeture de la session, impossible d'envoyer les messages restants wsRemoteEndpoint.invalidEncoder=L''encodeur spécifié de type [{0}] n''a pu être instancié diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -73,6 +73,7 @@ wsFrame.invalidUtf8=無効なバイトシーケンスが含まれていたため、UTF-8にデコードできなかったWebSocketテキストフレームが受信されました。 wsFrame.invalidUtf8Close=WebSocket は無効な UTF-8 バイト列を含むことを原因とするクローズフレームを受信しました。 wsFrame.ioeTriggeredClose=回復不能なIOException が発生したためコネクションを切断します。 +wsFrame.masked=サーバーフレームがマスクされましたが、サーバーフレームはマスクされてはいけません wsFrame.messageTooBig=メッセージは [{0}] バイトの長さでしたが、MessageHandlerには [{1}] バイトの制限があります wsFrame.noContinuation=continuation フレームが予想されたときに新しいメッセージが開始されました。 wsFrame.notMasked=クライアントのフレームはマスクされていませんが、全てのクライアントのフレームはマスクしなければなりません。 @@ -96,7 +97,7 @@ wsRemoteEndpoint.closedDuringMessage=WebSocket セッションが切断されているため残りのメッセージは送信できません wsRemoteEndpoint.closedOutputStream=このメソッドは、OutputStreamが閉じられたときに呼び出されない場合があります。 wsRemoteEndpoint.closedWriter=Writerがクローズされているため、このメソッドを呼び出すことはできません。 -wsRemoteEndpoint.encoderDestoryFailed=タイプ [{0}] のエンコーダーの破棄に失敗しました +wsRemoteEndpoint.encoderDestroyFailed=タイプ [{0}] のエンコーダーの破棄に失敗しました wsRemoteEndpoint.encoderError=エンコードエラー [{0}] wsRemoteEndpoint.flushOnCloseFailed=セッションが閉じられた後にまだ可能であったバッチメッセージ。残りのバッチメッセージをフラッシュできません。 wsRemoteEndpoint.invalidEncoder=指定されたタイプ [{0}] のエンコーダをインスタンス化できませんでした diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_ko.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_ko.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_ko.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_ko.properties 2026-01-23 19:33:36.000000000 +0000 @@ -93,7 +93,7 @@ wsRemoteEndpoint.closedDuringMessage=웹소켓 세션이 이미 닫혔기 때문에, 메시지의 나머지 부분은 전달되지 않을 것입니다. wsRemoteEndpoint.closedOutputStream=OutputStream이 이미 닫혀 있으므로, 이 메소드는 호출될 수 없습니다. wsRemoteEndpoint.closedWriter=Writer가 이미 닫혔기 때문에, 이 메소드는 호출될 수 없습니다. -wsRemoteEndpoint.encoderDestoryFailed=[{0}] 타입의 인코더를 소멸시키지 못했습니다. +wsRemoteEndpoint.encoderDestroyFailed=[{0}] 타입의 인코더를 소멸시키지 못했습니다. wsRemoteEndpoint.flushOnCloseFailed=세션이 이미 종료된 이후에도, 메시지들이 배치(batch)에 포함되어 있습니다. 배치에 남아있는 메시지들을 배출할 수 없습니다. wsRemoteEndpoint.invalidEncoder=지정된 타입 [{0}]의 Encoder의 인스턴스를 생성할 수 없었습니다. wsRemoteEndpoint.noEncoder=클래스 [{0}]의 객체를 위한 인코더가 지정되지 않았습니다. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -43,6 +43,8 @@ clientEndpointHolder.instanceCreationFailed=未能创建WebSocketEndpoint clientEndpointHolder.instanceRegistrationFailed=无法向InstanceManager注册Endpoint实例 +digestAuthenticator.algorithm=无法生成请求摘要[{0}] + futureToSendHandler.timeout=等待[{0}][{1}]完成后操作超时。 perMessageDeflate.alreadyClosed=转换器已经关闭且可能永远不会使用 @@ -93,7 +95,7 @@ wsRemoteEndpoint.closedDuringMessage=因为 WebSocket session 被关闭,消息的剩余部分将不会被送达 wsRemoteEndpoint.closedOutputStream=由于OutputStream已关闭,不应该调用此方法。 wsRemoteEndpoint.closedWriter=此方法不能调用,因为编写器已关闭。 -wsRemoteEndpoint.encoderDestoryFailed=未能销毁[{0}]类型的编码器 +wsRemoteEndpoint.encoderDestroyFailed=未能销毁[{0}]类型的编码器 wsRemoteEndpoint.flushOnCloseFailed=会话关闭后仍然启用批处理消息。无法刷新剩余的批量消息 wsRemoteEndpoint.invalidEncoder=无法实例化类型为[{0}]的指定编码器 wsRemoteEndpoint.noEncoder=没有为类 [{0}] 的对象指定编码器 diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/MessagePart.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/MessagePart.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/MessagePart.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/MessagePart.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,7 +20,7 @@ import jakarta.websocket.SendHandler; -class MessagePart { +public class MessagePart { private final boolean fin; private final int rsv; private final byte opCode; @@ -40,32 +40,26 @@ this.blockingWriteTimeoutExpiry = blockingWriteTimeoutExpiry; } - public boolean isFin() { return fin; } - public int getRsv() { return rsv; } - public byte getOpCode() { return opCode; } - public ByteBuffer getPayload() { return payload; } - public SendHandler getIntermediateHandler() { return intermediateHandler; } - public SendHandler getEndHandler() { return endHandler; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/PerMessageDeflate.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/PerMessageDeflate.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/PerMessageDeflate.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/PerMessageDeflate.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,6 +44,13 @@ public static final String NAME = "permessage-deflate"; + public static final TransformationBuilder BUILDER = new TransformationBuilder() { + @Override + public Transformation build(List> preferences, boolean isServer) { + return PerMessageDeflate.build(preferences, isServer); + } + }; + private final boolean serverContextTakeover; private final int serverMaxWindowBits; private final boolean clientContextTakeover; @@ -61,7 +68,7 @@ // Flag to track if a message is completely empty private volatile boolean emptyMessage = true; - static PerMessageDeflate negotiate(List> preferences, boolean isServer) { + static PerMessageDeflate build(List> preferences, boolean isServer) { // Accept the first preference that the endpoint is able to support for (List preference : preferences) { boolean ok = true; @@ -194,7 +201,8 @@ written = inflater.inflate(dest.array(), dest.arrayOffset() + dest.position(), dest.remaining()); } catch (DataFormatException e) { throw new IOException(sm.getString("perMessageDeflate.deflateFailed"), e); - } catch (NullPointerException e) { + } catch (IllegalStateException | NullPointerException e) { + // As of Java 25, the JRE throws an ISE rather than an NPE throw new IOException(sm.getString("perMessageDeflate.alreadyClosed"), e); } dest.position(dest.position() + written); @@ -354,7 +362,8 @@ compressedPayload.arrayOffset() + compressedPayload.position(), compressedPayload.remaining(), flush); compressedPayload.position(compressedPayload.position() + written); - } catch (NullPointerException e) { + } catch (IllegalStateException | NullPointerException e) { + // As of Java 25, the JRE throws an ISE rather than an NPE throw new IOException(sm.getString("perMessageDeflate.alreadyClosed"), e); } @@ -394,14 +403,14 @@ compressedPart = new MessagePart(false, getRsv(uncompressedPart), opCode, compressedPayload, uncompressedIntermediateHandler, uncompressedIntermediateHandler, blockingWriteTimeoutExpiry); - } else if (!fin && full && needsInput) { + } else if (!fin && full/* note: needsInput is true here */) { // Write buffer full and input message not fully read. // Output and get more data. compressedPart = new MessagePart(false, getRsv(uncompressedPart), opCode, compressedPayload, uncompressedIntermediateHandler, uncompressedIntermediateHandler, blockingWriteTimeoutExpiry); deflateRequired = false; - } else if (fin && full && needsInput) { + } else if (fin && full/* note: needsInput is true here */) { // Write buffer full. Input fully read. Deflater may be // in one of four states: // - output complete (just happened to align with end of diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/Transformation.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/Transformation.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/Transformation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/Transformation.java 2026-01-23 19:33:36.000000000 +0000 @@ -86,7 +86,7 @@ * * @param messageParts The list of messages to be transformed * - * @return The list of messages after this any any subsequent transformations have been applied. The size of the + * @return The list of messages after this and any subsequent transformations have been applied. The size of the * returned list may be bigger or smaller than the size of the input list * * @throws IOException If an error occurs during the transformation of the message parts diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/TransformationBuilder.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/TransformationBuilder.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/TransformationBuilder.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/TransformationBuilder.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,26 @@ +/* + * 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; + +import java.util.List; + +import jakarta.websocket.Extension; + +public interface TransformationBuilder { + + Transformation build(List> preferences, boolean isServer); +} diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/TransformationFactory.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/TransformationFactory.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/TransformationFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/TransformationFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,7 +16,12 @@ */ package org.apache.tomcat.websocket; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import jakarta.websocket.Extension; @@ -24,18 +29,46 @@ private static final TransformationFactory factory = new TransformationFactory(); + private Map builders = new HashMap<>(); + + private TransformationFactory() { // Hide default constructor + + // Configure the built-in transformations + builders.put(PerMessageDeflate.NAME, PerMessageDeflate.BUILDER); } + public static TransformationFactory getInstance() { return factory; } + public Transformation create(String name, List> preferences, boolean isServer) { - if (PerMessageDeflate.NAME.equals(name)) { - return PerMessageDeflate.negotiate(preferences, isServer); + TransformationBuilder builder = builders.get(name); + if (builder != null) { + return builder.build(preferences, isServer); } return null; } + + + public void registerExtension(String name, TransformationBuilder builder) { + builders.put(name, builder); + } + + + public Set getInstalledExtensionNames() { + return new HashSet<>(builders.keySet()); + } + + + public Set getInstalledExtensions() { + Set result = new HashSet<>(); + for (String extensionName : builders.keySet()) { + result.add(new WsExtension(extensionName)); + } + return Collections.unmodifiableSet(result); + } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/Util.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/Util.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/Util.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/Util.java 2026-01-23 19:33:36.000000000 +0000 @@ -140,7 +140,7 @@ static byte[] generateMask() { // SecureRandom is not thread-safe so need to make sure only one thread // uses it at a time. In theory, the pool could grow to the same size - // as the number of request processing threads. In reality it will be + // as the number of request processing threads. In reality, it will be // a lot smaller. // Get a SecureRandom from the pool @@ -206,7 +206,7 @@ Class superClazz = (Class) clazz.getSuperclass(); if (superClazz == null) { // Finished looking up the class hierarchy without finding anything - return null; + throw new IllegalStateException(); } TypeResult superClassTypeResult = getGenericType(type, superClazz); @@ -222,8 +222,8 @@ // the interface of interest // Map that unknown type to the generic types defined in this class ParameterizedType superClassType = (ParameterizedType) clazz.getGenericSuperclass(); - TypeResult result = getTypeParameter(clazz, - superClassType.getActualTypeArguments()[superClassTypeResult.getIndex()]); + TypeResult result = + getTypeParameter(clazz, superClassType.getActualTypeArguments()[superClassTypeResult.getIndex()]); result.incrementDimension(superClassTypeResult.getDimension()); if (result.getClazz() != null && result.getDimension() > 0) { superClassTypeResult = result; @@ -234,9 +234,7 @@ if (superClassTypeResult.getDimension() > 0) { StringBuilder className = new StringBuilder(); - for (int i = 0; i < dimension; i++) { - className.append('['); - } + className.append("[".repeat(Math.max(0, dimension))); className.append('L'); className.append(superClassTypeResult.getClazz().getCanonicalName()); className.append(';'); @@ -252,7 +250,7 @@ } // Error will be logged further up the call stack - return null; + throw new IllegalStateException(); } @@ -277,7 +275,7 @@ return new TypeResult(null, i, 0); } } - return null; + throw new IllegalStateException(); } } @@ -285,12 +283,11 @@ public static boolean isPrimitive(Class clazz) { if (clazz.isPrimitive()) { return true; - } else if (clazz.equals(Boolean.class) || clazz.equals(Byte.class) || clazz.equals(Character.class) || - clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Integer.class) || - clazz.equals(Long.class) || clazz.equals(Short.class)) { - return true; + } else { + return clazz.equals(Boolean.class) || clazz.equals(Byte.class) || clazz.equals(Character.class) || + clazz.equals(Double.class) || clazz.equals(Float.class) || clazz.equals(Integer.class) || + clazz.equals(Long.class) || clazz.equals(Short.class); } - return false; } @@ -346,8 +343,8 @@ // Don't need this instance, so destroy it instanceManager.destroyInstance(instance); } - } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException - | NamingException e) { + } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException | + NamingException e) { throw new DeploymentException( sm.getString("pojoMethodMapping.invalidDecoder", decoderClazz.getName()), e); } @@ -381,13 +378,16 @@ // the types expected by the frame handling code } else if (byte[].class.isAssignableFrom(target)) { boolean whole = MessageHandler.Whole.class.isAssignableFrom(listener.getClass()); - MessageHandlerResult result = new MessageHandlerResult(whole - ? new PojoMessageHandlerWholeBinary(listener, getOnMessageMethod(listener), session, endpointConfig, - matchDecoders(target, endpointConfig, true, ((WsSession) session).getInstanceManager()), - new Object[1], 0, true, -1, false, -1) - : new PojoMessageHandlerPartialBinary(listener, getOnMessagePartialMethod(listener), session, - new Object[2], 0, true, 1, -1, -1), - MessageHandlerResultType.BINARY); + MessageHandlerResult result = + new MessageHandlerResult(whole ? + new PojoMessageHandlerWholeBinary(listener, getOnMessageMethod(listener), session, + endpointConfig, + matchDecoders(target, endpointConfig, true, + ((WsSession) session).getInstanceManager()), + new Object[1], 0, true, -1, false, -1) : + new PojoMessageHandlerPartialBinary(listener, getOnMessagePartialMethod(listener), session, + new Object[2], 0, true, 1, -1, -1), + MessageHandlerResultType.BINARY); results.add(result); } else if (InputStream.class.isAssignableFrom(target)) { MessageHandlerResult result = new MessageHandlerResult( @@ -406,17 +406,17 @@ } else { // Handler needs wrapping and requires decoder to convert it to one // of the types expected by the frame handling code - DecoderMatch decoderMatch = matchDecoders(target, endpointConfig, - ((WsSession) session).getInstanceManager()); + DecoderMatch decoderMatch = + matchDecoders(target, endpointConfig, ((WsSession) session).getInstanceManager()); Method m = getOnMessageMethod(listener); - if (decoderMatch.getBinaryDecoders().size() > 0) { + if (!decoderMatch.getBinaryDecoders().isEmpty()) { MessageHandlerResult result = new MessageHandlerResult( new PojoMessageHandlerWholeBinary(listener, m, session, endpointConfig, decoderMatch.getBinaryDecoders(), new Object[1], 0, false, -1, false, -1), MessageHandlerResultType.BINARY); results.add(result); } - if (decoderMatch.getTextDecoders().size() > 0) { + if (!decoderMatch.getTextDecoders().isEmpty()) { MessageHandlerResult result = new MessageHandlerResult( new PojoMessageHandlerWholeText(listener, m, session, endpointConfig, decoderMatch.getTextDecoders(), new Object[1], 0, false, -1, -1), @@ -425,7 +425,7 @@ } } - if (results.size() == 0) { + if (results.isEmpty()) { throw new IllegalArgumentException(sm.getString("wsSession.unknownHandler", listener, target)); } @@ -436,10 +436,10 @@ boolean binary, InstanceManager instanceManager) { DecoderMatch decoderMatch = matchDecoders(target, endpointConfig, instanceManager); if (binary) { - if (decoderMatch.getBinaryDecoders().size() > 0) { + if (!decoderMatch.getBinaryDecoders().isEmpty()) { return decoderMatch.getBinaryDecoders(); } - } else if (decoderMatch.getTextDecoders().size() > 0) { + } else if (!decoderMatch.getTextDecoders().isEmpty()) { return decoderMatch.getTextDecoders(); } return null; @@ -475,11 +475,11 @@ // Step one, split the header into individual extensions using ',' as a // separator - String unparsedExtensions[] = header.split(","); + String[] unparsedExtensions = header.split(","); for (String unparsedExtension : unparsedExtensions) { // Step two, split the extension into the registered name and // parameter/value pairs using ';' as a separator - String unparsedParameters[] = unparsedExtension.split(";"); + String[] unparsedParameters = unparsedExtension.split(";"); WsExtension extension = new WsExtension(unparsedParameters[0].trim()); for (int i = 1; i < unparsedParameters.length; i++) { @@ -516,7 +516,7 @@ private static boolean containsDelims(String input) { - if (input == null || input.length() == 0) { + if (input == null || input.isEmpty()) { return false; } for (char c : input.toCharArray()) { @@ -605,7 +605,7 @@ public boolean hasMatches() { - return (textDecoders.size() > 0) || (binaryDecoders.size() > 0); + return (!textDecoders.isEmpty()) || (!binaryDecoders.isEmpty()); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsFrameBase.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsFrameBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsFrameBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsFrameBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -50,8 +50,7 @@ private final Transformation transformation; // Attributes for control messages - // Control messages can appear in the middle of other messages so need - // separate attributes + // They can appear in the middle of other messages so need separate attributes private final ByteBuffer controlBufferBinary = ByteBuffer.allocate(125); private final CharBuffer controlBufferText = CharBuffer.allocate(125); @@ -201,9 +200,12 @@ continuationExpected = !fin; } b = inputBuffer.get(); - // Client data must be masked if ((b & 0x80) == 0 && isMasked()) { + // Client data must be masked throw new WsIOException(new CloseReason(CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.notMasked"))); + } else if ((b & 0x80) != 0 && !isMasked()) { + // Server data must not masked + throw new WsIOException(new CloseReason(CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.masked"))); } payloadLength = b & 0x7F; state = State.PARTIAL_HEADER; @@ -860,7 +862,7 @@ } /** - * This method will be invoked when the read operation is resumed. As the suspend of the read operation can be + * This method will be invoked when the read operation is resumed. Since suspend of the read operation can be * invoked at any time, when implementing this method one should consider that there might still be data remaining * into the internal buffers that needs to be processed before reading again from the socket. */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsFrameClient.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsFrameClient.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsFrameClient.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsFrameClient.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,7 +34,7 @@ private static final StringManager sm = StringManager.getManager(WsFrameClient.class); private final AsyncChannelWrapper channel; - private final CompletionHandler handler; + private final CompletionHandler handler; // Not final as it may need to be re-sized private volatile ByteBuffer response; @@ -50,8 +50,8 @@ void startInputProcessing() { try { processSocketRead(); - } catch (IOException e) { - close(e); + } catch (IOException ioe) { + close(ioe); } } @@ -71,7 +71,7 @@ // There is still data available in the response buffer // Return here so that the response buffer will not be // cleared and there will be no data read from the - // socket. Thus when the read operation is resumed first + // socket. Thus, when the read operation is resumed first // the data left in the response buffer will be consumed // and then a new socket read will be performed return; @@ -143,7 +143,7 @@ return log; } - private class WsFrameClientCompletionHandler implements CompletionHandler { + private class WsFrameClientCompletionHandler implements CompletionHandler { @Override public void completed(Integer result, Void attachment) { @@ -206,7 +206,7 @@ private void resumeProcessing(boolean checkOpenOnError) { try { processSocketRead(); - } catch (IOException e) { + } catch (IOException ioe) { if (checkOpenOnError) { // Only send a close message on an IOException if the client // has not yet received a close control message from the server @@ -215,12 +215,12 @@ // control message. if (isOpen()) { if (log.isDebugEnabled()) { - log.debug(sm.getString("wsFrameClient.ioe"), e); + log.debug(sm.getString("wsFrameClient.ioe"), ioe); } - close(e); + close(ioe); } } else { - close(e); + close(ioe); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsHandshakeResponse.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsHandshakeResponse.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsHandshakeResponse.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsHandshakeResponse.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,15 +30,15 @@ */ public class WsHandshakeResponse implements HandshakeResponse { - private final Map> headers = new CaseInsensitiveKeyMap<>(); + private final Map> headers = new CaseInsensitiveKeyMap<>(); public WsHandshakeResponse() { } - public WsHandshakeResponse(Map> headers) { - for (Entry> entry : headers.entrySet()) { + public WsHandshakeResponse(Map> headers) { + for (Entry> entry : headers.entrySet()) { if (this.headers.containsKey(entry.getKey())) { this.headers.get(entry.getKey()).addAll(entry.getValue()); } else { @@ -50,7 +50,7 @@ @Override - public Map> getHeaders() { + public Map> getHeaders() { return headers; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsRemoteEndpointImplBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -86,7 +86,7 @@ private final AtomicBoolean batchingAllowed = new AtomicBoolean(false); private volatile long sendTimeout = -1; private WsSession wsSession; - private List encoderEntries = new ArrayList<>(); + private final List encoderEntries = new ArrayList<>(); protected void setTransformation(Transformation transformation) { @@ -208,8 +208,8 @@ throw new IllegalArgumentException(sm.getString("wsRemoteEndpoint.nullHandler")); } stateMachine.textStart(); - TextMessageSendHandler tmsh = new TextMessageSendHandler(handler, CharBuffer.wrap(text), true, encoder, - encoderBuffer, this); + TextMessageSendHandler tmsh = + new TextMessageSendHandler(handler, CharBuffer.wrap(text), true, encoder, encoderBuffer, this); tmsh.write(); // TextMessageSendHandler will update stateMachine when it completes } @@ -260,8 +260,8 @@ void sendMessageBlock(byte opCode, ByteBuffer payload, boolean last, long timeout) throws IOException { /* - * Get the timeout before we send the message. The message may trigger a session close and depending on timing - * the client session may close before we can read the timeout. + * Get the timeout before we send the message. The message may trigger a session close and depending on timing + * the client session may close before we can read the timeout. */ sendMessageBlockInternal(opCode, payload, last, getTimeoutExpiry(timeout)); } @@ -290,7 +290,7 @@ // Some extensions/transformations may buffer messages so it is possible // that no message parts will be returned. If this is the case simply // return. - if (messageParts.size() == 0) { + if (messageParts.isEmpty()) { return; } @@ -384,7 +384,7 @@ boolean doWrite = false; synchronized (messagePartLock) { if (Constants.OPCODE_CLOSE == mp.getOpCode() && getBatchingAllowed()) { - // Should not happen. To late to send batched messages now since + // Should not happen. Too late to send batched messages now since // the session has been closed. Complain loudly. log.warn(sm.getString("wsRemoteEndpoint.flushOnCloseFailed")); } @@ -416,7 +416,7 @@ void endMessage(SendHandler handler, SendResult result) { boolean doWrite = false; - MessagePart mpNext = null; + MessagePart mpNext; synchronized (messagePartLock) { fragmented = nextFragmented; @@ -512,9 +512,9 @@ if (getBatchingAllowed() || isMasked()) { // Need to write via output buffer - OutputBufferSendHandler obsh = new OutputBufferSendHandler(mp.getEndHandler(), - mp.getBlockingWriteTimeoutExpiry(), headerBuffer, mp.getPayload(), mask, outputBuffer, - !getBatchingAllowed(), this); + OutputBufferSendHandler obsh = + new OutputBufferSendHandler(mp.getEndHandler(), mp.getBlockingWriteTimeoutExpiry(), headerBuffer, + mp.getPayload(), mask, outputBuffer, !getBatchingAllowed(), this); obsh.write(); } else { // Can write directly @@ -573,10 +573,9 @@ /** * If a transformation needs to split a {@link MessagePart} into multiple {@link MessagePart}s, it uses this handler - * as the end handler for each of the additional {@link MessagePart}s. This handler notifies this this class that - * the {@link MessagePart} has been processed and that the next {@link MessagePart} in the queue should be started. - * The final {@link MessagePart} will use the {@link EndMessageHandler} provided with the original - * {@link MessagePart}. + * as the end handler for each of the additional {@link MessagePart}s. This handler notifies this class that the + * {@link MessagePart} has been processed and that the next {@link MessagePart} in the queue should be started. The + * final {@link MessagePart} will use the {@link EndMessageHandler} provided with the original {@link MessagePart}. */ private static class IntermediateMessageHandler implements SendHandler { @@ -740,7 +739,7 @@ try { instanceManager.destroyInstance(entry); } catch (IllegalAccessException | InvocationTargetException e) { - log.warn(sm.getString("wsRemoteEndpoint.encoderDestoryFailed", encoder.getClass()), e); + log.warn(sm.getString("wsRemoteEndpoint.encoderDestroyFailed", encoder.getClass()), e); } } } @@ -767,10 +766,10 @@ if (fin) { // Set the fin bit - b -= 128; + b -= (byte) 128; } - b += (rsv << 4); + b += (byte) (rsv << 4); if (first) { // This is the first fragment of this message @@ -786,7 +785,7 @@ b = 0; } - // Next write the mask && length length + // Next write the mask && length if (payload.remaining() < 126) { headerBuffer.put((byte) (payload.remaining() | b)); } else if (payload.remaining() < 65536) { @@ -1001,9 +1000,7 @@ @Override public void write(int b) throws IOException { - if (closed) { - throw new IllegalStateException(sm.getString("wsRemoteEndpoint.closedOutputStream")); - } + checkOpen(); used = true; if (buffer.remaining() == 0) { @@ -1014,9 +1011,7 @@ @Override public void write(byte[] b, int off, int len) throws IOException { - if (closed) { - throw new IllegalStateException(sm.getString("wsRemoteEndpoint.closedOutputStream")); - } + checkOpen(); if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } @@ -1044,9 +1039,7 @@ @Override public void flush() throws IOException { - if (closed) { - throw new IllegalStateException(sm.getString("wsRemoteEndpoint.closedOutputStream")); - } + checkOpen(); // Optimisation. If there is no data to flush then do not send an // empty message. @@ -1067,6 +1060,12 @@ doWrite(true); } + private void checkOpen() throws IOException { + if (closed) { + throw new IOException(sm.getString("wsRemoteEndpoint.closedOutputStream")); + } + } + private void doWrite(boolean last) throws IOException { if (used) { buffer.flip(); @@ -1092,9 +1091,7 @@ @Override public void write(char[] cbuf, int off, int len) throws IOException { - if (closed) { - throw new IllegalStateException(sm.getString("wsRemoteEndpoint.closedWriter")); - } + checkOpen(); if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } @@ -1122,9 +1119,7 @@ @Override public void flush() throws IOException { - if (closed) { - throw new IllegalStateException(sm.getString("wsRemoteEndpoint.closedWriter")); - } + checkOpen(); if (buffer.position() > 0) { doWrite(false); @@ -1143,6 +1138,12 @@ doWrite(true); } + private void checkOpen() throws IOException { + if (closed) { + throw new IOException(sm.getString("wsRemoteEndpoint.closedWriter")); + } + } + private void doWrite(boolean last) throws IOException { if (used) { buffer.flip(); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsSession.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsSession.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsSession.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsSession.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,6 +25,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -73,15 +74,15 @@ private static final boolean SEC_CONFIGURATOR_USES_IMPL_DEFAULT; - private static AtomicLong ids = new AtomicLong(0); + private static final AtomicLong ids = new AtomicLong(0); static { // Use fake end point and path. They are never used, they just need to // be sufficient to pass the validation tests. ServerEndpointConfig.Builder builder = ServerEndpointConfig.Builder.create(Object.class, "/"); ServerEndpointConfig sec = builder.build(); - SEC_CONFIGURATOR_USES_IMPL_DEFAULT = sec.getConfigurator().getClass() - .equals(DefaultServerEndpointConfigurator.class); + SEC_CONFIGURATOR_USES_IMPL_DEFAULT = + sec.getConfigurator().getClass().equals(DefaultServerEndpointConfigurator.class); } private final Endpoint localEndpoint; @@ -91,14 +92,14 @@ private final ClassLoader applicationClassLoader; private final WsWebSocketContainer webSocketContainer; private final URI requestUri; - private final Map> requestParameterMap; + private final Map> requestParameterMap; private final String queryString; private final Principal userPrincipal; private final EndpointConfig endpointConfig; private final List negotiatedExtensions; private final String subProtocol; - private final Map pathParameters; + private final Map pathParameters; private final boolean secure; private final String httpSessionId; private final String id; @@ -108,14 +109,14 @@ // Expected to handle message types of only private volatile MessageHandler binaryMessageHandler = null; private volatile MessageHandler.Whole pongMessageHandler = null; - private AtomicReference state = new AtomicReference<>(State.OPEN); - private final Map userProperties = new ConcurrentHashMap<>(); + private final AtomicReference state = new AtomicReference<>(State.OPEN); + private final Map userProperties = new ConcurrentHashMap<>(); private volatile int maxBinaryMessageBufferSize = Constants.DEFAULT_BUFFER_SIZE; private volatile int maxTextMessageBufferSize = Constants.DEFAULT_BUFFER_SIZE; private volatile long maxIdleTimeout = 0; private volatile long lastActiveRead = System.currentTimeMillis(); private volatile long lastActiveWrite = System.currentTimeMillis(); - private Map futures = new ConcurrentHashMap<>(); + private final Map futures = new ConcurrentHashMap<>(); private volatile Long sessionCloseTimeoutExpiry; @@ -138,7 +139,7 @@ */ public WsSession(ClientEndpointHolder clientEndpointHolder, WsRemoteEndpointImplBase wsRemoteEndpoint, WsWebSocketContainer wsWebSocketContainer, List negotiatedExtensions, String subProtocol, - Map pathParameters, boolean secure, ClientEndpointConfig clientEndpointConfig) + Map pathParameters, boolean secure, ClientEndpointConfig clientEndpointConfig) throws DeploymentException { this.wsRemoteEndpoint = wsRemoteEndpoint; this.wsRemoteEndpoint.setSession(this); @@ -156,11 +157,7 @@ this.userPrincipal = null; this.httpSessionId = null; this.negotiatedExtensions = negotiatedExtensions; - if (subProtocol == null) { - this.subProtocol = ""; - } else { - this.subProtocol = subProtocol; - } + this.subProtocol = Objects.requireNonNullElse(subProtocol, ""); this.pathParameters = pathParameters; this.secure = secure; this.wsRemoteEndpoint.setEncoders(clientEndpointConfig); @@ -204,9 +201,9 @@ * @throws DeploymentException if an invalid encode is specified */ public WsSession(WsRemoteEndpointImplBase wsRemoteEndpoint, WsWebSocketContainer wsWebSocketContainer, - URI requestUri, Map> requestParameterMap, String queryString, Principal userPrincipal, + URI requestUri, Map> requestParameterMap, String queryString, Principal userPrincipal, String httpSessionId, List negotiatedExtensions, String subProtocol, - Map pathParameters, boolean secure, ServerEndpointConfig serverEndpointConfig) + Map pathParameters, boolean secure, ServerEndpointConfig serverEndpointConfig) throws DeploymentException { this.wsRemoteEndpoint = wsRemoteEndpoint; @@ -220,20 +217,12 @@ this.maxTextMessageBufferSize = webSocketContainer.getDefaultMaxTextMessageBufferSize(); this.maxIdleTimeout = webSocketContainer.getDefaultMaxSessionIdleTimeout(); this.requestUri = requestUri; - if (requestParameterMap == null) { - this.requestParameterMap = Collections.emptyMap(); - } else { - this.requestParameterMap = requestParameterMap; - } + this.requestParameterMap = Objects.requireNonNullElse(requestParameterMap, Collections.emptyMap()); this.queryString = queryString; this.userPrincipal = userPrincipal; this.httpSessionId = httpSessionId; this.negotiatedExtensions = negotiatedExtensions; - if (subProtocol == null) { - this.subProtocol = ""; - } else { - this.subProtocol = subProtocol; - } + this.subProtocol = Objects.requireNonNullElse(subProtocol, ""); this.pathParameters = pathParameters; this.secure = secure; this.wsRemoteEndpoint.setEncoders(serverEndpointConfig); @@ -280,11 +269,7 @@ if (configurator.getClass().equals(DefaultServerEndpointConfigurator.class)) { return true; } - if (SEC_CONFIGURATOR_USES_IMPL_DEFAULT && - configurator.getClass().equals(ServerEndpointConfig.Configurator.class)) { - return true; - } - return false; + return SEC_CONFIGURATOR_USES_IMPL_DEFAULT && configurator.getClass().equals(Configurator.class); } @@ -647,9 +632,9 @@ closeConnection(); } else if (state.compareAndSet(State.OUTPUT_CLOSING, State.CLOSING)) { /* - * The local endpoint sent a close message the the same time as the remote endpoint. The local close is - * still being processed. Update the state so the the local close process will also close the network - * connection once it has finished sending a close message. + * The local endpoint sent a close message at the same time as the remote endpoint. The local close is still + * being processed. Update the state so the local close process will also close the network connection once + * it has finished sending a close message. */ } else if (state.compareAndSet(State.OUTPUT_CLOSED, State.CLOSED)) { /* @@ -781,7 +766,7 @@ } String reason = closeReason.getReasonPhrase(); - if (reason != null && reason.length() > 0) { + if (reason != null && !reason.isEmpty()) { appendCloseReasonWithTruncation(msg, reason); } msg.flip(); @@ -801,7 +786,7 @@ closeConnection(); // Failure to send a close message is not unexpected in the case of // an abnormal closure (usually triggered by a failure to read/write - // from/to the client. In this case do not trigger the endpoint's + // from/to the client). In this case do not trigger the endpoint's // error handling if (closeCode != CloseCodes.CLOSED_ABNORMALLY) { localEndpoint.onError(this, e); @@ -830,7 +815,7 @@ // Once the close code has been added there are a maximum of 123 bytes // left for the reason phrase. If it is truncated then care needs to be // taken to ensure the bytes are not truncated in the middle of a - // multi-byte UTF-8 character. + // multibyte UTF-8 character. byte[] reasonBytes = reason.getBytes(StandardCharsets.UTF_8); if (reasonBytes.length <= 123) { @@ -858,11 +843,11 @@ * * @param f2sh The handler */ - protected void registerFuture(FutureToSendHandler f2sh) { + void registerFuture(FutureToSendHandler f2sh) { // Ideally, this code should sync on stateLock so that the correct // action is taken based on the current state of the connection. // However, a sync on stateLock can't be used here as it will create the - // possibility of a dead-lock. See BZ 61183. + // possibility of a deadlock. See BZ 61183. // Therefore, a slightly less efficient approach is used. // Always register the future. @@ -909,7 +894,7 @@ * * @param f2sh The handler */ - protected void unregisterFuture(FutureToSendHandler f2sh) { + void unregisterFuture(FutureToSendHandler f2sh) { futures.remove(f2sh); } @@ -922,7 +907,7 @@ @Override - public Map> getRequestParameterMap() { + public Map> getRequestParameterMap() { checkState(); return requestParameterMap; } @@ -948,7 +933,7 @@ @Override - public Map getPathParameters() { + public Map getPathParameters() { checkState(); return pathParameters; } @@ -961,7 +946,7 @@ @Override - public Map getUserProperties() { + public Map getUserProperties() { checkState(); return userProperties; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsWebSocketContainer.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsWebSocketContainer.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/WsWebSocketContainer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/WsWebSocketContainer.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,6 +38,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; @@ -92,8 +93,8 @@ private final Log log = LogFactory.getLog(WsWebSocketContainer.class); // must not be static // Server side uses the endpoint path as the key // Client side uses the client endpoint instance - private final Map> endpointSessionMap = new HashMap<>(); - private final Map sessions = new ConcurrentHashMap<>(); + private final Map> endpointSessionMap = new HashMap<>(); + private final Map sessions = new ConcurrentHashMap<>(); private final Object endPointSessionMapLock = new Object(); private long defaultAsyncTimeout = -1; @@ -156,11 +157,8 @@ if (configurator != null) { builder.configurator(configurator); } - ClientEndpointConfig config = builder.decoders(Arrays.asList(annotation.decoders())) - .encoders(Arrays.asList(annotation.encoders())) + return builder.decoders(Arrays.asList(annotation.decoders())).encoders(Arrays.asList(annotation.encoders())) .preferredSubprotocols(Arrays.asList(annotation.subprotocols())).build(); - - return config; } @@ -241,7 +239,7 @@ } } - Map userProperties = clientEndpointConfiguration.getUserProperties(); + Map userProperties = clientEndpointConfiguration.getUserProperties(); // If sa is null, no proxy is configured so need to create sa if (sa == null) { @@ -252,7 +250,7 @@ } // Create the initial HTTP request to open the WebSocket connection - Map> reqHeaders = createRequestHeaders(host, port, secure, clientEndpointConfiguration); + Map> reqHeaders = createRequestHeaders(host, port, secure, clientEndpointConfiguration); clientEndpointConfiguration.getConfigurator().beforeRequest(reqHeaders); if (Constants.DEFAULT_ORIGIN_HEADER_VALUE != null && !reqHeaders.containsKey(Constants.ORIGIN_HEADER_NAME)) { List originValues = new ArrayList<>(1); @@ -343,8 +341,8 @@ if (httpResponse.status != 101) { if (isRedirectStatus(httpResponse.status)) { - List locationHeader = httpResponse.getHandshakeResponse().getHeaders() - .get(Constants.LOCATION_HEADER_NAME); + List locationHeader = + httpResponse.getHandshakeResponse().getHeaders().get(Constants.LOCATION_HEADER_NAME); if (locationHeader == null || locationHeader.isEmpty() || locationHeader.get(0) == null || locationHeader.get(0).isEmpty()) { @@ -389,7 +387,7 @@ // Sub-protocol List protocolHeaders = handshakeResponse.getHeaders().get(Constants.WS_PROTOCOL_HEADER_NAME); - if (protocolHeaders == null || protocolHeaders.size() == 0) { + if (protocolHeaders == null || protocolHeaders.isEmpty()) { subProtocol = null; } else if (protocolHeaders.size() == 1) { subProtocol = protocolHeaders.get(0); @@ -424,8 +422,8 @@ } success = true; - } catch (ExecutionException | InterruptedException | SSLException | EOFException | TimeoutException - | URISyntaxException | AuthenticationException e) { + } catch (ExecutionException | InterruptedException | SSLException | EOFException | TimeoutException | + URISyntaxException | AuthenticationException e) { throw new DeploymentException(sm.getString("wsWebSocketContainer.httpRequestFailed", path), e); } finally { if (!success) { @@ -445,7 +443,7 @@ WsRemoteEndpointImplClient wsRemoteEndpointClient = new WsRemoteEndpointImplClient(channel); WsSession wsSession = new WsSession(clientEndpointHolder, wsRemoteEndpointClient, this, extensionsAgreed, - subProtocol, Collections.emptyMap(), secure, clientEndpointConfiguration); + subProtocol, Collections.emptyMap(), secure, clientEndpointConfiguration); WsFrameClient wsFrameClient = new WsFrameClient(response, channel, wsSession, transformation); // WsFrame adds the necessary final transformations. Copy the @@ -471,7 +469,7 @@ private Session processAuthenticationChallenge(ClientEndpointHolder clientEndpointHolder, ClientEndpointConfig clientEndpointConfiguration, URI path, Set redirectSet, - Map userProperties, ByteBuffer request, HttpResponse httpResponse, + Map userProperties, ByteBuffer request, HttpResponse httpResponse, AuthenticationType authenticationType) throws DeploymentException, AuthenticationException { if (userProperties.get(authenticationType.getAuthorizationHeaderName()) != null) { @@ -479,8 +477,8 @@ Integer.valueOf(httpResponse.status), authenticationType.getAuthorizationHeaderName())); } - List authenticateHeaders = httpResponse.getHandshakeResponse().getHeaders() - .get(authenticationType.getAuthenticateHeaderName()); + List authenticateHeaders = + httpResponse.getHandshakeResponse().getHeaders().get(authenticationType.getAuthenticateHeaderName()); if (authenticateHeaders == null || authenticateHeaders.isEmpty() || authenticateHeaders.get(0) == null || authenticateHeaders.get(0).isEmpty()) { @@ -578,7 +576,7 @@ return; } synchronized (endPointSessionMapLock) { - if (endpointSessionMap.size() == 0) { + if (endpointSessionMap.isEmpty()) { BackgroundProcessManager.getInstance().register(this); } endpointSessionMap.computeIfAbsent(key, k -> new HashSet<>()).add(wsSession); @@ -593,11 +591,11 @@ Set wsSessions = endpointSessionMap.get(key); if (wsSessions != null) { wsSessions.remove(wsSession); - if (wsSessions.size() == 0) { + if (wsSessions.isEmpty()) { endpointSessionMap.remove(key); } } - if (endpointSessionMap.size() == 0) { + if (endpointSessionMap.isEmpty()) { BackgroundProcessManager.getInstance().unregister(this); } } @@ -621,13 +619,13 @@ return result; } - private static Map> createRequestHeaders(String host, int port, boolean secure, + private static Map> createRequestHeaders(String host, int port, boolean secure, ClientEndpointConfig clientEndpointConfiguration) { - Map> headers = new HashMap<>(); + Map> headers = new HashMap<>(); List extensions = clientEndpointConfiguration.getExtensions(); List subProtocols = clientEndpointConfiguration.getPreferredSubprotocols(); - Map userProperties = clientEndpointConfiguration.getUserProperties(); + Map userProperties = clientEndpointConfiguration.getUserProperties(); if (userProperties.get(Constants.AUTHORIZATION_HEADER_NAME) != null) { List authValues = new ArrayList<>(1); @@ -667,13 +665,25 @@ headers.put(Constants.WS_KEY_HEADER_NAME, wsKeyValues); // WebSocket sub-protocols - if (subProtocols != null && subProtocols.size() > 0) { + if (subProtocols != null && !subProtocols.isEmpty()) { headers.put(Constants.WS_PROTOCOL_HEADER_NAME, subProtocols); } // WebSocket extensions - if (extensions != null && extensions.size() > 0) { - headers.put(Constants.WS_EXTENSIONS_HEADER_NAME, generateExtensionHeaders(extensions)); + if (extensions != null) { + // Filter the requested extensions to remove any that are not supported by the client container. + Set installed = TransformationFactory.getInstance().getInstalledExtensionNames(); + List availableExtensions = new ArrayList<>(extensions); + Iterator availableExtensionsIter = availableExtensions.iterator(); + while (availableExtensionsIter.hasNext()) { + Extension e = availableExtensionsIter.next(); + if (!installed.contains(e.getName())) { + availableExtensionsIter.remove(); + } + } + if (!availableExtensions.isEmpty()) { + headers.put(Constants.WS_EXTENSIONS_HEADER_NAME, generateExtensionHeaders(availableExtensions)); + } } return headers; @@ -689,7 +699,7 @@ header.append(';'); header.append(param.getName()); String value = param.getValue(); - if (value != null && value.length() > 0) { + if (value != null && !value.isEmpty()) { header.append('='); header.append(value); } @@ -707,7 +717,7 @@ } - private static ByteBuffer createRequest(URI uri, Map> reqHeaders) { + private static ByteBuffer createRequest(URI uri, Map> reqHeaders) { ByteBuffer result = ByteBuffer.allocate(4 * 1024); // Request line @@ -726,7 +736,7 @@ result.put(HTTP_VERSION_BYTES); // Headers - for (Entry> entry : reqHeaders.entrySet()) { + for (Entry> entry : reqHeaders.entrySet()) { result = addHeader(result, entry.getKey(), entry.getValue()); } @@ -781,7 +791,7 @@ private HttpResponse processResponse(ByteBuffer response, AsyncChannelWrapper channel, long timeout) throws InterruptedException, ExecutionException, DeploymentException, EOFException, TimeoutException { - Map> headers = new CaseInsensitiveKeyMap<>(); + Map> headers = new CaseInsensitiveKeyMap<>(); int status = 0; boolean readStatus = false; @@ -847,7 +857,7 @@ } - private void parseHeaders(String line, Map> headers) { + private void parseHeaders(String line, Map> headers) { // Treat headers as single values by default. int index = line.indexOf(':'); @@ -855,7 +865,7 @@ log.warn(sm.getString("wsWebSocketContainer.invalidHeader", line)); return; } - // Header names are case insensitive so always use lower case + // Header names are case-insensitive so always use lower case String headerName = line.substring(0, index).trim().toLowerCase(Locale.ENGLISH); // Multi-value headers are stored as a single header and the client is // expected to handle splitting into individual values @@ -869,7 +879,7 @@ // All ISO-8859-1 StringBuilder sb = new StringBuilder(); - char c = 0; + char c; while (response.hasRemaining()) { c = (char) response.get(); sb.append(c); @@ -886,7 +896,7 @@ private SSLEngine createSSLEngine(ClientEndpointConfig clientEndpointConfig, String host, int port) throws DeploymentException { - Map userProperties = clientEndpointConfig.getUserProperties(); + Map userProperties = clientEndpointConfig.getUserProperties(); try { // See if a custom SSLContext has been provided SSLContext sslContext = clientEndpointConfig.getSSLContext(); @@ -915,8 +925,8 @@ KeyStoreUtil.load(ks, is, sslTrustStorePwdValue.toCharArray()); } - TrustManagerFactory tmf = TrustManagerFactory - .getInstance(TrustManagerFactory.getDefaultAlgorithm()); + TrustManagerFactory tmf = + TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); sslContext.init(null, tmf.getTrustManagers(), null); @@ -990,7 +1000,7 @@ */ @Override public Set getInstalledExtensions() { - return Collections.emptySet(); + return TransformationFactory.getInstance().getInstalledExtensions(); } @@ -1023,7 +1033,9 @@ try { session.close(cr); } catch (IOException ioe) { - log.debug(sm.getString("wsWebSocketContainer.sessionCloseFail", session.getId()), ioe); + if (log.isDebugEnabled()) { + log.debug(sm.getString("wsWebSocketContainer.sessionCloseFail", session.getId()), ioe); + } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings.properties 2026-01-23 19:33:36.000000000 +0000 @@ -23,11 +23,12 @@ pojoEndpointBase.onOpenFail=Failed to call onOpen method of POJO end point for POJO of type [{0}] pojoMessageHandlerBase.encodeFail=Encoding failed for POJO of tyoe [{0}] in session [{1}] +pojoMessageHandlerBase.onMessageFail=Exception during onMessage call to POJO of type [{0}] in session [{1}] pojoMessageHandlerWhole.decodeIoFail=IO error while decoding message pojoMessageHandlerWhole.maxBufferSize=The maximum supported message size for this implementation is Integer.MAX_VALUE -pojoMessageHandlerWholeBase.decodeDestoryFailed=Failed to destroy the decoder of type [{0}] +pojoMessageHandlerWholeBase.decodeDestroyFailed=Failed to destroy the decoder of type [{0}] pojoMethodMapping.decodePathParamFail=Failed to decode path parameter value [{0}] to expected type [{1}] pojoMethodMapping.duplicateAnnotation=Duplicate annotations [{0}] present on class [{1}] diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings_fr.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_fr.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings_fr.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_fr.properties 2026-01-23 19:33:36.000000000 +0000 @@ -22,10 +22,12 @@ pojoEndpointBase.onErrorFail=Echec de l''appel de la méthode onError du point de terminaison POJO pour le type [{0}] pojoEndpointBase.onOpenFail=Impossible d’appeler la méthode onOpen du point de terminaison POJO de type [{0}] +pojoMessageHandlerBase.onMessageFail=Exception lors de l''appel onMessage du POJO de type [{0}] dans la session [{1}] + pojoMessageHandlerWhole.decodeIoFail=Erreur d'IO lors du décodage du message pojoMessageHandlerWhole.maxBufferSize=La taille maximale de message supportée par cette implémentation est Integer.MAX_VALUE -pojoMessageHandlerWholeBase.decodeDestoryFailed=Echec de la destruction du décodeur de type [{0}] +pojoMessageHandlerWholeBase.decodeDestroyFailed=Echec de la destruction du décodeur de type [{0}] pojoMethodMapping.decodePathParamFail=Echec de décodage de la valeur de paramètre de chemin [{0}] vers le type attendu [{1}] pojoMethodMapping.duplicateAnnotation=Annotations dupliquées [{0}] présentes pour la classe [{1}] diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings_ja.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_ja.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings_ja.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_ja.properties 2026-01-23 19:33:36.000000000 +0000 @@ -22,10 +22,12 @@ pojoEndpointBase.onErrorFail=タイプ [{0}] のPOJOのPOJOエンドポイントのonErrorメソッドの呼び出しに失敗しました pojoEndpointBase.onOpenFail=タイプ [{0}] のPOJOのPOJOエンドポイントのonOpenメソッドの呼び出しに失敗しました +pojoMessageHandlerBase.onMessageFail=WebSocketセッション [{1}] に含まれるタイプ [{0}] の POJO に対する onMessage 呼び出し中に例外が発生しました + pojoMessageHandlerWhole.decodeIoFail=メッセージの復号中に入出力エラーが発生しました。 pojoMessageHandlerWhole.maxBufferSize=この実装で対応可能なメッセージサイズの上限値は Integer.MAX_VALUE です。 -pojoMessageHandlerWholeBase.decodeDestoryFailed=タイプ [{0}] のデコーダーの破棄に失敗しました +pojoMessageHandlerWholeBase.decodeDestroyFailed=タイプ [{0}] のデコーダーの破棄に失敗しました pojoMethodMapping.decodePathParamFail=パスパラメータの値 [{0}] を [{1}] 型として解釈できません。 pojoMethodMapping.duplicateAnnotation=クラス [{1}] にアノテーション [{0}] が重複しています diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings_ko.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_ko.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings_ko.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_ko.properties 2026-01-23 19:33:36.000000000 +0000 @@ -25,7 +25,7 @@ pojoMessageHandlerWhole.decodeIoFail=메시지를 디코딩하는 중 IO 오류 발생 pojoMessageHandlerWhole.maxBufferSize=이 구현을 위해 지원되는 최대 메시지 크기는 Integer.MAX_VALUE입니다. -pojoMessageHandlerWholeBase.decodeDestoryFailed=[{0}] 타입의 디코더를 소멸시키지 못했습니다. +pojoMessageHandlerWholeBase.decodeDestroyFailed=[{0}] 타입의 디코더를 소멸시키지 못했습니다. pojoMethodMapping.decodePathParamFail=경로 파라미터 값 [{0}]을(를), 요구되는 타입 [{1}](으)로 디코드하지 못했습니다. pojoMethodMapping.duplicateAnnotation=중복된 [{0}] annotation들이 클래스 [{1}]에 존재합니다. diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings_zh_CN.properties tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -25,7 +25,7 @@ pojoMessageHandlerWhole.decodeIoFail=解码消息时出现IO错误 pojoMessageHandlerWhole.maxBufferSize=此实现支持的最大消息大小为Integer.MAX_VALUE -pojoMessageHandlerWholeBase.decodeDestoryFailed=未能销毁[{0}]类型的解码器 +pojoMessageHandlerWholeBase.decodeDestroyFailed=未能销毁[{0}]类型的解码器 pojoMethodMapping.decodePathParamFail=未能将路径参数值[{0}]解码为预期的类型[{1}] pojoMethodMapping.duplicateAnnotation=类[{1}]上存在的重复注释[{0}] diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoEndpointBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,11 +42,11 @@ private static final StringManager sm = StringManager.getManager(PojoEndpointBase.class); private Object pojo; - private final Map pathParameters; + private final Map pathParameters; private PojoMethodMapping methodMapping; - protected PojoEndpointBase(Map pathParameters) { + protected PojoEndpointBase(Map pathParameters) { this.pathParameters = pathParameters; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoEndpointClient.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoEndpointClient.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoEndpointClient.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoEndpointClient.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,7 +34,7 @@ public PojoEndpointClient(Object pojo, List> decoders, InstanceManager instanceManager) throws DeploymentException { - super(Collections.emptyMap()); + super(Collections.emptyMap()); setPojo(pojo); setMethodMapping(new PojoMethodMapping(pojo.getClass(), decoders, null, instanceManager)); } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoEndpointServer.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,7 +28,7 @@ */ public class PojoEndpointServer extends PojoEndpointBase { - public PojoEndpointServer(Map pathParameters, Object pojo) { + public PojoEndpointServer(Map pathParameters, Object pojo) { super(pathParameters); setPojo(pojo); } @@ -39,8 +39,8 @@ ServerEndpointConfig sec = (ServerEndpointConfig) endpointConfig; - PojoMethodMapping methodMapping = (PojoMethodMapping) sec.getUserProperties() - .get(Constants.POJO_METHOD_MAPPING_KEY); + PojoMethodMapping methodMapping = + (PojoMethodMapping) sec.getUserProperties().get(Constants.POJO_METHOD_MAPPING_KEY); setMethodMapping(methodMapping); doOnOpen(session, endpointConfig); diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBase.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBase.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoMessageHandlerWholeBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -116,7 +116,7 @@ try { instanceManager.destroyInstance(decoder); } catch (IllegalAccessException | InvocationTargetException e) { - log.warn(sm.getString("pojoMessageHandlerWholeBase.decodeDestoryFailed", decoder.getClass()), e); + log.warn(sm.getString("pojoMessageHandlerWholeBase.decodeDestroyFailed", decoder.getClass()), e); } } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/pojo/PojoMethodMapping.java 2026-01-23 19:33:36.000000000 +0000 @@ -98,8 +98,8 @@ if (method.isSynthetic()) { // Skip all synthetic methods. // They may have copies of annotations from methods we are - // interested in and they will use the wrong parameter type - // (they always use Object) so we can't used them here. + // interested in, and they will use the wrong parameter type + // (they always use Object) so we can't use them here. continue; } if (method.getAnnotation(OnOpen.class) != null) { @@ -159,7 +159,7 @@ } currentClazz = currentClazz.getSuperclass(); } - // If the methods are not on clazzPojo and they are overridden + // If the methods are not on clazzPojo, and they are overridden // by a non annotated method in clazzPojo, they should be ignored if (open != null && open.getDeclaringClass() != clazzPojo) { if (isOverridenWithoutAnnotation(clazzPojoMethods, open, OnOpen.class)) { @@ -229,7 +229,7 @@ } - public Object[] getOnOpenArgs(Map pathParameters, Session session, EndpointConfig config) + public Object[] getOnOpenArgs(Map pathParameters, Session session, EndpointConfig config) throws DecodeException { return buildArgs(onOpenParams, pathParameters, session, config, null, null); } @@ -240,7 +240,7 @@ } - public Object[] getOnCloseArgs(Map pathParameters, Session session, CloseReason closeReason) + public Object[] getOnCloseArgs(Map pathParameters, Session session, CloseReason closeReason) throws DecodeException { return buildArgs(onCloseParams, pathParameters, session, null, null, closeReason); } @@ -251,7 +251,7 @@ } - public Object[] getOnErrorArgs(Map pathParameters, Session session, Throwable throwable) + public Object[] getOnErrorArgs(Map pathParameters, Session session, Throwable throwable) throws DecodeException { return buildArgs(onErrorParams, pathParameters, session, null, throwable, null); } @@ -262,7 +262,7 @@ } - public Set getMessageHandlers(Object pojo, Map pathParameters, Session session, + public Set getMessageHandlers(Object pojo, Map pathParameters, Session session, EndpointConfig config) { Set result = new HashSet<>(); for (MessageHandlerInfo messageMethod : onMessage) { @@ -314,7 +314,7 @@ } - private static Object[] buildArgs(PojoPathParam[] pathParams, Map pathParameters, Session session, + private static Object[] buildArgs(PojoPathParam[] pathParams, Map pathParameters, Session session, EndpointConfig config, Throwable throwable, CloseReason closeReason) throws DecodeException { Object[] result = new Object[pathParams.length]; for (int i = 0; i < pathParams.length; i++) { @@ -354,10 +354,10 @@ private int indexInputStream = -1; private int indexReader = -1; private int indexPrimitive = -1; - private Map indexPathParams = new HashMap<>(); + private final Map indexPathParams = new HashMap<>(); private int indexPayload = -1; private DecoderMatch decoderMatch = null; - private long maxMessageSize = -1; + private final long maxMessageSize; MessageHandlerInfo(Method m, List decoderEntries) throws DeploymentException { @@ -564,24 +564,24 @@ private boolean isText() { return indexString >= 0 || indexPrimitive >= 0 || indexReader >= 0 || - (decoderMatch != null && decoderMatch.getTextDecoders().size() > 0); + (decoderMatch != null && !decoderMatch.getTextDecoders().isEmpty()); } private boolean isBinary() { return indexByteArray >= 0 || indexByteBuffer >= 0 || indexInputStream >= 0 || - (decoderMatch != null && decoderMatch.getBinaryDecoders().size() > 0); + (decoderMatch != null && !decoderMatch.getBinaryDecoders().isEmpty()); } - public Set getMessageHandlers(Object pojo, Map pathParameters, Session session, + public Set getMessageHandlers(Object pojo, Map pathParameters, Session session, EndpointConfig config) { Object[] params = new Object[m.getParameterTypes().length]; - for (Map.Entry entry : indexPathParams.entrySet()) { + for (Map.Entry entry : indexPathParams.entrySet()) { PojoPathParam pathParam = entry.getValue(); String valueString = pathParameters.get(pathParam.getName()); - Object value = null; + Object value; try { value = Util.coerceToType(pathParam.getType(), valueString); } catch (Exception e) { @@ -617,21 +617,21 @@ indexInputStream, true, indexSession, true, maxMessageSize); results.add(mh); } else if (decoderMatch != null && decoderMatch.hasMatches()) { - if (decoderMatch.getBinaryDecoders().size() > 0) { + if (!decoderMatch.getBinaryDecoders().isEmpty()) { MessageHandler mh = new PojoMessageHandlerWholeBinary(pojo, m, session, config, decoderMatch.getBinaryDecoders(), params, indexPayload, true, indexSession, true, maxMessageSize); results.add(mh); } - if (decoderMatch.getTextDecoders().size() > 0) { + if (!decoderMatch.getTextDecoders().isEmpty()) { MessageHandler mh = new PojoMessageHandlerWholeText(pojo, m, session, config, decoderMatch.getTextDecoders(), params, indexPayload, true, indexSession, maxMessageSize); results.add(mh); } } else { - MessageHandler mh = new PojoMessageHandlerWholePong(pojo, m, session, params, indexPong, false, - indexSession); + MessageHandler mh = + new PojoMessageHandlerWholePong(pojo, m, session, params, indexPong, false, indexSession); results.add(mh); } } else { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/Constants.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/Constants.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/Constants.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/Constants.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,8 +21,10 @@ */ public class Constants { - public static final String BINARY_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM = "org.apache.tomcat.websocket.binaryBufferSize"; - public static final String TEXT_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM = "org.apache.tomcat.websocket.textBufferSize"; + public static final String BINARY_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM = + "org.apache.tomcat.websocket.binaryBufferSize"; + public static final String TEXT_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM = + "org.apache.tomcat.websocket.textBufferSize"; public static final String SERVER_CONTAINER_SERVLET_CONTEXT_ATTRIBUTE = "jakarta.websocket.server.ServerContainer"; diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/UpgradeUtil.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/UpgradeUtil.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/UpgradeUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/UpgradeUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,6 +38,7 @@ import jakarta.websocket.HandshakeResponse; import jakarta.websocket.server.ServerEndpointConfig; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.security.ConcurrentMessageDigest; import org.apache.tomcat.websocket.Constants; @@ -50,8 +51,8 @@ public class UpgradeUtil { private static final StringManager sm = StringManager.getManager(UpgradeUtil.class.getPackage().getName()); - private static final byte[] WS_ACCEPT = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - .getBytes(StandardCharsets.ISO_8859_1); + private static final byte[] WS_ACCEPT = + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".getBytes(StandardCharsets.ISO_8859_1); private UpgradeUtil() { // Utility class. Hide default constructor. @@ -74,17 +75,17 @@ return ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) && headerContainsToken((HttpServletRequest) request, Constants.UPGRADE_HEADER_NAME, Constants.UPGRADE_HEADER_VALUE) && - "GET".equals(((HttpServletRequest) request).getMethod())); + Method.GET.equals(((HttpServletRequest) request).getMethod())); } public static void doUpgrade(WsServerContainer sc, HttpServletRequest req, HttpServletResponse resp, - ServerEndpointConfig sec, Map pathParams) throws ServletException, IOException { + ServerEndpointConfig sec, Map pathParams) throws ServletException, IOException { // Validate the rest of the headers and reject the request if that // validation fails String key; - String subProtocol = null; + String subProtocol; if (!headerContainsToken(req, Constants.CONNECTION_HEADER_NAME, Constants.CONNECTION_HEADER_VALUE)) { resp.sendError(HttpServletResponse.SC_BAD_REQUEST); return; @@ -119,19 +120,19 @@ while (extHeaders.hasMoreElements()) { Util.parseExtensionHeader(extensionsRequested, extHeaders.nextElement()); } - // Negotiation phase 1. By default this simply filters out the + // Negotiation phase 1. By default, this simply filters out the // extensions that the server does not support but applications could // use a custom configurator to do more than this. - List installedExtensions = null; - if (sec.getExtensions().size() == 0) { + List installedExtensions; + if (sec.getExtensions().isEmpty()) { installedExtensions = Constants.INSTALLED_EXTENSIONS; } else { installedExtensions = new ArrayList<>(); installedExtensions.addAll(sec.getExtensions()); installedExtensions.addAll(Constants.INSTALLED_EXTENSIONS); } - List negotiatedExtensionsPhase1 = sec.getConfigurator().getNegotiatedExtensions(installedExtensions, - extensionsRequested); + List negotiatedExtensionsPhase1 = + sec.getConfigurator().getNegotiatedExtensions(installedExtensions, extensionsRequested); // Negotiation phase 2. Create the Transformations that will be applied // to this connection. Note than an extension may be dropped at this @@ -176,7 +177,7 @@ resp.setHeader(Constants.UPGRADE_HEADER_NAME, Constants.UPGRADE_HEADER_VALUE); resp.setHeader(Constants.CONNECTION_HEADER_NAME, Constants.CONNECTION_HEADER_VALUE); resp.setHeader(HandshakeResponse.SEC_WEBSOCKET_ACCEPT, getWebSocketAccept(key)); - if (subProtocol != null && subProtocol.length() > 0) { + if (subProtocol != null && !subProtocol.isEmpty()) { // RFC6455 4.2.2 explicitly states "" is not valid here resp.setHeader(Constants.WS_PROTOCOL_HEADER_NAME, subProtocol); } @@ -211,7 +212,7 @@ wsRequest.finished(); // Add any additional headers - for (Entry> entry : wsResponse.getHeaders().entrySet()) { + for (Entry> entry : wsResponse.getHeaders().entrySet()) { for (String headerValue : entry.getValue()) { resp.addHeader(entry.getKey(), headerValue); } @@ -253,7 +254,7 @@ TransformationFactory factory = TransformationFactory.getInstance(); - LinkedHashMap>> extensionPreferences = new LinkedHashMap<>(); + LinkedHashMap>> extensionPreferences = new LinkedHashMap<>(); // Result will likely be smaller than this List result = new ArrayList<>(negotiatedExtensions.size()); @@ -263,7 +264,7 @@ .add(extension.getParameters()); } - for (Map.Entry>> entry : extensionPreferences.entrySet()) { + for (Map.Entry>> entry : extensionPreferences.entrySet()) { Transformation transformation = factory.create(entry.getKey(), entry.getValue(), true); if (transformation != null) { result.add(transformation); @@ -274,7 +275,7 @@ private static void append(StringBuilder sb, Extension extension) { - if (extension == null || extension.getName() == null || extension.getName().length() == 0) { + if (extension == null || extension.getName() == null || extension.getName().isEmpty()) { return; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/UriTemplate.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/UriTemplate.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/UriTemplate.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/UriTemplate.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,8 +43,8 @@ public UriTemplate(String path) throws DeploymentException { - if (path == null || path.length() == 0 || !path.startsWith("/") || path.contains("/../") || - path.contains("/./") || path.contains("//")) { + if (path == null || !path.startsWith("/") || path.contains("/../") || path.contains("/./") || + path.contains("//")) { throw new DeploymentException(sm.getString("uriTemplate.invalidPath", path)); } @@ -58,7 +58,7 @@ for (int i = 0; i < segments.length; i++) { String segment = segments[i]; - if (segment.length() == 0) { + if (segment.isEmpty()) { if (i == 0 || (i == segments.length - 1 && paramCount == 0)) { // Ignore the first empty segment as the path must always // start with '/' @@ -97,9 +97,9 @@ } - public Map match(UriTemplate candidate) { + public Map match(UriTemplate candidate) { - Map result = new HashMap<>(); + Map result = new HashMap<>(); // Should not happen but for safety if (candidate.getSegmentCount() != getSegmentCount()) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsFrameServer.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsFrameServer.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsFrameServer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsFrameServer.java 2026-01-23 19:33:36.000000000 +0000 @@ -162,9 +162,9 @@ } try { return doOnDataAvailable(); - } catch (IOException e) { + } catch (IOException ioe) { changeReadState(ReadState.CLOSING); - throw e; + throw ioe; } case SUSPENDING_WAIT: if (!changeReadState(ReadState.SUSPENDING_WAIT, ReadState.SUSPENDED)) { diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsHandshakeRequest.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsHandshakeRequest.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsHandshakeRequest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsHandshakeRequest.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,16 +41,16 @@ private static final StringManager sm = StringManager.getManager(WsHandshakeRequest.class); private final URI requestUri; - private final Map> parameterMap; + private final Map> parameterMap; private final String queryString; private final Principal userPrincipal; - private final Map> headers; + private final Map> headers; private final Object httpSession; private volatile HttpServletRequest request; - public WsHandshakeRequest(HttpServletRequest request, Map pathParams) { + public WsHandshakeRequest(HttpServletRequest request, Map pathParams) { this.request = request; @@ -60,18 +60,18 @@ requestUri = buildRequestUri(request); // ParameterMap - Map originalParameters = request.getParameterMap(); - Map> newParameters = new HashMap<>(originalParameters.size()); - for (Entry entry : originalParameters.entrySet()) { + Map originalParameters = request.getParameterMap(); + Map> newParameters = new HashMap<>(originalParameters.size()); + for (Entry entry : originalParameters.entrySet()) { newParameters.put(entry.getKey(), Collections.unmodifiableList(Arrays.asList(entry.getValue()))); } - for (Entry entry : pathParams.entrySet()) { + for (Entry entry : pathParams.entrySet()) { newParameters.put(entry.getKey(), Collections.singletonList(entry.getValue())); } parameterMap = Collections.unmodifiableMap(newParameters); // Headers - Map> newHeaders = new CaseInsensitiveKeyMap<>(); + Map> newHeaders = new CaseInsensitiveKeyMap<>(); Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { @@ -89,7 +89,7 @@ } @Override - public Map> getParameterMap() { + public Map> getParameterMap() { return parameterMap; } @@ -104,7 +104,7 @@ } @Override - public Map> getHeaders() { + public Map> getHeaders() { return headers; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -54,7 +54,7 @@ private final ClassLoader applicationClassLoader; private SocketWrapperBase socketWrapper; - private UpgradeInfo upgradeInfo = new UpgradeInfo(); + private final UpgradeInfo upgradeInfo = new UpgradeInfo(); private Endpoint ep; private ServerEndpointConfig serverEndpointConfig; @@ -63,7 +63,7 @@ private List negotiatedExtensions; private String subProtocol; private Transformation transformation; - private Map pathParameters; + private Map pathParameters; private boolean secure; private WebConnection connection; @@ -85,7 +85,7 @@ public void preInit(ServerEndpointConfig serverEndpointConfig, WsServerContainer wsc, WsHandshakeRequest handshakeRequest, List negotiatedExtensionsPhase2, String subProtocol, - Transformation transformation, Map pathParameters, boolean secure) { + Transformation transformation, Map pathParameters, boolean secure) { this.serverEndpointConfig = serverEndpointConfig; this.webSocketContainer = wsc; this.handshakeRequest = handshakeRequest; @@ -117,8 +117,8 @@ ClassLoader cl = t.getContextClassLoader(); t.setContextClassLoader(applicationClassLoader); try { - wsRemoteEndpointServer = new WsRemoteEndpointImplServer(socketWrapper, upgradeInfo, webSocketContainer, - connection); + wsRemoteEndpointServer = + new WsRemoteEndpointImplServer(socketWrapper, upgradeInfo, webSocketContainer, connection); wsSession = new WsSession(wsRemoteEndpointServer, webSocketContainer, handshakeRequest.getRequestURI(), handshakeRequest.getParameterMap(), handshakeRequest.getQueryString(), handshakeRequest.getUserPrincipal(), httpSessionId, negotiatedExtensions, subProtocol, @@ -162,8 +162,8 @@ wsRemoteEndpointServer.onWritePossible(false); break; case STOP: - CloseReason cr = new CloseReason(CloseCodes.GOING_AWAY, - sm.getString("wsHttpUpgradeHandler.serverStop")); + CloseReason cr = + new CloseReason(CloseCodes.GOING_AWAY, sm.getString("wsHttpUpgradeHandler.serverStop")); try { wsSession.close(cr); } catch (IOException ioe) { @@ -189,7 +189,7 @@ /* * If a CLOSE frame has been received then wsFrame will be closed but need to keep the connection open until the - * CLOSE frame has been sent. Hence use the wsSession.isClosed() rather than wsFrame.isOpen() here. + * CLOSE frame has been sent. Hence, use the wsSession.isClosed() rather than wsFrame.isOpen() here. */ if (wsSession.isClosed()) { return SocketState.CLOSED; @@ -242,7 +242,7 @@ /* * Any call to this method is a result of a problem reading from the client. At this point that state of the * connection is unknown. First attempt to clear the handler for any in-flight message write (that probably - * failed). If using NIO2 is is possible that the original error occurred on a write but this method was called + * failed). If using NIO2 it is possible that the original error occurred on a write but this method was called * during a read. The in-progress write will block the sending of the close frame unless the handler is cleared * (effectively signalling the write failed). */ diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsMappingResult.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsMappingResult.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsMappingResult.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsMappingResult.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,10 +23,10 @@ class WsMappingResult { private final ServerEndpointConfig config; - private final Map pathParams; + private final Map pathParams; - WsMappingResult(ServerEndpointConfig config, Map pathParams) { + WsMappingResult(ServerEndpointConfig config, Map pathParams) { this.config = config; this.pathParams = pathParams; } @@ -37,7 +37,7 @@ } - Map getPathParams() { + Map getPathParams() { return pathParams; } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsPerSessionServerEndpointConfig.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsPerSessionServerEndpointConfig.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsPerSessionServerEndpointConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsPerSessionServerEndpointConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,7 +33,7 @@ class WsPerSessionServerEndpointConfig implements ServerEndpointConfig { private final ServerEndpointConfig perEndpointConfig; - private final Map perSessionUserProperties = new ConcurrentHashMap<>(); + private final Map perSessionUserProperties = new ConcurrentHashMap<>(); WsPerSessionServerEndpointConfig(ServerEndpointConfig perEndpointConfig) { this.perEndpointConfig = perEndpointConfig; @@ -51,7 +51,7 @@ } @Override - public Map getUserProperties() { + public Map getUserProperties() { return perSessionUserProperties; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsRemoteEndpointImplServer.java 2026-01-23 19:33:36.000000000 +0000 @@ -78,7 +78,7 @@ *

    * The close message is a special case. It needs to be blocking else implementing the clean-up that follows the * sending of the close message gets a lot more complicated. On the server, this creates additional complications as - * a dead-lock may occur in the following scenario: + * a deadlock may occur in the following scenario: *

      *
    1. Application thread writes message using non-blocking
    2. *
    3. Write does not complete (write logic holds message pending lock)
    4. @@ -88,9 +88,9 @@ *
    5. Container holds socket lock and is blocked waiting for message pending lock
    6. *
    7. Poller fires write possible event for socket
    8. *
    9. Container tries to process write possible event but is blocked waiting for socket lock
    10. - *
    11. Processing of the WebSocket connection is dead-locked until the original message write times out
    12. + *
    13. Processing of the WebSocket connection is deadlocked until the original message write times out
    14. *
    - * The purpose of this method is to break the above dead-lock. It does this by returning control of the processor to + * The purpose of this method is to break the above deadlock. It does this by returning control of the processor to * the socket wrapper and releasing the socket lock while waiting for the pending message write to complete. * Normally, that would be a terrible idea as it creates the possibility that the processor is returned to the pool * more than once under various error conditions. In this instance it is safe because these are upgrade processors @@ -105,7 +105,9 @@ /* * Special handling is required only when all of the following are true: + * * - A close message is being sent + * * - This thread currently holds the socketWrapper lock (i.e. the thread is current processing a socket event) * * Special handling is only possible if the socketWrapper lock is a ReentrantLock (it will be by default) @@ -151,7 +153,7 @@ protected void doWrite(SendHandler handler, long blockingWriteTimeoutExpiry, ByteBuffer... buffers) { if (socketWrapper.hasAsyncIO()) { final boolean block = (blockingWriteTimeoutExpiry != -1); - long timeout = -1; + long timeout; if (block) { timeout = blockingWriteTimeoutExpiry - System.currentTimeMillis(); if (timeout <= 0) { @@ -169,7 +171,7 @@ } } socketWrapper.write(block ? BlockingMode.BLOCK : BlockingMode.SEMI_BLOCK, timeout, TimeUnit.MILLISECONDS, - null, SocketWrapperBase.COMPLETE_WRITE_WITH_COMPLETION, new CompletionHandler() { + null, SocketWrapperBase.COMPLETE_WRITE_WITH_COMPLETION, new CompletionHandler() { @Override public void completed(Long result, Void attachment) { if (block) { @@ -226,8 +228,8 @@ socketWrapper.setWriteTimeout(timeout); socketWrapper.flush(true); handler.onResult(SENDRESULT_OK); - } catch (IOException e) { - SendResult sr = new SendResult(e); + } catch (IOException ioe) { + SendResult sr = new SendResult(ioe); handler.onResult(sr); } } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsSci.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsSci.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsSci.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsSci.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,7 +43,7 @@ WsServerContainer sc = init(ctx, true); - if (clazzes == null || clazzes.size() == 0) { + if (clazzes == null || clazzes.isEmpty()) { return; } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsServerContainer.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsServerContainer.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsServerContainer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsServerContainer.java 2026-01-23 19:33:36.000000000 +0000 @@ -66,9 +66,10 @@ private final WsWriteTimeout wsWriteTimeout = new WsWriteTimeout(); private final ServletContext servletContext; - private final Map configExactMatchMap = new ConcurrentHashMap<>(); - private final Map> configTemplateMatchMap = new ConcurrentHashMap<>(); - private final Map> authenticatedSessions = new ConcurrentHashMap<>(); + private final Map configExactMatchMap = new ConcurrentHashMap<>(); + private final Map> configTemplateMatchMap = + new ConcurrentHashMap<>(); + private final Map> authenticatedSessions = new ConcurrentHashMap<>(); private volatile boolean endpointsRegistered = false; private volatile boolean deploymentFailed = false; @@ -139,7 +140,7 @@ UriTemplate uriTemplate = new UriTemplate(path); if (uriTemplate.hasParameters()) { Integer key = Integer.valueOf(uriTemplate.getSegmentCount()); - ConcurrentSkipListMap templateMatches = configTemplateMatchMap.get(key); + ConcurrentSkipListMap templateMatches = configTemplateMatchMap.get(key); if (templateMatches == null) { // Ensure that if concurrent threads execute this block they // all end up using the same ConcurrentSkipListMap instance @@ -228,7 +229,7 @@ configurator = annotation.configurator().getConstructor().newInstance(); } catch (ReflectiveOperationException e) { throw new DeploymentException(sm.getString("serverContainer.configuratorFail", - annotation.configurator().getName(), pojo.getClass().getName()), e); + annotation.configurator().getName(), pojo.getName()), e); } } sec = ServerEndpointConfig.Builder.create(pojo, path).decoders(Arrays.asList(annotation.decoders())) @@ -260,7 +261,7 @@ @Override public void upgradeHttpToWebSocket(Object httpServletRequest, Object httpServletResponse, ServerEndpointConfig sec, - Map pathParameters) throws IOException, DeploymentException { + Map pathParameters) throws IOException, DeploymentException { try { UpgradeUtil.doUpgrade(this, (HttpServletRequest) httpServletRequest, (HttpServletResponse) httpServletResponse, sec, pathParameters); @@ -275,11 +276,11 @@ // Check an exact match. Simple case as there are no templates. ExactPathMatch match = configExactMatchMap.get(path); if (match != null) { - return new WsMappingResult(match.getConfig(), Collections.emptyMap()); + return new WsMappingResult(match.getConfig(), Collections.emptyMap()); } // No exact match. Need to look for template matches. - UriTemplate pathUriTemplate = null; + UriTemplate pathUriTemplate; try { pathUriTemplate = new UriTemplate(path); } catch (DeploymentException e) { @@ -289,7 +290,7 @@ // Number of segments has to match Integer key = Integer.valueOf(pathUriTemplate.getSegmentCount()); - ConcurrentSkipListMap templateMatches = configTemplateMatchMap.get(key); + ConcurrentSkipListMap templateMatches = configTemplateMatchMap.get(key); if (templateMatches == null) { // No templates with an equal number of segments so there will be @@ -300,7 +301,7 @@ // List is in alphabetical order of normalised templates. // Correct match is the first one that matches. ServerEndpointConfig sec = null; - Map pathParams = null; + Map pathParams = null; for (TemplatePathMatch templateMatch : templateMatches.values()) { pathParams = templateMatch.getUriTemplate().match(pathUriTemplate); if (pathParams != null) { @@ -383,7 +384,7 @@ for (WsSession wsSession : wsSessions) { try { wsSession.close(AUTHENTICATED_HTTP_SESSION_CLOSED); - } catch (IOException e) { + } catch (IOException ignore) { // Any IOExceptions during close will have been caught and the // onError method called. } diff -Nru tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsWriteTimeout.java tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsWriteTimeout.java --- tomcat10-10.1.34/java/org/apache/tomcat/websocket/server/WsWriteTimeout.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/java/org/apache/tomcat/websocket/server/WsWriteTimeout.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,8 +34,8 @@ /** * Note: The comparator imposes orderings that are inconsistent with equals */ - private final Set endpoints = new ConcurrentSkipListSet<>( - Comparator.comparingLong(WsRemoteEndpointImplServer::getTimeoutExpiry)); + private final Set endpoints = + new ConcurrentSkipListSet<>(Comparator.comparingLong(WsRemoteEndpointImplServer::getTimeoutExpiry)); private final AtomicInteger count = new AtomicInteger(0); private int backgroundProcessCount = 0; private volatile int processPeriod = 1; diff -Nru tomcat10-10.1.34/modules/cxf/.gitignore tomcat10-10.1.52/modules/cxf/.gitignore --- tomcat10-10.1.34/modules/cxf/.gitignore 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/cxf/.gitignore 2026-01-23 19:33:36.000000000 +0000 @@ -1,3 +1,19 @@ +# ----------------------------------------------------------------------------- +# 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. +# ----------------------------------------------------------------------------- target/ pom.xml.tag pom.xml.releaseBackup diff -Nru tomcat10-10.1.34/modules/cxf/pom.xml tomcat10-10.1.52/modules/cxf/pom.xml --- tomcat10-10.1.34/modules/cxf/pom.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/cxf/pom.xml 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,7 @@ org.apache apache - 26 + 34 org.apache.tomcat @@ -29,32 +29,32 @@ Apache CXF for Apache Tomcat CDI Apache CXF packaged for Apache Tomcat CDI - 3.5.3 + 4.1.4 jar - 1.3 - 1.1.4 - 1.0 - 1.2.18 + 4.0.1 + 2.1.3 + 3.0.1 + 2.0.2 - org.apache.geronimo.specs - geronimo-jcdi_2.0_spec - ${geronimo-jcdi.version} + jakarta.enterprise + jakarta.enterprise.cdi-api + ${jakarta-enterprise-cdi.version} provided - javax.json - javax.json-api - ${javax.json-api.version} + jakarta.json + jakarta.json-api + ${jakarta.json-api.version} - javax.json.bind - javax.json.bind-api - ${javax.json.bind-api.version} + jakarta.json.bind + jakarta.json.bind-api + ${jakarta.json.bind-api.version} @@ -96,16 +96,13 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 - 1.8 - 1.8 + 11 org.apache.maven.plugins maven-shade-plugin - 3.2.1 package @@ -144,13 +141,29 @@ + org.apache.johnzon:johnzon-jsonb + + META-INF/services/jakarta.enterprise.inject.spi.Extension + + + + org.apache.cxf:cxf-integration-cdi + + META-INF/beans.xml + + + *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA + META-INF/DEPENDENCIES + META-INF/LICENSE.md META-INF/LICENSE.txt META-INF/LICENSE + META-INF/MANIFEST.MF + META-INF/NOTICE.md META-INF/NOTICE.txt META-INF/NOTICE diff -Nru tomcat10-10.1.34/modules/cxf/src/main/java/tomcat/cxf/JsonBean.java tomcat10-10.1.52/modules/cxf/src/main/java/tomcat/cxf/JsonBean.java --- tomcat10-10.1.34/modules/cxf/src/main/java/tomcat/cxf/JsonBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/cxf/src/main/java/tomcat/cxf/JsonBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,9 +16,9 @@ */ package tomcat.cxf; -import javax.enterprise.context.Dependent; -import javax.ws.rs.Produces; -import javax.ws.rs.ext.Provider; +import jakarta.enterprise.context.Dependent; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.ext.Provider; import org.apache.johnzon.jaxrs.jsonb.jaxrs.JsonbJaxrsProvider; diff -Nru tomcat10-10.1.34/modules/jdbc-pool/NOTICE tomcat10-10.1.52/modules/jdbc-pool/NOTICE --- tomcat10-10.1.34/modules/jdbc-pool/NOTICE 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/NOTICE 2026-01-23 19:33:36.000000000 +0000 @@ -1,5 +1,5 @@ Apache Tomcat JDBC Pool -Copyright 2008-2024 The Apache Software Foundation +Copyright 2008-2026 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff -Nru tomcat10-10.1.34/modules/jdbc-pool/doc/changelog.xml tomcat10-10.1.52/modules/jdbc-pool/doc/changelog.xml --- tomcat10-10.1.34/modules/jdbc-pool/doc/changelog.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/doc/changelog.xml 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,6 @@ &project; - Filip Hanik Changelog diff -Nru tomcat10-10.1.34/modules/jdbc-pool/doc/jdbc-pool.xml tomcat10-10.1.52/modules/jdbc-pool/doc/jdbc-pool.xml --- tomcat10-10.1.34/modules/jdbc-pool/doc/jdbc-pool.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/doc/jdbc-pool.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Filip Hanik The Tomcat JDBC Connection Pool diff -Nru tomcat10-10.1.34/modules/jdbc-pool/pom.xml tomcat10-10.1.52/modules/jdbc-pool/pom.xml --- tomcat10-10.1.34/modules/jdbc-pool/pom.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/pom.xml 2026-01-23 19:33:36.000000000 +0000 @@ -24,12 +24,12 @@ org.apache apache - 15 + 34 org.apache.tomcat tomcat-jdbc - 8.0.15-SNAPSHOT + 1.1.0-SNAPSHOT jar @@ -65,7 +65,7 @@ org.apache.tomcat tomcat-juli - 10.1.0-M8 + 10.1.39 junit @@ -76,7 +76,7 @@ org.apache.tomcat tomcat-dbcp - 10.1.0-M8 + 10.1.39 test @@ -111,7 +111,6 @@ org.apache.maven.plugins maven-gpg-plugin - 1.2 sign-artifacts diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java --- tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/naming/GenericNamingResourcesFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -75,7 +75,7 @@ if (setProperty(o, param, value)) { } else { - log.debug("Property not configured["+param+"]. No setter found on["+o+"]."); + log.debug("Property not configured[" + param + "]. No setter found on[" + type + "]."); } } return o; @@ -83,8 +83,8 @@ @SuppressWarnings("null") // setPropertyMethodVoid can't be null when used private static boolean setProperty(Object o, String name, String value) { - if (log.isDebugEnabled()) { - log.debug("IntrospectionUtils: setProperty(" + + if (log.isTraceEnabled()) { + log.trace("IntrospectionUtils: setProperty(" + o.getClass() + " " + name + "=" + value + ")"); } diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java --- tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java 2026-01-23 19:33:36.000000000 +0000 @@ -439,7 +439,9 @@ interceptor.setProperties(proxies[i].getProperties()); interceptor.poolClosed(this); }catch (Exception x) { - log.debug("Unable to inform interceptor of pool closure.",x); + if (log.isDebugEnabled()) { + log.debug("Unable to inform interceptor of pool closure.",x); + } } } } //closePool @@ -796,7 +798,7 @@ } catch (Exception e) { error = true; if (log.isDebugEnabled()) { - log.debug("Unable to create a new JDBC connection.", e); + log.debug("Unable to create a new JDBC connection.", e); } if (e instanceof SQLException) { throw (SQLException)e; @@ -807,7 +809,7 @@ } } finally { // con can never be null here - if (error ) { + if (error) { release(con); } con.unlock(); @@ -955,6 +957,14 @@ if (con.isDiscarded()) { return true; } + try { + if (con.isClosed()) { + return true; + } + } catch (SQLException e) { + log.warn("Unable to check if connection is closed", e); + return true; + } if (isClosed()) { return true; } @@ -1110,7 +1120,9 @@ } } //while } catch (ConcurrentModificationException e) { - log.debug("checkAbandoned failed." ,e); + if (log.isDebugEnabled()) { + log.debug("checkAbandoned failed." ,e); + } } catch (Exception e) { log.warn("checkAbandoned failed, it will be retried.",e); } @@ -1158,7 +1170,9 @@ } } //while } catch (ConcurrentModificationException e) { - log.debug("checkIdle failed." ,e); + if (log.isDebugEnabled()) { + log.debug("checkIdle failed." ,e); + } } catch (Exception e) { log.warn("checkIdle failed, it will be retried.",e); } @@ -1217,7 +1231,9 @@ } } //while } catch (ConcurrentModificationException e) { - log.debug("testAllIdle failed." ,e); + if (log.isDebugEnabled()) { + log.debug("testAllIdle failed." ,e); + } } catch (Exception e) { log.warn("testAllIdle failed, it will be retried.",e); } @@ -1616,7 +1632,7 @@ pool.testAllIdle(true); } } catch (Exception x) { - log.error("", x); + log.error(x.toString(), x); } } } diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java --- tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,8 +51,6 @@ *
  • jmxEnabled - true of false, whether to register the pool with JMX.
  • *
  • fairQueue - true of false, whether the pool should sacrifice a little bit of performance for true fairness.
  • * - * @author Craig R. McClanahan - * @author Dirk Verbeeck */ public class DataSourceFactory implements ObjectFactory { private static final Log log = LogFactory.getLog(DataSourceFactory.class); @@ -571,14 +569,16 @@ log.warn("dataSourceJNDI property is configured, but local JNDI context is null."); } } catch (NamingException e) { - log.debug("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the local context."); + if (log.isDebugEnabled()) { + log.debug("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the local context.", e); + } } if (jndiDS==null) { try { context = new InitialContext(); jndiDS = context.lookup(poolProperties.getDataSourceJNDI()); } catch (NamingException e) { - log.warn("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the InitialContext."); + log.warn("The name \""+poolProperties.getDataSourceJNDI()+"\" cannot be found in the InitialContext.", e); } } if (jndiDS!=null) { diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java --- tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java 2026-01-23 19:33:36.000000000 +0000 @@ -627,9 +627,11 @@ } } buf.append(']'); - }catch (Exception x) { + } catch (Exception x) { //shouldn't happen - log.debug("toString() call failed", x); + if (log.isDebugEnabled()) { + log.debug("toString() call failed", x); + } } return buf.toString(); } @@ -939,8 +941,8 @@ if (propText != null) { try { props.load(new ByteArrayInputStream(propText.replace(';', '\n').getBytes())); - }catch (IOException x) { - throw new RuntimeException(x); + }catch (IOException ioe) { + throw new RuntimeException(ioe); } } return props; diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java --- tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PooledConnection.java 2026-01-23 19:33:36.000000000 +0000 @@ -214,7 +214,9 @@ try { this.disconnect(false); } catch (Exception x) { - log.debug("Unable to disconnect previous connection.", x); + if (log.isDebugEnabled()) { + log.debug("Unable to disconnect previous connection.", x); + } } //catch } //end if //if (poolProperties.getDataSource()==null && poolProperties.getDataSourceJNDI()!=null) { @@ -415,7 +417,7 @@ } else { xaConnection.close(); } - }catch (Exception ignore) { + } catch (Exception ignore) { if (log.isDebugEnabled()) { log.debug("Unable to close underlying SQL connection",ignore); } diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/Validator.java tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/Validator.java --- tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/Validator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/Validator.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,8 +20,6 @@ /** * Interface to be implemented by custom validator classes. - * - * @author mpassell */ public interface Validator { /** diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java --- tomcat10-10.1.34/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,7 +32,6 @@ /** * Implementation of JdbcInterceptor that proxies resultSets and statements. - * @author Guillermo Fernandes */ public class StatementDecoratorInterceptor extends AbstractCreateStatementInterceptor { diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/Async0IdleTestBug50477.java tomcat10-10.1.52/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/Async0IdleTestBug50477.java --- tomcat10-10.1.34/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/Async0IdleTestBug50477.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/Async0IdleTestBug50477.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,8 +22,6 @@ import org.junit.Test; -import org.apache.tomcat.jdbc.pool.DataSourceProxy; - public class Async0IdleTestBug50477 extends DefaultTestCase { @Test @@ -33,7 +31,7 @@ this.datasource.getPoolProperties().setFairQueue(true); this.datasource.getPoolProperties().setInitialSize(0); try { - Future cf = ((DataSourceProxy)datasource).getConnectionAsync(); + Future cf = datasource.getConnectionAsync(); cf.get(5, TimeUnit.SECONDS); }finally { tearDown(); diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidation.java tomcat10-10.1.52/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidation.java --- tomcat10-10.1.34/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/TestValidation.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,6 +33,8 @@ import org.junit.Before; import org.junit.Test; +import org.apache.tomcat.jdbc.pool.ConnectionPool; + public class TestValidation extends DefaultTestCase { public static final Boolean WITHAUTOCOMMIT = Boolean.TRUE; @@ -142,6 +144,27 @@ } @Test + public void returnClosedConnection() throws SQLException { + ConnectionPool pool = datasource.createPool(); + pool.resetStats(); + Assert.assertFalse(datasource.getPoolProperties().isTestOnBorrow()); + Assert.assertFalse(datasource.getPoolProperties().isTestOnReturn()); + Assert.assertFalse(datasource.getPoolProperties().isTestWhileIdle()); + try (Connection connection = datasource.getConnection()) { + Assert.assertEquals("size", 1, pool.getSize()); + Connection realConnection = ((PooledConnection) connection).getConnection(); + Assert.assertNotSame(connection, realConnection); + realConnection.close(); + Assert.assertTrue(realConnection.isClosed()); + Assert.assertFalse(connection.isClosed()); + } + Assert.assertEquals("borrowed", 1, pool.getBorrowedCount()); + Assert.assertEquals("returned", 1, pool.getReturnedCount()); + Assert.assertEquals("released", 1, pool.getReleasedCount()); + Assert.assertEquals("size", 0, pool.getSize()); + } + + @Test public void testOnConnectValidationSuccessWithValidationQueryAndAutoCommitEnabled() throws SQLException { checkOnConnectValidationWithOutcome(ValidationOutcome.SUCCESS, WITHVALIDATIONQUERY, WITHAUTOCOMMIT); } @@ -644,4 +667,4 @@ } } -} \ No newline at end of file +} diff -Nru tomcat10-10.1.34/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Connection.java tomcat10-10.1.52/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Connection.java --- tomcat10-10.1.34/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Connection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/jdbc-pool/src/test/java/org/apache/tomcat/jdbc/test/driver/Connection.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,6 +38,7 @@ public class Connection implements java.sql.Connection { Properties info; + private boolean closed = false; public Connection(Properties info) { this.info = info; @@ -57,6 +58,7 @@ @Override public void close() throws SQLException { + closed = true; Driver.disconnectCount.incrementAndGet(); } @@ -156,7 +158,7 @@ @Override public boolean isClosed() throws SQLException { - return false; + return closed; } @Override diff -Nru tomcat10-10.1.34/modules/owb/.gitignore tomcat10-10.1.52/modules/owb/.gitignore --- tomcat10-10.1.34/modules/owb/.gitignore 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/owb/.gitignore 2026-01-23 19:33:36.000000000 +0000 @@ -1,3 +1,19 @@ +# ----------------------------------------------------------------------------- +# 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. +# ----------------------------------------------------------------------------- target/ pom.xml.tag pom.xml.releaseBackup diff -Nru tomcat10-10.1.34/modules/owb/pom.xml tomcat10-10.1.52/modules/owb/pom.xml --- tomcat10-10.1.34/modules/owb/pom.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/owb/pom.xml 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,7 @@ org.apache apache - 30 + 34 org.apache.tomcat @@ -29,7 +29,7 @@ Apache Tomcat CDI 4 support Apache Tomcat CDI 4 support using Apache OpenWebBeans - 4.0.2 + 4.0.3 jar @@ -37,7 +37,7 @@ 2.1.0 4.0.1 2.1.1 - 10.1.26 + 10.1.39 diff -Nru tomcat10-10.1.34/modules/stuffed/.gitignore tomcat10-10.1.52/modules/stuffed/.gitignore --- tomcat10-10.1.34/modules/stuffed/.gitignore 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/stuffed/.gitignore 2026-01-23 19:33:36.000000000 +0000 @@ -1 +1,17 @@ +# ----------------------------------------------------------------------------- +# 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. +# ----------------------------------------------------------------------------- target/ diff -Nru tomcat10-10.1.34/modules/stuffed/conf/.gitignore tomcat10-10.1.52/modules/stuffed/conf/.gitignore --- tomcat10-10.1.34/modules/stuffed/conf/.gitignore 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/stuffed/conf/.gitignore 2026-01-23 19:33:36.000000000 +0000 @@ -1,3 +1,19 @@ +# ----------------------------------------------------------------------------- +# 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. +# ----------------------------------------------------------------------------- # Ignore everything in this directory * # Except this file diff -Nru tomcat10-10.1.34/modules/stuffed/pom.xml tomcat10-10.1.52/modules/stuffed/pom.xml --- tomcat10-10.1.34/modules/stuffed/pom.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/stuffed/pom.xml 2026-01-23 19:33:36.000000000 +0000 @@ -21,6 +21,12 @@ http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 + + org.apache + apache + 34 + + org.apache.tomcat tomcat-stuffed 1.0 @@ -29,7 +35,7 @@ UTF-8 org.apache.catalina.startup.Tomcat - 10.1.26 + 10.1.39 @@ -76,7 +82,6 @@ org.apache.maven.plugins maven-compiler-plugin - 3.11.0 11 @@ -84,7 +89,6 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.1 package diff -Nru tomcat10-10.1.34/modules/stuffed/webapps/.gitignore tomcat10-10.1.52/modules/stuffed/webapps/.gitignore --- tomcat10-10.1.34/modules/stuffed/webapps/.gitignore 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/modules/stuffed/webapps/.gitignore 2026-01-23 19:33:36.000000000 +0000 @@ -1,3 +1,19 @@ +# ----------------------------------------------------------------------------- +# 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. +# ----------------------------------------------------------------------------- # Ignore everything in this directory * # Except this file diff -Nru tomcat10-10.1.34/res/bnd/tomcat-embed-core.jar.tmp.bnd tomcat10-10.1.52/res/bnd/tomcat-embed-core.jar.tmp.bnd --- tomcat10-10.1.34/res/bnd/tomcat-embed-core.jar.tmp.bnd 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/bnd/tomcat-embed-core.jar.tmp.bnd 2026-01-23 19:33:36.000000000 +0000 @@ -64,6 +64,7 @@ org.apache.tomcat.util.codec.binary,\ org.apache.tomcat.util.collections,\ org.apache.tomcat.util.compat,\ + org.apache.tomcat.util.concurrent,\ org.apache.tomcat.util.descriptor,\ org.apache.tomcat.util.descriptor.tagplugin,\ org.apache.tomcat.util.descriptor.web,\ diff -Nru tomcat10-10.1.34/res/bnd/tomcat-util.jar.tmp.bnd tomcat10-10.1.52/res/bnd/tomcat-util.jar.tmp.bnd --- tomcat10-10.1.34/res/bnd/tomcat-util.jar.tmp.bnd 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/bnd/tomcat-util.jar.tmp.bnd 2026-01-23 19:33:36.000000000 +0000 @@ -23,6 +23,7 @@ org.apache.tomcat.util.codec.binary,\ org.apache.tomcat.util.collections,\ org.apache.tomcat.util.compat,\ + org.apache.tomcat.util.concurrent,\ org.apache.tomcat.util.file,\ org.apache.tomcat.util.res,\ org.apache.tomcat.util.security,\ diff -Nru tomcat10-10.1.34/res/checkstyle/header-al2.txt tomcat10-10.1.52/res/checkstyle/header-al2.txt --- tomcat10-10.1.34/res/checkstyle/header-al2.txt 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/checkstyle/header-al2.txt 2026-01-23 19:33:36.000000000 +0000 @@ -9,7 +9,7 @@ ^(rem)?\W*\(the "License"\); you may not use this file except in compliance with$ ^(rem)?\W*the License\. You may obtain a copy of the License at$ ^(rem)?\W*$ -^(rem)?\W*http://www.apache.org/licenses/LICENSE-2\.0$ +^(rem)?\W*http(s)?://www.apache.org/licenses/LICENSE-2\.0$ ^(rem)?\W*$ ^(rem)?\W*Unless required by applicable law or agreed to in writing, software$ ^(rem)?\W*distributed under the License is distributed on an "AS IS" BASIS,$ diff -Nru tomcat10-10.1.34/res/checkstyle/org-import-control.xml tomcat10-10.1.52/res/checkstyle/org-import-control.xml --- tomcat10-10.1.34/res/checkstyle/org-import-control.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/checkstyle/org-import-control.xml 2026-01-23 19:33:36.000000000 +0000 @@ -106,6 +106,7 @@ + @@ -127,6 +128,7 @@ + @@ -192,4 +194,4 @@ - \ No newline at end of file + diff -Nru tomcat10-10.1.34/res/ide-support/eclipse/formatting-asf-tomcat.xml tomcat10-10.1.52/res/ide-support/eclipse/formatting-asf-tomcat.xml --- tomcat10-10.1.34/res/ide-support/eclipse/formatting-asf-tomcat.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/ide-support/eclipse/formatting-asf-tomcat.xml 2026-01-23 19:33:36.000000000 +0000 @@ -166,7 +166,7 @@ - + @@ -181,6 +181,7 @@ + @@ -215,6 +216,7 @@ + @@ -295,6 +297,7 @@ + @@ -325,6 +328,7 @@ + diff -Nru tomcat10-10.1.34/res/ide-support/idea/codeStyles/Project.xml tomcat10-10.1.52/res/ide-support/idea/codeStyles/Project.xml --- tomcat10-10.1.34/res/ide-support/idea/codeStyles/Project.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/ide-support/idea/codeStyles/Project.xml 2026-01-23 19:33:36.000000000 +0000 @@ -17,9 +17,102 @@ --> + \ No newline at end of file Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/res/install-win/Uninstall.exe.sig and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/res/install-win/Uninstall.exe.sig differ Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/res/install-win/tomcat-installer.exe.sig and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/res/install-win/tomcat-installer.exe.sig differ diff -Nru tomcat10-10.1.34/res/install-win/tomcat.nsi tomcat10-10.1.52/res/install-win/tomcat.nsi --- tomcat10-10.1.34/res/install-win/tomcat.nsi 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/install-win/tomcat.nsi 2026-01-23 19:33:36.000000000 +0000 @@ -118,11 +118,11 @@ Page custom CheckUserType !insertmacro MUI_PAGE_FINISH - !ifdef UNINSTALLONLY - ;Uninstall Page order - !insertmacro MUI_UNPAGE_CONFIRM - !insertmacro MUI_UNPAGE_INSTFILES - !endif +!ifdef UNINSTALLONLY + ;Uninstall Page order + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES +!endif ;Language !insertmacro MUI_LANGUAGE English @@ -163,8 +163,12 @@ InstType Minimum InstType Full - ReserveFile System.dll - ReserveFile nsDialogs.dll +!ifdef UNINSTALLONLY + !uninstfinalize '@OS.CMD.COPY@ %1 Uninstall.exe' +!endif + + ReserveFile /plugin System.dll + ReserveFile /plugin nsDialogs.dll ReserveFile tomcat-users_1.xml ReserveFile tomcat-users_2.xml @@ -375,7 +379,7 @@ ; S-1-5-11 Authenticated users ; ; Grant admins, LocalService and Local System full control full control - nsExec::ExecToStack 'icacls "$INSTDIR" /inheritance:r /grant *S-1-5-19:(OI)(CI)(F) /grant *S-1-5-32-544:(OI)(CI)(F) /grant *S-1-5-18:(OI)(CI)(F)' + nsExec::ExecToStack '$SYSDIR\icacls "$INSTDIR" /inheritance:r /grant *S-1-5-19:(OI)(CI)(F) /grant *S-1-5-32-544:(OI)(CI)(F) /grant *S-1-5-18:(OI)(CI)(F)' Pop $0 Pop $1 StrCmp $0 "0" SetGroupPermissionsOk @@ -388,7 +392,7 @@ ClearErrors ; Make the icon readable to all authenticated users so it appears correctly in the uninstall UI - nsExec::ExecToStack 'icacls "$INSTDIR\tomcat.ico" /inheritance:e /grant *S-1-5-11:(R)' + nsExec::ExecToStack '$SYSDIR\icacls "$INSTDIR\tomcat.ico" /inheritance:e /grant *S-1-5-11:(R)' Pop $0 Pop $1 StrCmp $0 "0" SetIconPermissionsOk @@ -401,7 +405,7 @@ ClearErrors ; Make the uninstaller readable and executable to all authenticated users so the user that installed Tomcat can also uninstall it - nsExec::ExecToStack 'icacls "$INSTDIR\Uninstall.exe" /inheritance:e /grant *S-1-5-11:(RX)' + nsExec::ExecToStack '$SYSDIR\icacls "$INSTDIR\Uninstall.exe" /inheritance:e /grant *S-1-5-11:(RX)' Pop $0 Pop $1 StrCmp $0 "0" SetUninstallerPermissionsOk @@ -1249,6 +1253,7 @@ ;Uninstaller Section !ifdef UNINSTALLONLY + Section Uninstall ${If} $TomcatServiceName == "" @@ -1384,5 +1389,4 @@ FunctionEnd !endif - ;eof diff -Nru tomcat10-10.1.34/res/maven/mvn.properties.default tomcat10-10.1.52/res/maven/mvn.properties.default --- tomcat10-10.1.34/res/maven/mvn.properties.default 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/maven/mvn.properties.default 2026-01-23 19:33:36.000000000 +0000 @@ -39,7 +39,7 @@ maven.asf.release.repo.repositoryId=apache.releases.https # Release version info -maven.asf.release.deploy.version=10.1.34 +maven.asf.release.deploy.version=10.1.52 #Where do we load the libraries from tomcat.lib.path=../../output/build/lib @@ -59,7 +59,7 @@ base.path=${user.home}/tomcat-build-libs # ----- Maven Ant Tasks ----- -maven-resolver-ant-tasks.version=1.3.0 +maven-resolver-ant-tasks.version=1.6.0 maven-resolver-ant-tasks.home=${base.path}/maven-resolver-ant-tasks-${maven-resolver-ant-tasks.version} maven-resolver-ant-tasks.loc=https://repo1.maven.org/maven2/org/apache/maven/resolver/maven-resolver-ant-tasks/${maven-resolver-ant-tasks.version}/maven-resolver-ant-tasks-${maven-resolver-ant-tasks.version}-uber.jar maven-resolver-ant-tasks.jar=${maven-resolver-ant-tasks.home}/maven-resolver-ant-tasks-${maven-resolver-ant-tasks.version}-uber.jar diff -Nru tomcat10-10.1.34/res/maven/mvn.properties.release tomcat10-10.1.52/res/maven/mvn.properties.release --- tomcat10-10.1.34/res/maven/mvn.properties.release 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/maven/mvn.properties.release 2026-01-23 19:33:36.000000000 +0000 @@ -18,7 +18,7 @@ # This file was auto-generated by the pre-release Ant target. # Remove "-dev" from the version since this is not a development release. -maven.asf.release.deploy.version=10.1.34 +maven.asf.release.deploy.version=10.1.52 # Re-use the same GPG executable. gpg.exec=/usr/local/bin/gpg diff -Nru tomcat10-10.1.34/res/openssl/README.md tomcat10-10.1.52/res/openssl/README.md --- tomcat10-10.1.34/res/openssl/README.md 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/openssl/README.md 2026-01-23 19:33:36.000000000 +0000 @@ -3,22 +3,18 @@ ## Building The OpenSSL API support classes can be built using jextract from Java 22+. - -jextract is now available in its own standalone repository. Clone -`https://github.com/openjdk/jextract` in some location and -checkout the branch that supports Java 22. Please refer to the -instructions from the repository for building. It should be the -`panama` branch. +jextract can be downloaded from `https://jdk.java.net/jextract/`. Extract the +download and set the path as the `JEXTRACT_HOME` environment variable for +ease of use. This step is only useful to be able to use additional native APIs from OpenSSL or stdlib. Find include paths using `gcc -xc -E -v -`, on Fedora it is -`/usr/lib/gcc/x86_64-redhat-linux/12/include`. Edit `openssl-tomcat.conf` +`/usr/lib/gcc/x86_64-redhat-linux/14/include`. Edit `openssl-tomcat.conf` accordingly to set the appropriate path. ``` -export JEXTRACT_HOME=/jextract/build/jextract $JEXTRACT_HOME/bin/jextract @openssl-tomcat.conf openssl.h ``` Note: The build path for the JDK will be different on other platforms. @@ -29,7 +25,7 @@ The `openssl-tomcat.conf` will generate a trimmed down OpenSSL API. When developing new features, the full API can be generated instead using: ``` -$JEXTRACT_HOME/bin/jextract --source -t org.apache.tomcat.util.openssl -lssl -I /usr/lib/gcc/x86_64-redhat-linux/12/include openssl.h --output src/main/java +$JEXTRACT_HOME/bin/jextract --source -t org.apache.tomcat.util.openssl -lssl -I /usr/lib/gcc/x86_64-redhat-linux/14/include openssl.h --output src/main/java ``` The `openssl.conf` file lists all the API calls and constants that can be diff -Nru tomcat10-10.1.34/res/openssl/openssl-tomcat.conf tomcat10-10.1.52/res/openssl/openssl-tomcat.conf --- tomcat10-10.1.34/res/openssl/openssl-tomcat.conf 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/openssl/openssl-tomcat.conf 2026-01-23 19:33:36.000000000 +0000 @@ -16,7 +16,7 @@ -t org.apache.tomcat.util.openssl -lssl # Configure include path --I /usr/lib/gcc/x86_64-redhat-linux/12/include +-I /usr/lib/gcc/x86_64-redhat-linux/15/include --output ../../java #### Extracted from: /usr/include/openssl/asn1.h @@ -76,16 +76,6 @@ --include-function EC_KEY_free # deprecated header: /usr/include/openssl/ec.h --include-function EC_KEY_new_by_curve_name # deprecated header: /usr/include/openssl/ec.h -#### Extracted from: /usr/include/openssl/engine.h - ---include-function ENGINE_by_id # deprecated header: /usr/include/openssl/engine.h ---include-function ENGINE_ctrl_cmd_string # deprecated header: /usr/include/openssl/engine.h ---include-function ENGINE_free # deprecated header: /usr/include/openssl/engine.h ---include-function ENGINE_load_private_key # deprecated header: /usr/include/openssl/engine.h ---include-function ENGINE_register_all_complete # deprecated header: /usr/include/openssl/engine.h ---include-function ENGINE_set_default # deprecated header: /usr/include/openssl/engine.h ---include-constant ENGINE_METHOD_ALL # deprecated header: /usr/include/openssl/engine.h - #### Extracted from: /usr/include/openssl/err.h --include-function ERR_clear_error # header: /usr/include/openssl/err.h @@ -118,12 +108,17 @@ --include-function OCSP_REQUEST_free # header: /usr/include/openssl/ocsp.h --include-function OCSP_REQUEST_new # header: /usr/include/openssl/ocsp.h --include-function OCSP_RESPONSE_free # header: /usr/include/openssl/ocsp.h +--include-function OCSP_basic_verify # header: /usr/include/openssl/ocsp.h --include-function OCSP_cert_to_id # header: /usr/include/openssl/ocsp.h +--include-function OCSP_check_validity # header: /usr/include/openssl/ocsp.h +--include-function OCSP_check_nonce # header: /usr/include/openssl/ocsp.h --include-function OCSP_request_add0_id # header: /usr/include/openssl/ocsp.h +--include-function OCSP_request_add1_nonce # header: /usr/include/openssl/ocsp.h --include-function OCSP_response_get1_basic # header: /usr/include/openssl/ocsp.h --include-function OCSP_response_status # header: /usr/include/openssl/ocsp.h --include-function OCSP_resp_find # header: /usr/include/openssl/ocsp.h --include-function OCSP_resp_get0 # header: /usr/include/openssl/ocsp.h +--include-function OCSP_resp_get0_certs # header: /usr/include/openssl/ocsp.h --include-function OCSP_single_get0_status # header: /usr/include/openssl/ocsp.h --include-function d2i_OCSP_RESPONSE # header: /usr/include/openssl/ocsp.h --include-function i2d_OCSP_REQUEST # header: /usr/include/openssl/ocsp.h @@ -270,6 +265,7 @@ --include-constant SSL_CTRL_SESS_TIMEOUTS # header: /usr/include/openssl/ssl.h --include-constant SSL_CTRL_SET_DH_AUTO # header: /usr/include/openssl/ssl.h --include-constant SSL_CTRL_SET_GROUPS # header: /usr/include/openssl/ssl.h +--include-constant SSL_CTRL_SET_GROUPS_LIST # header: /usr/include/openssl/ssl.h --include-constant SSL_CTRL_SET_MAX_PROTO_VERSION # header: /usr/include/openssl/ssl.h --include-constant SSL_CTRL_SET_MIN_PROTO_VERSION # header: /usr/include/openssl/ssl.h --include-constant SSL_CTRL_SET_SESS_CACHE_MODE # header: /usr/include/openssl/ssl.h @@ -351,20 +347,26 @@ --include-function X509_STORE_CTX_get_error # header: /usr/include/openssl/x509_vfy.h --include-function X509_STORE_CTX_get_error_depth # header: /usr/include/openssl/x509_vfy.h --include-function X509_STORE_CTX_get_ex_data # header: /usr/include/openssl/x509_vfy.h ---include-function X509_STORE_CTX_get0_current_issuer # header: /usr/include/openssl/x509_vfy.h +--include-function X509_STORE_CTX_get0_store # header: /usr/include/openssl/x509_vfy.h --include-function X509_STORE_CTX_get0_untrusted # header: /usr/include/openssl/x509_vfy.h +--include-function X509_STORE_CTX_get1_issuer # header: /usr/include/openssl/x509_vfy.h --include-function X509_STORE_CTX_set_error # header: /usr/include/openssl/x509_vfy.h --include-function X509_STORE_set_flags # header: /usr/include/openssl/x509_vfy.h --include-constant X509_L_ADD_DIR # header: /usr/include/openssl/x509_vfy.h --include-constant X509_L_FILE_LOAD # header: /usr/include/openssl/x509_vfy.h ---include-constant X509_V_ERR_APPLICATION_VERIFICATION # header: /usr/include/openssl/x509_vfy.h ---include-constant X509_V_ERR_CERT_UNTRUSTED # header: /usr/include/openssl/x509_vfy.h +--include-constant X509_V_OK # header: /usr/include/openssl/x509_vfy.h +--include-constant X509_V_ERR_UNABLE_TO_GET_CRL # header: /usr/include/openssl/x509_vfy.h --include-constant X509_V_ERR_CRL_HAS_EXPIRED # header: /usr/include/openssl/x509_vfy.h --include-constant X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT # header: /usr/include/openssl/x509_vfy.h --include-constant X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN # header: /usr/include/openssl/x509_vfy.h --include-constant X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY # header: /usr/include/openssl/x509_vfy.h --include-constant X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE # header: /usr/include/openssl/x509_vfy.h +--include-constant X509_V_ERR_CERT_REVOKED # header: /usr/include/openssl/x509_vfy.h +--include-constant X509_V_ERR_CERT_UNTRUSTED # header: /usr/include/openssl/x509_vfy.h +--include-constant X509_V_ERR_APPLICATION_VERIFICATION # header: /usr/include/openssl/x509_vfy.h +--include-constant X509_V_ERR_OCSP_RESP_INVALID # header: /usr/include/openssl/x509_vfy.h +--include-constant X509_V_ERR_OCSP_SIGNATURE_FAILURE # header: /usr/include/openssl/x509_vfy.h +--include-constant X509_V_ERR_OCSP_NOT_YET_VALID # header: /usr/include/openssl/x509_vfy.h +--include-constant X509_V_ERR_OCSP_HAS_EXPIRED # header: /usr/include/openssl/x509_vfy.h --include-constant X509_V_FLAG_CRL_CHECK # header: /usr/include/openssl/x509_vfy.h --include-constant X509_V_FLAG_CRL_CHECK_ALL # header: /usr/include/openssl/x509_vfy.h ---include-constant X509_V_OK # header: /usr/include/openssl/x509_vfy.h - diff -Nru tomcat10-10.1.34/res/openssl/openssl.h tomcat10-10.1.52/res/openssl/openssl.h --- tomcat10-10.1.34/res/openssl/openssl.h 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/openssl/openssl.h 2026-01-23 19:33:36.000000000 +0000 @@ -26,6 +26,5 @@ #include #include #include -#include #include #include diff -Nru tomcat10-10.1.34/res/rat/rat-excludes.txt tomcat10-10.1.52/res/rat/rat-excludes.txt --- tomcat10-10.1.34/res/rat/rat-excludes.txt 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/rat/rat-excludes.txt 2026-01-23 19:33:36.000000000 +0000 @@ -46,11 +46,13 @@ - files used simply to ensure directories are not empty - - *.bmp, *.dia and *.vpd files are binary + - *.bmp and *.dia files are binary (*.gif, *.jpg are also binary, but are automatically detected by RAT as ones, so no explicit configuration is needed) - - Markdown files for display in their GitHub UI + - IDE configuration files that can't contain headers + + - Markdown files for display in the GitHub UI - Temporary cache files used by Checkstyle @@ -247,10 +249,11 @@ output/dist/temp/safeToDelete.tmp - **/*.bmp **/*.dia -**/*.vpd + +res/ide-support/idea/.name +output/dist/src/res/ide-support/idea/.name **/*.md diff -Nru tomcat10-10.1.34/res/scripts/check-mime.pl tomcat10-10.1.52/res/scripts/check-mime.pl --- tomcat10-10.1.34/res/scripts/check-mime.pl 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/scripts/check-mime.pl 2026-01-23 19:33:36.000000000 +0000 @@ -58,7 +58,7 @@ ################### BEGIN VARIABLES WHICH MUST BE MAINTAINED ##################### # Script version, printed via getopts with "--version" -our $VERSION = '1.1'; +our $VERSION = '1.2'; # Locale used via LC_COLLATE when sorting extensions my $LOCALE = 'en.UTF-8'; @@ -178,6 +178,8 @@ # Switch locale for alphabetical ordering setlocale(LC_COLLATE, $LOCALE); +print STDERR "INFO Using lists TOMCAT_KEEP and TOMCAT_ONLY defined in this script.\n"; + # Check whether TOMCAT_ONLY and TOMCAT_KEEP are disjoint for $extension (sort keys %TOMCAT_ONLY) { if (exists($TOMCAT_KEEP{$extension})) { @@ -185,7 +187,7 @@ } } if (@extensions > 0) { - print STDERR "FATAL Lists TOMCAT_ONLY and TOMCAT_KEEP must be disjoint.\n"; + print STDERR "FATAL TOMCAT_ONLY and TOMCAT_KEEP must be disjoint.\n"; print STDERR "FATAL Common entries are: " . join(', ', @extensions) . " - Aborting!\n"; exit 6; } @@ -337,41 +339,41 @@ if (exists($httpd{$extension})) { if ($httpd{$extension} eq $TOMCAT_ONLY{$extension}) { print STDERR "FATAL Consistent definition for '$extension' -> '$TOMCAT_ONLY{$extension}' exists in mime.types.\n"; - print STDERR "FATAL You must remove '$extension' from the TOMCAT_ONLY list - Aborting!\n"; + print STDERR "FATAL You must remove '$extension' from TOMCAT_ONLY - Aborting!\n"; exit 7; } else { print STDERR "FATAL Definition '$extension' -> '$httpd{$extension}' exists in mime.types but\n"; print STDERR "FATAL differs from '$extension' -> '$TOMCAT_ONLY{$extension}' in TOMCAT_ONLY.\n"; - print STDERR "FATAL You must either remove '$extension' from the TOMCAT_ONLY list to keep the mime.types variant,\n"; + print STDERR "FATAL You must either remove '$extension' from TOMCAT_ONLY to keep the mime.types variant,\n"; print STDERR "FATAL or move it to TOMCAT_KEEP to overwrite the mime.types variant - Aborting!\n"; exit 8; } } if (!exists($tomcat{$extension})) { - print STDERR "WARN Additional extension '$extension' allowed by TOMCAT_ONLY list, but not found in web.xml\n"; + print STDERR "WARN Additional extension '$extension' allowed by TOMCAT_ONLY, but not found in web.xml\n"; print STDERR "WARN Definition '$extension' -> '$TOMCAT_ONLY{$extension}' will be added again to generated web.xml.\n"; - print STDERR "WARN Consider removing it from TOMCAT_ONLY if you do not want to add back this extension.\n"; + print STDERR "WARN Consider remove it from TOMCAT_ONLY if you do not want to add back this extension.\n"; } } # Look for extensions in TOMCAT_KEEP. -# Abort if they do not exist in mime.types or have the same definition there.. +# Abort if they do not exist in mime.types or have the same definition there. # Warn if they are no longer existing in web.xml. for $extension (sort keys %TOMCAT_KEEP) { if (exists($httpd{$extension})) { if ($httpd{$extension} eq $TOMCAT_KEEP{$extension}) { print STDERR "FATAL Consistent definition for '$extension' -> '$TOMCAT_KEEP{$extension}' exists in mime.types.\n"; - print STDERR "FATAL You must remove '$extension' from the TOMCAT_KEEP list - Aborting!\n"; + print STDERR "FATAL You must remove '$extension' from TOMCAT_KEEP - Aborting!\n"; exit 9; } } else { - print STDERR "WARN Definition '$extension' -> '$TOMCAT_KEEP{$extension}' does not exist in mime.types,\n"; + print STDERR "FATAL Definition '$extension' -> '$TOMCAT_KEEP{$extension}' does not exist in mime.types,\n"; print STDERR "FATAL so you must move it from TOMCAT_KEEP to TOMCAT_ONLY - Aborting!\n"; exit 10; } if (!exists($tomcat{$extension})) { - print STDERR "WARN Additional extension '$extension' allowed by TOMCAT_KEEP list, but not found in web.xml\n"; + print STDERR "WARN Additional extension '$extension' allowed by TOMCAT_KEEP, but not found in web.xml\n"; print STDERR "WARN Definition '$extension' -> '$TOMCAT_KEEP{$extension}' will be added again to generated web.xml.\n"; print STDERR "WARN Consider removing it from TOMCAT_KEEP if you do not want to add back this extension.\n"; } @@ -383,12 +385,14 @@ for $extension (@tomcat_extensions) { if (!exists($httpd{$extension})) { if (!exists($TOMCAT_ONLY{$extension})) { - print STDERR "WARN Extension '$extension' found in web.xml but not in mime.types is missing from TOMCAT_ONLY list.\n"; + print STDERR "WARN Extension '$extension' found in web.xml but not in mime.types is missing from TOMCAT_ONLY.\n"; print STDERR "WARN Definition '$extension' -> '$tomcat{$extension}' will be removed from generated web.xml.\n"; + print STDERR "WARN Consider adding it to TOMCAT_ONLY if you want to keep this extension.\n"; } elsif ($tomcat{$extension} ne $TOMCAT_ONLY{$extension}) { print STDERR "WARN Additional extension '$extension' allowed by TOMCAT_ONLY list, but has new definition.\n"; print STDERR "WARN Definition '$extension' -> '$tomcat{$extension}' will be replaced" . " by '$extension' -> '$TOMCAT_ONLY{$extension}' in generated web.xml.\n"; + print STDERR "WARN Consider changing it in TOMCAT_ONLY if you want to keep the original definition.\n"; } } } @@ -399,13 +403,20 @@ for $extension (@tomcat_extensions) { if (exists($httpd{$extension}) && $tomcat{$extension} ne $httpd{$extension}) { if (!exists($TOMCAT_KEEP{$extension})) { - print STDERR "WARN Mapping '$extension' inconsistency is missing from TOMCAT_KEEP list.\n"; - print STDERR "WARN Definition '$extension' -> '$tomcat{$extension}' will be replaced" . - " by '$extension' -> '$httpd{$extension}' in generated web.xml.\n"; - } elsif ($tomcat{$extension} ne $TOMCAT_KEEP{$extension}) { - print STDERR "WARN Extension '$extension' inconsistency allowed by TOMCAT_KEEP list, but has new definition.\n"; - print STDERR "WARN Definition '$extension' -> '$tomcat{$extension}' will be replaced" . - " by '$extension' -> '$TOMCAT_KEEP{$extension}' in generated web.xml.\n"; + print STDERR "WARN Mapping '$extension' inconsistency is missing from TOMCAT_KEEP.\n"; + print STDERR "WARN Current definition '$extension' -> '$tomcat{$extension}' will be replaced" . + " by mime.types definition '$extension' -> '$httpd{$extension}' in generated web.xml.\n"; + print STDERR "WARN Consider adding it to TOMCAT_KEEP if you want to keep the original definition.\n"; + } else { + print STDERR "INFO Extension '$extension' inconsistency allowed by TOMCAT_KEEP.\n"; + print STDERR "INFO mime.types hat $httpd{$extension}, original web.xml has $tomcat{$extension}\n"; + print STDERR "INFO Consider removing it from TOMCAT_KEEP if you want to use the mime.types definition.\n"; + if ($tomcat{$extension} ne $TOMCAT_KEEP{$extension}) { + print STDERR "WARN Extension '$extension' is on TOMCAT_KEEP, but has a new definition in web.xml.\n"; + print STDERR "WARN Current definition '$extension' -> '$tomcat{$extension}' will be replaced" . + " by '$extension' -> '$TOMCAT_KEEP{$extension}' from TOMCAT_KEEP in generated web.xml.\n"; + print STDERR "WARN Consider changing it in TOMCAT_KEEP if you want to use the definition in the original web.xml.\n"; + } } } } diff -Nru tomcat10-10.1.34/res/spotbugs/filter-false-positives.xml tomcat10-10.1.52/res/spotbugs/filter-false-positives.xml --- tomcat10-10.1.34/res/spotbugs/filter-false-positives.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/res/spotbugs/filter-false-positives.xml 2026-01-23 19:33:36.000000000 +0000 @@ -636,12 +636,6 @@ - - - - - - @@ -665,11 +659,6 @@ - - - - - @@ -1024,12 +1013,6 @@ - - - - - - @@ -1630,27 +1613,12 @@ - - - - - - - - - - - - - - - @@ -1683,12 +1651,6 @@ - - - - - - @@ -1729,12 +1691,6 @@ - - - - - - diff -Nru tomcat10-10.1.34/test/conf/TesterRewriteMapB.txt tomcat10-10.1.52/test/conf/TesterRewriteMapB.txt --- tomcat10-10.1.34/test/conf/TesterRewriteMapB.txt 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/conf/TesterRewriteMapB.txt 2026-01-23 19:33:36.000000000 +0000 @@ -20,5 +20,5 @@ a aa -aa aaaa -b bb +aa aaaa #abc +b bb #abc diff -Nru tomcat10-10.1.34/test/jakarta/el/TestBeanELResolver.java tomcat10-10.1.52/test/jakarta/el/TestBeanELResolver.java --- tomcat10-10.1.34/test/jakarta/el/TestBeanELResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/el/TestBeanELResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -414,8 +414,8 @@ BeanELResolver resolver = new BeanELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), METHOD01_NAME, new Class[] {}, - new Object[] {}); + Object result = + resolver.invoke(context, new TesterBean(BEAN_NAME), METHOD01_NAME, new Class[] {}, new Object[] {}); Assert.assertEquals(BEAN_NAME, result); Assert.assertTrue(context.isPropertyResolved()); @@ -508,8 +508,8 @@ BeanELResolver resolver = new BeanELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", null, - new String[] { null }); + Object result = + resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", null, new String[] { null }); Assert.assertEquals(BEAN_NAME, result); } @@ -519,8 +519,8 @@ BeanELResolver resolver = new BeanELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", new Class[] { null }, - null); + Object result = + resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", new Class[] { null }, null); Assert.assertEquals(BEAN_NAME, result); } @@ -762,8 +762,8 @@ BeanELResolver resolver = new BeanELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", null, - new Object[] { null }); + Object result = + resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", null, new Object[] { null }); Assert.assertEquals(BEAN_NAME, result); } @@ -773,8 +773,8 @@ BeanELResolver resolver = new BeanELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Object result = resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", new Class[] { null }, - null); + Object result = + resolver.invoke(context, new TesterBean(BEAN_NAME), "getNameVarargs", new Class[] { null }, null); Assert.assertEquals(BEAN_NAME, result); } diff -Nru tomcat10-10.1.34/test/jakarta/el/TestBeanELResolverVarargsInvocation.java tomcat10-10.1.52/test/jakarta/el/TestBeanELResolverVarargsInvocation.java --- tomcat10-10.1.34/test/jakarta/el/TestBeanELResolverVarargsInvocation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/el/TestBeanELResolverVarargsInvocation.java 2026-01-23 19:33:36.000000000 +0000 @@ -55,7 +55,7 @@ beanELResolver = new BeanELResolver(); elContext = new ELContext() { private VariableMapper variableMapper = new VariableMapper() { - private Map vars = new HashMap<>(); + private Map vars = new HashMap<>(); @Override public ValueExpression setVariable(String arg0, ValueExpression arg1) { diff -Nru tomcat10-10.1.34/test/jakarta/el/TestELResolver.java tomcat10-10.1.52/test/jakarta/el/TestELResolver.java --- tomcat10-10.1.34/test/jakarta/el/TestELResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/el/TestELResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -119,8 +119,8 @@ public void testDefaultConvertToType() { ELContext context = new TesterELContext(new StaticFieldELResolver()); - ValueExpression ve = ELManager.getExpressionFactory().createValueExpression(context, "${!Boolean.FALSE}", - Boolean.class); + ValueExpression ve = + ELManager.getExpressionFactory().createValueExpression(context, "${!Boolean.FALSE}", Boolean.class); Boolean result = (Boolean) ve.getValue(context); diff -Nru tomcat10-10.1.34/test/jakarta/el/TestExpressionFactoryCache.java tomcat10-10.1.52/test/jakarta/el/TestExpressionFactoryCache.java --- tomcat10-10.1.34/test/jakarta/el/TestExpressionFactoryCache.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/el/TestExpressionFactoryCache.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package jakarta.el; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +public class TestExpressionFactoryCache { + private static class TestClassLoader extends ClassLoader { + } + + @Test + public void testCacheWithOneClassLoader() { + ExpressionFactory factory; + ClassLoader cl = new TestClassLoader(); + ExpressionFactoryCache cache = new ExpressionFactoryCache(); + + factory = cache.getOrCreateExpressionFactory(cl); + + Assert.assertEquals(factory, cache.getOrCreateExpressionFactory(cl)); + } + + @Test + public void testCacheWithInserts() { + final int numClassLoaders = 15; + ClassLoader[] loaders = new ClassLoader[numClassLoaders]; + ExpressionFactory[] factories = new ExpressionFactory[numClassLoaders]; + + ExpressionFactoryCache cache = new ExpressionFactoryCache(); + + for (int i = 0; i < numClassLoaders; i++) { + loaders[i] = new TestClassLoader(); + factories[i] = cache.getOrCreateExpressionFactory(loaders[i]); + + // make a second call, ensure the same instance comes back + Assert.assertEquals(factories[i], cache.getOrCreateExpressionFactory(loaders[i])); + } + + // use a Set<> to verify each factory is unique + Set factorySet = new HashSet<>(Arrays.asList(factories)); + Assert.assertEquals(numClassLoaders, factorySet.size()); + } + +} \ No newline at end of file diff -Nru tomcat10-10.1.34/test/jakarta/el/TestImportHandler.java tomcat10-10.1.52/test/jakarta/el/TestImportHandler.java --- tomcat10-10.1.34/test/jakarta/el/TestImportHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/el/TestImportHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,6 +21,7 @@ import org.junit.Assert; import org.junit.Test; +import org.apache.catalina.authenticator.DigestAuthenticator; import org.apache.tomcat.util.res.StringManager; public class TestImportHandler { @@ -265,4 +266,20 @@ importHandler.resolveClass("Foo"); } + + + /** + * Support for inner classes. + *

    + * https://bz.apache.org/bugzilla/show_bug.cgi?id=69635 + */ + @Test + public void testResolveInnerClass() { + ImportHandler importHandler = new ImportHandler(); + + importHandler.importClass("org.apache.catalina.authenticator.DigestAuthenticator.AuthDigest"); + Class clazz = importHandler.resolveClass("AuthDigest"); + + Assert.assertEquals(DigestAuthenticator.AuthDigest.class, clazz); + } } diff -Nru tomcat10-10.1.34/test/jakarta/el/TestImportHandlerStandardPackages.java tomcat10-10.1.52/test/jakarta/el/TestImportHandlerStandardPackages.java --- tomcat10-10.1.34/test/jakarta/el/TestImportHandlerStandardPackages.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/el/TestImportHandlerStandardPackages.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,9 +40,9 @@ Object obj = f.get(null); @SuppressWarnings("unchecked") - Map> standardPackageName = (Map>) obj; + Map> standardPackageName = (Map>) obj; - for (Map.Entry> entry : standardPackageName.entrySet()) { + for (Map.Entry> entry : standardPackageName.entrySet()) { checkPackageClassList(entry.getKey(), entry.getValue()); } } diff -Nru tomcat10-10.1.34/test/jakarta/el/TestMapELResolver.java tomcat10-10.1.52/test/jakarta/el/TestMapELResolver.java --- tomcat10-10.1.34/test/jakarta/el/TestMapELResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/el/TestMapELResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -83,7 +83,7 @@ MapELResolver mapELResolver = new MapELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Map map = new HashMap<>(); + Map map = new HashMap<>(); map.put("key", "value"); Object result = mapELResolver.getValue(context, map, "key"); @@ -132,7 +132,7 @@ MapELResolver mapELResolver = new MapELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Map map = new HashMap<>(); + Map map = new HashMap<>(); mapELResolver.setValue(context, map, "key", "value"); Assert.assertEquals("value", mapELResolver.getValue(context, map, "key")); @@ -147,7 +147,7 @@ MapELResolver mapELResolver = new MapELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Map map = Collections.unmodifiableMap(new HashMap<>()); + Map map = Collections.unmodifiableMap(new HashMap<>()); mapELResolver.setValue(context, map, "key", "value"); } @@ -210,7 +210,7 @@ MapELResolver mapELResolver = new MapELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Map map = Collections.unmodifiableMap(new HashMap<>()); + Map map = Collections.unmodifiableMap(new HashMap<>()); boolean result = mapELResolver.isReadOnly(context, map, new Object()); Assert.assertTrue(result); @@ -240,7 +240,7 @@ MapELResolver mapELResolver = new MapELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Map map = new HashMap<>(); + Map map = new HashMap<>(); map.put("key", "value"); Iterator result = mapELResolver.getFeatureDescriptors(context, map); diff -Nru tomcat10-10.1.34/test/jakarta/el/TestStaticFieldELResolver.java tomcat10-10.1.52/test/jakarta/el/TestStaticFieldELResolver.java --- tomcat10-10.1.34/test/jakarta/el/TestStaticFieldELResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/el/TestStaticFieldELResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -111,8 +111,8 @@ StaticFieldELResolver resolver = new StaticFieldELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Object result = resolver.getValue(context, new ELClass(MethodUnderTest.class), - MethodUnderTest.GET_TYPE.toString()); + Object result = + resolver.getValue(context, new ELClass(MethodUnderTest.class), MethodUnderTest.GET_TYPE.toString()); Assert.assertEquals(MethodUnderTest.GET_TYPE, result); Assert.assertTrue(context.isPropertyResolved()); @@ -284,8 +284,8 @@ StaticFieldELResolver resolver = new StaticFieldELResolver(); ELContext context = new StandardELContext(ELManager.getExpressionFactory()); - Class result = resolver.getType(context, new ELClass(MethodUnderTest.class), - MethodUnderTest.GET_TYPE.toString()); + Class result = + resolver.getType(context, new ELClass(MethodUnderTest.class), MethodUnderTest.GET_TYPE.toString()); // Resolver is read-only so this should return null Assert.assertNull(result); diff -Nru tomcat10-10.1.34/test/jakarta/el/TesterBeanNameResolver.java tomcat10-10.1.52/test/jakarta/el/TesterBeanNameResolver.java --- tomcat10-10.1.34/test/jakarta/el/TesterBeanNameResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/el/TesterBeanNameResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,7 +25,7 @@ public static final String THROWABLE_TRIGGER_NAME = "throwable"; public static final String READ_ONLY_NAME = "readonly"; - private Map beans = new HashMap<>(); + private Map beans = new HashMap<>(); private boolean allowCreate = true; diff -Nru tomcat10-10.1.34/test/jakarta/servlet/ServletRequestParametersBaseTest.java tomcat10-10.1.52/test/jakarta/servlet/ServletRequestParametersBaseTest.java --- tomcat10-10.1.34/test/jakarta/servlet/ServletRequestParametersBaseTest.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/ServletRequestParametersBaseTest.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,90 @@ +/* + * 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 jakarta.servlet; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.apache.catalina.startup.SimpleHttpClient; +import org.apache.catalina.startup.TomcatBaseTest; + +public class ServletRequestParametersBaseTest extends TomcatBaseTest { + + protected Map> parseReportedParameters(SimpleHttpClient client) { + Map> parameters = new LinkedHashMap<>(); + if (client.isResponse200()) { + // Response is written using "\n" so need to split on that. + String[] lines = client.getResponseBody().split("\n"); + for (String line : lines) { + // Every line should be name=value + int equalsPos = line.indexOf('='); + String name = line.substring(0, equalsPos); + String value = line.substring(equalsPos + 1); + + List values = parameters.computeIfAbsent(name, k -> new ArrayList<>()); + values.add(value); + } + } + return parameters; + } + + + protected static class ParameterParsingServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + resp.setContentType("text/plain"); + resp.setCharacterEncoding("UTF-8"); + PrintWriter pw = resp.getWriter(); + + Enumeration names = req.getParameterNames(); + while (names.hasMoreElements()) { + String name = names.nextElement(); + for (String value : req.getParameterValues(name)) { + pw.print(name + "=" + value + '\n'); + } + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + // Required parameter processing is the same as for GET + doGet(req, resp); + } + } + + + protected static class TestParameterClient extends SimpleHttpClient { + + @Override + public boolean isResponseBodyOK() { + return true; + } + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java tomcat10-10.1.52/test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java --- tomcat10-10.1.34/test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/HttpServletDoHeadBaseTest.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,6 +42,7 @@ import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap; import org.apache.tomcat.util.compat.JreCompat; +import org.apache.tomcat.util.http.Method; /* * Split into multiple tests as a single test takes so long it impacts the time @@ -257,7 +258,7 @@ boolean resetBufferSize = false; if (Boolean.parseBoolean(getServletConfig().getInitParameter(LEGACY_DO_HEAD)) && - JreCompat.isJre19Available() && "HEAD".equals(req.getMethod()) && useWriter && + JreCompat.isJre19Available() && Method.HEAD.equals(req.getMethod()) && useWriter && resetType != ResetType.NONE) { /* * Using legacy HEAD handling with a Writer on Java 19+. diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestCookie.java tomcat10-10.1.52/test/jakarta/servlet/http/TestCookie.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestCookie.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestCookie.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,8 @@ * Basic tests for Cookie in default configuration. */ public class TestCookie { - public static final BitSet CHAR; // - public static final BitSet CTL; // + public static final BitSet CHAR; // + public static final BitSet CTL; // public static final BitSet SEPARATORS; public static final BitSet TOKEN; // 1* @@ -141,7 +141,7 @@ Cookie cookie = new Cookie("name", "value"); Assert.assertEquals(-1, cookie.getMaxAge()); - for (int value : new int[] { Integer.MIN_VALUE, -2, -1, 0, 1, 2, Integer.MAX_VALUE}) { + for (int value : new int[] { Integer.MIN_VALUE, -2, -1, 0, 1, 2, Integer.MAX_VALUE }) { cookie.setMaxAge(value); Assert.assertEquals(value, cookie.getMaxAge()); } @@ -206,7 +206,7 @@ @Test public void testClone() { - Cookie a = new Cookie("a","a"); + Cookie a = new Cookie("a", "a"); a.setDomain("domain"); a.setHttpOnly(true); a.setMaxAge(123); diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServlet.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServlet.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,6 +32,7 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; import org.apache.catalina.core.StandardContext; @@ -41,6 +42,7 @@ import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.net.TesterSupport.SimpleServlet; public class TestHttpServlet extends TomcatBaseTest { @@ -59,9 +61,8 @@ tomcat.start(); - Map> resHeaders= new HashMap<>(); - int rc = headUrl("http://localhost:" + getPort() + "/", new ByteChunk(), - resHeaders); + Map> resHeaders = new HashMap<>(); + int rc = headUrl("http://localhost:" + getPort() + "/", new ByteChunk(), resHeaders); Assert.assertEquals(HttpServletResponse.SC_OK, rc); Assert.assertEquals(LargeBodyServlet.RESPONSE_LENGTH, resHeaders.get("Content-Length").get(0)); @@ -74,16 +75,15 @@ private static final String RESPONSE_LENGTH = "12345678901"; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setHeader("content-length", RESPONSE_LENGTH); } } /* - * Verifies that the same Content-Length is returned for both GET and HEAD - * operations when a Servlet includes content from another Servlet + * Verifies that the same Content-Length is returned for both GET and HEAD operations when a Servlet includes + * content from another Servlet */ @Test public void testBug57602() throws Exception { @@ -102,7 +102,7 @@ tomcat.start(); - Map> resHeaders= new CaseInsensitiveKeyMap<>(); + Map> resHeaders = new CaseInsensitiveKeyMap<>(); String path = "http://localhost:" + getPort() + "/outer"; ByteChunk out = new ByteChunk(); @@ -198,7 +198,7 @@ // Headers should be the same (apart from Date) Assert.assertEquals(getHeaders.size(), headHeaders.size()); - for (Map.Entry> getHeader : getHeaders.entrySet()) { + for (Map.Entry> getHeader : getHeaders.entrySet()) { String headerName = getHeader.getKey(); Assert.assertTrue(headerName, headHeaders.containsKey(headerName)); List getValues = getHeader.getValue(); @@ -225,7 +225,7 @@ } - private void doTestDoOptions(Servlet servlet, String expectedAllow) throws Exception{ + private void doTestDoOptions(Servlet servlet, String expectedAllow) throws Exception { Tomcat tomcat = getTomcatInstance(); // No file system docBase required @@ -237,9 +237,9 @@ tomcat.start(); - Map> resHeaders= new HashMap<>(); - int rc = methodUrl("http://localhost:" + getPort() + "/", new ByteChunk(), - DEFAULT_CLIENT_TIMEOUT_MS, null, resHeaders, "OPTIONS"); + Map> resHeaders = new HashMap<>(); + int rc = methodUrl("http://localhost:" + getPort() + "/", new ByteChunk(), DEFAULT_CLIENT_TIMEOUT_MS, null, + resHeaders, Method.OPTIONS); Assert.assertEquals(HttpServletResponse.SC_OK, rc); Assert.assertEquals(expectedAllow, resHeaders.get("Allow").get(0)); @@ -265,9 +265,8 @@ /* - * See org.apache.coyote.http2.TestHttpServlet for the HTTP/2 version of - * this test. It was placed in that package because it needed access to - * package private classes. + * See org.apache.coyote.http2.TestHttpServlet for the HTTP/2 version of this test. It was placed in that package + * because it needed access to package private classes. */ @@ -280,22 +279,22 @@ request.append(" HTTP/"); request.append(httpVersion); } - request.append(SimpleHttpClient.CRLF); + request.append(CRLF); request.append("Host: localhost:8080"); - request.append(SimpleHttpClient.CRLF); + request.append(CRLF); request.append("Connection: close"); - request.append(SimpleHttpClient.CRLF); + request.append(CRLF); - request.append(SimpleHttpClient.CRLF); + request.append(CRLF); Client client = new Client(request.toString(), "0.9".equals(httpVersion)); client.doRequest(); if (isHttp09) { - Assert.assertTrue( client.getResponseBody(), client.getResponseBody().contains(" 400 ")); + Assert.assertTrue(client.getResponseBody(), client.getResponseBody().contains(" 400 ")); } else if (isHttp10) { Assert.assertTrue(client.getResponseLine(), client.isResponse400()); } else { @@ -320,14 +319,17 @@ TraceClient client = new TraceClient(); client.setPort(getPort()); + // @formatter:off client.setRequest(new String[] { - "TRACE / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "X-aaa: a1, a2" + SimpleHttpClient.CRLF + - "X-aaa: a3" + SimpleHttpClient.CRLF + - "Cookie: c1-v1" + SimpleHttpClient.CRLF + - "Authorization: not-a-real-credential" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}); + "TRACE / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "X-aaa: a1, a2" + CRLF + + "X-aaa: a3" + CRLF + + "Cookie: c1-v1" + CRLF + + "Authorization: not-a-real-credential" + CRLF + + CRLF + }); + // @formatter:on client.setUseContentLength(true); client.connect(); @@ -365,7 +367,7 @@ private class Client extends SimpleHttpClient { Client(String request, boolean isHttp09) { - setRequest(new String[] {request}); + setRequest(new String[] { request }); setUseHttp09(isHttp09); } @@ -408,8 +410,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); PrintWriter pw = resp.getWriter(); @@ -425,8 +426,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); PrintWriter pw = resp.getWriter(); @@ -440,8 +440,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); PrintWriter pw = resp.getWriter(); @@ -463,8 +462,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); @@ -475,9 +473,9 @@ pw.write(new char[4 * 1024]); } else { ServletOutputStream sos = resp.getOutputStream(); - sos.write(new byte [4 * 1024]); + sos.write(new byte[4 * 1024]); resp.resetBuffer(); - sos.write(new byte [4 * 1024]); + sos.write(new byte[4 * 1024]); } } } @@ -494,8 +492,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); @@ -509,10 +506,10 @@ } else { ServletOutputStream sos = resp.getOutputStream(); resp.addHeader("aaa", "bbb"); - sos.write(new byte [4 * 1024]); + sos.write(new byte[4 * 1024]); resp.resetBuffer(); resp.addHeader("ccc", "ddd"); - sos.write(new byte [4 * 1024]); + sos.write(new byte[4 * 1024]); } } } @@ -529,8 +526,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AsyncContext ac = req.startAsync(req, resp); ac.setTimeout(3000); WriteListener wListener = new NonBlockingWriteListener(ac, bytesToWrite); @@ -576,8 +572,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); PrintWriter pw = resp.getWriter(); @@ -591,8 +586,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } } diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite0.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite0.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite0.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite0.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite0ValidWrite0 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(0), rt, + Integer.valueOf(0), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite0ValidWrite1 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(0), rt, + Integer.valueOf(1), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1023.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1023.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1023.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1023.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite0ValidWrite1023 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(0), rt, + Integer.valueOf(1023), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1024.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1024.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1024.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1024.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite0ValidWrite1024 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(0), rt, + Integer.valueOf(1024), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1025.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1025.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1025.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite1025.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite0ValidWrite1025 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(0), rt, + Integer.valueOf(1025), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite511.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite511.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite511.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite511.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite0ValidWrite511 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(0), rt, + Integer.valueOf(511), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite512.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite512.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite512.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite512.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite0ValidWrite512 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(0), rt, + Integer.valueOf(512), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite513.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite513.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite513.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite0ValidWrite513.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite0ValidWrite513 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(0), rt, + Integer.valueOf(513), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite0.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite0.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite0.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite0.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1023ValidWrite0 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1023), rt, + Integer.valueOf(0), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1023ValidWrite1 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1023), rt, + Integer.valueOf(1), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1023.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1023.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1023.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1023.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1023ValidWrite1023 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1023), rt, + Integer.valueOf(1023), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1024.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1024.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1024.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1024.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1023ValidWrite1024 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1023), rt, + Integer.valueOf(1024), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1025.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1025.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1025.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite1025.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1023ValidWrite1025 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1023), rt, + Integer.valueOf(1025), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite511.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite511.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite511.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite511.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1023ValidWrite511 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1023), rt, + Integer.valueOf(511), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite512.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite512.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite512.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite512.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1023ValidWrite512 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1023), rt, + Integer.valueOf(512), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite513.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite513.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite513.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1023ValidWrite513.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1023ValidWrite513 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1023), rt, + Integer.valueOf(513), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite0.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite0.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite0.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite0.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1024ValidWrite0 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1024), rt, + Integer.valueOf(0), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1024ValidWrite1 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1024), rt, + Integer.valueOf(1), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1023.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1023.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1023.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1023.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1024ValidWrite1023 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1024), rt, + Integer.valueOf(1023), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1024.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1024.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1024.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1024.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1024ValidWrite1024 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1024), rt, + Integer.valueOf(1024), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1025.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1025.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1025.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite1025.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1024ValidWrite1025 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1024), rt, + Integer.valueOf(1025), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite511.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite511.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite511.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite511.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1024ValidWrite511 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1024), rt, + Integer.valueOf(511), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite512.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite512.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite512.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite512.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1024ValidWrite512 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1024), rt, + Integer.valueOf(512), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite513.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite513.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite513.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1024ValidWrite513.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1024ValidWrite513 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1024), rt, + Integer.valueOf(513), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite0.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite0.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite0.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite0.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1025ValidWrite0 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1025), rt, + Integer.valueOf(0), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1025ValidWrite1 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1025), rt, + Integer.valueOf(1), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1023.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1023.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1023.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1023.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1025ValidWrite1023 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1025), rt, + Integer.valueOf(1023), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1024.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1024.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1024.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1024.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1025ValidWrite1024 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1025), rt, + Integer.valueOf(1024), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1025.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1025.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1025.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite1025.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1025ValidWrite1025 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1025), rt, + Integer.valueOf(1025), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite511.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite511.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite511.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite511.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1025ValidWrite511 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1025), rt, + Integer.valueOf(511), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite512.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite512.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite512.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite512.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1025ValidWrite512 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1025), rt, + Integer.valueOf(512), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite513.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite513.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite513.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1025ValidWrite513.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1025ValidWrite513 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1025), rt, + Integer.valueOf(513), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite0.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite0.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite0.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite0.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1ValidWrite0 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1), rt, + Integer.valueOf(0), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1ValidWrite1 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1), rt, + Integer.valueOf(1), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1023.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1023.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1023.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1023.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1ValidWrite1023 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1), rt, + Integer.valueOf(1023), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1024.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1024.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1024.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1024.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1ValidWrite1024 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1), rt, + Integer.valueOf(1024), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1025.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1025.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1025.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite1025.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1ValidWrite1025 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1), rt, + Integer.valueOf(1025), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite511.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite511.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite511.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite511.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1ValidWrite511 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1), rt, + Integer.valueOf(511), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite512.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite512.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite512.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite512.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1ValidWrite512 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1), rt, + Integer.valueOf(512), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite513.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite513.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite513.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite1ValidWrite513.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite1ValidWrite513 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(1), rt, + Integer.valueOf(513), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite0.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite0.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite0.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite0.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite511ValidWrite0 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(511), rt, + Integer.valueOf(0), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite511ValidWrite1 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(511), rt, + Integer.valueOf(1), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1023.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1023.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1023.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1023.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite511ValidWrite1023 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(511), rt, + Integer.valueOf(1023), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1024.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1024.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1024.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1024.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite511ValidWrite1024 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(511), rt, + Integer.valueOf(1024), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1025.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1025.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1025.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite1025.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite511ValidWrite1025 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(511), rt, + Integer.valueOf(1025), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite511.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite511.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite511.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite511.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite511ValidWrite511 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(511), rt, + Integer.valueOf(511), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite512.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite512.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite512.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite512.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite511ValidWrite512 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(511), rt, + Integer.valueOf(512), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite513.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite513.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite513.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite511ValidWrite513.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite511ValidWrite513 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(511), rt, + Integer.valueOf(513), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite0.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite0.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite0.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite0.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite512ValidWrite0 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(512), rt, + Integer.valueOf(0), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite512ValidWrite1 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(512), rt, + Integer.valueOf(1), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1023.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1023.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1023.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1023.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite512ValidWrite1023 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(512), rt, + Integer.valueOf(1023), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1024.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1024.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1024.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1024.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite512ValidWrite1024 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(512), rt, + Integer.valueOf(1024), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1025.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1025.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1025.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite1025.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite512ValidWrite1025 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(512), rt, + Integer.valueOf(1025), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite511.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite511.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite511.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite511.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite512ValidWrite511 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(512), rt, + Integer.valueOf(511), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite512.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite512.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite512.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite512.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite512ValidWrite512 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(512), rt, + Integer.valueOf(512), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite513.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite513.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite513.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite512ValidWrite513.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite512ValidWrite513 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(512), rt, + Integer.valueOf(513), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite0.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite0.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite0.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite0.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite513ValidWrite0 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(513), rt, + Integer.valueOf(0), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite513ValidWrite1 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(513), rt, + Integer.valueOf(1), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1023.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1023.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1023.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1023.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite513ValidWrite1023 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(513), rt, + Integer.valueOf(1023), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1024.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1024.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1024.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1024.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite513ValidWrite1024 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(513), rt, + Integer.valueOf(1024), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1025.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1025.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1025.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite1025.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite513ValidWrite1025 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(513), rt, + Integer.valueOf(1025), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite511.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite511.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite511.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite511.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite513ValidWrite511 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(513), rt, + Integer.valueOf(511), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite512.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite512.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite512.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite512.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite513ValidWrite512 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(513), rt, + Integer.valueOf(512), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite513.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite513.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite513.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadInvalidWrite513ValidWrite513.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,54 @@ +/* + * 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 jakarta.servlet.http; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Split into multiple tests as a single test takes so long it impacts the time + * of an entire test run. + */ +@RunWith(Parameterized.class) +public class TestHttpServletDoHeadInvalidWrite513ValidWrite513 extends HttpServletDoHeadBaseTest { + + @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") + public static Collection parameters() { + Collection baseData = data(); + + List parameterSets = new ArrayList<>(); + for (Object[] base : baseData) { + for (Boolean l : booleans) { + for (Integer buf : BUFFERS) { + for (Boolean w : booleans) { + for (ResetType rt : ResetType.values()) { + for (Boolean f : booleans) { + parameterSets.add(new Object[] { base[0], base[1], l, buf, w, Integer.valueOf(513), rt, + Integer.valueOf(513), f }); + } + } + } + } + } + } + return parameterSets; + } +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite0.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite0.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite0.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite0.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package jakarta.servlet.http; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/* - * Split into multiple tests as a single test takes so long it impacts the time - * of an entire test run. - */ -@RunWith(Parameterized.class) -public class TestHttpServletDoHeadValidWrite0 extends HttpServletDoHeadBaseTest { - - @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") - public static Collection parameters() { - Collection baseData = data(); - - List parameterSets = new ArrayList<>(); - for (Object[] base : baseData) { - for (Boolean l : booleans) { - for (Integer buf : BUFFERS) { - for (Boolean w : booleans) { - for (Integer c1 : COUNTS) { - for (ResetType rt : ResetType.values()) { - for (Boolean f : booleans) { - parameterSets.add(new Object[] { - base[0], base[1], - l, buf, w, c1, rt, Integer.valueOf(0), f }); - } - } - } - } - } - } - } - return parameterSets; - } -} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package jakarta.servlet.http; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/* - * Split into multiple tests as a single test takes so long it impacts the time - * of an entire test run. - */ -@RunWith(Parameterized.class) -public class TestHttpServletDoHeadValidWrite1 extends HttpServletDoHeadBaseTest { - - @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") - public static Collection parameters() { - Collection baseData = data(); - - List parameterSets = new ArrayList<>(); - for (Object[] base : baseData) { - for (Boolean l : booleans) { - for (Integer buf : BUFFERS) { - for (Boolean w : booleans) { - for (Integer c1 : COUNTS) { - for (ResetType rt : ResetType.values()) { - for (Boolean f : booleans) { - parameterSets.add(new Object[] { - base[0], base[1], - l, buf, w, c1, rt, Integer.valueOf(1), f }); - } - } - } - } - } - } - } - return parameterSets; - } -} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1023.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1023.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1023.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1023.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package jakarta.servlet.http; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/* - * Split into multiple tests as a single test takes so long it impacts the time - * of an entire test run. - */ -@RunWith(Parameterized.class) -public class TestHttpServletDoHeadValidWrite1023 extends HttpServletDoHeadBaseTest { - - @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") - public static Collection parameters() { - Collection baseData = data(); - - List parameterSets = new ArrayList<>(); - for (Object[] base : baseData) { - for (Boolean l : booleans) { - for (Integer buf : BUFFERS) { - for (Boolean w : booleans) { - for (Integer c1 : COUNTS) { - for (ResetType rt : ResetType.values()) { - for (Boolean f : booleans) { - parameterSets.add(new Object[] { - base[0], base[1], - l, buf, w, c1, rt, Integer.valueOf(1023), f }); - } - } - } - } - } - } - } - return parameterSets; - } -} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1024.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1024.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1024.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1024.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package jakarta.servlet.http; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/* - * Split into multiple tests as a single test takes so long it impacts the time - * of an entire test run. - */ -@RunWith(Parameterized.class) -public class TestHttpServletDoHeadValidWrite1024 extends HttpServletDoHeadBaseTest { - - @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") - public static Collection parameters() { - Collection baseData = data(); - - List parameterSets = new ArrayList<>(); - for (Object[] base : baseData) { - for (Boolean l : booleans) { - for (Integer buf : BUFFERS) { - for (Boolean w : booleans) { - for (Integer c1 : COUNTS) { - for (ResetType rt : ResetType.values()) { - for (Boolean f : booleans) { - parameterSets.add(new Object[] { - base[0], base[1], - l, buf, w, c1, rt, Integer.valueOf(1024), f }); - } - } - } - } - } - } - } - return parameterSets; - } -} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1025.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1025.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1025.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite1025.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package jakarta.servlet.http; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/* - * Split into multiple tests as a single test takes so long it impacts the time - * of an entire test run. - */ -@RunWith(Parameterized.class) -public class TestHttpServletDoHeadValidWrite1025 extends HttpServletDoHeadBaseTest { - - @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") - public static Collection parameters() { - Collection baseData = data(); - - List parameterSets = new ArrayList<>(); - for (Object[] base : baseData) { - for (Boolean l : booleans) { - for (Integer buf : BUFFERS) { - for (Boolean w : booleans) { - for (Integer c1 : COUNTS) { - for (ResetType rt : ResetType.values()) { - for (Boolean f : booleans) { - parameterSets.add(new Object[] { - base[0], base[1], - l, buf, w, c1, rt, Integer.valueOf(1025), f }); - } - } - } - } - } - } - } - return parameterSets; - } -} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite511.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite511.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite511.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite511.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package jakarta.servlet.http; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/* - * Split into multiple tests as a single test takes so long it impacts the time - * of an entire test run. - */ -@RunWith(Parameterized.class) -public class TestHttpServletDoHeadValidWrite511 extends HttpServletDoHeadBaseTest { - - @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") - public static Collection parameters() { - Collection baseData = data(); - - List parameterSets = new ArrayList<>(); - for (Object[] base : baseData) { - for (Boolean l : booleans) { - for (Integer buf : BUFFERS) { - for (Boolean w : booleans) { - for (Integer c1 : COUNTS) { - for (ResetType rt : ResetType.values()) { - for (Boolean f : booleans) { - parameterSets.add(new Object[] { - base[0], base[1], - l, buf, w, c1, rt, Integer.valueOf(511), f }); - } - } - } - } - } - } - } - return parameterSets; - } -} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite512.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite512.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite512.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite512.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package jakarta.servlet.http; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/* - * Split into multiple tests as a single test takes so long it impacts the time - * of an entire test run. - */ -@RunWith(Parameterized.class) -public class TestHttpServletDoHeadValidWrite512 extends HttpServletDoHeadBaseTest { - - @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") - public static Collection parameters() { - Collection baseData = data(); - - List parameterSets = new ArrayList<>(); - for (Object[] base : baseData) { - for (Boolean l : booleans) { - for (Integer buf : BUFFERS) { - for (Boolean w : booleans) { - for (Integer c1 : COUNTS) { - for (ResetType rt : ResetType.values()) { - for (Boolean f : booleans) { - parameterSets.add(new Object[] { - base[0], base[1], - l, buf, w, c1, rt, Integer.valueOf(512), f }); - } - } - } - } - } - } - } - return parameterSets; - } -} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite513.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite513.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite513.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletDoHeadValidWrite513.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package jakarta.servlet.http; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -/* - * Split into multiple tests as a single test takes so long it impacts the time - * of an entire test run. - */ -@RunWith(Parameterized.class) -public class TestHttpServletDoHeadValidWrite513 extends HttpServletDoHeadBaseTest { - - @Parameterized.Parameters(name = "{index}: {0} {1} {2} {3} {4} {5} {6} {7} {8}") - public static Collection parameters() { - Collection baseData = data(); - - List parameterSets = new ArrayList<>(); - for (Object[] base : baseData) { - for (Boolean l : booleans) { - for (Integer buf : BUFFERS) { - for (Boolean w : booleans) { - for (Integer c1 : COUNTS) { - for (ResetType rt : ResetType.values()) { - for (Boolean f : booleans) { - parameterSets.add(new Object[] { - base[0], base[1], - l, buf, w, c1, rt, Integer.valueOf(513), f }); - } - } - } - } - } - } - } - return parameterSets; - } -} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletResponseSendError.java tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletResponseSendError.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TestHttpServletResponseSendError.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TestHttpServletResponseSendError.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,14 +38,14 @@ import org.apache.tomcat.util.descriptor.web.ErrorPage; /** - * These tests evolved out of a discussion in the Jakarta Servlet project - * regarding the intended behaviour in various error scenarios. Async requests - * and/or async error pages added additional complexity. + * These tests evolved out of a discussion in the Jakarta Servlet project regarding the intended behaviour in various + * error scenarios. Async requests and/or async error pages added additional complexity. */ @RunWith(Parameterized.class) public class TestHttpServletResponseSendError extends TomcatBaseTest { - /* + /* @formatter:off + * * Implementation notes: * Original Request * - async @@ -57,24 +57,23 @@ * - async * - complete * - dispatch + * + * @formatter:on */ private enum AsyncErrorPoint { /* - * Thread A is the container thread the processes the original request. - * Thread B is the async thread (may or may not be a container thread) - * that is started by the async processing. + * Thread A is the container thread the processes the original request. Thread B is the async thread (may or may + * not be a container thread) that is started by the async processing. */ THREAD_A_BEFORE_START_ASYNC, THREAD_A_AFTER_START_ASYNC, THREAD_A_AFTER_START_RUNNABLE, THREAD_B_BEFORE_COMPLETE /* - * If the error is triggered after Thread B completes async processing - * there is essentially a race condition between thread B making the - * change and the container checking to see if the error flag has been - * set. We can't easily control the execution order here so we don't - * test it. + * If the error is triggered after Thread B completes async processing there is essentially a race condition + * between thread B making the change and the container checking to see if the error flag has been set. We can't + * easily control the execution order here so we don't test it. */ } @@ -95,15 +94,15 @@ // managed threads are not visible to the container. continue; } - parameterSets.add(new Object[] { async, throwException, useDispatch, - errorPoint, useStart} ); + parameterSets + .add(new Object[] { async, throwException, useDispatch, errorPoint, useStart }); } } } } else { // Ignore the async specific parameters parameterSets.add(new Object[] { async, throwException, Boolean.FALSE, - AsyncErrorPoint.THREAD_A_AFTER_START_ASYNC, Boolean.FALSE} ); + AsyncErrorPoint.THREAD_A_AFTER_START_ASYNC, Boolean.FALSE }); } } } @@ -189,8 +188,7 @@ @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (throwException) { throw new SendErrorException(); } else { @@ -207,9 +205,9 @@ private static final long serialVersionUID = 1L; private final boolean throwException; - private final boolean useDispatch; + private final boolean useDispatch; private final AsyncErrorPoint errorPoint; - private final boolean useStart; + private final boolean useStart; public TesterAsyncServlet(boolean throwException, boolean useDispatch, AsyncErrorPoint errorPoint, boolean useStart) { @@ -221,8 +219,7 @@ @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (errorPoint == AsyncErrorPoint.THREAD_A_BEFORE_START_ASYNC) { doError(resp); @@ -267,8 +264,7 @@ private final boolean useDispatch; private final AsyncErrorPoint errorPoint; - public AsyncRunnable(AsyncContext ac, boolean throwException, boolean useDispatch, - AsyncErrorPoint errorPoint) { + public AsyncRunnable(AsyncContext ac, boolean throwException, boolean useDispatch, AsyncErrorPoint errorPoint) { this.ac = ac; this.throwException = throwException; this.useDispatch = useDispatch; @@ -290,8 +286,8 @@ // reported try { ((HttpServletResponse) ac.getResponse()).sendError(599); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } } @@ -311,8 +307,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); resp.getWriter().write("DISPATCH"); @@ -332,8 +327,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); resp.getWriter().write("FAIL-599"); @@ -346,8 +340,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); resp.getWriter().write("FAIL-Exception"); diff -Nru tomcat10-10.1.34/test/jakarta/servlet/http/TesterHttpServletPerformance.java tomcat10-10.1.52/test/jakarta/servlet/http/TesterHttpServletPerformance.java --- tomcat10-10.1.34/test/jakarta/servlet/http/TesterHttpServletPerformance.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/http/TesterHttpServletPerformance.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,7 +33,7 @@ public class TesterHttpServletPerformance { @Test - public void testDoOptions() throws IOException, ServletException{ + public void testDoOptions() throws IOException, ServletException { TesterServlet testerServlet = new TesterServlet(); TesterRequest testerRequest = new TesterRequest(false); TesterHttpServletResponse testerResponse = new TesterHttpServletResponse(); diff -Nru tomcat10-10.1.34/test/jakarta/servlet/jsp/TestPageContext.java tomcat10-10.1.52/test/jakarta/servlet/jsp/TestPageContext.java --- tomcat10-10.1.34/test/jakarta/servlet/jsp/TestPageContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/jsp/TestPageContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,7 @@ public void testBug49196() throws Exception { getTomcatInstanceTestWebapp(false, true); - ByteChunk res = getUrl("http://localhost:" + getPort() + - "/test/bug49nnn/bug49196.jsp"); + ByteChunk res = getUrl("http://localhost:" + getPort() + "/test/bug49nnn/bug49196.jsp"); String result = res.toString(); Assert.assertTrue(result.contains("OK")); diff -Nru tomcat10-10.1.34/test/jakarta/servlet/jsp/TesterPageContext.java tomcat10-10.1.52/test/jakarta/servlet/jsp/TesterPageContext.java --- tomcat10-10.1.34/test/jakarta/servlet/jsp/TesterPageContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/jsp/TesterPageContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,10 +29,18 @@ import jakarta.servlet.http.HttpSession; public class TesterPageContext extends PageContext { + private final ELContext elContext; + + public TesterPageContext() { + this.elContext = null; + } + + public TesterPageContext(ELContext elContext) { + this.elContext = elContext; + } @Override - public void initialize(Servlet servlet, ServletRequest request, - ServletResponse response, String errorPageURL, + public void initialize(Servlet servlet, ServletRequest request, ServletResponse response, String errorPageURL, boolean needsSession, int bufferSize, boolean autoFlush) throws IOException, IllegalStateException, IllegalArgumentException { // NO-OP @@ -86,33 +94,28 @@ } @Override - public void forward(String relativeUrlPath) throws ServletException, - IOException { + public void forward(String relativeUrlPath) throws ServletException, IOException { // NO-OP } @Override - public void include(String relativeUrlPath) throws ServletException, - IOException { + public void include(String relativeUrlPath) throws ServletException, IOException { // NO-OP } @Override - public void include(String relativeUrlPath, boolean flush) - throws ServletException, IOException { + public void include(String relativeUrlPath, boolean flush) throws ServletException, IOException { // NO-OP } @Override - public void handlePageException(Exception e) throws ServletException, - IOException { + public void handlePageException(Exception e) throws ServletException, IOException { // NO-OP } @Override - public void handlePageException(Throwable t) throws ServletException, - IOException { + public void handlePageException(Throwable t) throws ServletException, IOException { // NO-OP } @@ -181,8 +184,7 @@ @Override public ELContext getELContext() { - // NO-OP - return null; + return elContext; } @Override diff -Nru tomcat10-10.1.34/test/jakarta/servlet/jsp/TesterPageContextWithAttributes.java tomcat10-10.1.52/test/jakarta/servlet/jsp/TesterPageContextWithAttributes.java --- tomcat10-10.1.34/test/jakarta/servlet/jsp/TesterPageContextWithAttributes.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/jsp/TesterPageContextWithAttributes.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,126 @@ +/* + * 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 jakarta.servlet.jsp; + +import java.util.HashMap; +import java.util.Map; + +import jakarta.el.ELContext; + +import org.apache.jasper.compiler.Localizer; + +public class TesterPageContextWithAttributes extends TesterPageContext { + private final Map applicationAttributes = new HashMap<>(); + private final Map pageAttributes = new HashMap<>(); + private final Map requestAttributes = new HashMap<>(); + private final Map sessionAttributes = new HashMap<>(); + + public TesterPageContextWithAttributes() { + super(); + } + + public TesterPageContextWithAttributes(ELContext elContext) { + super(elContext); + } + + @Override + public Object getAttribute(String name) { + return getAttribute(name, PAGE_SCOPE); + } + + @Override + public Object getAttribute(String name, int scope) { + if (name == null) { + throw new NullPointerException(Localizer.getMessage("jsp.error.attribute.null_name")); + } + + switch (scope) { + case PAGE_SCOPE: + return pageAttributes.get(name); + case REQUEST_SCOPE: + return requestAttributes.get(name); + case SESSION_SCOPE: + return sessionAttributes.get(name); + case APPLICATION_SCOPE: + return applicationAttributes.get(name); + default: + throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); + } + } + + @Override + public void removeAttribute(String name) { + removeAttribute(name, PAGE_SCOPE); + removeAttribute(name, REQUEST_SCOPE); + removeAttribute(name, SESSION_SCOPE); + removeAttribute(name, APPLICATION_SCOPE); + } + + @Override + public void removeAttribute(String name, int scope) { + switch (scope) { + case PageContext.APPLICATION_SCOPE: + applicationAttributes.remove(name); + break; + case PageContext.PAGE_SCOPE: + pageAttributes.remove(name); + break; + case PageContext.REQUEST_SCOPE: + requestAttributes.remove(name); + break; + case PageContext.SESSION_SCOPE: + sessionAttributes.remove(name); + break; + default: + throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); + } + } + + @Override + public void setAttribute(String name, Object value) { + setAttribute(name, value, PAGE_SCOPE); + } + + @Override + public void setAttribute(String name, Object value, int scope) { + if (value == null) { + removeAttribute(name, scope); + } else { + switch (scope) { + case PAGE_SCOPE: + pageAttributes.put(name, value); + break; + + case REQUEST_SCOPE: + requestAttributes.put(name, value); + break; + + case SESSION_SCOPE: + sessionAttributes.put(name, value); + break; + + case APPLICATION_SCOPE: + applicationAttributes.put(name, value); + break; + + default: + throw new IllegalArgumentException(Localizer.getMessage("jsp.error.page.invalid.scope")); + } + } + } + +} diff -Nru tomcat10-10.1.34/test/jakarta/servlet/jsp/el/TestScopedAttributeELResolver.java tomcat10-10.1.52/test/jakarta/servlet/jsp/el/TestScopedAttributeELResolver.java --- tomcat10-10.1.34/test/jakarta/servlet/jsp/el/TestScopedAttributeELResolver.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/jsp/el/TestScopedAttributeELResolver.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,7 @@ public void testBug49196() throws Exception { getTomcatInstanceTestWebapp(true, true); - ByteChunk res = getUrl("http://localhost:" + getPort() + - "/test/bug6nnnn/bug62453.jsp"); + ByteChunk res = getUrl("http://localhost:" + getPort() + "/test/bug6nnnn/bug62453.jsp"); String result = res.toString(); Assert.assertTrue(result, result.contains("

    foo: OK
    ")); diff -Nru tomcat10-10.1.34/test/jakarta/servlet/jsp/el/TesterScopedAttributeELResolverPerformance.java tomcat10-10.1.52/test/jakarta/servlet/jsp/el/TesterScopedAttributeELResolverPerformance.java --- tomcat10-10.1.34/test/jakarta/servlet/jsp/el/TesterScopedAttributeELResolverPerformance.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/jsp/el/TesterScopedAttributeELResolverPerformance.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,8 +32,7 @@ public class TesterScopedAttributeELResolverPerformance { /* - * With the caching of NotFound responses this test takes ~20ms. Without the - * caching it takes ~6s. + * With the caching of NotFound responses this test takes ~20ms. Without the caching it takes ~6s. */ @Test public void testGetValuePerformance() throws Exception { diff -Nru tomcat10-10.1.34/test/jakarta/servlet/resources/TestSchemaValidation.java tomcat10-10.1.52/test/jakarta/servlet/resources/TestSchemaValidation.java --- tomcat10-10.1.34/test/jakarta/servlet/resources/TestSchemaValidation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/servlet/resources/TestSchemaValidation.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,12 +33,10 @@ @Test public void testWebapp() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp/WEB-INF/web.xml")); Assert.assertEquals("6.0", desc.getVersion()); Assert.assertEquals(0, handler.getErrors().size()); Assert.assertEquals(0, handler.getWarnings().size()); @@ -47,12 +45,10 @@ @Test public void testWebapp_2_2() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp-2.2/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp-2.2/WEB-INF/web.xml")); Assert.assertEquals("2.2", desc.getVersion()); Assert.assertEquals(XmlIdentifiers.WEB_22_PUBLIC, desc.getPublicId()); Assert.assertEquals(0, handler.getErrors().size()); @@ -62,12 +58,10 @@ @Test public void testWebapp_2_3() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp-2.3/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp-2.3/WEB-INF/web.xml")); Assert.assertEquals("2.3", desc.getVersion()); Assert.assertEquals(XmlIdentifiers.WEB_23_PUBLIC, desc.getPublicId()); Assert.assertEquals(0, handler.getErrors().size()); @@ -77,12 +71,10 @@ @Test public void testWebapp_2_4() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp-2.4/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp-2.4/WEB-INF/web.xml")); Assert.assertEquals("2.4", desc.getVersion()); Assert.assertEquals(0, handler.getErrors().size()); Assert.assertEquals(0, handler.getWarnings().size()); @@ -91,12 +83,10 @@ @Test public void testWebapp_2_5() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp-2.5/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp-2.5/WEB-INF/web.xml")); Assert.assertEquals("2.5", desc.getVersion()); Assert.assertEquals(0, handler.getErrors().size()); Assert.assertEquals(0, handler.getWarnings().size()); @@ -105,12 +95,10 @@ @Test public void testWebapp_3_0() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp-3.0/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp-3.0/WEB-INF/web.xml")); Assert.assertEquals("3.0", desc.getVersion()); Assert.assertEquals(0, handler.getErrors().size()); Assert.assertEquals(0, handler.getWarnings().size()); @@ -119,12 +107,10 @@ @Test public void testWebapp_3_1() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp-3.1/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp-3.1/WEB-INF/web.xml")); Assert.assertEquals("3.1", desc.getVersion()); Assert.assertEquals(0, handler.getErrors().size()); Assert.assertEquals(0, handler.getWarnings().size()); @@ -133,12 +119,10 @@ @Test public void testWebapp_4_0() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp-4.0/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp-4.0/WEB-INF/web.xml")); Assert.assertEquals("4.0", desc.getVersion()); Assert.assertEquals(0, handler.getErrors().size()); Assert.assertEquals(0, handler.getWarnings().size()); @@ -147,12 +131,10 @@ @Test public void testWebapp_5_0() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp-5.0/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp-5.0/WEB-INF/web.xml")); Assert.assertEquals("5.0", desc.getVersion()); Assert.assertEquals(0, handler.getErrors().size()); Assert.assertEquals(0, handler.getWarnings().size()); @@ -162,12 +144,10 @@ @Test public void testWebapp_6_0() throws Exception { XmlErrorHandler handler = new XmlErrorHandler(); - Digester digester = DigesterFactory.newDigester( - true, true, new WebRuleSet(false), true); + Digester digester = DigesterFactory.newDigester(true, true, new WebRuleSet(false), true); digester.setErrorHandler(handler); digester.push(new WebXml()); - WebXml desc = (WebXml) digester.parse( - new File("test/webapp-6.0/WEB-INF/web.xml")); + WebXml desc = (WebXml) digester.parse(new File("test/webapp-6.0/WEB-INF/web.xml")); Assert.assertEquals("6.0", desc.getVersion()); Assert.assertEquals(0, handler.getErrors().size()); Assert.assertEquals(0, handler.getWarnings().size()); diff -Nru tomcat10-10.1.34/test/jakarta/websocket/TesterContainerProviderPerformance.java tomcat10-10.1.52/test/jakarta/websocket/TesterContainerProviderPerformance.java --- tomcat10-10.1.34/test/jakarta/websocket/TesterContainerProviderPerformance.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/jakarta/websocket/TesterContainerProviderPerformance.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,8 +32,7 @@ @Test public void testGetWebSocketContainer() throws Exception { for (int i = 1; i < 9; i++) { - TesterThreadedPerformance test = - new TesterThreadedPerformance(i, 250000, new TestInstanceSupplier()); + TesterThreadedPerformance test = new TesterThreadedPerformance(i, 250000, new TestInstanceSupplier()); long duration = test.doTest(); System.out.println(i + " threads completed in " + duration + "ns"); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/ant/TestDeployTask.java tomcat10-10.1.52/test/org/apache/catalina/ant/TestDeployTask.java --- tomcat10-10.1.34/test/org/apache/catalina/ant/TestDeployTask.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/ant/TestDeployTask.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,7 +40,8 @@ Assert.assertEquals("application/octet-stream", contentType); try { istream.close(); - } catch (IOException e) { + } catch (IOException ignore) { + // Ignore } } @@ -54,8 +55,8 @@ testExecute(deployTask, "jar:" + new File("test/deployment/context.jar").toURI().toString() + "!/context.war"); testExecute(deployTask, new File("test/deployment/dir with spaces/context.war").toURI().toString()); testExecute(deployTask, new File("test/deployment/dir with spaces/context.war").getAbsolutePath()); - testExecute(deployTask, "jar:" + new File("test/deployment/dir with spaces/context.jar").toURI().toString() - + "!/context.war"); + testExecute(deployTask, + "jar:" + new File("test/deployment/dir with spaces/context.jar").toURI().toString() + "!/context.war"); testExecute(deployTask, "file:./test/deployment/dir%20with%20spaces/context.war"); } @@ -99,7 +100,8 @@ Assert.assertEquals("application/octet-stream", contentType); try { istream.close(); - } catch (IOException e) { + } catch (IOException ignore) { + // Ignore } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/ResponseDescriptor.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/ResponseDescriptor.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/ResponseDescriptor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/ResponseDescriptor.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,17 +23,17 @@ * This class incorporates test response data */ class ResponseDescriptor { - private Map> headers; + private Map> headers; private String body; private int responseCode; - public Map> getHeaders() { + public Map> getHeaders() { return headers; } - public void setHeaders(Map> headers) { + public void setHeaders(Map> headers) { this.headers = headers; } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestAuthInfoResponseHeaders.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestAuthInfoResponseHeaders.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestAuthInfoResponseHeaders.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestAuthInfoResponseHeaders.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,9 +49,8 @@ private static String CLIENT_AUTH_HEADER = "authorization"; /* - * Encapsulate the logic to generate an HTTP header - * for BASIC Authentication. - * Note: only used internally, so no need to validate arguments. + * Encapsulate the logic to generate an HTTP header for BASIC Authentication. Note: only used internally, so no need + * to validate arguments. */ private static final class BasicCredentials { @@ -60,16 +59,14 @@ private final String password; private final String credentials; - private BasicCredentials(String aMethod, - String aUsername, String aPassword) { + private BasicCredentials(String aMethod, String aUsername, String aPassword) { method = aMethod; username = aUsername; password = aPassword; String userCredentials = username + ":" + password; - byte[] credentialsBytes = - userCredentials.getBytes(StandardCharsets.ISO_8859_1); + byte[] credentialsBytes = userCredentials.getBytes(StandardCharsets.ISO_8859_1); String base64auth = Base64.getEncoder().encodeToString(credentialsBytes); - credentials= method + " " + base64auth; + credentials = method + " " + base64auth; } private String getCredentials() { @@ -87,13 +84,11 @@ doTest(USER, PWD, CONTEXT_PATH + URI, true); } - public void doTest(String user, String pwd, String uri, boolean expectResponseAuthHeaders) - throws Exception { + public void doTest(String user, String pwd, String uri, boolean expectResponseAuthHeaders) throws Exception { if (expectResponseAuthHeaders) { BasicAuthenticator auth = - (BasicAuthenticator) getTomcatInstance().getHost().findChild( - CONTEXT_PATH).getPipeline().getFirst(); + (BasicAuthenticator) getTomcatInstance().getHost().findChild(CONTEXT_PATH).getPipeline().getFirst(); auth.setSendAuthInfoResponseHeaders(true); } getTomcatInstance().start(); @@ -114,8 +109,7 @@ Map> respHeaders = new HashMap<>(); ByteChunk bc = new ByteChunk(); - int rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders, - respHeaders); + int rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders, respHeaders); Assert.assertEquals(200, rc); Assert.assertEquals("OK", bc.toString()); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestAuthenticatorBaseCorsPreflight.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestAuthenticatorBaseCorsPreflight.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestAuthenticatorBaseCorsPreflight.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestAuthenticatorBaseCorsPreflight.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,6 +45,7 @@ import org.apache.tomcat.util.descriptor.web.LoginConfig; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; +import org.apache.tomcat.util.http.Method; @RunWith(Parameterized.class) public class TestAuthenticatorBaseCorsPreflight extends TomcatBaseTest { @@ -53,26 +54,36 @@ private static final String EMPTY_ORIGIN = ""; private static final String INVALID_ORIGIN = "http://%20"; private static final String SAME_ORIGIN = "http://localhost"; - private static final String ALLOWED_METHOD = "GET"; - private static final String BLOCKED_METHOD = "POST"; + private static final String ALLOWED_METHOD = Method.GET; + private static final String BLOCKED_METHOD = Method.POST; private static final String EMPTY_METHOD = ""; @Parameterized.Parameters(name = "{index}: input[{0}]") public static Collection parameters() { List parameterSets = new ArrayList<>(); - parameterSets.add(new Object[] { AllowCorsPreflight.NEVER, "/*", "OPTIONS", null, null, Boolean.FALSE }); - parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", null, null, Boolean.FALSE }); - parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.TRUE }); - parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", EMPTY_ORIGIN, ALLOWED_METHOD, Boolean.FALSE}); - parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", INVALID_ORIGIN, ALLOWED_METHOD, Boolean.FALSE }); - parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", SAME_ORIGIN, ALLOWED_METHOD, Boolean.FALSE }); - parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "GET", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.FALSE }); - parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, BLOCKED_METHOD, Boolean.FALSE }); - parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, EMPTY_METHOD, Boolean.FALSE}); - parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", "OPTIONS", ALLOWED_ORIGIN, null, Boolean.FALSE}); - parameterSets.add(new Object[] { AllowCorsPreflight.FILTER, "/*", "OPTIONS", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.TRUE }); - parameterSets.add(new Object[] { AllowCorsPreflight.FILTER, "/x", "OPTIONS", ALLOWED_ORIGIN, ALLOWED_METHOD, Boolean.FALSE }); + parameterSets.add(new Object[] { AllowCorsPreflight.NEVER, "/*", Method.OPTIONS, null, null, Boolean.FALSE }); + parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", Method.OPTIONS, null, null, Boolean.FALSE }); + parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", Method.OPTIONS, ALLOWED_ORIGIN, ALLOWED_METHOD, + Boolean.TRUE }); + parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", Method.OPTIONS, EMPTY_ORIGIN, ALLOWED_METHOD, + Boolean.FALSE }); + parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", Method.OPTIONS, INVALID_ORIGIN, ALLOWED_METHOD, + Boolean.FALSE }); + parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", Method.OPTIONS, SAME_ORIGIN, ALLOWED_METHOD, + Boolean.FALSE }); + parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", Method.GET, ALLOWED_ORIGIN, ALLOWED_METHOD, + Boolean.FALSE }); + parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", Method.OPTIONS, ALLOWED_ORIGIN, BLOCKED_METHOD, + Boolean.FALSE }); + parameterSets.add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", Method.OPTIONS, ALLOWED_ORIGIN, EMPTY_METHOD, + Boolean.FALSE }); + parameterSets + .add(new Object[] { AllowCorsPreflight.ALWAYS, "/*", Method.OPTIONS, ALLOWED_ORIGIN, null, Boolean.FALSE }); + parameterSets.add(new Object[] { AllowCorsPreflight.FILTER, "/*", Method.OPTIONS, ALLOWED_ORIGIN, ALLOWED_METHOD, + Boolean.TRUE }); + parameterSets.add(new Object[] { AllowCorsPreflight.FILTER, "/x", Method.OPTIONS, ALLOWED_ORIGIN, ALLOWED_METHOD, + Boolean.FALSE }); return parameterSets; } @@ -108,7 +119,7 @@ Tomcat.addServlet(ctx, "default", new DefaultServlet()); ctx.addServletMappingDecoded("/", "default"); - LoginConfig loginConfig = new LoginConfig(); + LoginConfig loginConfig = new LoginConfig(); loginConfig.setAuthMethod("BASIC"); ctx.setLoginConfig(loginConfig); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestBasicAuthParser.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestBasicAuthParser.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestBasicAuthParser.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestBasicAuthParser.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,7 @@ import org.apache.tomcat.util.buf.ByteChunk; /** - * Test the BasicAuthenticator's BasicCredentials inner class and the - * associated Base64 decoder. + * Test the BasicAuthenticator's BasicCredentials inner class and the associated Base64 decoder. */ public class TestBasicAuthParser { @@ -36,27 +35,22 @@ private static final String PASSWORD = "secret"; /* - * test cases with good BASIC Auth credentials - Base64 strings - * can have zero, one or two trailing pad characters + * test cases with good BASIC Auth credentials - Base64 strings can have zero, one or two trailing pad characters */ @Test public void testGoodCredentials() throws Exception { - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD, credentials.getPassword()); } @Test public void testGoodCredentialsNoPassword() throws Exception { - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, null); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, null); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertNull(credentials.getPassword()); } @@ -64,11 +58,9 @@ @Test public void testGoodCrib() throws Exception { final String BASE64_CRIB = "dXNlcmlkOnNlY3JldA=="; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD, credentials.getPassword()); } @@ -76,11 +68,9 @@ @Test public void testGoodCribUserOnly() throws Exception { final String BASE64_CRIB = "dXNlcmlk"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertNull(credentials.getPassword()); } @@ -89,11 +79,9 @@ public void testGoodCribOnePad() throws Exception { final String PASSWORD1 = "secrets"; final String BASE64_CRIB = "dXNlcmlkOnNlY3JldHM="; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD1, credentials.getPassword()); } @@ -101,53 +89,44 @@ /* * Line breaks are not permitted inside the base64 encoded value. */ - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testLineWrap() throws Exception { - final String BASE64_CRIB = "QUJDREVGR0hJSktMTU5PUFFSU1RVVldY" - + "WVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0" - + "\n" + "NTY3ODkrL0FBQUFCQkJCQ0NDQ0REREQ="; + final String BASE64_CRIB = "QUJDREVGR0hJSktMTU5PUFFSU1RVVldY" + "WVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0" + + "\n" + "NTY3ODkrL0FBQUFCQkJCQ0NDQ0REREQ="; final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); @SuppressWarnings("unused") BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); } /* - * RFC 2045 says the Base64 encoded string should be represented - * as lines of no more than 76 characters. However, RFC 2617 - * says a base64-user-pass token is not limited to 76 char/line. + * RFC 2045 says the Base64 encoded string should be represented as lines of no more than 76 characters. However, + * RFC 2617 says a base64-user-pass token is not limited to 76 char/line. */ @Test public void testGoodCribBase64Big() throws Exception { // Our decoder accepts a long token without complaint. - final String USER_LONG = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - + "abcdefghijklmnopqrstuvwxyz0123456789+/AAAABBBBCCCC" - + "DDDD"; // 80 characters - final String BASE64_CRIB = "QUJDREVGR0hJSktMTU5PUFFSU1RVVldY" - + "WVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0" - + "NTY3ODkrL0FBQUFCQkJCQ0NDQ0REREQ="; // no new line - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); + final String USER_LONG = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz0123456789+/AAAABBBBCCCC" + "DDDD"; // 80 + // characters + final String BASE64_CRIB = "QUJDREVGR0hJSktMTU5PUFFSU1RVVldY" + "WVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0" + + "NTY3ODkrL0FBQUFCQkJCQ0NDQ0REREQ="; // no new line + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_LONG, credentials.getUsername()); } /* - * verify the parser follows RFC2617 by treating the auth-scheme - * token as case-insensitive. + * verify the parser follows RFC2617 by treating the auth-scheme token as case-insensitive. */ @Test public void testAuthMethodCaseBasic() throws Exception { final String METHOD = "bAsIc"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(METHOD, USER_NAME, PASSWORD); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(METHOD, USER_NAME, PASSWORD); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD, credentials.getPassword()); } @@ -158,8 +137,7 @@ @Test(expected = IllegalArgumentException.class) public void testAuthMethodBadMethod() throws Exception { final String METHOD = "BadMethod"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(METHOD, USER_NAME, PASSWORD); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(METHOD, USER_NAME, PASSWORD); @SuppressWarnings("unused") BasicAuthenticator.BasicCredentials credentials = new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); @@ -168,10 +146,9 @@ /* * Confirm the Basic parser allows exactly one space after the authentication method. */ - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testAuthMethodExtraLeadingSpace() throws Exception { - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD + " ", USER_NAME, PASSWORD); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD + " ", USER_NAME, PASSWORD); @SuppressWarnings("unused") final BasicAuthenticator.BasicCredentials credentials = new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); @@ -184,11 +161,9 @@ @Test public void testWrongPassword() throws Exception { final String PWD_WRONG = "wrong"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, PWD_WRONG); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, PWD_WRONG); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertNotSame(PASSWORD, credentials.getPassword()); } @@ -196,11 +171,9 @@ @Test public void testMissingUsername() throws Exception { final String EMPTY_USER_NAME = ""; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, EMPTY_USER_NAME, PASSWORD); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, EMPTY_USER_NAME, PASSWORD); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(EMPTY_USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD, credentials.getPassword()); } @@ -208,11 +181,9 @@ @Test public void testShortUsername() throws Exception { final String SHORT_USER_NAME = "a"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, SHORT_USER_NAME, PASSWORD); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, SHORT_USER_NAME, PASSWORD); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(SHORT_USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD, credentials.getPassword()); } @@ -220,11 +191,9 @@ @Test public void testShortPassword() throws Exception { final String SHORT_PASSWORD = "a"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, SHORT_PASSWORD); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, SHORT_PASSWORD); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(SHORT_PASSWORD, credentials.getPassword()); } @@ -232,11 +201,9 @@ @Test public void testPasswordHasSpaceEmbedded() throws Exception { final String PASSWORD_SPACE = "abc def"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_SPACE); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_SPACE); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD_SPACE, credentials.getPassword()); } @@ -244,11 +211,9 @@ @Test public void testPasswordHasColonEmbedded() throws Exception { final String PASSWORD_COLON = "abc:def"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_COLON); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_COLON); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD_COLON, credentials.getPassword()); } @@ -256,11 +221,9 @@ @Test public void testPasswordHasColonLeading() throws Exception { final String PASSWORD_COLON = ":abcdef"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_COLON); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_COLON); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD_COLON, credentials.getPassword()); } @@ -268,11 +231,9 @@ @Test public void testPasswordHasColonTrailing() throws Exception { final String PASSWORD_COLON = "abcdef:"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_COLON); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD_COLON); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD_COLON, credentials.getPassword()); } @@ -280,7 +241,7 @@ /* * Confirm the Basic parser does not tolerate excess white space after the base64 blob. */ - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testAuthMethodExtraTrailingSpace() throws Exception { final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, PASSWORD, " "); @SuppressWarnings("unused") @@ -293,11 +254,9 @@ */ @Test public void testUserExtraSpace() throws Exception { - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, " " + USER_NAME + " ", PASSWORD); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, " " + USER_NAME + " ", PASSWORD); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertNotEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(USER_NAME, credentials.getUsername().trim()); Assert.assertEquals(PASSWORD, credentials.getPassword()); @@ -309,12 +268,10 @@ */ @Test public void testUserExtraSpaceWithTrimCredentials() throws Exception { - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, " " + USER_NAME + " ", PASSWORD); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, " " + USER_NAME + " ", PASSWORD); @SuppressWarnings("deprecation") BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD, credentials.getPassword()); } @@ -324,11 +281,9 @@ */ @Test public void testPasswordExtraSpace() throws Exception { - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, " " + PASSWORD + " "); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, " " + PASSWORD + " "); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertNotEquals(PASSWORD, credentials.getPassword()); Assert.assertEquals(PASSWORD, credentials.getPassword().trim()); @@ -340,12 +295,10 @@ */ @Test public void testPasswordExtraSpaceWithTrimCredentials() throws Exception { - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, USER_NAME, " " + PASSWORD + " "); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, USER_NAME, " " + PASSWORD + " "); @SuppressWarnings("deprecation") BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8, true); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD, credentials.getPassword()); } @@ -354,9 +307,7 @@ /* * invalid base64 string tests * - * Refer to - * - RFC 7617 (Basic Auth) - * - RFC 4648 (base 64) + * Refer to - RFC 7617 (Basic Auth) - RFC 4648 (base 64) */ /* @@ -365,34 +316,29 @@ @Test(expected = IllegalArgumentException.class) public void testBadBase64InlineEquals() throws Exception { final String BASE64_CRIB = "dXNlcmlkOnNlY3J=dAo="; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); @SuppressWarnings("unused") // Exception will be thrown. BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); } /* - * "-" is not a legal base64 character. The RFC says it must be - * ignored by the decoder. This will scramble the decoded string - * and eventually result in an IllegalArgumentException. + * "-" is not a legal base64 character. The RFC says it must be ignored by the decoder. This will scramble the + * decoded string and eventually result in an IllegalArgumentException. */ @Test(expected = IllegalArgumentException.class) public void testBadBase64Char() throws Exception { final String BASE64_CRIB = "dXNlcmlkOnNl-3JldHM="; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); @SuppressWarnings("unused") BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); } /* * "-" is not a legal base64 character. */ - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testBadBase64LastChar() throws Exception { final String BASE64_CRIB = "dXNlcmlkOnNlY3JldA-="; final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); @@ -404,7 +350,7 @@ /* * The trailing third "=" is illegal. */ - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testBadBase64TooManyEquals() throws Exception { final String BASE64_CRIB = "dXNlcmlkOnNlY3JldA==="; final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); @@ -414,65 +360,51 @@ } /* - * there should be a multiple of 4 encoded characters. However, - * the RFC says the decoder should pad the input string with - * zero bits out to the next boundary. An error will not be detected - * unless the payload has been damaged in some way - this - * particular crib has no damage. + * there should be a multiple of 4 encoded characters. However, the RFC says the decoder should pad the input string + * with zero bits out to the next boundary. An error will not be detected unless the payload has been damaged in + * some way - this particular crib has no damage. */ @Test public void testBadBase64BadLength() throws Exception { final String BASE64_CRIB = "dXNlcmlkOnNlY3JldA"; - final BasicAuthHeader AUTH_HEADER = - new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); + final BasicAuthHeader AUTH_HEADER = new BasicAuthHeader(NICE_METHOD, BASE64_CRIB); BasicAuthenticator.BasicCredentials credentials = - new BasicAuthenticator.BasicCredentials( - AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); + new BasicAuthenticator.BasicCredentials(AUTH_HEADER.getHeader(), StandardCharsets.UTF_8); Assert.assertEquals(USER_NAME, credentials.getUsername()); Assert.assertEquals(PASSWORD, credentials.getPassword()); } /* - * Encapsulate the logic to generate an HTTP header - * for BASIC Authentication. - * Note: only used internally, so no need to validate arguments. + * Encapsulate the logic to generate an HTTP header for BASIC Authentication. Note: only used internally, so no need + * to validate arguments. */ public static final class BasicAuthHeader { - private static final byte[] HEADER = - "authorization: ".getBytes(StandardCharsets.ISO_8859_1); + private static final byte[] HEADER = "authorization: ".getBytes(StandardCharsets.ISO_8859_1); private ByteChunk authHeader; private int initialOffset = 0; /* * This method creates a valid base64 blob */ - public BasicAuthHeader(String method, String username, - String password) { + public BasicAuthHeader(String method, String username, String password) { this(method, username, password, null); } /* * This method creates valid base64 blobs with optional trailing data */ - private BasicAuthHeader(String method, String username, - String password, String extraBlob) { + private BasicAuthHeader(String method, String username, String password, String extraBlob) { prefix(method); String userCredentials = - ((password == null) || (password.length() < 1)) - ? username - : username + ":" + password; - byte[] credentialsBytes = - userCredentials.getBytes(StandardCharsets.ISO_8859_1); + ((password == null) || (password.length() < 1)) ? username : username + ":" + password; + byte[] credentialsBytes = userCredentials.getBytes(StandardCharsets.ISO_8859_1); String base64auth = Base64.getEncoder().encodeToString(credentialsBytes); - byte[] base64Bytes = - base64auth.getBytes(StandardCharsets.ISO_8859_1); + byte[] base64Bytes = base64auth.getBytes(StandardCharsets.ISO_8859_1); - byte[] extraBytes = - ((extraBlob == null) || (extraBlob.length() < 1)) - ? null : + byte[] extraBytes = ((extraBlob == null) || (extraBlob.length() < 1)) ? null : extraBlob.getBytes(StandardCharsets.ISO_8859_1); try { @@ -480,18 +412,15 @@ if (extraBytes != null) { authHeader.append(extraBytes, 0, extraBytes.length); } - } - catch (IOException ioe) { - throw new IllegalStateException("unable to extend ByteChunk:" - + ioe.getMessage()); + } catch (IOException ioe) { + throw new IllegalStateException("unable to extend ByteChunk:" + ioe.getMessage()); } // emulate tomcat server - offset points to method in header authHeader.setStart(initialOffset); } /* - * This method allows injection of cribbed base64 blobs, - * without any validation of the contents + * This method allows injection of cribbed base64 blobs, without any validation of the contents */ private BasicAuthHeader(String method, String fakeBase64) { prefix(method); @@ -500,10 +429,8 @@ try { authHeader.append(fakeBytes, 0, fakeBytes.length); - } - catch (IOException ioe) { - throw new IllegalStateException("unable to extend ByteChunk:" - + ioe.getMessage()); + } catch (IOException ioe) { + throw new IllegalStateException("unable to extend ByteChunk:" + ioe.getMessage()); } // emulate tomcat server - offset points to method in header authHeader.setStart(initialOffset); @@ -522,10 +449,8 @@ try { authHeader.append(methodBytes, 0, methodBytes.length); - } - catch (IOException ioe) { - throw new IllegalStateException("unable to extend ByteChunk:" - + ioe.getMessage()); + } catch (IOException ioe) { + throw new IllegalStateException("unable to extend ByteChunk:" + ioe.getMessage()); } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestDigestAuthenticator.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestDigestAuthenticator.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestDigestAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestDigestAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -75,122 +75,103 @@ nonces.add(digestAuthenticator.generateNonce(request)); } - Assert.assertEquals(count, nonces.size()); + Assert.assertEquals(count, nonces.size()); } @Test public void testAllValid() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - NC1, NC2, CNONCE, QOP, true, true); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, NC1, NC2, CNONCE, QOP, true, true); } @Test public void testValidNoQop() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - null, null, null, null, true, true); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, null, null, null, null, true, true); } @Test public void testValidQuery() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI + QUERY, false, true, REALM, true, - true, NC1, NC2, CNONCE, QOP, true, true); + doTest(USER, PWD, CONTEXT_PATH + URI + QUERY, false, true, REALM, true, true, NC1, NC2, CNONCE, QOP, true, + true); } @Test public void testInvalidUriFail() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, true, true, REALM, true, true, - NC1, NC2, CNONCE, QOP, false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, true, true, REALM, true, true, NC1, NC2, CNONCE, QOP, false, false); } @Test public void testInvalidUriPass() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, true, false, REALM, true, true, - NC1, NC2, CNONCE, QOP, true, true); + doTest(USER, PWD, CONTEXT_PATH + URI, true, false, REALM, true, true, NC1, NC2, CNONCE, QOP, true, true); } @Test public void testInvalidRealm() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, "null", true, true, - NC1, NC2, CNONCE, QOP, false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, "null", true, true, NC1, NC2, CNONCE, QOP, false, false); } @Test public void testInvalidNonce() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, false, true, - NC1, NC2, CNONCE, QOP, false, true); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, false, true, NC1, NC2, CNONCE, QOP, false, true); } @Test public void testInvalidOpaque() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, false, - NC1, NC2, CNONCE, QOP, false, true); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, false, NC1, NC2, CNONCE, QOP, false, true); } @Test public void testInvalidNc1() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - "null", null, CNONCE, QOP, false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, "null", null, CNONCE, QOP, false, false); } @Test public void testInvalidQop() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - NC1, NC2, CNONCE, "null", false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, NC1, NC2, CNONCE, "null", false, false); } @Test public void testInvalidQopCombo1() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - NC1, NC2, CNONCE, null, false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, NC1, NC2, CNONCE, null, false, false); } @Test public void testInvalidQopCombo2() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - NC1, NC2, null, QOP, false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, NC1, NC2, null, QOP, false, false); } @Test public void testInvalidQopCombo3() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - NC1, NC2, null, null, false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, NC1, NC2, null, null, false, false); } @Test public void testInvalidQopCombo4() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - null, null, CNONCE, QOP, false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, null, null, CNONCE, QOP, false, false); } @Test public void testInvalidQopCombo5() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - null, null, CNONCE, null, false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, null, null, CNONCE, null, false, false); } @Test public void testInvalidQopCombo6() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - null, null, null, QOP, false, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, null, null, null, QOP, false, false); } @Test public void testReplay() throws Exception { - doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, - NC1, NC1, CNONCE, QOP, true, false); + doTest(USER, PWD, CONTEXT_PATH + URI, false, true, REALM, true, true, NC1, NC1, CNONCE, QOP, true, false); } - public void doTest(String user, String pwd, String uri, boolean breakUri, - boolean validateUri, String realm, boolean useServerNonce, - boolean useServerOpaque, String nc1, String nc2, String cnonce, - String qop, boolean req2expect200, boolean req3expect200) - throws Exception { + public void doTest(String user, String pwd, String uri, boolean breakUri, boolean validateUri, String realm, + boolean useServerNonce, boolean useServerOpaque, String nc1, String nc2, String cnonce, String qop, + boolean req2expect200, boolean req3expect200) throws Exception { if (!validateUri) { - DigestAuthenticator auth = - (DigestAuthenticator) getTomcatInstance().getHost().findChild( - CONTEXT_PATH).getPipeline().getFirst(); + DigestAuthenticator auth = (DigestAuthenticator) getTomcatInstance().getHost().findChild(CONTEXT_PATH) + .getPipeline().getFirst(); auth.setValidateUri(false); } getTomcatInstance().start(); @@ -202,8 +183,7 @@ digestUri = uri; } List auth = new ArrayList<>(); - auth.add(buildDigestResponse(user, pwd, digestUri, realm, "null", - "null", nc1, cnonce, qop)); + auth.add(buildDigestResponse(user, pwd, digestUri, realm, "null", "null", nc1, cnonce, qop)); Map> reqHeaders = new HashMap<>(); reqHeaders.put(CLIENT_AUTH_HEADER, auth); @@ -211,8 +191,7 @@ // The first request will fail - but we need to extract the nonce ByteChunk bc = new ByteChunk(); - int rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders, - respHeaders); + int rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders, respHeaders); Assert.assertEquals(401, rc); Assert.assertTrue(bc.getLength() > 0); bc.recycle(); @@ -221,19 +200,17 @@ auth.clear(); if (useServerNonce) { if (useServerOpaque) { - auth.add(buildDigestResponse(user, pwd, digestUri, realm, - getNonce(respHeaders), getOpaque(respHeaders), nc1, - cnonce, qop)); + auth.add(buildDigestResponse(user, pwd, digestUri, realm, getNonce(respHeaders), getOpaque(respHeaders), + nc1, cnonce, qop)); } else { - auth.add(buildDigestResponse(user, pwd, digestUri, realm, - getNonce(respHeaders), "null", nc1, cnonce, qop)); + auth.add(buildDigestResponse(user, pwd, digestUri, realm, getNonce(respHeaders), "null", nc1, cnonce, + qop)); } } else { - auth.add(buildDigestResponse(user, pwd, digestUri, realm, - "null", getOpaque(respHeaders), nc1, cnonce, QOP)); + auth.add( + buildDigestResponse(user, pwd, digestUri, realm, "null", getOpaque(respHeaders), nc1, cnonce, QOP)); } - rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders, - null); + rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders, null); if (req2expect200) { Assert.assertEquals(200, rc); @@ -246,11 +223,9 @@ // Third request should succeed if we increment nc auth.clear(); bc.recycle(); - auth.add(buildDigestResponse(user, pwd, digestUri, realm, - getNonce(respHeaders), getOpaque(respHeaders), nc2, cnonce, - qop)); - rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders, - null); + auth.add(buildDigestResponse(user, pwd, digestUri, realm, getNonce(respHeaders), getOpaque(respHeaders), nc2, + cnonce, qop)); + rc = getUrl("http://localhost:" + getPort() + uri, bc, reqHeaders, null); if (req3expect200) { Assert.assertEquals(200, rc); @@ -296,8 +271,7 @@ } protected static String getNonce(Map> respHeaders) { - List authHeaders = - respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME); + List authHeaders = respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME); // Assume there is only one String authHeader = authHeaders.iterator().next(); @@ -307,8 +281,7 @@ } protected static String getOpaque(Map> respHeaders) { - List authHeaders = - respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME); + List authHeaders = respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME); // Assume there is only one String authHeader = authHeaders.iterator().next(); @@ -318,21 +291,23 @@ } /* + * @formatter:off + * * Notes from RFC2617 * H(data) = MD5(data) * KD(secret, data) = H(concat(secret, ":", data)) * A1 = unq(username-value) ":" unq(realm-value) ":" passwd * A2 = Method ":" digest-uri-value * request-digest = <"> < KD ( H(A1), unq(nonce-value) - ":" nc-value - ":" unq(cnonce-value) - ":" unq(qop-value) - ":" H(A2) - ) <"> + * ":" nc-value + * ":" unq(cnonce-value) + * ":" unq(qop-value) + * ":" H(A2) + * ) <"> + * @formatter:on */ - private static String buildDigestResponse(String user, String pwd, - String uri, String realm, String nonce, String opaque, String nc, - String cnonce, String qop) { + private static String buildDigestResponse(String user, String pwd, String uri, String realm, String nonce, + String opaque, String nc, String cnonce, String qop) { String a1 = user + ":" + realm + ":" + pwd; String a2 = "GET:" + uri; @@ -344,8 +319,7 @@ if (qop == null) { response = digestA1 + ":" + nonce + ":" + digestA2; } else { - response = digestA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + - qop + ":" + digestA2; + response = digestA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + digestA2; } String md5response = digest(response); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestDigestAuthenticatorAlgorithms.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestDigestAuthenticatorAlgorithms.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestDigestAuthenticatorAlgorithms.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestDigestAuthenticatorAlgorithms.java 2026-01-23 19:33:36.000000000 +0000 @@ -93,7 +93,8 @@ } else { user = USER; } - parameterSets.add(new Object[] { algorithms.toString(), algorithm, digestPassword, user, Boolean.valueOf(authExpected) }); + parameterSets.add(new Object[] { algorithms.toString(), algorithm, digestPassword, user, + Boolean.valueOf(authExpected) }); } } } @@ -169,7 +170,7 @@ tomcat.start(); // The first request will always fail - but we need the challenge - Map> respHeaders = new HashMap<>(); + Map> respHeaders = new HashMap<>(); ByteChunk bc = new ByteChunk(); int rc = getUrl("http://localhost:" + getPort() + URI, bc, respHeaders); Assert.assertEquals(401, rc); @@ -180,7 +181,7 @@ List auth = new ArrayList<>(); auth.add(buildDigestResponse(user, PASSWORD, URI, REALM_NAME, clientAlgorithm, respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME), "00000001", CNONCE, DigestAuthenticator.QOP)); - Map> reqHeaders = new HashMap<>(); + Map> reqHeaders = new HashMap<>(); reqHeaders.put("authorization", auth); rc = getUrl("http://localhost:" + getPort() + URI, bc, reqHeaders, null); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestFormAuthenticatorA.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestFormAuthenticatorA.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestFormAuthenticatorA.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestFormAuthenticatorA.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,6 +44,7 @@ import org.apache.tomcat.util.descriptor.web.LoginConfig; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.websocket.server.WsContextListener; /* @@ -108,34 +109,27 @@ @Test public void testGetWithCookies() throws Exception { - doTest("GET", "GET", NO_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.GET, Method.GET, NO_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } - - // next, a set of tests where the server Context is configured to never // use cookies and the session ID is only carried as a url path parameter // Bug 53584 @Test public void testGetNoServerCookies() throws Exception { - doTest("GET", "GET", NO_100_CONTINUE, - CLIENT_NO_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.GET, Method.GET, NO_100_CONTINUE, CLIENT_NO_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); } - - // next, a set of tests where the server Context uses cookies, // but the client refuses to return them and tries to use // the session ID if carried as a url path parameter @Test public void testGetNoClientCookies() throws Exception { - doTest("GET", "GET", NO_100_CONTINUE, - CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.GET, Method.GET, NO_100_CONTINUE, CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } @@ -144,23 +138,18 @@ @Test public void testNoChangedSessidWithCookies() throws Exception { - doTest("GET", "GET", NO_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_USE_COOKIES, - SERVER_FREEZE_SESSID); + doTest(Method.GET, Method.GET, NO_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_FREEZE_SESSID); } @Test public void testNoChangedSessidWithoutCookies() throws Exception { - doTest("GET", "GET", NO_100_CONTINUE, - CLIENT_NO_COOKIES, SERVER_USE_COOKIES, - SERVER_FREEZE_SESSID); + doTest(Method.GET, Method.GET, NO_100_CONTINUE, CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_FREEZE_SESSID); } @Test public void testTimeoutWithoutCookies() throws Exception { - String protectedUri = doTest("GET", "GET", NO_100_CONTINUE, - CLIENT_NO_COOKIES, SERVER_USE_COOKIES, - SERVER_FREEZE_SESSID); + String protectedUri = + doTest(Method.GET, Method.GET, NO_100_CONTINUE, CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_FREEZE_SESSID); // Force session to expire one second from now Context context = (Context) getTomcatInstance().getHost().findChildren()[0]; @@ -172,15 +161,13 @@ // then try to continue using the expired session to get the // protected resource once more. // should get login challenge or timeout status 408 - doTestProtected("GET", protectedUri, NO_100_CONTINUE, - FormAuthClient.LOGIN_REQUIRED, 1); + doTestProtected(Method.GET, protectedUri, NO_100_CONTINUE, FormAuthClient.LOGIN_REQUIRED, 1); } // HTTP 1.0 test @Test public void testGetWithCookiesHttp10() throws Exception { - doTest("GET", "GET", NO_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID, + doTest(Method.GET, Method.GET, NO_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID, CLIENT_USE_HTTP_10); } @@ -188,30 +175,26 @@ @Test public void testSelectedMethods() throws Exception { - FormAuthClientSelectedMethods client = - new FormAuthClientSelectedMethods(true, true, true, true); + FormAuthClientSelectedMethods client = new FormAuthClientSelectedMethods(true, true, true, true); // First request for protected resource gets the login page - client.doResourceRequest("PUT", true, "/test?" + - SelectedMethodsServlet.PARAM + "=" + - SelectedMethodsServlet.VALUE, null); + client.doResourceRequest(Method.PUT, true, + "/test?" + SelectedMethodsServlet.PARAM + "=" + SelectedMethodsServlet.VALUE, null); Assert.assertTrue(client.getResponseLine(), client.isResponse200()); Assert.assertTrue(client.isResponseBodyOK()); String originalSessionId = client.getSessionId(); client.reset(); // Second request replies to the login challenge - client.doResourceRequest("POST", true, "/test/j_security_check", - FormAuthClientBase.LOGIN_REPLY); - Assert.assertTrue("login failed " + client.getResponseLine(), - client.isResponse303()); + client.doResourceRequest(Method.POST, true, "/test/j_security_check", FormAuthClientBase.LOGIN_REPLY); + Assert.assertTrue("login failed " + client.getResponseLine(), client.isResponse303()); Assert.assertTrue(client.isResponseBodyOK()); String redirectUri = client.getRedirectUri(); client.reset(); // Third request - the login was successful so // follow the redirect to the protected resource - client.doResourceRequest("GET", true, redirectUri, null); + client.doResourceRequest(Method.GET, true, redirectUri, null); Assert.assertTrue(client.isResponse200()); Assert.assertTrue(client.isResponseBodyOK()); String newSessionId = client.getSessionId(); @@ -229,7 +212,7 @@ Tomcat tomcat = getTomcatInstance(); File appDir = new File(getBuildDirectory(), "webapps/examples"); - Context ctxt = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); + Context ctxt = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); FormAuthenticator form = new FormAuthenticator(); form.setSecurePagesWithPragma(true); ctxt.getPipeline().addValve(form); @@ -254,36 +237,34 @@ /* - * Choreograph the steps of the test dialogue with the server - * 1. while not authenticated, try to access a protected resource - * 2. respond to the login challenge with good credentials - * 3. after successful login, follow the redirect to the original page - * 4. repeatedly access the protected resource to demonstrate - * persistence of the authenticated session + * Choreograph the steps of the test dialogue with the server 1. while not authenticated, try to access a protected + * resource 2. respond to the login challenge with good credentials 3. after successful login, follow the redirect + * to the original page 4. repeatedly access the protected resource to demonstrate persistence of the authenticated + * session * * @param resourceMethod HTTP method for accessing the protected resource + * * @param redirectMethod HTTP method for the login FORM reply + * * @param useContinue whether the HTTP client should expect a 100 Continue + * * @param clientShouldUseCookies whether the client should send cookies + * * @param serverWillUseCookies whether the server should send cookies * */ - private String doTest(String resourceMethod, String redirectMethod, - boolean useContinue, boolean clientShouldUseCookies, - boolean serverWillUseCookies, boolean serverWillChangeSessid) + private String doTest(String resourceMethod, String redirectMethod, boolean useContinue, + boolean clientShouldUseCookies, boolean serverWillUseCookies, boolean serverWillChangeSessid) throws Exception { - return doTest(resourceMethod, redirectMethod, useContinue, - clientShouldUseCookies, serverWillUseCookies, + return doTest(resourceMethod, redirectMethod, useContinue, clientShouldUseCookies, serverWillUseCookies, serverWillChangeSessid, true); } - private String doTest(String resourceMethod, String redirectMethod, - boolean useContinue, boolean clientShouldUseCookies, - boolean serverWillUseCookies, boolean serverWillChangeSessid, + private String doTest(String resourceMethod, String redirectMethod, boolean useContinue, + boolean clientShouldUseCookies, boolean serverWillUseCookies, boolean serverWillChangeSessid, boolean clientShouldUseHttp11) throws Exception { - client = new FormAuthClient(clientShouldUseCookies, - clientShouldUseHttp11, serverWillUseCookies, + client = new FormAuthClient(clientShouldUseCookies, clientShouldUseHttp11, serverWillUseCookies, serverWillChangeSessid); // First request for protected resource gets the login page @@ -291,9 +272,7 @@ client.doResourceRequest(resourceMethod, false, null, null); Assert.assertTrue(client.isResponse200()); Assert.assertTrue(client.isResponseBodyOK()); - String loginUri = client.extractBodyUri( - FormAuthClient.LOGIN_PARAM_TAG, - FormAuthClient.LOGIN_RESOURCE); + String loginUri = client.extractBodyUri(FormAuthClient.LOGIN_PARAM_TAG, FormAuthClient.LOGIN_RESOURCE); String originalSessionId = null; if (serverWillUseCookies && clientShouldUseCookies) { originalSessionId = client.getSessionId(); @@ -306,11 +285,9 @@ client.setUseContinue(useContinue); client.doLoginRequest(loginUri); if (clientShouldUseHttp11) { - Assert.assertTrue("login failed " + client.getResponseLine(), - client.isResponse303()); + Assert.assertTrue("login failed " + client.getResponseLine(), client.isResponse303()); } else { - Assert.assertTrue("login failed " + client.getResponseLine(), - client.isResponse302()); + Assert.assertTrue("login failed " + client.getResponseLine(), client.isResponse302()); } Assert.assertTrue(client.isResponseBodyOK()); String redirectUri = client.getRedirectUri(); @@ -319,14 +296,13 @@ // Third request - the login was successful so // follow the redirect to the protected resource client.doResourceRequest(redirectMethod, true, redirectUri, null); - if ("POST".equals(redirectMethod)) { + if (Method.POST.equals(redirectMethod)) { client.setUseContinue(useContinue); } Assert.assertTrue(client.isResponse200()); Assert.assertTrue(client.isResponseBodyOK()); - String protectedUri = client.extractBodyUri( - FormAuthClient.RESOURCE_PARAM_TAG, - FormAuthClient.PROTECTED_RESOURCE); + String protectedUri = + client.extractBodyUri(FormAuthClient.RESOURCE_PARAM_TAG, FormAuthClient.PROTECTED_RESOURCE); String newSessionId = null; if (serverWillUseCookies && clientShouldUseCookies) { newSessionId = client.getSessionId(); @@ -338,30 +314,28 @@ client.reset(); // Subsequent requests - keep accessing the protected resource - doTestProtected(resourceMethod, protectedUri, useContinue, - FormAuthClient.LOGIN_SUCCESSFUL, 5); + doTestProtected(resourceMethod, protectedUri, useContinue, FormAuthClient.LOGIN_SUCCESSFUL, 5); - return protectedUri; // in case more requests will be issued + return protectedUri; // in case more requests will be issued } /* - * Repeatedly access the protected resource after the client has - * successfully logged-in to the webapp. The current session attributes - * will be used and cannot be changed. - * 3. after successful login, follow the redirect to the original page - * 4. repeatedly access the protected resource to demonstrate - * persistence of the authenticated session + * Repeatedly access the protected resource after the client has successfully logged-in to the webapp. The current + * session attributes will be used and cannot be changed. * * @param resourceMethod HTTP method for accessing the protected resource + * * @param protectedUri to access (with or without sessionid) + * * @param useContinue whether the HTTP client should expect a 100 Continue + * * @param clientShouldUseCookies whether the client should send cookies + * * @param serverWillUseCookies whether the server should send cookies * */ - private void doTestProtected(String resourceMethod, String protectedUri, - boolean useContinue, int phase, int repeatCount) - throws Exception { + private void doTestProtected(String resourceMethod, String protectedUri, boolean useContinue, int phase, + int repeatCount) throws Exception { // Subsequent requests - keep accessing the protected resource for (int i = 0; i < repeatCount; i++) { @@ -374,21 +348,18 @@ } /* - * Encapsulate the logic needed to run a suitably-configured tomcat - * instance, send it an HTTP request and process the server response + * Encapsulate the logic needed to run a suitably-configured tomcat instance, send it an HTTP request and process + * the server response */ private abstract static class FormAuthClientBase extends SimpleHttpClient { protected static final String LOGIN_PARAM_TAG = "action="; protected static final String LOGIN_RESOURCE = "j_security_check"; - protected static final String LOGIN_REPLY = - "j_username=tomcat&j_password=tomcat"; + protected static final String LOGIN_REPLY = "j_username=tomcat&j_password=tomcat"; - protected static final String PROTECTED_RELATIVE_PATH = - "/examples/jsp/security/protected/"; + protected static final String PROTECTED_RELATIVE_PATH = "/examples/jsp/security/protected/"; protected static final String PROTECTED_RESOURCE = "index.jsp"; - private static final String PROTECTED_RESOURCE_URL = - PROTECTED_RELATIVE_PATH + PROTECTED_RESOURCE; + private static final String PROTECTED_RESOURCE_URL = PROTECTED_RELATIVE_PATH + PROTECTED_RESOURCE; protected static final String RESOURCE_PARAM_TAG = "href="; private static final char PARAM_DELIM = '?'; @@ -399,28 +370,24 @@ private int requestCount = 0; // todo: forgot this change and making it up again! - protected final String SESSION_PARAMETER_START = - SESSION_PARAMETER_NAME + "="; + protected final String SESSION_PARAMETER_START = SESSION_PARAMETER_NAME + "="; protected boolean clientShouldUseHttp11; protected void doLoginRequest(String loginUri) throws Exception { - doResourceRequest("POST", true, - PROTECTED_RELATIVE_PATH + loginUri, LOGIN_REPLY); + doResourceRequest(Method.POST, true, PROTECTED_RELATIVE_PATH + loginUri, LOGIN_REPLY); } /* - * Prepare the resource request HTTP headers and issue the request. - * Three kinds of uri are supported: - * 1. fully qualified uri. - * 2. minimal uri without webapp path. - * 3. null - use the default protected resource - * Cookies are sent if available and supported by the test. Otherwise, the - * caller is expected to have provided a session id as a path parameter. + * Prepare the resource request HTTP headers and issue the request. Three kinds of uri are supported: 1. fully + * qualified uri. 2. minimal uri without webapp path. 3. null - use the default protected resource + * + * Cookies are sent if available and supported by the test. Otherwise, the caller is expected to have provided a + * session id as a path parameter. */ - protected void doResourceRequest(String method, boolean isFullQualUri, - String resourceUri, String requestTail) throws Exception { + protected void doResourceRequest(String method, boolean isFullQualUri, String resourceUri, String requestTail) + throws Exception { // build the HTTP request while assembling the uri StringBuilder requestHead = new StringBuilder(128); @@ -432,10 +399,9 @@ // the default relative url requestHead.append(PROTECTED_RESOURCE_URL); } else { - requestHead.append(PROTECTED_RELATIVE_PATH) - .append(resourceUri); + requestHead.append(PROTECTED_RELATIVE_PATH).append(resourceUri); } - if ("GET".equals(method)) { + if (Method.GET.equals(method)) { requestHead.append("?role=bar"); } } @@ -456,14 +422,13 @@ if (getUseCookies()) { String sessionId = getSessionId(); if (sessionId != null) { - requestHead.append("Cookie: ") - .append(SESSION_COOKIE_NAME) - .append('=').append(sessionId).append(CRLF); + requestHead.append("Cookie: ").append(SESSION_COOKIE_NAME).append('=').append(sessionId) + .append(CRLF); } } // finally, for posts only, deal with the request content - if ("POST".equals(method)) { + if (Method.POST.equals(method)) { if (requestTail == null) { requestTail = "role=bar"; } @@ -491,8 +456,7 @@ } /* - * verify the server response HTML body is the page we expect, - * based on the dialogue position within doTest. + * verify the server response HTML body is the page we expect, based on the dialogue position within doTest. */ @Override public boolean isResponseBodyOK() { @@ -500,15 +464,14 @@ } /* - * verify the server response HTML body is the page we expect, - * based on the dialogue position given by the caller. + * verify the server response HTML body is the page we expect, based on the dialogue position given by the + * caller. */ public boolean isResponseBodyOK(int testPhase) { switch (testPhase) { case LOGIN_REQUIRED: // First request should return in the login page - assertContains(getResponseBody(), - "Login Page for Examples"); + assertContains(getResponseBody(), "Login Page for Examples"); return true; case REDIRECTING: // Second request should result in redirect without a body @@ -517,17 +480,14 @@ // Subsequent requests should return in the protected page. // Our role parameter should be appear in the page. String body = getResponseBody(); - assertContains(body, - "Protected Page for Examples"); - assertContains(body, - "Protected Page for Examples"); + assertContains(body, " -1) { iStart += SESSION_PARAMETER_START.length(); String remainder = url.substring(iStart); - StringTokenizer parser = new StringTokenizer(remainder, - SESSION_PATH_PARAMETER_TAILS); + StringTokenizer parser = new StringTokenizer(remainder, SESSION_PATH_PARAMETER_TAILS); if (parser.hasMoreElements()) { sessionId = parser.nextToken(); } else { @@ -576,27 +535,22 @@ private void assertContains(String body, String expected) { if (!body.contains(expected)) { - Assert.fail("Response number " + requestCount - + ": body check failure.\n" - + "Expected to contain substring: [" + expected - + "]\nActual: [" + body + "]"); + Assert.fail("Response number " + requestCount + ": body check failure.\n" + + "Expected to contain substring: [" + expected + "]\nActual: [" + body + "]"); } } } private class FormAuthClient extends FormAuthClientBase { - private FormAuthClient(boolean clientShouldUseCookies, - boolean clientShouldUseHttp11, - boolean serverShouldUseCookies, - boolean serverShouldChangeSessid) throws Exception { + private FormAuthClient(boolean clientShouldUseCookies, boolean clientShouldUseHttp11, + boolean serverShouldUseCookies, boolean serverShouldChangeSessid) throws Exception { this.clientShouldUseHttp11 = clientShouldUseHttp11; Tomcat tomcat = getTomcatInstance(); File appDir = new File(System.getProperty("tomcat.test.basedir"), "webapps/examples"); - Context ctx = tomcat.addWebapp(null, "/examples", - appDir.getAbsolutePath()); + Context ctx = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); setUseCookies(clientShouldUseCookies); ctx.setCookies(serverShouldUseCookies); ctx.addApplicationListener(WsContextListener.class.getName()); @@ -612,9 +566,7 @@ Valve[] valves = ctx.getPipeline().getValves(); for (Valve valve : valves) { if (valve instanceof AuthenticatorBase) { - ((AuthenticatorBase)valve) - .setChangeSessionIdOnAuthentication( - serverShouldChangeSessid); + ((AuthenticatorBase) valve).setChangeSessionIdOnAuthentication(serverShouldChangeSessid); break; } } @@ -626,41 +578,33 @@ /** - * Encapsulate the logic needed to run a suitably-configured Tomcat - * instance, send it an HTTP request and process the server response when - * the protected resource is only protected for some HTTP methods. The use - * case of particular interest is when GET and POST are not protected since - * those are the methods used by the login form and the redirect and if - * those methods are not protected the authenticator may not process the - * associated requests. + * Encapsulate the logic needed to run a suitably-configured Tomcat instance, send it an HTTP request and process + * the server response when the protected resource is only protected for some HTTP methods. The use case of + * particular interest is when GET and POST are not protected since those are the methods used by the login form and + * the redirect and if those methods are not protected the authenticator may not process the associated requests. */ private class FormAuthClientSelectedMethods extends FormAuthClientBase { - private FormAuthClientSelectedMethods(boolean clientShouldUseCookies, - boolean clientShouldUseHttp11, - boolean serverShouldUseCookies, - boolean serverShouldChangeSessid) throws Exception { + private FormAuthClientSelectedMethods(boolean clientShouldUseCookies, boolean clientShouldUseHttp11, + boolean serverShouldUseCookies, boolean serverShouldChangeSessid) throws Exception { this.clientShouldUseHttp11 = clientShouldUseHttp11; Tomcat tomcat = getTomcatInstance(); - Context ctx = tomcat.addContext( - "", System.getProperty("java.io.tmpdir")); - Tomcat.addServlet(ctx, "SelectedMethods", - new SelectedMethodsServlet()); + Context ctx = tomcat.addContext("", System.getProperty("java.io.tmpdir")); + Tomcat.addServlet(ctx, "SelectedMethods", new SelectedMethodsServlet()); ctx.addServletMappingDecoded("/test", "SelectedMethods"); // Login servlet just needs to respond "OK". Client will handle // creating a valid response. No need for a form. - Tomcat.addServlet(ctx, "Login", - new TesterServlet()); + Tomcat.addServlet(ctx, "Login", new TesterServlet()); ctx.addServletMappingDecoded("/login", "Login"); // Configure the security constraints SecurityConstraint constraint = new SecurityConstraint(); SecurityCollection collection = new SecurityCollection(); collection.setName("Protect PUT"); - collection.addMethod("PUT"); + collection.addMethod(Method.PUT); collection.addPatternDecoded("/test"); constraint.addCollection(collection); constraint.addAuthRole("tomcat"); @@ -687,9 +631,7 @@ Valve[] valves = ctx.getPipeline().getValves(); for (Valve valve : valves) { if (valve instanceof AuthenticatorBase) { - ((AuthenticatorBase)valve) - .setChangeSessionIdOnAuthentication( - serverShouldChangeSessid); + ((AuthenticatorBase) valve).setChangeSessionIdOnAuthentication(serverShouldChangeSessid); break; } } @@ -717,12 +659,10 @@ public static final String VALUE = "TestValue"; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain;charset=UTF-8"); - if (VALUE.equals(req.getParameter(PARAM)) && - req.isUserInRole("tomcat")) { + if (VALUE.equals(req.getParameter(PARAM)) && req.isUserInRole("tomcat")) { resp.getWriter().print("OK"); } else { resp.getWriter().print("FAIL"); @@ -730,15 +670,13 @@ } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Same as GET for this test case doGet(req, resp); } @Override - protected void doPut(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Same as GET for this test case doGet(req, resp); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestFormAuthenticatorB.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestFormAuthenticatorB.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestFormAuthenticatorB.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestFormAuthenticatorB.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,6 +29,7 @@ import org.apache.catalina.startup.TesterMapRealm; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.websocket.server.WsContextListener; /* @@ -89,15 +90,13 @@ @Test public void testPostNoContinueWithCookies() throws Exception { - doTest("POST", "GET", NO_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.POST, Method.GET, NO_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } // Bug 49779 @Test public void testPostNoContinuePostRedirectWithCookies() throws Exception { - doTest("POST", "POST", NO_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.POST, Method.POST, NO_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } @@ -106,76 +105,64 @@ @Test public void testPostNoContinueNoServerCookies() throws Exception { - doTest("POST", "GET", NO_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.POST, Method.GET, NO_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); } // variant of Bug 49779 @Test - public void testPostNoContinuePostRedirectNoServerCookies() - throws Exception { - doTest("POST", "POST", NO_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); + public void testPostNoContinuePostRedirectNoServerCookies() throws Exception { + doTest(Method.POST, Method.POST, NO_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); } - - // next, a set of tests where the server Context uses cookies, // but the client refuses to return them and tries to use // the session ID if carried as a url path parameter @Test public void testPostNoContinueNoClientCookies() throws Exception { - doTest("POST", "GET", NO_100_CONTINUE, - CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.POST, Method.GET, NO_100_CONTINUE, CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } // variant of Bug 49779 @Test - public void testPostNoContinuePostRedirectNoClientCookies() - throws Exception { - doTest("POST", "POST", NO_100_CONTINUE, - CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + public void testPostNoContinuePostRedirectNoClientCookies() throws Exception { + doTest(Method.POST, Method.POST, NO_100_CONTINUE, CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } - - // finally, a set of tests to explore quirky situations // but there is not need to replicate all the scenarios above. /* - * Choreograph the steps of the test dialogue with the server - * 1. while not authenticated, try to access a protected resource - * 2. respond to the login challenge with good credentials - * 3. after successful login, follow the redirect to the original page - * 4. repeatedly access the protected resource to demonstrate - * persistence of the authenticated session + * Choreograph the steps of the test dialogue with the server 1. while not authenticated, try to access a protected + * resource 2. respond to the login challenge with good credentials 3. after successful login, follow the redirect + * to the original page 4. repeatedly access the protected resource to demonstrate persistence of the authenticated + * session * * @param resourceMethod HTTP method for accessing the protected resource + * * @param redirectMethod HTTP method for the login FORM reply + * * @param useContinue whether the HTTP client should expect a 100 Continue + * * @param clientShouldUseCookies whether the client should send cookies + * * @param serverWillUseCookies whether the server should send cookies * */ - private String doTest(String resourceMethod, String redirectMethod, - boolean useContinue, boolean clientShouldUseCookies, - boolean serverWillUseCookies, boolean serverWillChangeSessid) + private String doTest(String resourceMethod, String redirectMethod, boolean useContinue, + boolean clientShouldUseCookies, boolean serverWillUseCookies, boolean serverWillChangeSessid) throws Exception { - return doTest(resourceMethod, redirectMethod, useContinue, - clientShouldUseCookies, serverWillUseCookies, + return doTest(resourceMethod, redirectMethod, useContinue, clientShouldUseCookies, serverWillUseCookies, serverWillChangeSessid, true); } - private String doTest(String resourceMethod, String redirectMethod, - boolean useContinue, boolean clientShouldUseCookies, - boolean serverWillUseCookies, boolean serverWillChangeSessid, + private String doTest(String resourceMethod, String redirectMethod, boolean useContinue, + boolean clientShouldUseCookies, boolean serverWillUseCookies, boolean serverWillChangeSessid, boolean clientShouldUseHttp11) throws Exception { - client = new FormAuthClient(clientShouldUseCookies, - clientShouldUseHttp11, serverWillUseCookies, + client = new FormAuthClient(clientShouldUseCookies, clientShouldUseHttp11, serverWillUseCookies, serverWillChangeSessid); // First request for protected resource gets the login page @@ -183,9 +170,7 @@ client.doResourceRequest(resourceMethod, false, null, null); Assert.assertTrue(client.isResponse200()); Assert.assertTrue(client.isResponseBodyOK()); - String loginUri = client.extractBodyUri( - FormAuthClient.LOGIN_PARAM_TAG, - FormAuthClient.LOGIN_RESOURCE); + String loginUri = client.extractBodyUri(FormAuthClient.LOGIN_PARAM_TAG, FormAuthClient.LOGIN_RESOURCE); String originalSessionId = null; if (serverWillUseCookies && clientShouldUseCookies) { originalSessionId = client.getSessionId(); @@ -198,11 +183,9 @@ client.setUseContinue(useContinue); client.doLoginRequest(loginUri); if (clientShouldUseHttp11) { - Assert.assertTrue("login failed " + client.getResponseLine(), - client.isResponse303()); + Assert.assertTrue("login failed " + client.getResponseLine(), client.isResponse303()); } else { - Assert.assertTrue("login failed " + client.getResponseLine(), - client.isResponse302()); + Assert.assertTrue("login failed " + client.getResponseLine(), client.isResponse302()); } Assert.assertTrue(client.isResponseBodyOK()); String redirectUri = client.getRedirectUri(); @@ -211,14 +194,13 @@ // Third request - the login was successful so // follow the redirect to the protected resource client.doResourceRequest(redirectMethod, true, redirectUri, null); - if ("POST".equals(redirectMethod)) { + if (Method.POST.equals(redirectMethod)) { client.setUseContinue(useContinue); } Assert.assertTrue(client.isResponse200()); Assert.assertTrue(client.isResponseBodyOK()); - String protectedUri = client.extractBodyUri( - FormAuthClient.RESOURCE_PARAM_TAG, - FormAuthClient.PROTECTED_RESOURCE); + String protectedUri = + client.extractBodyUri(FormAuthClient.RESOURCE_PARAM_TAG, FormAuthClient.PROTECTED_RESOURCE); String newSessionId = null; if (serverWillUseCookies && clientShouldUseCookies) { newSessionId = client.getSessionId(); @@ -230,30 +212,28 @@ client.reset(); // Subsequent requests - keep accessing the protected resource - doTestProtected(resourceMethod, protectedUri, useContinue, - FormAuthClient.LOGIN_SUCCESSFUL, 5); + doTestProtected(resourceMethod, protectedUri, useContinue, FormAuthClient.LOGIN_SUCCESSFUL, 5); - return protectedUri; // in case more requests will be issued + return protectedUri; // in case more requests will be issued } /* - * Repeatedly access the protected resource after the client has - * successfully logged-in to the webapp. The current session attributes - * will be used and cannot be changed. - * 3. after successful login, follow the redirect to the original page - * 4. repeatedly access the protected resource to demonstrate - * persistence of the authenticated session + * Repeatedly access the protected resource after the client has successfully logged-in to the webapp. The current + * session attributes will be used and cannot be changed. * * @param resourceMethod HTTP method for accessing the protected resource + * * @param protectedUri to access (with or without sessionid) + * * @param useContinue whether the HTTP client should expect a 100 Continue + * * @param clientShouldUseCookies whether the client should send cookies + * * @param serverWillUseCookies whether the server should send cookies * */ - private void doTestProtected(String resourceMethod, String protectedUri, - boolean useContinue, int phase, int repeatCount) - throws Exception { + private void doTestProtected(String resourceMethod, String protectedUri, boolean useContinue, int phase, + int repeatCount) throws Exception { // Subsequent requests - keep accessing the protected resource for (int i = 0; i < repeatCount; i++) { @@ -266,21 +246,18 @@ } /* - * Encapsulate the logic needed to run a suitably-configured tomcat - * instance, send it an HTTP request and process the server response + * Encapsulate the logic needed to run a suitably-configured tomcat instance, send it an HTTP request and process + * the server response */ private abstract static class FormAuthClientBase extends SimpleHttpClient { protected static final String LOGIN_PARAM_TAG = "action="; protected static final String LOGIN_RESOURCE = "j_security_check"; - protected static final String LOGIN_REPLY = - "j_username=tomcat&j_password=tomcat"; + protected static final String LOGIN_REPLY = "j_username=tomcat&j_password=tomcat"; - protected static final String PROTECTED_RELATIVE_PATH = - "/examples/jsp/security/protected/"; + protected static final String PROTECTED_RELATIVE_PATH = "/examples/jsp/security/protected/"; protected static final String PROTECTED_RESOURCE = "index.jsp"; - private static final String PROTECTED_RESOURCE_URL = - PROTECTED_RELATIVE_PATH + PROTECTED_RESOURCE; + private static final String PROTECTED_RESOURCE_URL = PROTECTED_RELATIVE_PATH + PROTECTED_RESOURCE; protected static final String RESOURCE_PARAM_TAG = "href="; private static final char PARAM_DELIM = '?'; @@ -291,28 +268,24 @@ private int requestCount = 0; // todo: forgot this change and making it up again! - protected final String SESSION_PARAMETER_START = - SESSION_PARAMETER_NAME + "="; + protected final String SESSION_PARAMETER_START = SESSION_PARAMETER_NAME + "="; protected boolean clientShouldUseHttp11; protected void doLoginRequest(String loginUri) throws Exception { - doResourceRequest("POST", true, - PROTECTED_RELATIVE_PATH + loginUri, LOGIN_REPLY); + doResourceRequest(Method.POST, true, PROTECTED_RELATIVE_PATH + loginUri, LOGIN_REPLY); } /* - * Prepare the resource request HTTP headers and issue the request. - * Three kinds of uri are supported: - * 1. fully qualified uri. - * 2. minimal uri without webapp path. - * 3. null - use the default protected resource - * Cookies are sent if available and supported by the test. Otherwise, the - * caller is expected to have provided a session id as a path parameter. + * Prepare the resource request HTTP headers and issue the request. Three kinds of uri are supported: 1. fully + * qualified uri. 2. minimal uri without webapp path. 3. null - use the default protected resource + * + * Cookies are sent if available and supported by the test. Otherwise, the caller is expected to have provided a + * session id as a path parameter. */ - protected void doResourceRequest(String method, boolean isFullQualUri, - String resourceUri, String requestTail) throws Exception { + protected void doResourceRequest(String method, boolean isFullQualUri, String resourceUri, String requestTail) + throws Exception { // build the HTTP request while assembling the uri StringBuilder requestHead = new StringBuilder(128); @@ -324,10 +297,9 @@ // the default relative url requestHead.append(PROTECTED_RESOURCE_URL); } else { - requestHead.append(PROTECTED_RELATIVE_PATH) - .append(resourceUri); + requestHead.append(PROTECTED_RELATIVE_PATH).append(resourceUri); } - if ("GET".equals(method)) { + if (Method.GET.equals(method)) { requestHead.append("?role=bar"); } } @@ -348,14 +320,13 @@ if (getUseCookies()) { String sessionId = getSessionId(); if (sessionId != null) { - requestHead.append("Cookie: ") - .append(SESSION_COOKIE_NAME) - .append('=').append(sessionId).append(CRLF); + requestHead.append("Cookie: ").append(SESSION_COOKIE_NAME).append('=').append(sessionId) + .append(CRLF); } } // finally, for posts only, deal with the request content - if ("POST".equals(method)) { + if (Method.POST.equals(method)) { if (requestTail == null) { requestTail = "role=bar"; } @@ -383,8 +354,7 @@ } /* - * verify the server response HTML body is the page we expect, - * based on the dialogue position within doTest. + * verify the server response HTML body is the page we expect, based on the dialogue position within doTest. */ @Override public boolean isResponseBodyOK() { @@ -392,15 +362,14 @@ } /* - * verify the server response HTML body is the page we expect, - * based on the dialogue position given by the caller. + * verify the server response HTML body is the page we expect, based on the dialogue position given by the + * caller. */ public boolean isResponseBodyOK(int testPhase) { switch (testPhase) { case LOGIN_REQUIRED: // First request should return in the login page - assertContains(getResponseBody(), - "Login Page for Examples"); + assertContains(getResponseBody(), "Login Page for Examples"); return true; case REDIRECTING: // Second request should result in redirect without a body @@ -409,17 +378,14 @@ // Subsequent requests should return in the protected page. // Our role parameter should be appear in the page. String body = getResponseBody(); - assertContains(body, - "Protected Page for Examples"); - assertContains(body, - "Protected Page for Examples"); + assertContains(body, " -1) { iStart += SESSION_PARAMETER_START.length(); String remainder = url.substring(iStart); - StringTokenizer parser = new StringTokenizer(remainder, - SESSION_PATH_PARAMETER_TAILS); + StringTokenizer parser = new StringTokenizer(remainder, SESSION_PATH_PARAMETER_TAILS); if (parser.hasMoreElements()) { sessionId = parser.nextToken(); } else { @@ -468,27 +433,22 @@ private void assertContains(String body, String expected) { if (!body.contains(expected)) { - Assert.fail("Response number " + requestCount - + ": body check failure.\n" - + "Expected to contain substring: [" + expected - + "]\nActual: [" + body + "]"); + Assert.fail("Response number " + requestCount + ": body check failure.\n" + + "Expected to contain substring: [" + expected + "]\nActual: [" + body + "]"); } } } private class FormAuthClient extends FormAuthClientBase { - private FormAuthClient(boolean clientShouldUseCookies, - boolean clientShouldUseHttp11, - boolean serverShouldUseCookies, - boolean serverShouldChangeSessid) throws Exception { + private FormAuthClient(boolean clientShouldUseCookies, boolean clientShouldUseHttp11, + boolean serverShouldUseCookies, boolean serverShouldChangeSessid) throws Exception { this.clientShouldUseHttp11 = clientShouldUseHttp11; Tomcat tomcat = getTomcatInstance(); File appDir = new File(System.getProperty("tomcat.test.basedir"), "webapps/examples"); - Context ctx = tomcat.addWebapp(null, "/examples", - appDir.getAbsolutePath()); + Context ctx = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); setUseCookies(clientShouldUseCookies); ctx.setCookies(serverShouldUseCookies); ctx.addApplicationListener(WsContextListener.class.getName()); @@ -504,9 +464,7 @@ Valve[] valves = ctx.getPipeline().getValves(); for (Valve valve : valves) { if (valve instanceof AuthenticatorBase) { - ((AuthenticatorBase)valve) - .setChangeSessionIdOnAuthentication( - serverShouldChangeSessid); + ((AuthenticatorBase) valve).setChangeSessionIdOnAuthentication(serverShouldChangeSessid); break; } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestFormAuthenticatorC.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestFormAuthenticatorC.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestFormAuthenticatorC.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestFormAuthenticatorC.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,6 +29,7 @@ import org.apache.catalina.startup.TesterMapRealm; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.websocket.server.WsContextListener; /* @@ -89,15 +90,13 @@ @Test public void testPostWithContinueAndCookies() throws Exception { - doTest("POST", "GET", USE_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.POST, Method.GET, USE_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } // Bug 49779 @Test public void testPostWithContinuePostRedirectWithCookies() throws Exception { - doTest("POST", "POST", USE_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.POST, Method.POST, USE_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } @@ -106,16 +105,13 @@ @Test public void testPostWithContinueNoServerCookies() throws Exception { - doTest("POST", "GET", USE_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.POST, Method.GET, USE_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); } // variant of Bug 49779 @Test - public void testPostWithContinuePostRedirectNoServerCookies() - throws Exception { - doTest("POST", "POST", USE_100_CONTINUE, - CLIENT_USE_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); + public void testPostWithContinuePostRedirectNoServerCookies() throws Exception { + doTest(Method.POST, Method.POST, USE_100_CONTINUE, CLIENT_USE_COOKIES, SERVER_NO_COOKIES, SERVER_CHANGE_SESSID); } @@ -125,22 +121,18 @@ @Test public void testGetNoClientCookies() throws Exception { - doTest("GET", "GET", NO_100_CONTINUE, - CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.GET, Method.GET, NO_100_CONTINUE, CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } @Test public void testPostWithContinueNoClientCookies() throws Exception { - doTest("POST", "GET", USE_100_CONTINUE, - CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + doTest(Method.POST, Method.GET, USE_100_CONTINUE, CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } // variant of Bug 49779 @Test - public void testPostWithContinuePostRedirectNoClientCookies() - throws Exception { - doTest("POST", "POST", USE_100_CONTINUE, - CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); + public void testPostWithContinuePostRedirectNoClientCookies() throws Exception { + doTest(Method.POST, Method.POST, USE_100_CONTINUE, CLIENT_NO_COOKIES, SERVER_USE_COOKIES, SERVER_CHANGE_SESSID); } @@ -148,36 +140,34 @@ // but there is not need to replicate all the scenarios above. /* - * Choreograph the steps of the test dialogue with the server - * 1. while not authenticated, try to access a protected resource - * 2. respond to the login challenge with good credentials - * 3. after successful login, follow the redirect to the original page - * 4. repeatedly access the protected resource to demonstrate - * persistence of the authenticated session + * Choreograph the steps of the test dialogue with the server 1. while not authenticated, try to access a protected + * resource 2. respond to the login challenge with good credentials 3. after successful login, follow the redirect + * to the original page 4. repeatedly access the protected resource to demonstrate persistence of the authenticated + * session * * @param resourceMethod HTTP method for accessing the protected resource + * * @param redirectMethod HTTP method for the login FORM reply + * * @param useContinue whether the HTTP client should expect a 100 Continue + * * @param clientShouldUseCookies whether the client should send cookies + * * @param serverWillUseCookies whether the server should send cookies * */ - private String doTest(String resourceMethod, String redirectMethod, - boolean useContinue, boolean clientShouldUseCookies, - boolean serverWillUseCookies, boolean serverWillChangeSessid) + private String doTest(String resourceMethod, String redirectMethod, boolean useContinue, + boolean clientShouldUseCookies, boolean serverWillUseCookies, boolean serverWillChangeSessid) throws Exception { - return doTest(resourceMethod, redirectMethod, useContinue, - clientShouldUseCookies, serverWillUseCookies, + return doTest(resourceMethod, redirectMethod, useContinue, clientShouldUseCookies, serverWillUseCookies, serverWillChangeSessid, true); } - private String doTest(String resourceMethod, String redirectMethod, - boolean useContinue, boolean clientShouldUseCookies, - boolean serverWillUseCookies, boolean serverWillChangeSessid, + private String doTest(String resourceMethod, String redirectMethod, boolean useContinue, + boolean clientShouldUseCookies, boolean serverWillUseCookies, boolean serverWillChangeSessid, boolean clientShouldUseHttp11) throws Exception { - client = new FormAuthClient(clientShouldUseCookies, - clientShouldUseHttp11, serverWillUseCookies, + client = new FormAuthClient(clientShouldUseCookies, clientShouldUseHttp11, serverWillUseCookies, serverWillChangeSessid); // First request for protected resource gets the login page @@ -185,9 +175,7 @@ client.doResourceRequest(resourceMethod, false, null, null); Assert.assertTrue(client.isResponse200()); Assert.assertTrue(client.isResponseBodyOK()); - String loginUri = client.extractBodyUri( - FormAuthClient.LOGIN_PARAM_TAG, - FormAuthClient.LOGIN_RESOURCE); + String loginUri = client.extractBodyUri(FormAuthClient.LOGIN_PARAM_TAG, FormAuthClient.LOGIN_RESOURCE); String originalSessionId = null; if (serverWillUseCookies && clientShouldUseCookies) { originalSessionId = client.getSessionId(); @@ -200,11 +188,9 @@ client.setUseContinue(useContinue); client.doLoginRequest(loginUri); if (clientShouldUseHttp11) { - Assert.assertTrue("login failed " + client.getResponseLine(), - client.isResponse303()); + Assert.assertTrue("login failed " + client.getResponseLine(), client.isResponse303()); } else { - Assert.assertTrue("login failed " + client.getResponseLine(), - client.isResponse302()); + Assert.assertTrue("login failed " + client.getResponseLine(), client.isResponse302()); } Assert.assertTrue(client.isResponseBodyOK()); String redirectUri = client.getRedirectUri(); @@ -213,14 +199,13 @@ // Third request - the login was successful so // follow the redirect to the protected resource client.doResourceRequest(redirectMethod, true, redirectUri, null); - if ("POST".equals(redirectMethod)) { + if (Method.POST.equals(redirectMethod)) { client.setUseContinue(useContinue); } Assert.assertTrue(client.isResponse200()); Assert.assertTrue(client.isResponseBodyOK()); - String protectedUri = client.extractBodyUri( - FormAuthClient.RESOURCE_PARAM_TAG, - FormAuthClient.PROTECTED_RESOURCE); + String protectedUri = + client.extractBodyUri(FormAuthClient.RESOURCE_PARAM_TAG, FormAuthClient.PROTECTED_RESOURCE); String newSessionId = null; if (serverWillUseCookies && clientShouldUseCookies) { newSessionId = client.getSessionId(); @@ -232,30 +217,28 @@ client.reset(); // Subsequent requests - keep accessing the protected resource - doTestProtected(resourceMethod, protectedUri, useContinue, - FormAuthClient.LOGIN_SUCCESSFUL, 5); + doTestProtected(resourceMethod, protectedUri, useContinue, FormAuthClient.LOGIN_SUCCESSFUL, 5); - return protectedUri; // in case more requests will be issued + return protectedUri; // in case more requests will be issued } /* - * Repeatedly access the protected resource after the client has - * successfully logged-in to the webapp. The current session attributes - * will be used and cannot be changed. - * 3. after successful login, follow the redirect to the original page - * 4. repeatedly access the protected resource to demonstrate - * persistence of the authenticated session + * Repeatedly access the protected resource after the client has successfully logged-in to the webapp. The current + * session attributes will be used and cannot be changed. * * @param resourceMethod HTTP method for accessing the protected resource + * * @param protectedUri to access (with or without sessionid) + * * @param useContinue whether the HTTP client should expect a 100 Continue + * * @param clientShouldUseCookies whether the client should send cookies + * * @param serverWillUseCookies whether the server should send cookies * */ - private void doTestProtected(String resourceMethod, String protectedUri, - boolean useContinue, int phase, int repeatCount) - throws Exception { + private void doTestProtected(String resourceMethod, String protectedUri, boolean useContinue, int phase, + int repeatCount) throws Exception { // Subsequent requests - keep accessing the protected resource for (int i = 0; i < repeatCount; i++) { @@ -268,21 +251,18 @@ } /* - * Encapsulate the logic needed to run a suitably-configured tomcat - * instance, send it an HTTP request and process the server response + * Encapsulate the logic needed to run a suitably-configured tomcat instance, send it an HTTP request and process + * the server response */ private abstract static class FormAuthClientBase extends SimpleHttpClient { protected static final String LOGIN_PARAM_TAG = "action="; protected static final String LOGIN_RESOURCE = "j_security_check"; - protected static final String LOGIN_REPLY = - "j_username=tomcat&j_password=tomcat"; + protected static final String LOGIN_REPLY = "j_username=tomcat&j_password=tomcat"; - protected static final String PROTECTED_RELATIVE_PATH = - "/examples/jsp/security/protected/"; + protected static final String PROTECTED_RELATIVE_PATH = "/examples/jsp/security/protected/"; protected static final String PROTECTED_RESOURCE = "index.jsp"; - private static final String PROTECTED_RESOURCE_URL = - PROTECTED_RELATIVE_PATH + PROTECTED_RESOURCE; + private static final String PROTECTED_RESOURCE_URL = PROTECTED_RELATIVE_PATH + PROTECTED_RESOURCE; protected static final String RESOURCE_PARAM_TAG = "href="; private static final char PARAM_DELIM = '?'; @@ -293,28 +273,24 @@ private int requestCount = 0; // todo: forgot this change and making it up again! - protected final String SESSION_PARAMETER_START = - SESSION_PARAMETER_NAME + "="; + protected final String SESSION_PARAMETER_START = SESSION_PARAMETER_NAME + "="; protected boolean clientShouldUseHttp11; protected void doLoginRequest(String loginUri) throws Exception { - doResourceRequest("POST", true, - PROTECTED_RELATIVE_PATH + loginUri, LOGIN_REPLY); + doResourceRequest(Method.POST, true, PROTECTED_RELATIVE_PATH + loginUri, LOGIN_REPLY); } /* - * Prepare the resource request HTTP headers and issue the request. - * Three kinds of uri are supported: - * 1. fully qualified uri. - * 2. minimal uri without webapp path. - * 3. null - use the default protected resource - * Cookies are sent if available and supported by the test. Otherwise, the - * caller is expected to have provided a session id as a path parameter. + * Prepare the resource request HTTP headers and issue the request. Three kinds of uri are supported: 1. fully + * qualified uri. 2. minimal uri without webapp path. 3. null - use the default protected resource + * + * Cookies are sent if available and supported by the test. Otherwise, the caller is expected to have provided a + * session id as a path parameter. */ - protected void doResourceRequest(String method, boolean isFullQualUri, - String resourceUri, String requestTail) throws Exception { + protected void doResourceRequest(String method, boolean isFullQualUri, String resourceUri, String requestTail) + throws Exception { // build the HTTP request while assembling the uri StringBuilder requestHead = new StringBuilder(128); @@ -326,10 +302,9 @@ // the default relative url requestHead.append(PROTECTED_RESOURCE_URL); } else { - requestHead.append(PROTECTED_RELATIVE_PATH) - .append(resourceUri); + requestHead.append(PROTECTED_RELATIVE_PATH).append(resourceUri); } - if ("GET".equals(method)) { + if (Method.GET.equals(method)) { requestHead.append("?role=bar"); } } @@ -350,14 +325,13 @@ if (getUseCookies()) { String sessionId = getSessionId(); if (sessionId != null) { - requestHead.append("Cookie: ") - .append(SESSION_COOKIE_NAME) - .append('=').append(sessionId).append(CRLF); + requestHead.append("Cookie: ").append(SESSION_COOKIE_NAME).append('=').append(sessionId) + .append(CRLF); } } // finally, for posts only, deal with the request content - if ("POST".equals(method)) { + if (Method.POST.equals(method)) { if (requestTail == null) { requestTail = "role=bar"; } @@ -385,8 +359,7 @@ } /* - * verify the server response HTML body is the page we expect, - * based on the dialogue position within doTest. + * verify the server response HTML body is the page we expect, based on the dialogue position within doTest. */ @Override public boolean isResponseBodyOK() { @@ -394,15 +367,14 @@ } /* - * verify the server response HTML body is the page we expect, - * based on the dialogue position given by the caller. + * verify the server response HTML body is the page we expect, based on the dialogue position given by the + * caller. */ public boolean isResponseBodyOK(int testPhase) { switch (testPhase) { case LOGIN_REQUIRED: // First request should return in the login page - assertContains(getResponseBody(), - "Login Page for Examples"); + assertContains(getResponseBody(), "Login Page for Examples"); return true; case REDIRECTING: // Second request should result in redirect without a body @@ -411,17 +383,14 @@ // Subsequent requests should return in the protected page. // Our role parameter should be appear in the page. String body = getResponseBody(); - assertContains(body, - "Protected Page for Examples"); - assertContains(body, - "Protected Page for Examples"); + assertContains(body, " -1) { iStart += SESSION_PARAMETER_START.length(); String remainder = url.substring(iStart); - StringTokenizer parser = new StringTokenizer(remainder, - SESSION_PATH_PARAMETER_TAILS); + StringTokenizer parser = new StringTokenizer(remainder, SESSION_PATH_PARAMETER_TAILS); if (parser.hasMoreElements()) { sessionId = parser.nextToken(); } else { @@ -470,27 +438,22 @@ private void assertContains(String body, String expected) { if (!body.contains(expected)) { - Assert.fail("Response number " + requestCount - + ": body check failure.\n" - + "Expected to contain substring: [" + expected - + "]\nActual: [" + body + "]"); + Assert.fail("Response number " + requestCount + ": body check failure.\n" + + "Expected to contain substring: [" + expected + "]\nActual: [" + body + "]"); } } } private class FormAuthClient extends FormAuthClientBase { - private FormAuthClient(boolean clientShouldUseCookies, - boolean clientShouldUseHttp11, - boolean serverShouldUseCookies, - boolean serverShouldChangeSessid) throws Exception { + private FormAuthClient(boolean clientShouldUseCookies, boolean clientShouldUseHttp11, + boolean serverShouldUseCookies, boolean serverShouldChangeSessid) throws Exception { this.clientShouldUseHttp11 = clientShouldUseHttp11; Tomcat tomcat = getTomcatInstance(); File appDir = new File(System.getProperty("tomcat.test.basedir"), "webapps/examples"); - Context ctx = tomcat.addWebapp(null, "/examples", - appDir.getAbsolutePath()); + Context ctx = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); setUseCookies(clientShouldUseCookies); ctx.setCookies(serverShouldUseCookies); ctx.addApplicationListener(WsContextListener.class.getName()); @@ -506,9 +469,7 @@ Valve[] valves = ctx.getPipeline().getValves(); for (Valve valve : valves) { if (valve instanceof AuthenticatorBase) { - ((AuthenticatorBase)valve) - .setChangeSessionIdOnAuthentication( - serverShouldChangeSessid); + ((AuthenticatorBase) valve).setChangeSessionIdOnAuthentication(serverShouldChangeSessid); break; } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestJaspicCallbackHandlerInAuthenticator.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestJaspicCallbackHandlerInAuthenticator.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestJaspicCallbackHandlerInAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestJaspicCallbackHandlerInAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -107,11 +107,9 @@ CallbackHandler callbackHandler = createCallbackHandler(null); Subject clientSubject = new Subject(); CallerPrincipalCallback cpc = new CallerPrincipalCallback(clientSubject, "name"); - GroupPrincipalCallback gpc1 = new GroupPrincipalCallback(clientSubject, - new String[] { "group1", "group2" }); + GroupPrincipalCallback gpc1 = new GroupPrincipalCallback(clientSubject, new String[] { "group1", "group2" }); callbackHandler.handle(new Callback[] { cpc, gpc1 }); - GroupPrincipalCallback gpc2 = new GroupPrincipalCallback(clientSubject, - new String[] { "group3", "group4" }); + GroupPrincipalCallback gpc2 = new GroupPrincipalCallback(clientSubject, new String[] { "group3", "group4" }); callbackHandler.handle(new Callback[] { cpc, gpc2 }); Set credentials = clientSubject.getPrivateCredentials(); Assert.assertTrue(credentials.size() == 1); @@ -132,12 +130,12 @@ container.setRealm(new TestRealm()); ((Contained) callbackHandler).setContainer(container); Subject clientSubject = new Subject(); - PasswordValidationCallback pvc1 = new PasswordValidationCallback(clientSubject, "name1", - "password".toCharArray()); + PasswordValidationCallback pvc1 = + new PasswordValidationCallback(clientSubject, "name1", "password".toCharArray()); callbackHandler.handle(new Callback[] { pvc1 }); Assert.assertTrue(pvc1.getResult()); - PasswordValidationCallback pvc2 = new PasswordValidationCallback(clientSubject, "name2", - "invalid".toCharArray()); + PasswordValidationCallback pvc2 = + new PasswordValidationCallback(clientSubject, "name2", "invalid".toCharArray()); callbackHandler.handle(new Callback[] { pvc2 }); Assert.assertFalse(pvc2.getResult()); Set credentials = clientSubject.getPrivateCredentials(); @@ -162,8 +160,7 @@ private static class TestAuthenticator extends AuthenticatorBase { @Override - protected boolean doAuthenticate(Request request, HttpServletResponse response) - throws IOException { + protected boolean doAuthenticate(Request request, HttpServletResponse response) throws IOException { return false; } @@ -189,7 +186,7 @@ @Override public Principal authenticate(String username, String password) { if (getPassword(username).equals(password)) { - return getPrincipal(username); + return getPrincipal(username); } return null; } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestNonLoginAndBasicAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,23 +39,15 @@ import org.apache.tomcat.util.descriptor.web.SecurityConstraint; /** - * Test BasicAuthenticator and NonLoginAuthenticator when a - * SingleSignOn Valve is not active. - * + * Test BasicAuthenticator and NonLoginAuthenticator when a SingleSignOn Valve is not active. *

    - * In the absence of SSO support, these two authenticator classes - * both have quite simple behaviour. By testing them together, we - * can make sure they operate independently and confirm that no - * SSO logic has been accidentally triggered. - * + * In the absence of SSO support, these two authenticator classes both have quite simple behaviour. By testing them + * together, we can make sure they operate independently and confirm that no SSO logic has been accidentally triggered. *

    - * r1495169 refactored BasicAuthenticator by creating an inner class - * called BasicCredentials. All edge cases associated with strangely - * encoded Base64 credentials are tested thoroughly by TestBasicAuthParser. - * Therefore, TestNonLoginAndBasicAuthenticator only needs to examine - * a sufficient set of test cases to verify the interface between - * BasicAuthenticator and BasicCredentials, which it does by running - * each test under a separate tomcat instance. + * r1495169 refactored BasicAuthenticator by creating an inner class called BasicCredentials. All edge cases associated + * with strangely encoded Base64 credentials are tested thoroughly by TestBasicAuthParser. Therefore, + * TestNonLoginAndBasicAuthenticator only needs to examine a sufficient set of test cases to verify the interface + * between BasicAuthenticator and BasicCredentials, which it does by running each test under a separate tomcat instance. */ public class TestNonLoginAndBasicAuthenticator extends TomcatBaseTest { @@ -77,10 +69,8 @@ private static final int MANAGER_SCAN_INTERVAL_SECS = 2; private static final int MANAGER_EXPIRE_SESSIONS_FAST = 1; private static final int EXTRA_DELAY_SECS = 5; - private static final long TIMEOUT_DELAY_MSECS = - ((SHORT_SESSION_TIMEOUT_SECS + - (MANAGER_SCAN_INTERVAL_SECS * MANAGER_EXPIRE_SESSIONS_FAST) + - EXTRA_DELAY_SECS) * 1000); + private static final long TIMEOUT_DELAY_MSECS = ((SHORT_SESSION_TIMEOUT_SECS + + (MANAGER_SCAN_INTERVAL_SECS * MANAGER_EXPIRE_SESSIONS_FAST) + EXTRA_DELAY_SECS) * 1000); private static final String CLIENT_AUTH_HEADER = "authorization"; private static final String SERVER_AUTH_HEADER = "WWW-Authenticate"; @@ -88,14 +78,10 @@ private static final String CLIENT_COOKIE_HEADER = "Cookie"; private static final BasicCredentials NO_CREDENTIALS = null; - private static final BasicCredentials GOOD_CREDENTIALS = - new BasicCredentials(NICE_METHOD, USER, PWD); - private static final BasicCredentials STRANGE_CREDENTIALS = - new BasicCredentials("bAsIc", USER, PWD); - private static final BasicCredentials BAD_CREDENTIALS = - new BasicCredentials(NICE_METHOD, USER, "wrong"); - private static final BasicCredentials BAD_METHOD = - new BasicCredentials("BadMethod", USER, PWD); + private static final BasicCredentials GOOD_CREDENTIALS = new BasicCredentials(NICE_METHOD, USER, PWD); + private static final BasicCredentials STRANGE_CREDENTIALS = new BasicCredentials("bAsIc", USER, PWD); + private static final BasicCredentials BAD_CREDENTIALS = new BasicCredentials(NICE_METHOD, USER, "wrong"); + private static final BasicCredentials BAD_METHOD = new BasicCredentials("BadMethod", USER, PWD); private Tomcat tomcat; private Context basicContext; @@ -103,190 +89,159 @@ private List cookies; /* - * Try to access an unprotected resource in a webapp that - * does not have a login method defined. - * This should be permitted. + * Try to access an unprotected resource in a webapp that does not have a login method defined. This should be + * permitted. */ @Test public void testAcceptPublicNonLogin() throws Exception { - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC, NO_COOKIES, - HttpServletResponse.SC_OK); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC, NO_COOKIES, HttpServletResponse.SC_OK); } /* - * Try to access a protected resource in a webapp that - * does not have a login method defined. - * This should be rejected with SC_FORBIDDEN 403 status. + * Try to access a protected resource in a webapp that does not have a login method defined. This should be rejected + * with SC_FORBIDDEN 403 status. */ @Test public void testRejectProtectedNonLogin() throws Exception { - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, NO_COOKIES, - HttpServletResponse.SC_FORBIDDEN); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, NO_COOKIES, HttpServletResponse.SC_FORBIDDEN); } /* - * Try to access an unprotected resource in a webapp that - * has a BASIC login method defined. - * This should be permitted without a challenge. + * Try to access an unprotected resource in a webapp that has a BASIC login method defined. This should be permitted + * without a challenge. */ @Test public void testAcceptPublicBasic() throws Exception { - doTestBasic(CONTEXT_PATH_LOGIN + URI_PUBLIC, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PUBLIC, NO_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); } /* - * Try to access a protected resource in a webapp that - * has a BASIC login method defined. The access will be - * challenged with 401 SC_UNAUTHORIZED, and then be permitted - * once authenticated. + * Try to access a protected resource in a webapp that has a BASIC login method defined. The access will be + * challenged with 401 SC_UNAUTHORIZED, and then be permitted once authenticated. */ @Test public void testAcceptProtectedBasic() throws Exception { - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); } /* - * This is the same as testAcceptProtectedBasic (above), except - * using an invalid password. + * This is the same as testAcceptProtectedBasic (above), except using an invalid password. */ @Test public void testAuthMethodBadCredentials() throws Exception { - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, BAD_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, BAD_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); } /* - * This is the same as testAcceptProtectedBasic (above), except - * to verify the server follows RFC2617 by treating the auth-scheme - * token as case-insensitive. + * This is the same as testAcceptProtectedBasic (above), except to verify the server follows RFC2617 by treating the + * auth-scheme token as case-insensitive. */ @Test public void testAuthMethodCaseBasic() throws Exception { - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, STRANGE_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, STRANGE_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); } /* - * This is the same as testAcceptProtectedBasic (above), except - * using an invalid authentication method. + * This is the same as testAcceptProtectedBasic (above), except using an invalid authentication method. * - * Note: the container ensures the Basic login method is called. - * BasicAuthenticator does not find the expected authentication - * header method, and so does not extract any credentials. + * Note: the container ensures the Basic login method is called. BasicAuthenticator does not find the expected + * authentication header method, and so does not extract any credentials. * - * The request is rejected with 401 SC_UNAUTHORIZED status. RFC2616 - * says the response body should identify the auth-schemes that are - * acceptable for the container. + * The request is rejected with 401 SC_UNAUTHORIZED status. RFC2616 says the response body should identify the + * auth-schemes that are acceptable for the container. */ @Test public void testAuthMethodBadMethod() throws Exception { - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, BAD_METHOD, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, BAD_METHOD, NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); } /* - * The default behaviour of BASIC authentication does NOT create - * a session on the server. Verify that the client is required to - * send a valid authenticate header with every request to access - * protected resources. + * The default behaviour of BASIC authentication does NOT create a session on the server. Verify that the client is + * required to send a valid authenticate header with every request to access protected resources. */ @Test public void testBasicLoginWithoutSession() throws Exception { // this section is identical to testAuthMethodCaseBasic - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); // next, try to access the protected resource while not providing // credentials. This confirms the server has not retained any state // data which might allow it to authenticate the client. Expect // to be challenged with 401 SC_UNAUTHORIZED. - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); // finally, provide credentials to confirm the resource // can still be accessed with an authentication header. - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); } /* - * Test the optional behaviour of BASIC authentication to create - * a session on the server. The server will return a session cookie. + * Test the optional behaviour of BASIC authentication to create a session on the server. The server will return a + * session cookie. * - * 1. try to access a protected resource without credentials, so - * get Unauthorized status. - * 2. try to access a protected resource when providing credentials, - * so get OK status and a server session cookie. - * 3. access the protected resource once more using a session cookie. - * 4. repeat using the session cookie. + * 1. try to access a protected resource without credentials, so get Unauthorized status. 2. try to access a + * protected resource when providing credentials, so get OK status and a server session cookie. 3. access the + * protected resource once more using a session cookie. 4. repeat using the session cookie. * - * Note: The FormAuthenticator is a two-step process and is protected - * from session fixation attacks by the default AuthenticatorBase - * changeSessionIdOnAuthentication setting of true. However, - * BasicAuthenticator is a one-step process and so the - * AuthenticatorBase does not reissue the sessionId. + * Note: The FormAuthenticator is a two-step process and is protected from session fixation attacks by the default + * AuthenticatorBase changeSessionIdOnAuthentication setting of true. However, BasicAuthenticator is a one-step + * process and so the AuthenticatorBase does not reissue the sessionId. */ - @Test + @Test public void testBasicLoginSessionPersistence() throws Exception { setAlwaysUseSession(); // this section is identical to testAuthMethodCaseBasic - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); // confirm the session is not recognised by the server alone - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); // now provide the harvested session cookie for authentication - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - USE_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK); // finally, do it again with the cookie to be sure - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - USE_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK); } /* - * Verify the timeout mechanism works for BASIC sessions. This test - * follows the flow of testBasicLoginSessionPersistence (above). + * Verify the timeout mechanism works for BASIC sessions. This test follows the flow of + * testBasicLoginSessionPersistence (above). */ - @Test + @Test public void testBasicLoginSessionTimeout() throws Exception { - setAlwaysUseSession(); - setRapidSessionTimeout(); + setAlwaysUseSession(); + setRapidSessionTimeout(); - // this section is identical to testAuthMethodCaseBasic - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + // this section is identical to testAuthMethodCaseBasic + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); // now provide the harvested session cookie for authentication List originalCookies = cookies; - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - USE_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK); // Force session to expire one second from now - forceSessionMaxInactiveInterval( - (Context) getTomcatInstance().getHost().findChild(CONTEXT_PATH_LOGIN), + forceSessionMaxInactiveInterval((Context) getTomcatInstance().getHost().findChild(CONTEXT_PATH_LOGIN), SHORT_SESSION_TIMEOUT_SECS); // allow the session to time out and lose authentication @@ -294,14 +249,13 @@ // provide the harvested session cookie for authentication // to confirm it has expired - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - USE_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); // finally, do BASIC reauthentication and get another session - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); // slightly paranoid verification boolean sameCookies = originalCookies.equals(cookies); @@ -309,30 +263,24 @@ } /* - * Logon to access a protected resource in a webapp that uses - * BASIC authentication. Then try to access a protected resource - * in a different webapp which does not have a login method. - * This should be rejected with SC_FORBIDDEN 403 status, confirming - * there has been no cross-authentication between the webapps. + * Logon to access a protected resource in a webapp that uses BASIC authentication. Then try to access a protected + * resource in a different webapp which does not have a login method. This should be rejected with SC_FORBIDDEN 403 + * status, confirming there has been no cross-authentication between the webapps. */ @Test public void testBasicLoginRejectProtected() throws Exception { - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - NO_COOKIES, HttpServletResponse.SC_FORBIDDEN); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, NO_COOKIES, HttpServletResponse.SC_FORBIDDEN); } /* - * Try to use the session cookie from the BASIC webapp to request - * access to the webapp that does not have a login method. (This - * is equivalent to Single Signon, but without the Valve.) + * Try to use the session cookie from the BASIC webapp to request access to the webapp that does not have a login + * method. (This is equivalent to Single Signon, but without the Valve.) * - * Verify there is no cross-authentication when using similar logic - * to testBasicLoginRejectProtected (above). + * Verify there is no cross-authentication when using similar logic to testBasicLoginRejectProtected (above). * * This should be rejected with SC_FORBIDDEN 403 status. */ @@ -341,19 +289,16 @@ setAlwaysUseSession(); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, + HttpServletResponse.SC_UNAUTHORIZED); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); // use the session cookie harvested with the other webapp - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - USE_COOKIES, HttpServletResponse.SC_FORBIDDEN); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN); } - private void doTestNonLogin(String uri, boolean useCookie, - int expectedRC) throws Exception { + private void doTestNonLogin(String uri, boolean useCookie, int expectedRC) throws Exception { Map> reqHeaders = new HashMap<>(); Map> respHeaders = new HashMap<>(); @@ -363,8 +308,7 @@ } ByteChunk bc = new ByteChunk(); - int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, - respHeaders); + int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, respHeaders); if (expectedRC != HttpServletResponse.SC_OK) { Assert.assertEquals(expectedRC, rc); @@ -374,8 +318,8 @@ } } - private void doTestBasic(String uri, BasicCredentials credentials, - boolean useCookie, int expectedRC) throws Exception { + private void doTestBasic(String uri, BasicCredentials credentials, boolean useCookie, int expectedRC) + throws Exception { Map> reqHeaders = new HashMap<>(); Map> respHeaders = new HashMap<>(); @@ -391,8 +335,7 @@ } ByteChunk bc = new ByteChunk(); - int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, - respHeaders); + int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, respHeaders); if (expectedRC != HttpServletResponse.SC_OK) { Assert.assertEquals(expectedRC, rc); @@ -448,8 +391,7 @@ private void setUpNonLogin() throws Exception { // Must have a real docBase for webapps - just use temp - nonloginContext = tomcat.addContext(CONTEXT_PATH_NOLOGIN, - System.getProperty("java.io.tmpdir")); + nonloginContext = tomcat.addContext(CONTEXT_PATH_NOLOGIN, System.getProperty("java.io.tmpdir")); // Add protected servlet to the context Tomcat.addServlet(nonloginContext, "TesterServlet1", new TesterServlet()); @@ -484,8 +426,7 @@ private void setUpLogin() throws Exception { // Must have a real docBase for webapps - just use temp - basicContext = tomcat.addContext(CONTEXT_PATH_LOGIN, - System.getProperty("java.io.tmpdir")); + basicContext = tomcat.addContext(CONTEXT_PATH_LOGIN, System.getProperty("java.io.tmpdir")); // Add protected servlet to the context Tomcat.addServlet(basicContext, "TesterServlet3", new TesterServlet()); @@ -521,27 +462,22 @@ */ private void setAlwaysUseSession() { - ((AuthenticatorBase)basicContext.getAuthenticator()) - .setAlwaysUseSession(true); - ((AuthenticatorBase)nonloginContext.getAuthenticator()) - .setAlwaysUseSession(true); + ((AuthenticatorBase) basicContext.getAuthenticator()).setAlwaysUseSession(true); + ((AuthenticatorBase) nonloginContext.getAuthenticator()).setAlwaysUseSession(true); } /* - * Force rapid timeout scanning for the Basic Authentication webapp - * The StandardManager default service cycle time is 10 seconds, - * with a session expiry scan every 6 cycles. + * Force rapid timeout scanning for the Basic Authentication webapp The StandardManager default service cycle time + * is 10 seconds, with a session expiry scan every 6 cycles. */ private void setRapidSessionTimeout() { - basicContext.getParent().getParent().setBackgroundProcessorDelay( - MANAGER_SCAN_INTERVAL_SECS); - ((ManagerBase) basicContext.getManager()) - .setProcessExpiresFrequency(MANAGER_EXPIRE_SESSIONS_FAST); + basicContext.getParent().getParent().setBackgroundProcessorDelay(MANAGER_SCAN_INTERVAL_SECS); + ((ManagerBase) basicContext.getManager()).setProcessExpiresFrequency(MANAGER_EXPIRE_SESSIONS_FAST); } + /* - * Encapsulate the logic to generate an HTTP header - * for BASIC Authentication. - * Note: only used internally, so no need to validate arguments. + * Encapsulate the logic to generate an HTTP header for BASIC Authentication. Note: only used internally, so no need + * to validate arguments. */ private static final class BasicCredentials { @@ -550,16 +486,14 @@ private final String password; private final String credentials; - private BasicCredentials(String aMethod, - String aUsername, String aPassword) { + private BasicCredentials(String aMethod, String aUsername, String aPassword) { method = aMethod; username = aUsername; password = aPassword; String userCredentials = username + ":" + password; - byte[] credentialsBytes = - userCredentials.getBytes(StandardCharsets.ISO_8859_1); + byte[] credentialsBytes = userCredentials.getBytes(StandardCharsets.ISO_8859_1); String base64auth = Base64.getEncoder().encodeToString(credentialsBytes); - credentials= method + " " + base64auth; + credentials = method + " " + base64auth; } private String getCredentials() { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestSSOChangeSessionId.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestSSOChangeSessionId.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestSSOChangeSessionId.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestSSOChangeSessionId.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.authenticator; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.Manager; +import org.apache.catalina.Session; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.startup.TesterServlet; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.catalina.valves.ValveBase; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.descriptor.web.LoginConfig; +import org.apache.tomcat.util.descriptor.web.SecurityCollection; +import org.apache.tomcat.util.descriptor.web.SecurityConstraint; + +public class TestSSOChangeSessionId extends TomcatBaseTest { + + protected static final boolean USE_COOKIES = true; + + private static final String NOTSORANDOMID = "NOTSORANNDOMID"; + private static final String USER = "user"; + private static final String PWD = "pwd"; + private static final String ROLE = "role"; + + private static final String HTTP_PREFIX = "http://localhost:"; + private static final String CONTEXT_PATH = "/test"; + private static final String URI_PROTECTED = "/protected"; + private static final String URI_AUTHENTICATION = "/authentication"; + + private Context testContext; + private SingleSignOn singleSignOn; + + private static final String SERVER_COOKIE_HEADER = "Set-Cookie"; + private static final String CLIENT_COOKIE_HEADER = "Cookie"; + + private List cookies; + + /* + * setup two webapps for every test + * + * note: the super class tearDown method will stop tomcat + */ + @Override + public void setUp() throws Exception { + super.setUp(); + + // create a tomcat server using the default in-memory Realm + final Tomcat tomcat = getTomcatInstance(); + + // Must have a real docBase for webapps - just use temp + testContext = tomcat.addContext(CONTEXT_PATH, System.getProperty("java.io.tmpdir")); + + singleSignOn = new SingleSignOn(); + tomcat.getHost().getPipeline().addValve(singleSignOn); + + testContext.getPipeline().addValve(new SessionManipulationFilter()); + + // Add protected servlet to the context + Tomcat.addServlet(testContext, "TesterServlet1", new TesterServlet()); + testContext.addServletMappingDecoded(URI_PROTECTED, "TesterServlet1"); + + SecurityCollection collection1 = new SecurityCollection(); + collection1.addPatternDecoded(URI_PROTECTED); + SecurityConstraint sc1 = new SecurityConstraint(); + sc1.addAuthRole(ROLE); + sc1.addCollection(collection1); + testContext.addConstraint(sc1); + + // Add Authenticator + Tomcat.addServlet(testContext, "LoginServlet", new LoginServlet()); + testContext.addServletMappingDecoded(URI_AUTHENTICATION, "LoginServlet"); + + SecurityCollection collection2 = new SecurityCollection(); + collection2.addPatternDecoded(URI_AUTHENTICATION); + SecurityConstraint sc2 = new SecurityConstraint(); + // do not add a role - which signals access permitted without one + sc2.addCollection(collection2); + testContext.addConstraint(sc2); + + // Configure the authenticator and inherit the Realm from Engine + LoginConfig lc = new LoginConfig(); + lc.setAuthMethod("NONE"); + testContext.setLoginConfig(lc); + AuthenticatorBase nonloginAuthenticator = new NonLoginAuthenticator(); + testContext.getPipeline().addValve(nonloginAuthenticator); + + // add the test user and role to the Realm + tomcat.addUser(USER, PWD); + tomcat.addRole(USER, ROLE); + + tomcat.start(); + } + + private static class SessionManipulationFilter extends ValveBase { + @Override + public void invoke(Request request, Response response) throws IOException, ServletException { + + if (request.getPrincipal() != null) { + final Manager manager = request.getContext().getManager(); + final Session session = manager.findSession(request.getSession().getId()); + manager.changeSessionId(session, NOTSORANDOMID); + } + + getNext().invoke(request, response); + } + } + + private static class LoginServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + if (request.getUserPrincipal() == null) { + request.login(USER, PWD); + + response.setContentType("text/plain"); + PrintWriter out = response.getWriter(); + out.print("OK"); + + } else { + response.setStatus(HttpServletResponse.SC_CONFLICT); + } + } + } + + @Test + public void testChangeSessionId() throws Exception { + final Map cache = singleSignOn.cache; + Assert.assertTrue("No SSO entries should be present at startup", cache.isEmpty()); + + // Authenticate a session + doTest(CONTEXT_PATH + URI_AUTHENTICATION, USE_COOKIES, HttpServletResponse.SC_OK); + + Assert.assertFalse("SSO must now be present", cache.isEmpty()); + + Assert.assertEquals("Only one SSO entry must be present", 1, cache.size()); + final Collection ssoEntries = cache.values(); + Assert.assertEquals("Only one session should be present, as there is only one context in the test", 1, + ssoEntries.size()); + + final SingleSignOnEntry singleSignOnEntry = ssoEntries.iterator().next(); + final Set sessions = singleSignOnEntry.findSessions(); + + Assert.assertEquals(1, sessions.size()); + + final SingleSignOnSessionKey singleSignOnSessionKey1 = sessions.iterator().next(); + Assert.assertNotEquals("A random SessionId is expected", NOTSORANDOMID, singleSignOnSessionKey1.getSessionId()); + + // Perform the request that changes the session id: + doTest(CONTEXT_PATH + URI_PROTECTED, USE_COOKIES, HttpServletResponse.SC_OK); + + Assert.assertEquals("2 sessions means we have a zombie SingleSignOnSessionKey", 1, sessions.size()); + + final SingleSignOnSessionKey singleSignOnSessionKey2 = sessions.iterator().next(); + Assert.assertEquals(NOTSORANDOMID, singleSignOnSessionKey2.getSessionId()); + + final Session session = testContext.getManager().findSession(NOTSORANDOMID); + Assert.assertNotNull("We need a session in order to test expiry", session); + + session.expire(); + + Assert.assertTrue("No SSO entries should be present after expiration of the session", cache.isEmpty()); + + // Shouldn't this return 401? + doTest(CONTEXT_PATH + URI_PROTECTED, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN); + } + + @After + @Override + public void tearDown() throws Exception { + super.tearDown(); + } + + public void doTest(String uri, boolean useCookie, int expectedRC) throws Exception { + + Map> reqHeaders = new HashMap<>(); + Map> respHeaders = new HashMap<>(); + + if (useCookie && (cookies != null)) { + addCookies(reqHeaders); + } + + ByteChunk bc = new ByteChunk(); + int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, respHeaders); + if (expectedRC != HttpServletResponse.SC_OK) { + Assert.assertEquals(expectedRC, rc); + Assert.assertTrue(bc.getLength() > 0); + } else { + Assert.assertEquals("OK", bc.toString()); + saveCookies(respHeaders); + } + } + + /* + * add all saved cookies to the outgoing request + */ + protected void addCookies(Map> reqHeaders) { + if ((cookies != null) && (cookies.size() > 0)) { + StringBuilder cookieHeader = new StringBuilder(); + boolean first = true; + for (String cookie : cookies) { + if (!first) { + cookieHeader.append(';'); + } else { + first = false; + } + cookieHeader.append(cookie); + } + List cookieHeaderList = new ArrayList<>(1); + cookieHeaderList.add(cookieHeader.toString()); + reqHeaders.put(CLIENT_COOKIE_HEADER, cookieHeaderList); + } + } + + /* + * extract and save the server cookies from the incoming response + */ + protected void saveCookies(Map> respHeaders) { + // we only save the Cookie values, not header prefix + List cookieHeaders = respHeaders.get(SERVER_COOKIE_HEADER); + if (cookieHeaders == null) { + cookies = null; + } else { + cookies = new ArrayList<>(cookieHeaders.size()); + for (String cookieHeader : cookieHeaders) { + cookies.add(cookieHeader.substring(0, cookieHeader.indexOf(';'))); + } + } + } +} \ No newline at end of file diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,34 +40,23 @@ import org.apache.tomcat.util.descriptor.web.SecurityConstraint; /** - * Test BasicAuthenticator and NonLoginAuthenticator when a - * SingleSignOn Valve is active. - * + * Test BasicAuthenticator and NonLoginAuthenticator when a SingleSignOn Valve is active. *

    - * In the absence of SSO support, a webapp using NonLoginAuthenticator - * simply cannot access protected resources. These tests exercise the - * the way successfully authenticating a different webapp under the - * BasicAuthenticator triggers the additional SSO logic for both webapps. - * + * In the absence of SSO support, a webapp using NonLoginAuthenticator simply cannot access protected resources. These + * tests exercise the the way successfully authenticating a different webapp under the BasicAuthenticator triggers the + * additional SSO logic for both webapps. *

    - * The two Authenticators are thoroughly exercised by two other unit test - * classes: TestBasicAuthParser and TestNonLoginAndBasicAuthenticator. - * This class mainly examines the way the Single SignOn Valve interacts with - * two webapps when the second cannot be authenticated directly, but needs - * to inherit its authentication via the other. - * + * The two Authenticators are thoroughly exercised by two other unit test classes: TestBasicAuthParser and + * TestNonLoginAndBasicAuthenticator. This class mainly examines the way the Single SignOn Valve interacts with two + * webapps when the second cannot be authenticated directly, but needs to inherit its authentication via the other. *

    - * When the server and client can both use cookies, the authentication - * is preserved through the exchange of a JSSOSESSIONID cookie, which - * is different to the individual and unique JSESSIONID cookies assigned - * separately to the two webapp sessions. - * + * When the server and client can both use cookies, the authentication is preserved through the exchange of a + * JSSOSESSIONID cookie, which is different to the individual and unique JSESSIONID cookies assigned separately to the + * two webapp sessions. *

    - * The other situation examined is where the server returns authentication - * cookies, but the client is configured to ignore them. The Tomcat - * documentation clearly states that SSO requires the client to - * support cookies, so access to resources in other webapp containers - * receives no SSO assistance. + * The other situation examined is where the server returns authentication cookies, but the client is configured to + * ignore them. The Tomcat documentation clearly states that SSO requires the client to support cookies, so + * access to resources in other webapp containers receives no SSO assistance. */ public class TestSSOnonLoginAndBasicAuthenticator extends TomcatBaseTest { @@ -95,8 +84,8 @@ // now compute some delays - beware of the units! private static final int EXTRA_DELAY_SECS = 5; - private static final int TIMEOUT_WAIT_SECS = EXTRA_DELAY_SECS + - (MANAGER_SCAN_INTERVAL_SECS * MANAGER_EXPIRE_SESSIONS_FAST) * 5; + private static final int TIMEOUT_WAIT_SECS = + EXTRA_DELAY_SECS + (MANAGER_SCAN_INTERVAL_SECS * MANAGER_EXPIRE_SESSIONS_FAST) * 5; private static final String CLIENT_AUTH_HEADER = "authorization"; private static final String SERVER_AUTH_HEADER = "WWW-Authenticate"; @@ -105,14 +94,9 @@ private static final String ENCODE_SESSION_PARAM = "jsessionid"; private static final String ENCODE_SSOSESSION_PARAM = "jssosessionid"; - private static final - TestSSOnonLoginAndBasicAuthenticator.BasicCredentials - NO_CREDENTIALS = null; - private static final - TestSSOnonLoginAndBasicAuthenticator.BasicCredentials - GOOD_CREDENTIALS = - new TestSSOnonLoginAndBasicAuthenticator.BasicCredentials( - NICE_METHOD, USER, PWD); + private static final TestSSOnonLoginAndBasicAuthenticator.BasicCredentials NO_CREDENTIALS = null; + private static final TestSSOnonLoginAndBasicAuthenticator.BasicCredentials GOOD_CREDENTIALS = + new TestSSOnonLoginAndBasicAuthenticator.BasicCredentials(NICE_METHOD, USER, PWD); private Tomcat tomcat; private Context basicContext; @@ -121,22 +105,18 @@ private String encodedURL; /* - * Run some checks without an established SSO session - * to make sure the test environment is correct. + * Run some checks without an established SSO session to make sure the test environment is correct. */ @Test public void testEssentialEnvironment() throws Exception { // should be permitted to access an unprotected resource. - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC, - USE_COOKIES, HttpServletResponse.SC_OK); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC, USE_COOKIES, HttpServletResponse.SC_OK); // should not be permitted to access a protected resource // with the two Authenticators used in the remaining tests. - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - USE_COOKIES, HttpServletResponse.SC_FORBIDDEN); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - NO_CREDENTIALS, USE_COOKIES, + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); } @@ -144,23 +124,19 @@ public void testEssentialEnvironmentWithoutCookies() throws Exception { // should be permitted to access an unprotected resource. - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC, - NO_COOKIES, HttpServletResponse.SC_OK); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC, NO_COOKIES, HttpServletResponse.SC_OK); // should not be permitted to access a protected resource // with the two Authenticators used in the remaining tests. - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - NO_COOKIES, HttpServletResponse.SC_FORBIDDEN); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - NO_CREDENTIALS, NO_COOKIES, + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, NO_COOKIES, HttpServletResponse.SC_FORBIDDEN); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); } /* - * Logon to access a protected resource using BASIC authentication, - * which will establish an SSO session. - * Wait until the SSO session times-out, then try to re-access - * the resource. This should be rejected with SC_FORBIDDEN 401 status. + * Logon to access a protected resource using BASIC authentication, which will establish an SSO session. Wait until + * the SSO session times-out, then try to re-access the resource. This should be rejected with SC_FORBIDDEN 401 + * status. * * Note: this test should run for ~10 seconds. */ @@ -169,77 +145,57 @@ setRapidSessionTimeoutDetection(); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - NO_CREDENTIALS, USE_COOKIES, + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - GOOD_CREDENTIALS, USE_COOKIES, - HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK); // verify the SSOID exists as a cookie - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - GOOD_CREDENTIALS, USE_COOKIES, - HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK); // make the session time out and lose authentication doImminentSessionTimeout(basicContext); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - NO_CREDENTIALS, USE_COOKIES, + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); } /* - * Logon to access a protected resource using BASIC authentication, - * which will establish an SSO session. - * Immediately try to access a protected resource in the NonLogin - * webapp while providing the SSO session cookie received from the - * first webapp. This should be successful with SC_OK 200 status. + * Logon to access a protected resource using BASIC authentication, which will establish an SSO session. Immediately + * try to access a protected resource in the NonLogin webapp while providing the SSO session cookie received from + * the first webapp. This should be successful with SC_OK 200 status. */ @Test public void testBasicLoginThenAcceptWithCookies() throws Exception { - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - NO_CREDENTIALS, NO_COOKIES, + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - GOOD_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK); // send the cookie which proves we have an authenticated SSO session - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - USE_COOKIES, HttpServletResponse.SC_OK); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, USE_COOKIES, HttpServletResponse.SC_OK); } /* - * Logon to access a protected resource using BASIC authentication, - * which will establish an SSO session. - * Immediately try to access a protected resource in the NonLogin - * webapp, but without sending the SSO session cookie. - * This should be rejected with SC_FORBIDDEN 403 status. + * Logon to access a protected resource using BASIC authentication, which will establish an SSO session. Immediately + * try to access a protected resource in the NonLogin webapp, but without sending the SSO session cookie. This + * should be rejected with SC_FORBIDDEN 403 status. */ @Test public void testBasicLoginThenRejectWithoutCookie() throws Exception { - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - NO_CREDENTIALS, USE_COOKIES, + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - GOOD_CREDENTIALS, USE_COOKIES, - HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK); // fail to send the authentication cookie to the other webapp. - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - NO_COOKIES, HttpServletResponse.SC_FORBIDDEN); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, NO_COOKIES, HttpServletResponse.SC_FORBIDDEN); } /* - * Logon to access a protected resource using BASIC authentication, - * which will establish an SSO session. - * Then try to access a protected resource in the NonLogin - * webapp by sending the JSESSIONID from the redirect header. - * The access request should be rejected because the Basic webapp's - * sessionID is not valid for any other container. + * Logon to access a protected resource using BASIC authentication, which will establish an SSO session. Then try to + * access a protected resource in the NonLogin webapp by sending the JSESSIONID from the redirect header. The access + * request should be rejected because the Basic webapp's sessionID is not valid for any other container. */ @Test public void testBasicAccessThenAcceptAuthWithUri() throws Exception { @@ -247,20 +203,16 @@ setAlwaysUseSession(); // first, fail to access the protected resource without credentials - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - NO_CREDENTIALS, NO_COOKIES, + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); // now, access the protected resource with good credentials // to establish the session - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - GOOD_CREDENTIALS, NO_COOKIES, - HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); // next, access it again to harvest the session id url parameter String forwardParam = "?nextUrl=" + CONTEXT_PATH_LOGIN + URI_PROTECTED; - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED + forwardParam, - GOOD_CREDENTIALS, NO_COOKIES, + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED + forwardParam, GOOD_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); // verify the sessionID was encoded in the absolute URL @@ -268,9 +220,7 @@ Assert.assertTrue(firstEncodedURL.contains(ENCODE_SESSION_PARAM)); // access the protected resource with the encoded url (with session id) - doTestBasic(firstEncodedURL + forwardParam, - NO_CREDENTIALS, NO_COOKIES, - HttpServletResponse.SC_OK); + doTestBasic(firstEncodedURL + forwardParam, NO_CREDENTIALS, NO_COOKIES, HttpServletResponse.SC_OK); // verify the sessionID has not changed // verify the SSO sessionID was not encoded @@ -283,25 +233,21 @@ String sessionId = secondEncodedURL.substring(ix); // expect to fail using that sessionID in a different container - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED + ";" + sessionId, - NO_COOKIES, HttpServletResponse.SC_FORBIDDEN); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED + ";" + sessionId, NO_COOKIES, + HttpServletResponse.SC_FORBIDDEN); } /* - * Logon to access a protected resource using BASIC authentication, - * which will establish an SSO session. - * Immediately try to access a protected resource in the NonLogin - * webapp while providing the SSO session cookie received from the - * first webapp. This should be successful with SC_OK 200 status. + * Logon to access a protected resource using BASIC authentication, which will establish an SSO session. Immediately + * try to access a protected resource in the NonLogin webapp while providing the SSO session cookie received from + * the first webapp. This should be successful with SC_OK 200 status. * - * Then, wait long enough for the BASIC session to expire. (The SSO - * session should remain active because the NonLogin session has - * not yet expired). - * Try to access the protected resource again, before the SSO session - * has expired. This should be successful with SC_OK 200 status. + * Then, wait long enough for the BASIC session to expire. (The SSO session should remain active because the + * NonLogin session has not yet expired). Try to access the protected resource again, before the SSO session has + * expired. This should be successful with SC_OK 200 status. * - * Finally, wait for the non-login session to expire and try again.. - * This should be rejected with SC_FORBIDDEN 403 status. + * Finally, wait for the non-login session to expire and try again.. This should be rejected with SC_FORBIDDEN 403 + * status. * * (see bugfix https://bz.apache.org/bugzilla/show_bug.cgi?id=52303) * @@ -313,14 +259,10 @@ setRapidSessionTimeoutDetection(); // begin with a repeat of testBasicLoginAcceptProtectedWithCookies - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - NO_CREDENTIALS, USE_COOKIES, + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - GOOD_CREDENTIALS, USE_COOKIES, - HttpServletResponse.SC_OK); - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - USE_COOKIES, HttpServletResponse.SC_OK); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, GOOD_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_OK); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, USE_COOKIES, HttpServletResponse.SC_OK); // wait long enough for the BASIC session to expire, // but not long enough for the NonLogin session expiry. @@ -328,24 +270,20 @@ // this successful NonLogin access should replenish the // the individual session expiry time and keep the SSO session alive - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - USE_COOKIES, HttpServletResponse.SC_OK); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, USE_COOKIES, HttpServletResponse.SC_OK); // wait long enough for the NonLogin session to expire, // which will also tear down the SSO session at the same time. doImminentSessionTimeout(nonloginContext); - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, USE_COOKIES, - HttpServletResponse.SC_FORBIDDEN); - doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, - NO_CREDENTIALS, USE_COOKIES, + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN); + doTestBasic(CONTEXT_PATH_LOGIN + URI_PROTECTED, NO_CREDENTIALS, USE_COOKIES, HttpServletResponse.SC_UNAUTHORIZED); } - public void doTestNonLogin(String uri, boolean useCookie, - int expectedRC) throws Exception { + public void doTestNonLogin(String uri, boolean useCookie, int expectedRC) throws Exception { Map> reqHeaders = new HashMap<>(); Map> respHeaders = new HashMap<>(); @@ -355,8 +293,7 @@ } ByteChunk bc = new ByteChunk(); - int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, - respHeaders); + int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, respHeaders); if (expectedRC != HttpServletResponse.SC_OK) { Assert.assertEquals(expectedRC, rc); @@ -364,10 +301,9 @@ } else { Assert.assertEquals("OK", bc.toString()); } -} + } - private void doTestBasic(String uri, - TestSSOnonLoginAndBasicAuthenticator.BasicCredentials credentials, + private void doTestBasic(String uri, TestSSOnonLoginAndBasicAuthenticator.BasicCredentials credentials, boolean useCookie, int expectedRC) throws Exception { Map> reqHeaders = new HashMap<>(); @@ -384,8 +320,7 @@ } ByteChunk bc = new ByteChunk(); - int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, - respHeaders); + int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, respHeaders); Assert.assertEquals("Unexpected Return Code", expectedRC, rc); if (expectedRC != HttpServletResponse.SC_OK) { @@ -430,8 +365,6 @@ } - - /* * setup two webapps for every test * @@ -469,13 +402,11 @@ private void setUpNonLogin() throws Exception { // Must have a real docBase for webapps - just use temp - nonloginContext = tomcat.addContext(CONTEXT_PATH_NOLOGIN, - System.getProperty("java.io.tmpdir")); + nonloginContext = tomcat.addContext(CONTEXT_PATH_NOLOGIN, System.getProperty("java.io.tmpdir")); nonloginContext.setSessionTimeout(LONG_SESSION_TIMEOUT_MINS); // Add protected servlet to the context - Tomcat.addServlet(nonloginContext, "TesterServlet1", - new TesterServletEncodeUrl()); + Tomcat.addServlet(nonloginContext, "TesterServlet1", new TesterServletEncodeUrl()); nonloginContext.addServletMappingDecoded(URI_PROTECTED, "TesterServlet1"); SecurityCollection collection1 = new SecurityCollection(); @@ -486,8 +417,7 @@ nonloginContext.addConstraint(sc1); // Add unprotected servlet to the context - Tomcat.addServlet(nonloginContext, "TesterServlet2", - new TesterServletEncodeUrl()); + Tomcat.addServlet(nonloginContext, "TesterServlet2", new TesterServletEncodeUrl()); nonloginContext.addServletMappingDecoded(URI_PUBLIC, "TesterServlet2"); SecurityCollection collection2 = new SecurityCollection(); @@ -508,13 +438,11 @@ private void setUpLogin() throws Exception { // Must have a real docBase for webapps - just use temp - basicContext = tomcat.addContext(CONTEXT_PATH_LOGIN, - System.getProperty("java.io.tmpdir")); + basicContext = tomcat.addContext(CONTEXT_PATH_LOGIN, System.getProperty("java.io.tmpdir")); basicContext.setSessionTimeout(SHORT_SESSION_TIMEOUT_MINS); // Add protected servlet to the context - Tomcat.addServlet(basicContext, "TesterServlet3", - new TesterServletEncodeUrl()); + Tomcat.addServlet(basicContext, "TesterServlet3", new TesterServletEncodeUrl()); basicContext.addServletMappingDecoded(URI_PROTECTED, "TesterServlet3"); SecurityCollection collection = new SecurityCollection(); collection.addPatternDecoded(URI_PROTECTED); @@ -524,8 +452,7 @@ basicContext.addConstraint(sc); // Add unprotected servlet to the context - Tomcat.addServlet(basicContext, "TesterServlet4", - new TesterServletEncodeUrl()); + Tomcat.addServlet(basicContext, "TesterServlet4", new TesterServletEncodeUrl()); basicContext.addServletMappingDecoded(URI_PUBLIC, "TesterServlet4"); SecurityCollection collection2 = new SecurityCollection(); collection2.addPatternDecoded(URI_PUBLIC); @@ -580,25 +507,19 @@ } /* - * Force non-default behaviour for both Authenticators. - * The session id will not be regenerated after authentication, - * which is less secure but needed for browsers that will not - * handle cookies. + * Force non-default behaviour for both Authenticators. The session id will not be regenerated after authentication, + * which is less secure but needed for browsers that will not handle cookies. */ private void setAlwaysUseSession() { - ((AuthenticatorBase) basicContext.getAuthenticator()) - .setAlwaysUseSession(true); - ((AuthenticatorBase) nonloginContext.getAuthenticator()) - .setAlwaysUseSession(true); + ((AuthenticatorBase) basicContext.getAuthenticator()).setAlwaysUseSession(true); + ((AuthenticatorBase) nonloginContext.getAuthenticator()).setAlwaysUseSession(true); } /* - * Force faster timeout for an active Container than can - * be defined in web.xml. By getting to the active Session we - * can choose seconds instead of minutes. - * Note: shamelessly cloned from ManagerBase - beware of synch issues - * on the underlying sessions. + * Force faster timeout for an active Container than can be defined in web.xml. By getting to the active Session we + * can choose seconds instead of minutes. Note: shamelessly cloned from ManagerBase - beware of synch issues on the + * underlying sessions. */ private void doImminentSessionTimeout(Context activeContext) { @@ -636,22 +557,18 @@ } /* - * Force rapid timeout scanning for both webapps - * The StandardManager default service cycle time is 10 seconds, - * with a session expiry scan every 6 cycles. + * Force rapid timeout scanning for both webapps The StandardManager default service cycle time is 10 seconds, with + * a session expiry scan every 6 cycles. */ private void setRapidSessionTimeoutDetection() { - ((ManagerBase) basicContext.getManager()) - .setProcessExpiresFrequency(MANAGER_EXPIRE_SESSIONS_FAST); - ((ManagerBase) nonloginContext.getManager()) - .setProcessExpiresFrequency(MANAGER_EXPIRE_SESSIONS_FAST); + ((ManagerBase) basicContext.getManager()).setProcessExpiresFrequency(MANAGER_EXPIRE_SESSIONS_FAST); + ((ManagerBase) nonloginContext.getManager()).setProcessExpiresFrequency(MANAGER_EXPIRE_SESSIONS_FAST); } /* - * Encapsulate the logic to generate an HTTP header - * for BASIC Authentication. - * Note: only used internally, so no need to validate arguments. + * Encapsulate the logic to generate an HTTP header for BASIC Authentication. Note: only used internally, so no need + * to validate arguments. */ private static final class BasicCredentials { @@ -660,16 +577,14 @@ private final String password; private final String credentials; - private BasicCredentials(String aMethod, - String aUsername, String aPassword) { + private BasicCredentials(String aMethod, String aUsername, String aPassword) { method = aMethod; username = aUsername; password = aPassword; String userCredentials = username + ":" + password; - byte[] credentialsBytes = - userCredentials.getBytes(StandardCharsets.ISO_8859_1); + byte[] credentialsBytes = userCredentials.getBytes(StandardCharsets.ISO_8859_1); String base64auth = Base64.getEncoder().encodeToString(credentialsBytes); - credentials= method + " " + base64auth; + credentials = method + " " + base64auth; } private String getCredentials() { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TestSSOnonLoginAndDigestAuthenticator.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,19 +37,14 @@ import org.apache.tomcat.util.security.ConcurrentMessageDigest; /** - * Test DigestAuthenticator and NonLoginAuthenticator when a - * SingleSignOn Valve is active. - * + * Test DigestAuthenticator and NonLoginAuthenticator when a SingleSignOn Valve is active. *

    - * In the absence of SSO support, a webapp using NonLoginAuthenticator - * simply cannot access protected resources. These tests exercise the - * the way successfully authenticating a different webapp under the - * DigestAuthenticator triggers the additional SSO logic for both webapps. - * + * In the absence of SSO support, a webapp using NonLoginAuthenticator simply cannot access protected resources. These + * tests exercise the the way successfully authenticating a different webapp under the DigestAuthenticator triggers the + * additional SSO logic for both webapps. *

    - * Note: these tests are intended to exercise the SSO logic of the - * Authenticator, but not to comprehensively test all of its logic paths. - * That is the responsibility of the non-SSO test suite. + * Note: these tests are intended to exercise the SSO logic of the Authenticator, but not to comprehensively test all of + * its logic paths. That is the responsibility of the non-SSO test suite. */ public class TestSSOnonLoginAndDigestAuthenticator extends TomcatBaseTest { @@ -64,11 +59,9 @@ private static final String URI_PUBLIC = "/anyoneCanAccess"; private static final int SHORT_TIMEOUT_SECS = 4; - private static final long SHORT_TIMEOUT_DELAY_MSECS = - ((SHORT_TIMEOUT_SECS + 3) * 1000); + private static final long SHORT_TIMEOUT_DELAY_MSECS = ((SHORT_TIMEOUT_SECS + 3) * 1000); private static final int LONG_TIMEOUT_SECS = 10; - private static final long LONG_TIMEOUT_DELAY_MSECS = - ((LONG_TIMEOUT_SECS + 2) * 1000); + private static final long LONG_TIMEOUT_DELAY_MSECS = ((LONG_TIMEOUT_SECS + 2) * 1000); private static final String CLIENT_AUTH_HEADER = "authorization"; private static final String OPAQUE = "opaque"; @@ -86,121 +79,92 @@ private List cookies; /* - * Try to access an unprotected resource without an - * established SSO session. - * This should be permitted. + * Try to access an unprotected resource without an established SSO session. This should be permitted. */ @Test public void testAcceptPublicNonLogin() throws Exception { - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC, - true, false, 200); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PUBLIC, true, false, 200); } /* - * Try to access a protected resource without an established - * SSO session. - * This should be rejected with SC_FORBIDDEN 403 status. + * Try to access a protected resource without an established SSO session. This should be rejected with SC_FORBIDDEN + * 403 status. */ @Test public void testRejectProtectedNonLogin() throws Exception { - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - false, true, 403); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, false, true, 403); } /* - * Logon to access a protected resource using DIGEST authentication, - * which will establish an SSO session. - * Wait until the SSO session times-out, then try to re-access - * the resource. - * This should be rejected with SC_FORBIDDEN 401 status, which - * will then be followed by successful re-authentication. + * Logon to access a protected resource using DIGEST authentication, which will establish an SSO session. Wait until + * the SSO session times-out, then try to re-access the resource. This should be rejected with SC_FORBIDDEN 401 + * status, which will then be followed by successful re-authentication. */ @Test public void testDigestLoginSessionTimeout() throws Exception { - doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, - true, 401, true, true, NC1, CNONCE, QOP, true); + doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, true, 401, true, true, NC1, CNONCE, QOP, true); // wait long enough for my session to expire Thread.sleep(LONG_TIMEOUT_DELAY_MSECS); // must change the client nonce to succeed - doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, - true, 401, true, true, NC2, CNONCE, QOP, true); - } + doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, true, 401, true, true, NC2, CNONCE, QOP, true); + } /* - * Logon to access a protected resource using DIGEST authentication, - * which will establish an SSO session. - * Immediately try to access a protected resource in the NonLogin - * webapp, but without sending the SSO session cookie. - * This should be rejected with SC_FORBIDDEN 403 status. + * Logon to access a protected resource using DIGEST authentication, which will establish an SSO session. + * Immediately try to access a protected resource in the NonLogin webapp, but without sending the SSO session + * cookie. This should be rejected with SC_FORBIDDEN 403 status. */ @Test public void testDigestLoginRejectProtectedWithoutCookies() throws Exception { - doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, - true, 401, true, true, NC1, CNONCE, QOP, true); - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - false, true, 403); + doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, true, 401, true, true, NC1, CNONCE, QOP, true); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, false, true, 403); } /* - * Logon to access a protected resource using DIGEST authentication, - * which will establish an SSO session. - * Immediately try to access a protected resource in the NonLogin - * webapp while sending the SSO session cookie provided by the - * first webapp. - * This should be successful with SC_OK 200 status. + * Logon to access a protected resource using DIGEST authentication, which will establish an SSO session. + * Immediately try to access a protected resource in the NonLogin webapp while sending the SSO session cookie + * provided by the first webapp. This should be successful with SC_OK 200 status. */ @Test public void testDigestLoginAcceptProtectedWithCookies() throws Exception { - doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, - true, 401, true, true, NC1, CNONCE, QOP, true); - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - true, false, 200); + doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, true, 401, true, true, NC1, CNONCE, QOP, true); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, true, false, 200); } /* - * Logon to access a protected resource using DIGEST authentication, - * which will establish an SSO session. - * Immediately try to access a protected resource in the NonLogin - * webapp while sending the SSO session cookie provided by the - * first webapp. - * This should be successful with SC_OK 200 status. + * Logon to access a protected resource using DIGEST authentication, which will establish an SSO session. + * Immediately try to access a protected resource in the NonLogin webapp while sending the SSO session cookie + * provided by the first webapp. This should be successful with SC_OK 200 status. * - * Then, wait long enough for the DIGEST session to expire. (The SSO - * session should remain active because the NonLogin session has - * not yet expired). + * Then, wait long enough for the DIGEST session to expire. (The SSO session should remain active because the + * NonLogin session has not yet expired). * - * Try to access the protected resource again, before the SSO session - * has expired. - * This should be successful with SC_OK 200 status. + * Try to access the protected resource again, before the SSO session has expired. This should be successful with + * SC_OK 200 status. * - * Finally, wait for the non-login session to expire and try again.. - * This should be rejected with SC_FORBIDDEN 403 status. + * Finally, wait for the non-login session to expire and try again.. This should be rejected with SC_FORBIDDEN 403 + * status. * * (see bugfix https://bz.apache.org/bugzilla/show_bug.cgi?id=52303) */ @Test public void testDigestExpiredAcceptProtectedWithCookies() throws Exception { - doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, - true, 401, true, true, NC1, CNONCE, QOP, true); - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - true, false, 200); + doTestDigest(USER, PWD, CONTEXT_PATH_DIGEST + URI_PROTECTED, true, 401, true, true, NC1, CNONCE, QOP, true); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, true, false, 200); // wait long enough for the BASIC session to expire, // but not long enough for NonLogin session expiry Thread.sleep(SHORT_TIMEOUT_DELAY_MSECS); - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - true, false, 200); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, true, false, 200); // wait long enough for my NonLogin session to expire // and tear down the SSO session at the same time. Thread.sleep(LONG_TIMEOUT_DELAY_MSECS); - doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, - false, true, 403); + doTestNonLogin(CONTEXT_PATH_NOLOGIN + URI_PROTECTED, false, true, 403); } - public void doTestNonLogin(String uri, boolean addCookies, - boolean expectedReject, int expectedRC) + public void doTestNonLogin(String uri, boolean addCookies, boolean expectedReject, int expectedRC) throws Exception { Map> reqHeaders = new HashMap<>(); @@ -210,8 +174,7 @@ if (addCookies) { addCookies(reqHeaders); } - int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, - respHeaders); + int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, respHeaders); if (expectedReject) { Assert.assertEquals(expectedRC, rc); @@ -221,29 +184,24 @@ Assert.assertEquals("OK", bc.toString()); saveCookies(respHeaders); } -} + } - public void doTestDigest(String user, String pwd, String uri, - boolean expectedReject1, int expectedRC1, - boolean useServerNonce, boolean useServerOpaque, - String nc1, String cnonce, - String qop, boolean req2expect200) - throws Exception { + public void doTestDigest(String user, String pwd, String uri, boolean expectedReject1, int expectedRC1, + boolean useServerNonce, boolean useServerOpaque, String nc1, String cnonce, String qop, + boolean req2expect200) throws Exception { - String digestUri= uri; + String digestUri = uri; List auth = new ArrayList<>(); Map> reqHeaders1 = new HashMap<>(); Map> respHeaders1 = new HashMap<>(); // the first access attempt should be challenged - auth.add(buildDigestResponse(user, pwd, digestUri, REALM, "null", - "null", nc1, cnonce, qop)); + auth.add(buildDigestResponse(user, pwd, digestUri, REALM, "null", "null", nc1, cnonce, qop)); reqHeaders1.put(CLIENT_AUTH_HEADER, auth); ByteChunk bc = new ByteChunk(); - int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders1, - respHeaders1); + int rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders1, respHeaders1); if (expectedReject1) { Assert.assertEquals(expectedRC1, rc); @@ -262,28 +220,20 @@ auth.clear(); if (useServerNonce) { if (useServerOpaque) { - auth.add(buildDigestResponse(user, pwd, digestUri, - getAuthToken(respHeaders1, REALM), - getAuthToken(respHeaders1, NONCE), - getAuthToken(respHeaders1, OPAQUE), - nc1, cnonce, qop)); + auth.add(buildDigestResponse(user, pwd, digestUri, getAuthToken(respHeaders1, REALM), + getAuthToken(respHeaders1, NONCE), getAuthToken(respHeaders1, OPAQUE), nc1, cnonce, qop)); } else { - auth.add(buildDigestResponse(user, pwd, digestUri, - getAuthToken(respHeaders1, REALM), - getAuthToken(respHeaders1, NONCE), - "null", nc1, cnonce, qop)); + auth.add(buildDigestResponse(user, pwd, digestUri, getAuthToken(respHeaders1, REALM), + getAuthToken(respHeaders1, NONCE), "null", nc1, cnonce, qop)); } } else { - auth.add(buildDigestResponse(user, pwd, digestUri, - getAuthToken(respHeaders2, REALM), - "null", getAuthToken(respHeaders1, OPAQUE), - nc1, cnonce, QOP)); + auth.add(buildDigestResponse(user, pwd, digestUri, getAuthToken(respHeaders2, REALM), "null", + getAuthToken(respHeaders1, OPAQUE), nc1, cnonce, QOP)); } reqHeaders2.put(CLIENT_AUTH_HEADER, auth); bc.recycle(); - rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders2, - respHeaders2); + rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders2, respHeaders2); if (req2expect200) { Assert.assertEquals(200, rc); @@ -321,8 +271,7 @@ private void setUpNonLogin(Tomcat tomcat) throws Exception { // Must have a real docBase for webapps - just use temp - Context ctxt = tomcat.addContext(CONTEXT_PATH_NOLOGIN, - System.getProperty("java.io.tmpdir")); + Context ctxt = tomcat.addContext(CONTEXT_PATH_NOLOGIN, System.getProperty("java.io.tmpdir")); ctxt.setSessionTimeout(LONG_TIMEOUT_SECS); // Add protected servlet @@ -355,8 +304,7 @@ private void setUpDigest(Tomcat tomcat) throws Exception { // Must have a real docBase for webapps - just use temp - Context ctxt = tomcat.addContext(CONTEXT_PATH_DIGEST, - System.getProperty("java.io.tmpdir")); + Context ctxt = tomcat.addContext(CONTEXT_PATH_DIGEST, System.getProperty("java.io.tmpdir")); ctxt.setSessionTimeout(SHORT_TIMEOUT_SECS); // Add protected servlet @@ -376,13 +324,11 @@ ctxt.getPipeline().addValve(new DigestAuthenticator()); } - protected static String getAuthToken( - Map> respHeaders, String token) { + protected static String getAuthToken(Map> respHeaders, String token) { final String AUTH_PREFIX = "=\""; final String AUTH_SUFFIX = "\""; - List authHeaders = - respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME); + List authHeaders = respHeaders.get(AuthenticatorBase.AUTH_HEADER_NAME); // Assume there is only one String authHeader = authHeaders.get(0); @@ -393,21 +339,12 @@ } /* - * Notes from RFC2617 - * H(data) = MD5(data) - * KD(secret, data) = H(concat(secret, ":", data)) - * A1 = unq(username-value) ":" unq(realm-value) ":" passwd - * A2 = Method ":" digest-uri-value - * request-digest = <"> < KD ( H(A1), unq(nonce-value) - ":" nc-value - ":" unq(cnonce-value) - ":" unq(qop-value) - ":" H(A2) - ) <"> + * Notes from RFC2617 H(data) = MD5(data) KD(secret, data) = H(concat(secret, ":", data)) A1 = unq(username-value) + * ":" unq(realm-value) ":" passwd A2 = Method ":" digest-uri-value request-digest = <"> < KD ( H(A1), + * unq(nonce-value) ":" nc-value ":" unq(cnonce-value) ":" unq(qop-value) ":" H(A2) ) <"> */ - private static String buildDigestResponse(String user, String pwd, - String uri, String realm, String nonce, String opaque, String nc, - String cnonce, String qop) { + private static String buildDigestResponse(String user, String pwd, String uri, String realm, String nonce, + String opaque, String nc, String cnonce, String qop) { String a1 = user + ":" + realm + ":" + pwd; String a2 = "GET:" + uri; @@ -419,8 +356,7 @@ if (qop == null) { response = digestA1 + ":" + nonce + ":" + digestA2; } else { - response = digestA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + - qop + ":" + digestA2; + response = digestA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + qop + ":" + digestA2; } String md5response = digest(response); @@ -457,8 +393,7 @@ } private static String digest(String input) { - return HexUtils.toHexString(ConcurrentMessageDigest.digestMD5( - input.getBytes(StandardCharsets.UTF_8))); + return HexUtils.toHexString(ConcurrentMessageDigest.digestMD5(input.getBytes(StandardCharsets.UTF_8))); } /* diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/TesterDigestAuthenticatorPerformance.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/TesterDigestAuthenticatorPerformance.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/TesterDigestAuthenticatorPerformance.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/TesterDigestAuthenticatorPerformance.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,6 +39,7 @@ import org.apache.catalina.startup.TesterMapRealm; import org.apache.tomcat.util.buf.HexUtils; import org.apache.tomcat.util.descriptor.web.LoginConfig; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.security.ConcurrentMessageDigest; /* @@ -50,7 +51,6 @@ private static String USER = "user"; private static String PWD = "pwd"; private static String ROLE = "role"; - private static String METHOD = "GET"; private static String URI = "/protected"; private static String CONTEXT_PATH = "/foo"; private static String CLIENT_AUTH_HEADER = "authorization"; @@ -76,8 +76,7 @@ // Create the runnables & threads for (int i = 0; i < threadCount; i++) { - runnables[i] = - new TesterRunnable(authenticator, nonce, requestCount); + runnables[i] = new TesterRunnable(authenticator, nonce, requestCount); threads[i] = new Thread(runnables[i]); } @@ -98,18 +97,15 @@ double totalTime = 0; int totalSuccess = 0; for (int i = 0; i < threadCount; i++) { - System.out.println("Thread: " + i + " Success: " + - runnables[i].getSuccess()); + System.out.println("Thread: " + i + " Success: " + runnables[i].getSuccess()); totalSuccess = totalSuccess + runnables[i].getSuccess(); totalTime = totalTime + runnables[i].getTime(); } - System.out.println("Average time per request (user): " + - totalTime/(threadCount * requestCount)); - System.out.println("Average time per request (wall): " + - wallTime/(threadCount * requestCount)); + System.out.println("Average time per request (user): " + totalTime / (threadCount * requestCount)); + System.out.println("Average time per request (wall): " + wallTime / (threadCount * requestCount)); - Assert.assertEquals(((long)requestCount) * threadCount, totalSuccess); + Assert.assertEquals(((long) requestCount) * threadCount, totalSuccess); } @Before @@ -162,18 +158,16 @@ private DigestAuthenticator authenticator; private static final String A1 = USER + ":" + REALM + ":" + PWD; - private static final String A2 = METHOD + ":" + CONTEXT_PATH + URI; - - private static final String DIGEST_A1 = HexUtils.toHexString( - ConcurrentMessageDigest.digest("MD5", A1.getBytes(StandardCharsets.UTF_8))); - private static final String DIGEST_A2 = HexUtils.toHexString( - ConcurrentMessageDigest.digest("MD5", A2.getBytes(StandardCharsets.UTF_8))); + private static final String A2 = Method.GET + ":" + CONTEXT_PATH + URI; + private static final String DIGEST_A1 = + HexUtils.toHexString(ConcurrentMessageDigest.digest("MD5", A1.getBytes(StandardCharsets.UTF_8))); + private static final String DIGEST_A2 = + HexUtils.toHexString(ConcurrentMessageDigest.digest("MD5", A2.getBytes(StandardCharsets.UTF_8))); // All init code should be in here. run() needs to be quick - TesterRunnable(DigestAuthenticator authenticator, - String nonce, int requestCount) throws Exception { + TesterRunnable(DigestAuthenticator authenticator, String nonce, int requestCount) throws Exception { this.authenticator = authenticator; this.nonce = nonce; this.requestCount = requestCount; @@ -212,15 +206,13 @@ private String buildDigestResponse(String nonce) { - String ncString = String.format("%1$08x", - Integer.valueOf(nonceCount.incrementAndGet())); + String ncString = String.format("%1$08x", Integer.valueOf(nonceCount.incrementAndGet())); String cnonce = "cnonce"; - String response = DIGEST_A1 + ":" + nonce + ":" + ncString + ":" + - cnonce + ":" + QOP + ":" + DIGEST_A2; + String response = DIGEST_A1 + ":" + nonce + ":" + ncString + ":" + cnonce + ":" + QOP + ":" + DIGEST_A2; - String md5response = HexUtils.toHexString(ConcurrentMessageDigest.digest( - "MD5", response.getBytes(StandardCharsets.UTF_8))); + String md5response = HexUtils + .toHexString(ConcurrentMessageDigest.digest("MD5", response.getBytes(StandardCharsets.UTF_8))); StringBuilder auth = new StringBuilder(); auth.append("Digest username=\""); @@ -277,7 +269,7 @@ @Override public String getMethod() { - return METHOD; + return Method.GET; } @Override diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TestAuthConfigFactoryImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,19 +41,19 @@ @Test public void testRegistrationNullLayer() { - doTestRegistration(null, "AC_1", ":AC_1"); + doTestRegistration(null, "AC_1", ":AC_1"); } @Test public void testRegistrationNullAppContext() { - doTestRegistration("L_1", null, "L_1:"); + doTestRegistration("L_1", null, "L_1:"); } @Test public void testRegistrationNullLayerAndNullAppContext() { - doTestRegistration(null, null, ":"); + doTestRegistration(null, null, ":"); } @@ -209,8 +209,8 @@ } - private void doTestRegistrationInsert(String newLayer, String newAppContext, - String expectedListenerLayer, String expectedListenerAppContext) { + private void doTestRegistrationInsert(String newLayer, String newAppContext, String expectedListenerLayer, + String expectedListenerAppContext) { // Set up AuthConfigFactory factory = new AuthConfigFactoryImpl(); AuthConfigProvider acp1 = new SimpleAuthConfigProvider(null, null); @@ -245,7 +245,7 @@ for (SimpleRegistrationListener listener : listeners) { if (listener.wasCalled()) { Assert.assertEquals(listener.layer, expectedListenerLayer); - Assert.assertEquals(listener.appContext, expectedListenerAppContext); + Assert.assertEquals(listener.appContext, expectedListenerAppContext); Assert.assertTrue(listener.wasCorrectlyCalled()); } else { Assert.assertFalse((listener.layer.equals(expectedListenerLayer) && @@ -330,7 +330,7 @@ @After public void cleanUp() { - if (oldCatalinaBase != null ) { + if (oldCatalinaBase != null) { System.setProperty(Globals.CATALINA_BASE_PROP, oldCatalinaBase); } else { System.clearProperty(Globals.CATALINA_BASE_PROP); @@ -346,19 +346,18 @@ @Test public void testRemovePersistentRegistration() { - AuthConfigFactory factory = new AuthConfigFactoryImpl(); - factory.registerConfigProvider( - SimpleAuthConfigProvider.class.getName(), null, "L_1", "AC_1", null); - String registrationId2 = factory.registerConfigProvider( - SimpleAuthConfigProvider.class.getName(), null, "L_2", "AC_2", null); - - factory.removeRegistration(registrationId2); - factory.refresh(); - - String[] registrationIds = factory.getRegistrationIDs(null); - for (String registrationId : registrationIds) { - Assert.assertNotEquals(registrationId2, registrationId); - } + AuthConfigFactory factory = new AuthConfigFactoryImpl(); + factory.registerConfigProvider(SimpleAuthConfigProvider.class.getName(), null, "L_1", "AC_1", null); + String registrationId2 = + factory.registerConfigProvider(SimpleAuthConfigProvider.class.getName(), null, "L_2", "AC_2", null); + + factory.removeRegistration(registrationId2); + factory.refresh(); + + String[] registrationIds = factory.getRegistrationIDs(null); + for (String registrationId : registrationIds) { + Assert.assertNotEquals(registrationId2, registrationId); + } } @@ -381,18 +380,18 @@ private void doTestNullClassName(boolean shouldOverrideExistingProvider, String layer, String appContext) { - AuthConfigFactory factory = new AuthConfigFactoryImpl(); - if (shouldOverrideExistingProvider) { - factory.registerConfigProvider(SimpleAuthConfigProvider.class.getName(), null, layer, appContext, null); - } - String registrationId = factory.registerConfigProvider(null, null, layer, appContext, null); - factory.refresh(); + AuthConfigFactory factory = new AuthConfigFactoryImpl(); + if (shouldOverrideExistingProvider) { + factory.registerConfigProvider(SimpleAuthConfigProvider.class.getName(), null, layer, appContext, null); + } + String registrationId = factory.registerConfigProvider(null, null, layer, appContext, null); + factory.refresh(); - String[] registrationIds = factory.getRegistrationIDs(null); - Set ids = new HashSet<>(Arrays.asList(registrationIds)); - Assert.assertTrue(ids.contains(registrationId)); - AuthConfigProvider provider = factory.getConfigProvider(layer, appContext, null); - Assert.assertNull(provider); + String[] registrationIds = factory.getRegistrationIDs(null); + Set ids = new HashSet<>(Arrays.asList(registrationIds)); + Assert.assertTrue(ids.contains(registrationId)); + AuthConfigProvider provider = factory.getConfigProvider(layer, appContext, null); + Assert.assertNull(provider); } @@ -424,8 +423,7 @@ public boolean wasCorrectlyCalled() { - return called && areTheSame(layer, layerNotified) && - areTheSame(appContext, appContextNotified); + return called && areTheSame(layer, layerNotified) && areTheSame(appContext, appContextNotified); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/jaspic/TestPersistentProviderRegistrations.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TestPersistentProviderRegistrations.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/jaspic/TestPersistentProviderRegistrations.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TestPersistentProviderRegistrations.java 2026-01-23 19:33:36.000000000 +0000 @@ -1,19 +1,19 @@ - /** - * 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. - */ +/** +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ package org.apache.catalina.authenticator.jaspic; import java.io.File; @@ -30,7 +30,7 @@ public void testLoadEmpty() { File f = new File("test/conf/jaspic-test-01.xml"); Providers result = PersistentProviderRegistrations.loadProviders(f); - Assert.assertEquals(0, result.getProviders().size()); + Assert.assertEquals(0, result.getProviders().size()); } @@ -43,14 +43,14 @@ private void validateSimple(Providers providers) { - Assert.assertEquals(1, providers.getProviders().size()); + Assert.assertEquals(1, providers.getProviders().size()); Provider p = providers.getProviders().get(0); Assert.assertEquals("a", p.getClassName()); Assert.assertEquals("b", p.getLayer()); Assert.assertEquals("c", p.getAppContext()); Assert.assertEquals("d", p.getDescription()); - Assert.assertEquals(2, p.getProperties().size()); + Assert.assertEquals(2, p.getProperties().size()); Assert.assertEquals("f", p.getProperties().get("e")); Assert.assertEquals("h", p.getProperties().get("g")); } @@ -95,7 +95,7 @@ private void validateNoLayerAndAC(Providers providers) { - Assert.assertEquals(1, providers.getProviders().size()); + Assert.assertEquals(1, providers.getProviders().size()); Provider p = providers.getProviders().get(0); Assert.assertEquals("a", p.getClassName()); Assert.assertNull(p.getLayer()); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/jaspic/TestSimpleServerAuthConfig.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TestSimpleServerAuthConfig.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/jaspic/TestSimpleServerAuthConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TestSimpleServerAuthConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -35,14 +35,12 @@ private static final Map CONFIG_PROPERTIES; static { CONFIG_PROPERTIES = new HashMap<>(); - CONFIG_PROPERTIES.put(SERVER_AUTH_MODULE_KEY_PREFIX + "1", - TesterServerAuthModuleA.class.getName()); + CONFIG_PROPERTIES.put(SERVER_AUTH_MODULE_KEY_PREFIX + "1", TesterServerAuthModuleA.class.getName()); } @Test public void testConfigOnServerAuthConfig() throws Exception { - ServerAuthConfig serverAuthConfig = - new SimpleServerAuthConfig(null, null, null, CONFIG_PROPERTIES); + ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, CONFIG_PROPERTIES); ServerAuthContext serverAuthContext = serverAuthConfig.getAuthContext(null, null, null); validateServerAuthContext(serverAuthContext); @@ -51,17 +49,16 @@ @Test public void testConfigOnGetAuthContext() throws Exception { - ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null); - ServerAuthContext serverAuthContext = - serverAuthConfig.getAuthContext(null, null, CONFIG_PROPERTIES); + ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null); + ServerAuthContext serverAuthContext = serverAuthConfig.getAuthContext(null, null, CONFIG_PROPERTIES); validateServerAuthContext(serverAuthContext); } - @Test(expected=AuthException.class) + @Test(expected = AuthException.class) public void testConfigNone() throws Exception { - ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null); + ServerAuthConfig serverAuthConfig = new SimpleServerAuthConfig(null, null, null, null); serverAuthConfig.getAuthContext(null, null, null); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/authenticator/jaspic/TesterServerAuthModuleA.java tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TesterServerAuthModuleA.java --- tomcat10-10.1.34/test/org/apache/catalina/authenticator/jaspic/TesterServerAuthModuleA.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/authenticator/jaspic/TesterServerAuthModuleA.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,14 +32,13 @@ private StringBuilder trace = new StringBuilder("init()-"); @Override - public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, - Subject serviceSubject) throws AuthException { + public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) + throws AuthException { return null; } @Override - public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) - throws AuthException { + public AuthStatus secureResponse(MessageInfo messageInfo, Subject serviceSubject) throws AuthException { return null; } @@ -50,9 +49,8 @@ } @Override - public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, - CallbackHandler handler, @SuppressWarnings("rawtypes") Map options) - throws AuthException { + public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy, CallbackHandler handler, + @SuppressWarnings("rawtypes") Map options) throws AuthException { // NO-OP } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestConnector.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestConnector.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestConnector.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestConnector.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,6 +36,7 @@ import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.http.Method; /** * Test cases for {@link Connector}. @@ -47,8 +48,7 @@ Tomcat tomcat = getTomcatInstance(); Context root = tomcat.addContext("", TEMP_DIR); - Wrapper w = - Tomcat.addServlet(root, "tester", new TesterServlet()); + Wrapper w = Tomcat.addServlet(root, "tester", new TesterServlet()); w.setAsyncSupported(true); root.addServletMappingDecoded("/", "tester"); @@ -68,8 +68,7 @@ connector.stop(); try { - rc = getUrl("http://localhost:" + getPort() + "/", bc, 1000, - null, null); + rc = getUrl("http://localhost:" + getPort() + "/", bc, 1000, null, null); } catch (SocketTimeoutException ste) { // May also see this with NIO // Make sure the test passes if we do @@ -101,7 +100,7 @@ } - @Test(expected=LifecycleException.class) + @Test(expected = LifecycleException.class) public void testInvalidProtocolThrows() throws Exception { doTestInvalidProtocol(true); } @@ -121,7 +120,7 @@ } - @Test(expected=LifecycleException.class) + @Test(expected = LifecycleException.class) public void testDuplicatePortThrows() throws Exception { doTestDuplicatePort(true); } @@ -198,14 +197,13 @@ ByteChunk bc = new ByteChunk(); Map> respHeaders = new HashMap<>(); - int rc = methodUrl("http://localhost:" + getPort() + "/index.html", - bc, 30000, null, respHeaders, "OPTIONS"); + int rc = methodUrl("http://localhost:" + getPort() + "/index.html", bc, 30000, null, respHeaders, Method.OPTIONS); Assert.assertEquals(200, rc); boolean foundTrace = false; for (String header : respHeaders.get("Allow")) { - if (header.contains("TRACE")) { + if (header.contains(Method.TRACE)) { foundTrace = true; break; } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteAdapter.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteAdapter.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteAdapter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteAdapter.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,6 +31,7 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; import org.apache.catalina.startup.SimpleHttpClient; @@ -52,6 +53,7 @@ TEXT_8K = sb.toString(); BYTES_8K = TEXT_8K.getBytes(StandardCharsets.UTF_8); } + @Test public void testPathParmsRootNone() throws Exception { pathParamTest("/", "none"); @@ -153,8 +155,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); PrintWriter pw = resp.getWriter(); String sessionId = req.getRequestedSessionId(); @@ -180,8 +181,7 @@ pathParamExtensionTest("/testapp/blah;x=y/blah.txt", "none"); } - private void pathParamExtensionTest(String path, String expected) - throws Exception { + private void pathParamExtensionTest(String path, String expected) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -227,8 +227,7 @@ doTestUriDecoding("/foo%ed%a0%80", "UTF-8", null); } - private void doTestUriDecoding(String path, String encoding, - String expectedPathInfo) throws Exception{ + private void doTestUriDecoding(String path, String encoding, String expectedPathInfo) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -244,8 +243,7 @@ tomcat.start(); - int rc = getUrl("http://localhost:" + getPort() + path, - new ByteChunk(), null); + int rc = getUrl("http://localhost:" + getPort() + path, new ByteChunk(), null); if (expectedPathInfo == null) { // Invalid URI @@ -268,8 +266,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Not thread safe. Concurrent requests to this servlet will // over-write all the results but the last processed. @@ -300,11 +297,15 @@ } }; - String request = "GET /async HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: a" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /async HTTP/1.1" + CRLF + + "Host: a" + CRLF + + CRLF; + // @formatter:on client.setPort(getPort()); - client.setRequest(new String[] {request}); + client.setRequest(new String[] { request }); client.connect(); client.sendRequest(); @@ -323,8 +324,7 @@ long startTime = System.nanoTime(); t.join(5000); long endTime = System.nanoTime(); - log.info("Waited for servlet thread to stop for " - + (endTime - startTime) / 1000000 + " ms"); + log.info("Waited for servlet thread to stop for " + (endTime - startTime) / 1000000 + " ms"); Assert.assertTrue(servlet.isCompleted()); } @@ -377,8 +377,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); @@ -398,7 +397,7 @@ // because the client has gone away). In some cases // there may be a large (ish) buffer to fill before // the write fails. - for (int j = 0 ; j < 8; j++) { + for (int j = 0; j < 8; j++) { os.write(BYTES_8K); } os.flush(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteAdapterCanonicalization.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteAdapterCanonicalization.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteAdapterCanonicalization.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteAdapterCanonicalization.java 2026-01-23 19:33:36.000000000 +0000 @@ -180,11 +180,13 @@ tomcat.start(); Client client = new Client(tomcat.getConnector().getLocalPort(), canonicalizedURI); + // @formatter:off client.setRequest(new String[] { "GET " + requestURI + " HTTP/1.1" + CRLF + "Host: localhost" + CRLF + CRLF - }); + }); + // @formatter:on client.setResponseBodyEncoding(StandardCharsets.UTF_8); client.connect(); @@ -207,8 +209,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); resp.getWriter().write(req.getServletPath()); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteAdapterRequestFuzzing.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteAdapterRequestFuzzing.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteAdapterRequestFuzzing.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteAdapterRequestFuzzing.java 2026-01-23 19:33:36.000000000 +0000 @@ -58,6 +58,7 @@ public static Collection parameters() { List parameterSets = new ArrayList<>(); + // @formatter:off parameterSets.add(new Object[] { "GET /00 HTTP/1.1", "Host: lÿ#" + CRLF, "400" } ); @@ -94,7 +95,7 @@ "Host: localhost" + CRLF + COOKIE_250, "400" } ); - + // @formatter:on return parameterSets; } @@ -121,7 +122,7 @@ tomcat.start(); Client client = new Client(tomcat.getConnector().getLocalPort()); - client.setRequest(new String[] {requestLine + CRLF, headers + CRLF}); + client.setRequest(new String[] { requestLine + CRLF, headers + CRLF }); client.connect(); try { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteInputStream.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteInputStream.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteInputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteInputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,8 +47,7 @@ ByteChunk bc = new ByteChunk(); String requestBody = "HelloWorld"; - int rc = postUrl(requestBody.getBytes(StandardCharsets.UTF_8), - "http://localhost:" + getPort() + "/", bc, null); + int rc = postUrl(requestBody.getBytes(StandardCharsets.UTF_8), "http://localhost:" + getPort() + "/", bc, null); Assert.assertEquals(HttpServletResponse.SC_OK, rc); Assert.assertTrue(requestBody.equals(bc.toString())); } @@ -58,8 +57,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { CoyoteInputStream is = (CoyoteInputStream) req.getInputStream(); ByteBuffer buffer = ByteBuffer.allocate(256); is.read(buffer); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteOutputStream.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteOutputStream.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestCoyoteOutputStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestCoyoteOutputStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -123,8 +123,8 @@ } } - private void doNonBlockingTest(int asyncWriteTarget, int syncWriteTarget, - boolean useContainerThreadToSetListener) throws Exception { + private void doNonBlockingTest(int asyncWriteTarget, int syncWriteTarget, boolean useContainerThreadToSetListener) + throws Exception { Tomcat tomcat = getTomcatInstance(); @@ -133,8 +133,7 @@ new NonBlockingWriteServlet(asyncWriteTarget, useContainerThreadToSetListener)); w.setAsyncSupported(true); root.addServletMappingDecoded("/nbWrite", "nbWrite"); - Tomcat.addServlet(root, "write", - new BlockingWriteServlet(asyncWriteTarget, syncWriteTarget)); + Tomcat.addServlet(root, "write", new BlockingWriteServlet(asyncWriteTarget, syncWriteTarget)); w.setAsyncSupported(true); root.addServletMappingDecoded("/write", "write"); @@ -142,8 +141,7 @@ ByteChunk bc = new ByteChunk(); // Extend timeout to 5 mins for debugging - int rc = getUrl("http://localhost:" + getPort() + "/nbWrite", bc, - 300000, null, null); + int rc = getUrl("http://localhost:" + getPort() + "/nbWrite", bc, 300000, null, null); int totalCount = asyncWriteTarget + syncWriteTarget; StringBuilder sb = new StringBuilder(totalCount * 16); @@ -166,15 +164,13 @@ private final AtomicInteger asyncWriteCount = new AtomicInteger(0); private final boolean useContainerThreadToSetListener; - NonBlockingWriteServlet(int asyncWriteTarget, - boolean useContainerThreadToSetListener) { + NonBlockingWriteServlet(int asyncWriteTarget, boolean useContainerThreadToSetListener) { this.asyncWriteTarget = asyncWriteTarget; this.useContainerThreadToSetListener = useContainerThreadToSetListener; } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); @@ -192,14 +188,11 @@ } } - private void doAsyncWrite(AsyncContext asyncCtxt, - ServletOutputStream sos) throws IOException { + private void doAsyncWrite(AsyncContext asyncCtxt, ServletOutputStream sos) throws IOException { while (sos.isReady()) { int next = asyncWriteCount.getAndIncrement(); if (next < asyncWriteTarget) { - sos.write( - ("OK - " + next + System.lineSeparator()).getBytes( - StandardCharsets.UTF_8)); + sos.write(("OK - " + next + System.lineSeparator()).getBytes(StandardCharsets.UTF_8)); sos.flush(); } else { asyncCtxt.dispatch("/write"); @@ -229,8 +222,7 @@ private final AsyncContext asyncCtxt; private final ServletOutputStream sos; - MyWriteListener(AsyncContext asyncCtxt, - ServletOutputStream sos) { + MyWriteListener(AsyncContext asyncCtxt, ServletOutputStream sos) { this.asyncCtxt = asyncCtxt; this.sos = sos; } @@ -261,16 +253,14 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); ServletOutputStream sos = resp.getOutputStream(); for (int i = start; i < start + len; i++) { - sos.write(("OK - " + i + System.lineSeparator()).getBytes( - StandardCharsets.UTF_8)); + sos.write(("OK - " + i + System.lineSeparator()).getBytes(StandardCharsets.UTF_8)); } } } @@ -280,8 +270,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { CoyoteOutputStream os = (CoyoteOutputStream) resp.getOutputStream(); File file = new File("test/org/apache/catalina/connector/test_content.txt"); try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestInputBuffer.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestInputBuffer.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestInputBuffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestInputBuffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -79,8 +79,7 @@ } - private void doUtf8BodyTest(String description, int[] input, - String expected) throws Exception { + private void doUtf8BodyTest(String description, int[] input, String expected) throws Exception { byte[] bytes = new byte[input.length]; for (int i = 0; i < input.length; i++) { @@ -88,8 +87,7 @@ } ByteChunk bc = new ByteChunk(); - int rc = postUrl(bytes, "http://localhost:" + getPort() + "/test", bc, - null); + int rc = postUrl(bytes, "http://localhost:" + getPort() + "/test", bc, null); if (expected == null) { Assert.assertEquals(description, HttpServletResponse.SC_OK, rc); @@ -108,15 +106,13 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Should use POST resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); Reader r = req.getReader(); @@ -145,8 +141,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { StringBuilder builder = new StringBuilder(); try (BufferedReader reader = req.getReader()) { String line; diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestKeepAliveCount.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestKeepAliveCount.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestKeepAliveCount.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestKeepAliveCount.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,7 +53,7 @@ private synchronized void init() { if (init) { - return; + return; } Tomcat tomcat = getTomcatInstance(); @@ -77,11 +77,14 @@ // Send request in two parts String[] request = new String[1]; + // @formatter:off request[0] = - "GET /test HTTP/1.0" + CRLF + CRLF; + "GET /test HTTP/1.0" + CRLF + + CRLF; + // @formatter:on setRequest(request); processRequest(false); // blocks until response has been read - boolean passed = (this.readLine()==null); + boolean passed = (this.readLine() == null); // Close the connection disconnect(); reset(); @@ -100,19 +103,22 @@ // Send request in two parts String[] request = new String[1]; + // @formatter:off request[0] = "GET /test HTTP/1.1" + CRLF + "Host: localhost" + CRLF + - "Connection: Keep-Alive" + CRLF+ - "Keep-Alive: 300"+ CRLF+ CRLF; + "Connection: Keep-Alive" + CRLF + + "Keep-Alive: 300"+ CRLF + + CRLF; + // @formatter:on setRequest(request); - for (int i=0; i<5; i++) { + for (int i = 0; i < 5; i++) { processRequest(false); // blocks until response has been read - Assert.assertTrue(getResponseLine()!=null && getResponseLine().startsWith("HTTP/1.1 200 ")); + Assert.assertTrue(getResponseLine() != null && getResponseLine().startsWith("HTTP/1.1 200 ")); } - boolean passed = (this.readLine()==null); + boolean passed = (this.readLine() == null); // Close the connection disconnect(); reset(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestMaxConnections.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestMaxConnections.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestMaxConnections.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestMaxConnections.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,9 +40,9 @@ public void testConnector() throws Exception { init(); ConnectThread[] t = new ConnectThread[10]; - for (int i=0; ibug - * 38118. + * Test case for bug 38118. */ @Test public void testBug38113() throws Exception { @@ -297,8 +297,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); PrintWriter pw = resp.getWriter(); pw.print("QueryString=" + req.getQueryString()); @@ -306,11 +305,10 @@ } /* - * Test case for {@link Request#login(String, String)} and - * {@link Request#logout()}. + * Test case for {@link Request#login(String, String)} and {@link Request#logout()}. */ @Test - public void testLoginLogout() throws Exception{ + public void testLoginLogout() throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -342,25 +340,24 @@ private static final String OK = "OK"; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.login(USER, PWD); if (!req.getRemoteUser().equals(USER)) { - throw new ServletException(); + throw new ServletException(); } if (!req.getUserPrincipal().getName().equals(USER)) { - throw new ServletException(); + throw new ServletException(); } req.logout(); if (req.getRemoteUser() != null) { - throw new ServletException(); + throw new ServletException(); } if (req.getUserPrincipal() != null) { - throw new ServletException(); + throw new ServletException(); } resp.getWriter().write(OK); @@ -371,8 +368,7 @@ @Test public void testBug49424NoChunking() throws Exception { Tomcat tomcat = getTomcatInstance(); - Context root = tomcat.addContext("", - System.getProperty("java.io.tmpdir")); + Context root = tomcat.addContext("", System.getProperty("java.io.tmpdir")); Tomcat.addServlet(root, "Bug37794", new Bug37794Servlet()); root.addServletMappingDecoded("/", "Bug37794"); tomcat.start(); @@ -385,8 +381,7 @@ @Test public void testBug49424WithChunking() throws Exception { Tomcat tomcat = getTomcatInstance(); - Context root = tomcat.addContext("", - System.getProperty("java.io.tmpdir")); + Context root = tomcat.addContext("", System.getProperty("java.io.tmpdir")); Tomcat.addServlet(root, "Bug37794", new Bug37794Servlet()); root.addServletMappingDecoded("/", "Bug37794"); tomcat.start(); @@ -398,23 +393,18 @@ } /** - * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=48692 - * PUT requests should be able to fetch request parameters coming from - * the request body (when properly configured using the new parseBodyMethod - * setting). + * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=48692 PUT requests should be able to fetch request + * parameters coming from the request body (when properly configured using the new parseBodyMethod setting). */ @Test public void testBug48692() { Bug48692Client client = new Bug48692Client(); // Make sure GET works properly - client.doRequest("GET", "foo=bar", null, null, false); + client.doRequest(Method.GET, "foo=bar", null, null, false); - Assert.assertTrue("Non-200 response for GET request", - client.isResponse200()); - Assert.assertEquals("Incorrect response for GET request", - "foo=bar", - client.getResponseBody()); + Assert.assertTrue("Non-200 response for GET request", client.isResponse200()); + Assert.assertEquals("Incorrect response for GET request", "foo=bar", client.getResponseBody()); client.reset(); @@ -422,46 +412,36 @@ // Make sure POST works properly // // POST with separate GET and POST parameters - client.doRequest("POST", "foo=bar", Globals.CONTENT_TYPE_FORM_URL_ENCODING, "bar=baz", true); + client.doRequest(Method.POST, "foo=bar", Globals.CONTENT_TYPE_FORM_URL_ENCODING, "bar=baz", true); - Assert.assertTrue("Non-200 response for POST request", - client.isResponse200()); - Assert.assertEquals("Incorrect response for POST request", - "bar=baz,foo=bar", - client.getResponseBody()); + Assert.assertTrue("Non-200 response for POST request", client.isResponse200()); + Assert.assertEquals("Incorrect response for POST request", "bar=baz,foo=bar", client.getResponseBody()); client.reset(); // POST with overlapping GET and POST parameters - client.doRequest("POST", "foo=bar&bar=foo", Globals.CONTENT_TYPE_FORM_URL_ENCODING, "bar=baz&foo=baz", true); + client.doRequest(Method.POST, "foo=bar&bar=foo", Globals.CONTENT_TYPE_FORM_URL_ENCODING, "bar=baz&foo=baz", true); - Assert.assertTrue("Non-200 response for POST request", - client.isResponse200()); - Assert.assertEquals("Incorrect response for POST request", - "bar=baz,bar=foo,foo=bar,foo=baz", - client.getResponseBody()); + Assert.assertTrue("Non-200 response for POST request", client.isResponse200()); + Assert.assertEquals("Incorrect response for POST request", "bar=baz,bar=foo,foo=bar,foo=baz", + client.getResponseBody()); client.reset(); // PUT without POST-style parsing - client.doRequest("PUT", "foo=bar&bar=foo", Globals.CONTENT_TYPE_FORM_URL_ENCODING, "bar=baz&foo=baz", false); + client.doRequest(Method.PUT, "foo=bar&bar=foo", Globals.CONTENT_TYPE_FORM_URL_ENCODING, "bar=baz&foo=baz", false); - Assert.assertTrue("Non-200 response for PUT/noparse request", - client.isResponse200()); - Assert.assertEquals("Incorrect response for PUT request", - "bar=foo,foo=bar", - client.getResponseBody()); + Assert.assertTrue("Non-200 response for PUT/noparse request", client.isResponse200()); + Assert.assertEquals("Incorrect response for PUT request", "bar=foo,foo=bar", client.getResponseBody()); client.reset(); // PUT with POST-style parsing - client.doRequest("PUT", "foo=bar&bar=foo", Globals.CONTENT_TYPE_FORM_URL_ENCODING, "bar=baz&foo=baz", true); + client.doRequest(Method.PUT, "foo=bar&bar=foo", Globals.CONTENT_TYPE_FORM_URL_ENCODING, "bar=baz&foo=baz", true); - Assert.assertTrue("Non-200 response for PUT request", - client.isResponse200()); - Assert.assertEquals("Incorrect response for PUT/parse request", - "bar=baz,bar=foo,foo=bar,foo=baz", - client.getResponseBody()); + Assert.assertTrue("Non-200 response for PUT request", client.isResponse200()); + Assert.assertEquals("Incorrect response for PUT/parse request", "bar=baz,bar=foo,foo=bar,foo=baz", + client.getResponseBody()); client.reset(); } @@ -469,15 +449,13 @@ @Test public void testBug54984() throws Exception { Tomcat tomcat = getTomcatInstance(); - Context root = tomcat.addContext("", - System.getProperty("java.io.tmpdir")); + Context root = tomcat.addContext("", System.getProperty("java.io.tmpdir")); root.setAllowCasualMultipartParsing(true); Tomcat.addServlet(root, "Bug54984", new Bug54984Servlet()); root.addServletMappingDecoded("/", "Bug54984"); tomcat.start(); - HttpURLConnection conn = getConnection("http://localhost:" + getPort() - + "/parseParametersBeforeParseParts"); + HttpURLConnection conn = getConnection("http://localhost:" + getPort() + "/parseParametersBeforeParseParts"); prepareRequestBug54984(conn); @@ -502,33 +480,30 @@ private static final long serialVersionUID = 1L; /** - * Only interested in the parameters and values for requests. - * Note: echos parameters in alphabetical order. + * Only interested in the parameters and values for requests. Note: echos parameters in alphabetical order. */ @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Just echo the parameters and values back as plain text resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); PrintWriter out = resp.getWriter(); - TreeMap parameters = - new TreeMap<>(req.getParameterMap()); + TreeMap parameters = new TreeMap<>(req.getParameterMap()); boolean first = true; - for(String name: parameters.keySet()) { + for (String name : parameters.keySet()) { String[] values = req.getParameterValues(name); Arrays.sort(values); for (String value : values) { if (first) { - first = false; + first = false; } else { - out.print(","); + out.print(","); } out.print(name + "=" + value); @@ -546,7 +521,7 @@ private synchronized void init() throws Exception { if (init) { - return; + return; } Tomcat tomcat = getTomcatInstance(); @@ -560,45 +535,39 @@ init = true; } - private Exception doRequest(String method, - String queryString, - String contentType, - String requestBody, - boolean allowBody) { + private Exception doRequest(String method, String queryString, String contentType, String requestBody, + boolean allowBody) { Tomcat tomcat = getTomcatInstance(); try { init(); - if(allowBody) { - tomcat.getConnector().setParseBodyMethods(method); - } - else { - tomcat.getConnector().setParseBodyMethods(""); // never parse + if (allowBody) { + tomcat.getConnector().setParseBodyMethods(method); + } else { + tomcat.getConnector().setParseBodyMethods(""); // never parse } // Open connection connect(); // Re-encode the request body so that bytes = characters - if(null != requestBody) { - requestBody = new String(requestBody.getBytes("UTF-8"), "ASCII"); + if (null != requestBody) { + requestBody = new String(requestBody.getBytes("UTF-8"), "ASCII"); } // Send specified request body using method + // @formatter:off String[] request = { - ( - method + " http://localhost:" + getPort() + "/echo" - + (null == queryString ? "" : ("?" + queryString)) - + " HTTP/1.1" + CRLF - + "Host: localhost:" + getPort() + CRLF - + (null == contentType ? "" - : ("Content-Type: " + contentType + CRLF)) - + "Connection: close" + CRLF - + (null == requestBody ? "" : "Content-Length: " + requestBody.length() + CRLF) - + CRLF - + (null == requestBody ? "" : requestBody) - ) + method + " http://localhost:" + getPort() + "/echo" + + (null == queryString ? "" : ("?" + queryString)) + " HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + (null == contentType ? "" : ("Content-Type: " + contentType + CRLF)) + + "Connection: close" + CRLF + + (null == requestBody ? "" : "Content-Length: " + requestBody.length() + CRLF) + + CRLF + + (null == requestBody ? "" : requestBody) }; + // @formatter:on setRequest(request); processRequest(); // blocks until response has been read @@ -621,7 +590,7 @@ URL postURL; postURL = new URL(query); HttpURLConnection conn = (HttpURLConnection) postURL.openConnection(); - conn.setRequestMethod("POST"); + conn.setRequestMethod(Method.POST); conn.setDoInput(true); conn.setDoOutput(true); @@ -636,8 +605,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); if (req.getRequestURI().endsWith("parseParametersBeforeParseParts")) { @@ -653,11 +621,9 @@ } } - private void prepareRequestBug54984(HttpURLConnection conn) - throws Exception { + private void prepareRequestBug54984(HttpURLConnection conn) throws Exception { String boundary = "-----" + System.currentTimeMillis(); - conn.setRequestProperty("Content-Type", - "multipart/form-data; boundary=" + boundary); + conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); try (OutputStreamWriter osw = new OutputStreamWriter(conn.getOutputStream(), "UTF-8"); PrintWriter writer = new PrintWriter(osw, true)) { @@ -675,8 +641,7 @@ } } - private void checkResponseBug54984(HttpURLConnection conn) - throws Exception { + private void checkResponseBug54984(HttpURLConnection conn) throws Exception { List response = new ArrayList<>(); int status = conn.getResponseCode(); if (status == HttpURLConnection.HTTP_OK) { @@ -853,8 +818,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.getWriter().print(req.getContextPath()); } @@ -874,8 +838,7 @@ } /* - * Reverse header order of getLocaleMultipleHeaders01() and make sure the - * result is the same. + * Reverse header order of getLocaleMultipleHeaders01() and make sure the result is the same. */ @Test public void getLocaleMultipleHeaders02() throws Exception { @@ -903,8 +866,7 @@ } - private void doTestGetReader(String userAgentCharacterEncoding, boolean expect200) - throws Exception { + private void doTestGetReader(String userAgentCharacterEncoding, boolean expect200) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -927,7 +889,7 @@ ByteChunk bc = new ByteChunk(); Map> reqHeaders = new HashMap<>(); reqHeaders.put("Content-Type", - Arrays.asList(new String[] {"text/plain;charset=" + userAgentCharacterEncoding})); + Arrays.asList(new String[] { "text/plain;charset=" + userAgentCharacterEncoding })); int rc = postUrl(body, "http://localhost:" + getPort() + "/", bc, reqHeaders, null); @@ -944,15 +906,13 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // This is intended for POST requests resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Container will handle any errors req.getReader(); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestResponse.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestResponse.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestResponse.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestResponse.java 2026-01-23 19:33:36.000000000 +0000 @@ -68,8 +68,7 @@ if (header.getKey() == null) { // Expected if this is the response line List values = header.getValue(); - if (values.size() == 1 && - values.get(0).startsWith("HTTP/1.1")) { + if (values.size() == 1 && values.get(0).startsWith("HTTP/1.1")) { continue; } Assert.fail("Null header name detected for value " + values); @@ -80,7 +79,7 @@ int count = 0; for (String headerName : headers.keySet()) { if ("Set-Cookie".equals(headerName)) { - count ++; + count++; } } Assert.assertEquals(1, count); @@ -90,8 +89,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(true); session.invalidate(); req.getSession(true); @@ -125,8 +123,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter pw = resp.getWriter(); resp.setHeader("Content-Type", "text/plain;charset=UTF-8"); @@ -169,8 +166,7 @@ String result = resp.toAbsolute("./bar.html"); - Assert.assertEquals("http://localhost:8080/level1/level2/bar.html", - result); + Assert.assertEquals("http://localhost:8080/level1/level2/bar.html", result); } @@ -210,7 +206,7 @@ } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testBug53062e() throws Exception { Request req = new TesterRequest(); Response resp = new Response(); @@ -228,8 +224,7 @@ String result = resp.toAbsolute("bar.html"); - Assert.assertEquals( - "http://localhost:8080/level1/level2/bar.html", result); + Assert.assertEquals("http://localhost:8080/level1/level2/bar.html", result); } @@ -241,8 +236,7 @@ String result = resp.toAbsolute("bar.html?x=/../"); - Assert.assertEquals( - "http://localhost:8080/level1/level2/bar.html?x=/../", result); + Assert.assertEquals("http://localhost:8080/level1/level2/bar.html?x=/../", result); } @@ -254,9 +248,7 @@ String result = resp.toAbsolute("bar.html?x=/../../"); - Assert.assertEquals( - "http://localhost:8080/level1/level2/bar.html?x=/../../", - result); + Assert.assertEquals("http://localhost:8080/level1/level2/bar.html?x=/../../", result); } @@ -268,8 +260,7 @@ String result = resp.toAbsolute("./.?x=/../../"); - Assert.assertEquals( - "http://localhost:8080/level1/level2/?x=/../../", result); + Assert.assertEquals("http://localhost:8080/level1/level2/?x=/../../", result); } @@ -293,9 +284,7 @@ String result = resp.toAbsolute("./..?x=/../.."); - Assert.assertEquals( - "http://localhost:8080/level1/?x=/../..", - result); + Assert.assertEquals("http://localhost:8080/level1/?x=/../..", result); } @@ -307,8 +296,7 @@ String result = resp.toAbsolute("bar.html#/../"); - Assert.assertEquals( - "http://localhost:8080/level1/level2/bar.html#/../", result); + Assert.assertEquals("http://localhost:8080/level1/level2/bar.html#/../", result); } @@ -320,8 +308,7 @@ String result = resp.toAbsolute("bar.html#/../../"); - Assert.assertEquals( - "http://localhost:8080/level1/level2/bar.html#/../../", result); + Assert.assertEquals("http://localhost:8080/level1/level2/bar.html#/../../", result); } @@ -333,8 +320,7 @@ String result = resp.toAbsolute("./.#/../../"); - Assert.assertEquals( - "http://localhost:8080/level1/level2/#/../../", result); + Assert.assertEquals("http://localhost:8080/level1/level2/#/../../", result); } @@ -505,7 +491,7 @@ } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testEncodeRedirectURL05() throws Exception { doTestEncodeRedirectURL("../../..", "throws IAE"); } @@ -611,7 +597,7 @@ // Do test response.sendRedirect(input); String location = response.getHeader("Location"); - Assert.assertEquals(expectedLocation, location); + Assert.assertEquals(expectedLocation, location); } @@ -711,7 +697,7 @@ } - @Test(expected = UnsupportedEncodingException.class) + @Test(expected = UnsupportedEncodingException.class) public void testSetCharacterEncoding06() throws IOException { Response response = setupResponse(); @@ -806,7 +792,7 @@ } - @Test(expected = UnsupportedEncodingException.class) + @Test(expected = UnsupportedEncodingException.class) public void testSetContentType05() throws IOException { Response response = setupResponse(); response.getContext().addLocaleEncodingMappingParameter(Locale.UK.toLanguageTag(), UNKNOWN); @@ -885,7 +871,7 @@ } - @Test(expected = UnsupportedEncodingException.class) + @Test(expected = UnsupportedEncodingException.class) public void testSetLocale05() throws IOException { Response response = setupResponse(); @@ -1003,12 +989,9 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - resp.setContentType("multipart/related;" + - "boundary=1_4F50BD36_CDF8C28;" + - "Start=\"<31671603.smil>\";" + + resp.setContentType("multipart/related;" + "boundary=1_4F50BD36_CDF8C28;" + "Start=\"<31671603.smil>\";" + "Type=\"application/smil;charset=UTF-8\""); // Should be ISO-8859-1 because the charset in the above is part @@ -1025,9 +1008,9 @@ private static final class ErrorPageServlet extends HttpServlet { private static final long serialVersionUID = 1L; + @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (resp.getStatus() == 404) { resp.setStatus(202); } else { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestResponsePerformance.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestResponsePerformance.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestResponsePerformance.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestResponsePerformance.java 2026-01-23 19:33:36.000000000 +0000 @@ -43,7 +43,7 @@ doUri(); // Note: With Java 11 the 'homebrew' approach is consistently 3-4 times faster on both MacOS (Intel) and Linux - // With Java 22 EA the 'homebrew' approach is consistently a little over 2x faster on MacOS (M1) + // With Java 22 EA the 'homebrew' approach is consistently a little over 2x faster on MacOS (M1) // To allow for timing differences between runs, a "best of n" approach // is taken for this test @@ -77,8 +77,7 @@ private long doUri() { long start = System.currentTimeMillis(); for (int i = 0; i < ITERATIONS; i++) { - URI base = URI.create( - "http://localhost:8080/level1/level2/foo.html"); + URI base = URI.create("http://localhost:8080/level1/level2/foo.html"); base.resolve(URI.create("bar.html")).toASCIIString(); } return System.currentTimeMillis() - start; diff -Nru tomcat10-10.1.34/test/org/apache/catalina/connector/TestSendFile.java tomcat10-10.1.52/test/org/apache/catalina/connector/TestSendFile.java --- tomcat10-10.1.34/test/org/apache/catalina/connector/TestSendFile.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/connector/TestSendFile.java 2026-01-23 19:33:36.000000000 +0000 @@ -72,16 +72,14 @@ tomcat.start(); ByteChunk bc = new ByteChunk(); - Map> respHeaders = new HashMap<>(); + Map> respHeaders = new HashMap<>(); for (int i = 0; i < ITERATIONS; i++) { long start = System.currentTimeMillis(); - int rc = getUrl("http://localhost:" + getPort() + "/servlet" + i, bc, null, - respHeaders); + int rc = getUrl("http://localhost:" + getPort() + "/servlet" + i, bc, null, respHeaders); Assert.assertEquals(HttpServletResponse.SC_OK, rc); - System.out.println("Client received " + bc.getLength() + " bytes in " - + (System.currentTimeMillis() - start) + " ms."); - Assert.assertEquals("Expected [" + EXPECTED_CONTENT_LENGTH * (i + 1L) + - "], was [" + bc.getLength() + "]", + System.out.println( + "Client received " + bc.getLength() + " bytes in " + (System.currentTimeMillis() - start) + " ms."); + Assert.assertEquals("Expected [" + EXPECTED_CONTENT_LENGTH * (i + 1L) + "], was [" + bc.getLength() + "]", EXPECTED_CONTENT_LENGTH * (i + 1L), bc.getLength()); bc.recycle(); @@ -102,8 +100,7 @@ } w.flush(); } - System.out.println( - "Created file:" + f.getAbsolutePath() + " with " + f.length() + " bytes."); + System.out.println("Created file:" + f.getAbsolutePath() + " with " + f.length() + " bytes."); return f; } @@ -120,8 +117,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("'application/octet-stream"); resp.setCharacterEncoding("ISO-8859-1"); @@ -143,8 +139,8 @@ written += len; } } while (len > 0); - System.out.println("Server Wrote " + written + " bytes in " - + (System.currentTimeMillis() - start) + " ms."); + System.out.println( + "Server Wrote " + written + " bytes in " + (System.currentTimeMillis() - start) + " ms."); } } } @@ -164,21 +160,16 @@ ByteChunk bc = new ByteChunk(); try { - getUrl("http://localhost:" + getPort() + "/test/?" + Globals.SENDFILE_SUPPORTED_ATTR - + "=true", bc, null); - } catch (IOException e) { + getUrl("http://localhost:" + getPort() + "/test/?" + Globals.SENDFILE_SUPPORTED_ATTR + "=true", bc, null); + } catch (IOException ioe) { // Ignore possible IOE due to file delete on the server - System.out.println("Ignored: " + e.getMessage()); + System.out.println("Ignored: " + ioe.getMessage()); } CountDownLatch latch = new CountDownLatch(2); List throwables = new CopyOnWriteArrayList<>(); - new Thread( - new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, throwables)) - .start(); - new Thread( - new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, throwables)) - .start(); + new Thread(new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, throwables)).start(); + new Thread(new RequestExecutor("http://localhost:" + getPort() + "/test/", latch, throwables)).start(); latch.await(3000, TimeUnit.MILLISECONDS); @@ -196,8 +187,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (Boolean.valueOf(req.getParameter(Globals.SENDFILE_SUPPORTED_ATTR)).booleanValue()) { resp.setContentType("'application/octet-stream"); resp.setCharacterEncoding("ISO-8859-1"); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContext.java tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContext.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,8 +49,7 @@ public void testBug53257() throws Exception { getTomcatInstanceTestWebapp(false, true); - ByteChunk res = getUrl("http://localhost:" + getPort() + - "/test/bug53257/index.jsp"); + ByteChunk res = getUrl("http://localhost:" + getPort() + "/test/bug53257/index.jsp"); String result = res.toString(); String[] lines = result.split("\n"); @@ -67,8 +66,7 @@ getTomcatInstanceTestWebapp(false, true); ByteChunk res = new ByteChunk(); - int rc = getUrl("http://localhost:" + getPort() + - "/test/bug5nnnn/bug53467%5D.jsp", res, null); + int rc = getUrl("http://localhost:" + getPort() + "/test/bug5nnnn/bug53467%5D.jsp", res, null); Assert.assertEquals(HttpServletResponse.SC_OK, rc); Assert.assertTrue(res.toString().contains("

    OK

    ")); @@ -103,8 +101,7 @@ public void testGetJspConfigDescriptor() throws Exception { Tomcat tomcat = getTomcatInstanceTestWebapp(false, false); - StandardContext standardContext = - (StandardContext) tomcat.getHost().findChildren()[0]; + StandardContext standardContext = (StandardContext) tomcat.getHost().findChildren()[0]; ServletContext servletContext = standardContext.getServletContext(); @@ -119,8 +116,7 @@ public void testJspPropertyGroupsAreIsolated() throws Exception { Tomcat tomcat = getTomcatInstanceTestWebapp(false, false); - StandardContext standardContext = - (StandardContext) tomcat.getHost().findChildren()[0]; + StandardContext standardContext = (StandardContext) tomcat.getHost().findChildren()[0]; ServletContext servletContext = standardContext.getServletContext(); @@ -128,10 +124,8 @@ tomcat.start(); - JspConfigDescriptor jspConfigDescriptor = - servletContext.getJspConfigDescriptor(); - Collection propertyGroups = - jspConfigDescriptor.getJspPropertyGroups(); + JspConfigDescriptor jspConfigDescriptor = servletContext.getJspConfigDescriptor(); + Collection propertyGroups = jspConfigDescriptor.getJspPropertyGroups(); Assert.assertFalse(propertyGroups.isEmpty()); propertyGroups.clear(); @@ -144,8 +138,7 @@ private ServletContext getServletContext() throws LifecycleException { Tomcat tomcat = getTomcatInstanceTestWebapp(false, false); - StandardContext standardContext = - (StandardContext) tomcat.getHost().findChildren()[0]; + StandardContext standardContext = (StandardContext) tomcat.getHost().findChildren()[0]; return standardContext.getServletContext(); } @@ -213,8 +206,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); PrintWriter pw = resp.getWriter(); ServletContext sc = req.getServletContext(); @@ -249,9 +241,8 @@ /* - * The expectation is that you can set a context attribute on - * ServletContextB from ServletContextA and then access that attribute via - * a cross-context dispatch to ServletContextB. + * The expectation is that you can set a context attribute on ServletContextB from ServletContextA and then access + * that attribute via a cross-context dispatch to ServletContextB. */ @Test public void testCrossContextSetAttribute() throws Exception { @@ -296,8 +287,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ServletContext sc; if (targetContextPath == null) { sc = req.getServletContext(); @@ -315,12 +305,10 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); PrintWriter pw = resp.getWriter(); - String value = (String) req.getServletContext().getAttribute( - SetAttributeServlet.ATTRIBUTE_NAME); + String value = (String) req.getServletContext().getAttribute(SetAttributeServlet.ATTRIBUTE_NAME); if (SetAttributeServlet.ATTRIBUTE_VALUE.equals(value)) { pw.print("01-PASS"); } else { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextFacadeSecurityManager.java tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextFacadeSecurityManager.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextFacadeSecurityManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextFacadeSecurityManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,23 +40,20 @@ public final class TestApplicationContextFacadeSecurityManager extends SecurityManagerBaseTest { /** - * @return {@link Collection} of non-static, non-object, public {@link - * Method}s in {@link ApplicationContextFacade} to be run with the the Java - * 2 {@link SecurityManager} been enabled. + * @return {@link Collection} of non-static, non-object, public {@link Method}s in {@link ApplicationContextFacade} + * to be run with the the Java 2 {@link SecurityManager} been enabled. */ @Parameterized.Parameters(name = "{index}: method={0}") public static Collection publicApplicationContextFacadeMethods() { return Arrays.stream(ApplicationContextFacade.class.getMethods()) - .filter(method -> !Modifier.isStatic(method.getModifiers())) - .filter(method -> { + .filter(method -> !Modifier.isStatic(method.getModifiers())).filter(method -> { try { Object.class.getMethod(method.getName(), method.getParameterTypes()); return false; } catch (final NoSuchMethodException e) { return true; } - }) - .collect(Collectors.toList()); + }).collect(Collectors.toList()); } @@ -87,26 +84,21 @@ /** - * Test for - * Bug - * 64735 which confirms that {@link ApplicationContextFacade} behaves - * correctly when the Java 2 {@link SecurityManager} has been enabled. + * Test for Bug 64735 which confirms that + * {@link ApplicationContextFacade} behaves correctly when the Java 2 {@link SecurityManager} has been enabled. * * @throws NoSuchMethodException Should never happen * @throws IllegalAccessException Should never happen * @throws InvocationTargetException Should never happen */ @Test - public void testBug64735() - throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + public void testBug64735() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Assert.assertTrue(SecurityUtil.isPackageProtectionEnabled()); // Mock the ApplicationContext that we provide to the ApplicationContextFacade. final ApplicationContext mockAppContext = EasyMock.createMock(ApplicationContext.class); final Method expectedAppContextMethod = - ApplicationContext.class.getMethod( - methodToTest.getName(), - methodToTest.getParameterTypes()); + ApplicationContext.class.getMethod(methodToTest.getName(), methodToTest.getParameterTypes()); // Expect that only the provided method which is being tested will be called exactly once. final IExpectationSetters expectationSetters; @@ -115,31 +107,21 @@ expectationSetters = EasyMock.expectLastCall(); } else { expectationSetters = - EasyMock.expect(expectedAppContextMethod.invoke( - mockAppContext, getDefaultParams(methodToTest))); + EasyMock.expect(expectedAppContextMethod.invoke(mockAppContext, getDefaultParams(methodToTest))); } - expectationSetters - .andAnswer(() -> { - Assert.assertEquals( - expectedAppContextMethod, - LastControl.getCurrentInvocation().getMethod()); - return getDefaultValue(expectedAppContextMethod.getReturnType()); - }).once(); + expectationSetters.andAnswer(() -> { + Assert.assertEquals(expectedAppContextMethod, LastControl.getCurrentInvocation().getMethod()); + return getDefaultValue(expectedAppContextMethod.getReturnType()); + }).once(); EasyMock.replay(mockAppContext); EasyMock.verifyUnexpectedCalls(mockAppContext); // Invoke the method on ApplicationContextFacade. Fail if any unexpected exceptions are // thrown. try { - methodToTest.invoke( - new ApplicationContextFacade(mockAppContext), - getDefaultParams(methodToTest)); + methodToTest.invoke(new ApplicationContextFacade(mockAppContext), getDefaultParams(methodToTest)); } catch (final IllegalAccessException | InvocationTargetException e) { - throw new AssertionError( - "Failed to call " + - methodToTest + - " with SecurityManager enabled.", - e); + throw new AssertionError("Failed to call " + methodToTest + " with SecurityManager enabled.", e); } // Verify that the method called through to the wrapped ApplicationContext correctly. diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcher.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,10 +53,7 @@ @Parameters(name = "{index}: useAsync[{0}]") public static Collection data() { - return Arrays.asList(new Object[][]{ - {Boolean.TRUE}, - {Boolean.FALSE} - }); + return Arrays.asList(new Object[][] { { Boolean.TRUE }, { Boolean.FALSE } }); } @Test @@ -73,78 +70,67 @@ @Test public void testGetRequestDispatcherOutsideContextRoot01() throws Exception { - doTestGetRequestDispatcher( - true, "/start", null, "../outside", "/target", DispatcherServlet.NULL); + doTestGetRequestDispatcher(true, "/start", null, "../outside", "/target", DispatcherServlet.NULL); } @Test public void testGetRequestDispatcherOutsideContextRoot02() throws Exception { - doTestGetRequestDispatcher( - false, "/start", null, "../outside", "/target", DispatcherServlet.NULL); - } - - - @Test - public void testGetRequestDispatcherEncodedTraversal() throws Exception { - doTestGetRequestDispatcher( - true, "/prefix/start", null, "%2E%2E/target", "/target", DispatcherServlet.NULL); + doTestGetRequestDispatcher(false, "/start", null, "../outside", "/target", DispatcherServlet.NULL); } @Test public void testGetRequestDispatcherTraversal01() throws Exception { - doTestGetRequestDispatcher( - true, "/prefix/start", null, "../target", "/target", TargetServlet.OK); + doTestGetRequestDispatcher(true, "/prefix/start", null, "../target", "/target", TargetServlet.OK); } @Test public void testGetRequestDispatcherTraversal02() throws Exception { - doTestGetRequestDispatcher( - false, "/prefix/start", null, "../target", "/target", TargetServlet.OK); + doTestGetRequestDispatcher(false, "/prefix/start", null, "../target", "/target", TargetServlet.OK); } @Test public void testGetRequestDispatcherTraversal03() throws Exception { - doTestGetRequestDispatcher( - true, "/prefix/start", null, "../target?a=b", "/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(true, "/prefix/start", null, "../target?a=b", "/target", TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcherTraversal04() throws Exception { - doTestGetRequestDispatcher( - false, "/prefix/start", null, "../target?a=b", "/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(false, "/prefix/start", null, "../target?a=b", "/target", TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcherTraversal05() throws Exception { - doTestGetRequestDispatcher( - true, "/prefix/start", "a=b", "../target", "/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "../target", "/target", TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcherTraversal06() throws Exception { - doTestGetRequestDispatcher( - false, "/prefix/start", "a=b", "../target", "/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "../target", "/target", TargetServlet.OK + "a=b"); + } + + + @Test + public void testGetRequestDispatcherTraversal07() throws Exception { + doTestGetRequestDispatcher(true, "/prefix/start", null, "../../target", "/target", DispatcherServlet.NULL); } @Test public void testGetRequestDispatcher01() throws Exception { - doTestGetRequestDispatcher( - true, "/prefix/start", null, "target", "/prefix/target", TargetServlet.OK); + doTestGetRequestDispatcher(true, "/prefix/start", null, "target", "/prefix/target", TargetServlet.OK); } @Test public void testGetRequestDispatcher02() throws Exception { - doTestGetRequestDispatcher( - false, "/prefix/start", null, "target", "/prefix/target", TargetServlet.OK); + doTestGetRequestDispatcher(false, "/prefix/start", null, "target", "/prefix/target", TargetServlet.OK); } @@ -164,22 +150,20 @@ @Test public void testGetRequestDispatcher05() throws Exception { - doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "target", "/prefix/target", - TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "target", "/prefix/target", TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher06() throws Exception { - doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "target", "/prefix/target", - TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "target", "/prefix/target", TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher11() throws Exception { - doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", null, "target", - "/aa%3Fbb%3Dcc/target", TargetServlet.OK); + doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", null, "target", "/aa%3Fbb%3Dcc/target", + TargetServlet.OK); } @@ -188,15 +172,15 @@ // Expected to fail because when the RD processes this as unencoded it // sees /aa?bb=cc/target which it thinks is a query string. This is why // Tomcat encodes by default. - doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", null, "target", - "/aa%3Fbb%3Dcc/target", Default404Servlet.DEFAULT_404); + doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", null, "target", "/aa%3Fbb%3Dcc/target", + Default404Servlet.DEFAULT_404); } @Test public void testGetRequestDispatcher13() throws Exception { - doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", null, "target?a=b", - "/aa%3Fbb%3Dcc/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", null, "target?a=b", "/aa%3Fbb%3Dcc/target", + TargetServlet.OK + "a=b"); } @@ -205,15 +189,15 @@ // Expected to fail because when the RD processes this as unencoded it // sees /aa?bb=cc/target which it thinks is a query string. This is why // Tomcat encodes by default. - doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", null, "target?a=b", - "/aa%3Fbb%3Dcc/target", Default404Servlet.DEFAULT_404); + doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", null, "target?a=b", "/aa%3Fbb%3Dcc/target", + Default404Servlet.DEFAULT_404); } @Test public void testGetRequestDispatcher15() throws Exception { - doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", "a=b", "target", - "/aa%3Fbb%3Dcc/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(true, "/aa%3Fbb%3Dcc/start", "a=b", "target", "/aa%3Fbb%3Dcc/target", + TargetServlet.OK + "a=b"); } @@ -222,154 +206,151 @@ // Expected to fail because when the RD processes this as unencoded it // sees /aa?bb=cc/target which it thinks is a query string. This is why // Tomcat encodes by default. - doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", "a=b", "target", - "/aa%3Fbb%3Dcc/target", Default404Servlet.DEFAULT_404); + doTestGetRequestDispatcher(false, "/aa%3Fbb%3Dcc/start", "a=b", "target", "/aa%3Fbb%3Dcc/target", + Default404Servlet.DEFAULT_404); } @Test public void testGetRequestDispatcher21() throws Exception { - doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", null, "target", - "/aa%3Dbb%3Dcc/target", TargetServlet.OK); + doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", null, "target", "/aa%3Dbb%3Dcc/target", + TargetServlet.OK); } @Test public void testGetRequestDispatcher22() throws Exception { - doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", null, "target", - "/aa%3Dbb%3Dcc/target", TargetServlet.OK); + doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", null, "target", "/aa%3Dbb%3Dcc/target", + TargetServlet.OK); } @Test public void testGetRequestDispatcher23() throws Exception { - doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", null, "target?a=b", - "/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", null, "target?a=b", "/aa%3Dbb%3Dcc/target", + TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher24() throws Exception { - doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", null, "target?a=b", - "/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", null, "target?a=b", "/aa%3Dbb%3Dcc/target", + TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher25() throws Exception { - doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", "a=b", "target", - "/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(true, "/aa%3Dbb%3Dcc/start", "a=b", "target", "/aa%3Dbb%3Dcc/target", + TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher26() throws Exception { - doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", "a=b", "target", - "/aa%3Dbb%3Dcc/target", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(false, "/aa%3Dbb%3Dcc/start", "a=b", "target", "/aa%3Dbb%3Dcc/target", + TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher31() throws Exception { - doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc", - "/prefix/aa%3Fbb%3Dcc", TargetServlet.OK); + doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc", "/prefix/aa%3Fbb%3Dcc", + TargetServlet.OK); } @Test public void testGetRequestDispatcher32() throws Exception { - doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc", - "/prefix/aa%3Fbb%3Dcc", Default404Servlet.DEFAULT_404); + doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc", "/prefix/aa%3Fbb%3Dcc", + Default404Servlet.DEFAULT_404); } @Test public void testGetRequestDispatcher33() throws Exception { - doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", - "/prefix/aa%3Fbb%3Dcc", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", "/prefix/aa%3Fbb%3Dcc", + TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher34() throws Exception { - doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", - "/prefix/aa%3Fbb%3Dcc", Default404Servlet.DEFAULT_404); + doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", "/prefix/aa%3Fbb%3Dcc", + Default404Servlet.DEFAULT_404); } @Test public void testGetRequestDispatcher35() throws Exception { - doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", - "/prefix/aa%3Fbb%3Dcc", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", "/prefix/aa%3Fbb%3Dcc", + TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher36() throws Exception { - doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", - "/prefix/aa%3Fbb%3Dcc", Default404Servlet.DEFAULT_404); + doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", "/prefix/aa%3Fbb%3Dcc", + Default404Servlet.DEFAULT_404); } @Test public void testGetRequestDispatcher41() throws Exception { - doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc", - "/prefix/aa%253Fbb%253Dcc", Default404Servlet.DEFAULT_404); + doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc", "/prefix/aa%253Fbb%253Dcc", + Default404Servlet.DEFAULT_404); } @Test public void testGetRequestDispatcher42() throws Exception { - doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc", - "/prefix/aa%253Fbb%253Dcc", TargetServlet.OK); + doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc", "/prefix/aa%253Fbb%253Dcc", + TargetServlet.OK); } @Test public void testGetRequestDispatcher43() throws Exception { - doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", - "/prefix/aa%253Fbb%253Dcc", Default404Servlet.DEFAULT_404); + doTestGetRequestDispatcher(true, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", "/prefix/aa%253Fbb%253Dcc", + Default404Servlet.DEFAULT_404); } @Test public void testGetRequestDispatcher44() throws Exception { - doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", - "/prefix/aa%253Fbb%253Dcc", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(false, "/prefix/start", null, "aa%3Fbb%3Dcc?a=b", "/prefix/aa%253Fbb%253Dcc", + TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher45() throws Exception { - doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", - "/prefix/aa%253Fbb%253Dcc", Default404Servlet.DEFAULT_404); + doTestGetRequestDispatcher(true, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", "/prefix/aa%253Fbb%253Dcc", + Default404Servlet.DEFAULT_404); } @Test public void testGetRequestDispatcher46() throws Exception { - doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", - "/prefix/aa%253Fbb%253Dcc", TargetServlet.OK + "a=b"); + doTestGetRequestDispatcher(false, "/prefix/start", "a=b", "aa%3Fbb%3Dcc", "/prefix/aa%253Fbb%253Dcc", + TargetServlet.OK + "a=b"); } @Test public void testGetRequestDispatcher47() throws Exception { - doTestGetRequestDispatcher(true, "/prefix/start", null, "aa+bb", - "/prefix/aa+bb", TargetServlet.OK); + doTestGetRequestDispatcher(true, "/prefix/start", null, "aa+bb", "/prefix/aa+bb", TargetServlet.OK); } @Test public void testGetRequestDispatcher48() throws Exception { - doTestGetRequestDispatcher(false, "/prefix/start", null, "aa+bb", - "/prefix/aa+bb", TargetServlet.OK); + doTestGetRequestDispatcher(false, "/prefix/start", null, "aa+bb", "/prefix/aa+bb", TargetServlet.OK); } - private void doTestGetRequestDispatcher(boolean useEncodedDispatchPaths, String startPath, - String startQueryString, String dispatchPath, String targetPath, String expectedBody) - throws Exception { + private void doTestGetRequestDispatcher(boolean useEncodedDispatchPaths, String startPath, String startQueryString, + String dispatchPath, String targetPath, String expectedBody) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -384,12 +365,10 @@ // Add a target servlet to dispatch to Tomcat.addServlet(ctx, "target", new TargetServlet()); - ctx.addServletMappingDecoded( - UDecoder.URLDecode(targetPath, StandardCharsets.UTF_8), "target"); + ctx.addServletMappingDecoded(UDecoder.URLDecode(targetPath, StandardCharsets.UTF_8), "target"); if (useAsync) { - Wrapper w = Tomcat.addServlet( - ctx, "rd", new AsyncDispatcherServlet(dispatchPath, useEncodedDispatchPaths)); + Wrapper w = Tomcat.addServlet(ctx, "rd", new AsyncDispatcherServlet(dispatchPath, useEncodedDispatchPaths)); w.setAsyncSupported(true); } else { Tomcat.addServlet(ctx, "rd", new DispatcherServlet(dispatchPath)); @@ -420,8 +399,7 @@ private static final String DEFAULT_404 = "DEFAULT-404"; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); resp.getWriter().print(DEFAULT_404); @@ -442,8 +420,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher rd = req.getRequestDispatcher(dispatchPath); if (rd == null) { @@ -463,8 +440,7 @@ private static final String OK = "OK"; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); String contextPath = req.getContextPath(); @@ -495,8 +471,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AsyncContext ac = req.startAsync(); // Quick and dirty. Sufficient for this test but ignores lots of diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherB.java tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherB.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherB.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherB.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,305 +51,206 @@ "targetMapping[{3}], targetUri[{4}], useEncodedDispatchPaths[{5}], " + "expectedRequestURI[{6}], expectedContextPath[{7}], expectedServletPath[{8}], " + "expectedPathInfo[{9}], expectedQueryString[{10}], expectedMappingMatch[{11}, " + - "expectedMappingPattern[{12}], expectedMappingMatchValue[{13}], " + - "expectedMappingServletName[{14}], " + + "expectedMappingPattern[{12}], expectedMappingMatchValue[{13}], " + "expectedMappingServletName[{14}], " + "expectedDispatcherRequestURI[{15}], expectedDispatcherContextPath[{16}], " + "expectedDispatcherServletPath[{17}], expectedDispatcherPathInfo[{18}], " + "expectedDispatcherQueryString[{19}], expectedDispatcherMappingMatch[{20}]," + "expectedDispatcherMappingPattern[{21}], expectedDispatcherMappingMatchValue[{22}]," + - "expectedDispatcherMappingServletName[{23}]," + - "expectedBody") + "expectedDispatcherMappingServletName[{23}]," + "expectedBody") public static Collection data() { - return Arrays.asList(new Object[][]{ - // Simple dispatch for each type - { "/start", "/start", DispatcherType.INCLUDE, "/target", "/target", Boolean.TRUE, - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "OK"}, - { "/start", "/start", DispatcherType.FORWARD, "/target", "/target", Boolean.TRUE, - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", "/start", DispatcherType.ASYNC, "/target", "/target", Boolean.TRUE, - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - // Simple dispatch with query strings - { "/start", "/start?abcde=fghij", DispatcherType.INCLUDE, "/target", "/target?zyxwv=utsrq", Boolean.TRUE, - "/test/start", "/test", "/start", null, "abcde=fghij", - MappingMatch.EXACT, "/start", "start", "rd", - "/test/target", "/test", "/target", null, "zyxwv=utsrq", - MappingMatch.EXACT, "/target", "target", "target", - "OK"}, - { "/start", "/start?abcde=fghij", DispatcherType.FORWARD, "/target", "/target?zyxwv=utsrq", Boolean.TRUE, - "/test/target", "/test", "/target", null, "zyxwv=utsrq", - MappingMatch.EXACT, "/target", "target", "target", - "/test/start", "/test", "/start", null, "abcde=fghij", - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", "/start?abcde=fghij", DispatcherType.ASYNC, "/target", "/target?zyxwv=utsrq", Boolean.TRUE, - "/test/target", "/test", "/target", null, "zyxwv=utsrq", - MappingMatch.EXACT, "/target", "target", "target", - "/test/start", "/test", "/start", null, "abcde=fghij", - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - // Simple dispatch with trailing path parameters at start - { "/start", "/start;abcde=fghij", DispatcherType.INCLUDE, "/target", "/target", Boolean.TRUE, - "/test/start;abcde=fghij", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "OK"}, - { "/start", "/start;abcde=fghij", DispatcherType.FORWARD, "/target", "/target", Boolean.TRUE, - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start;abcde=fghij", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", "/start;abcde=fghij", DispatcherType.ASYNC, "/target", "/target", Boolean.TRUE, - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start;abcde=fghij", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - // Simple dispatch with path parameters at start - { "/start", ";abcde=fghij/start", DispatcherType.INCLUDE, "/target", "/target", Boolean.TRUE, - "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "OK"}, - { "/start", ";abcde=fghij/start", DispatcherType.FORWARD, "/target", "/target", Boolean.TRUE, - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", ";abcde=fghij/start", DispatcherType.ASYNC, "/target", "/target", Boolean.TRUE, - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - // Simple dispatch with path parameters on dispatch - { "/start", "/start", DispatcherType.INCLUDE, "/target", "/target;abcde=fghij", Boolean.TRUE, - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/target;abcde=fghij", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "OK"}, - { "/start", "/start", DispatcherType.FORWARD, "/target", "/target;abcde=fghij", Boolean.TRUE, - "/test/target;abcde=fghij", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", "/start", DispatcherType.ASYNC, "/target", "/target;abcde=fghij", Boolean.TRUE, - "/test/target;abcde=fghij", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - // Simple dispatch with multiple path parameters on start and dispatch - { "/start", "/start;abcde=fghij", DispatcherType.INCLUDE, "/target", ";klmno=pqrst/target;uvwxy=z0123", Boolean.TRUE, - "/test/start;abcde=fghij", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/;klmno=pqrst/target;uvwxy=z0123", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "OK"}, - { "/start", "/start;abcde=fghij", DispatcherType.FORWARD, "/target", ";klmno=pqrst/target;uvwxy=z0123", Boolean.TRUE, - "/test/;klmno=pqrst/target;uvwxy=z0123", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start;abcde=fghij", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", "/start;abcde=fghij", DispatcherType.ASYNC, "/target", ";klmno=pqrst/target;uvwxy=z0123", Boolean.TRUE, - "/test/;klmno=pqrst/target;uvwxy=z0123", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start;abcde=fghij", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "ASYNC-IAE"}, - // Simple dispatch with directory traversal - { "/start/*", "/start/foo", DispatcherType.INCLUDE, "/target", "../target", Boolean.TRUE, - "/test/start/foo", "/test", "/start", "/foo", null, - MappingMatch.PATH, "/start/*", "foo", "rd", - "/test/start/../target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "OK"}, - { "/start/*", "/start/foo", DispatcherType.FORWARD, "/target", "../target", Boolean.TRUE, - "/test/start/../target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start/foo", "/test", "/start", "/foo", null, - MappingMatch.PATH, "/start/*", "foo", "rd", - "OK"}, - { "/start/*", "/start/foo", DispatcherType.ASYNC, "/target", "../target", Boolean.TRUE, - "/test/start/../target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start/foo", "/test", "/start", "/foo", null, - MappingMatch.PATH, "/start/*", "foo", "rd", - "ASYNC-IAE"}, - // Simple dispatch with directory traversal and path parameters - // Note comments in Request.getRequestDispatcher(String) that - // explain why the path parameter abcde=fghij is not present on the - // dispatched requestURI - { "/start/*", "/start;abcde=fghij/foo", DispatcherType.INCLUDE, "/target", "../target;klmno=pqrst", Boolean.TRUE, - "/test/start;abcde=fghij/foo", "/test", "/start", "/foo", null, - MappingMatch.PATH, "/start/*", "foo", "rd", - "/test/start/../target;klmno=pqrst", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "OK"}, - { "/start/*", "/start;abcde=fghij/foo", DispatcherType.FORWARD, "/target", "../target;klmno=pqrst", Boolean.TRUE, - "/test/start/../target;klmno=pqrst", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start;abcde=fghij/foo", "/test", "/start", "/foo", null, - MappingMatch.PATH, "/start/*", "foo", "rd", - "OK"}, - { "/start/*", "/start;abcde=fghij/foo", DispatcherType.ASYNC, "/target", "../target;klmno=pqrst", Boolean.TRUE, - "/test/start;abcde=fghij/../target;klmno=pqrst", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start;abcde=fghij/foo", "/test", "/start", "/foo", null, - MappingMatch.PATH, "/start/*", "foo", "rd", - "ASYNC-IAE"}, - // Simple dispatch with invalid directory traversal - { "/start/*", "/start/foo", DispatcherType.INCLUDE, "/target", "../../target", Boolean.TRUE, - "/test/start/foo", "/test", "/start", "/foo", null, - MappingMatch.PATH, "/start/*", "foo", "rd", - "/test/start/../target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "RD-NULL"}, - { "/start/*", "/start/foo", DispatcherType.FORWARD, "/target", "../../target", Boolean.TRUE, - "/test/start/../target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start/foo", "/test", "/start", "/foo", null, - MappingMatch.PATH, "/start/*", "foo", "rd", - "RD-NULL"}, - { "/start/*", "/start/foo", DispatcherType.ASYNC, "/target", "../../target", Boolean.TRUE, - "/test/start/../target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start/foo", "/test", "/start", "/foo", null, - MappingMatch.PATH, "/start/*", "foo", "rd", - "ASYNC-IAE"}, - // Simple dispatch with invalid target - { "/start", "/start", DispatcherType.INCLUDE, "/target", "/does-not-exist", Boolean.TRUE, - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "RD-NULL"}, - { "/start", "/start", DispatcherType.FORWARD, "/target", "/does-not-exist", Boolean.TRUE, - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "RD-NULL"}, - { "/start", "/start", DispatcherType.ASYNC, "/target", "/does-not-exist", Boolean.TRUE, - "/test/target", "/test", "/target", null, null, - MappingMatch.EXACT, "/target", "target", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "ASYNC-RD-NULL"}, - // Welcome files - { "/start", "/start", DispatcherType.INCLUDE, "*.html", "/", Boolean.TRUE, - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "OK"}, - { "/start", "/start", DispatcherType.FORWARD, "*.html", "/", Boolean.TRUE, - "/test/", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", "/start", DispatcherType.ASYNC, "*.html", "/", Boolean.TRUE, - "/test/", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - // Welcome files with query strings - { "/start", "/start?abcde=fghij", DispatcherType.INCLUDE, "*.html", "/?zyxwv=utsrq", Boolean.TRUE, - "/test/start", "/test", "/start", null, "abcde=fghij", - MappingMatch.EXACT, "/start", "start", "rd", - "/test/", "/test", "/index.html", null, "zyxwv=utsrq", - MappingMatch.EXTENSION, "*.html", "index", "target", - "OK"}, - { "/start", "/start?abcde=fghij", DispatcherType.FORWARD, "*.html", "/?zyxwv=utsrq", Boolean.TRUE, - "/test/", "/test", "/index.html", null, "zyxwv=utsrq", - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test/start", "/test", "/start", null, "abcde=fghij", - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", "/start?abcde=fghij", DispatcherType.ASYNC, "*.html", "/?zyxwv=utsrq", Boolean.TRUE, - "/test/", "/test", "/index.html", null, "zyxwv=utsrq", - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test/start", "/test", "/start", null, "abcde=fghij", - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - // Welcome files with trailing path parameters at start - { "/start", "/start;abcde=fghij", DispatcherType.INCLUDE, "*.html", "/", Boolean.TRUE, - "/test/start;abcde=fghij", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "OK"}, - { "/start", "/start;abcde=fghij", DispatcherType.FORWARD, "*.html", "/", Boolean.TRUE, - "/test/", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test/start;abcde=fghij", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", "/start;abcde=fghij", DispatcherType.ASYNC, "*.html", "/", Boolean.TRUE, - "/test/", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test/start;abcde=fghij", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - // Welcome files with path parameters at start - { "/start", ";abcde=fghij/start", DispatcherType.INCLUDE, "*.html", "/", Boolean.TRUE, - "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "OK"}, - { "/start", ";abcde=fghij/start", DispatcherType.FORWARD, "*.html", "/", Boolean.TRUE, - "/test/", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", ";abcde=fghij/start", DispatcherType.ASYNC, "*.html", "/", Boolean.TRUE, - "/test/", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - // Welcome files with trailing path parameters on dispatch - { "/start", "/start", DispatcherType.INCLUDE, "*.html", "/;abcde=fghij", Boolean.TRUE, - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "/test/;abcde=fghij", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "OK"}, - { "/start", "/start", DispatcherType.FORWARD, "*.html", "/;abcde=fghij", Boolean.TRUE, - "/test/;abcde=fghij", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - { "/start", "/start", DispatcherType.ASYNC, "*.html", "/;abcde=fghij", Boolean.TRUE, - "/test/;abcde=fghij", "/test", "/index.html", null, null, - MappingMatch.EXTENSION, "*.html", "index", "target", - "/test/start", "/test", "/start", null, null, - MappingMatch.EXACT, "/start", "start", "rd", - "OK"}, - }); + return Arrays.asList(new Object[][] { + // Simple dispatch for each type + { "/start", "/start", DispatcherType.INCLUDE, "/target", "/target", Boolean.TRUE, "/test/start", + "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", "/test/target", + "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", "target", "OK" }, + { "/start", "/start", DispatcherType.FORWARD, "/target", "/target", Boolean.TRUE, "/test/target", + "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", "target", + "/test/start", "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", + "OK" }, + { "/start", "/start", DispatcherType.ASYNC, "/target", "/target", Boolean.TRUE, "/test/target", "/test", + "/target", null, null, MappingMatch.EXACT, "/target", "target", "target", "/test/start", + "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", "OK" }, + // Simple dispatch with query strings + { "/start", "/start?abcde=fghij", DispatcherType.INCLUDE, "/target", "/target?zyxwv=utsrq", + Boolean.TRUE, "/test/start", "/test", "/start", null, "abcde=fghij", MappingMatch.EXACT, + "/start", "start", "rd", "/test/target", "/test", "/target", null, "zyxwv=utsrq", + MappingMatch.EXACT, "/target", "target", "target", "OK" }, + { "/start", "/start?abcde=fghij", DispatcherType.FORWARD, "/target", "/target?zyxwv=utsrq", + Boolean.TRUE, "/test/target", "/test", "/target", null, "zyxwv=utsrq", MappingMatch.EXACT, + "/target", "target", "target", "/test/start", "/test", "/start", null, "abcde=fghij", + MappingMatch.EXACT, "/start", "start", "rd", "OK" }, + { "/start", "/start?abcde=fghij", DispatcherType.ASYNC, "/target", "/target?zyxwv=utsrq", Boolean.TRUE, + "/test/target", "/test", "/target", null, "zyxwv=utsrq", MappingMatch.EXACT, "/target", + "target", "target", "/test/start", "/test", "/start", null, "abcde=fghij", MappingMatch.EXACT, + "/start", "start", "rd", "OK" }, + // Simple dispatch with trailing path parameters at start + { "/start", "/start;abcde=fghij", DispatcherType.INCLUDE, "/target", "/target", Boolean.TRUE, + "/test/start;abcde=fghij", "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", + "rd", "/test/target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", + "target", "OK" }, + { "/start", "/start;abcde=fghij", DispatcherType.FORWARD, "/target", "/target", Boolean.TRUE, + "/test/target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", + "target", "/test/start;abcde=fghij", "/test", "/start", null, null, MappingMatch.EXACT, + "/start", "start", "rd", "OK" }, + { "/start", "/start;abcde=fghij", DispatcherType.ASYNC, "/target", "/target", Boolean.TRUE, + "/test/target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", + "target", "/test/start;abcde=fghij", "/test", "/start", null, null, MappingMatch.EXACT, + "/start", "start", "rd", "OK" }, + // Simple dispatch with path parameters at start + { "/start", ";abcde=fghij/start", DispatcherType.INCLUDE, "/target", "/target", Boolean.TRUE, + "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, MappingMatch.EXACT, + "/start", "start", "rd", "/test/target", "/test", "/target", null, null, MappingMatch.EXACT, + "/target", "target", "target", "OK" }, + { "/start", ";abcde=fghij/start", DispatcherType.FORWARD, "/target", "/target", Boolean.TRUE, + "/test/target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", + "target", "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, + MappingMatch.EXACT, "/start", "start", "rd", "OK" }, + { "/start", ";abcde=fghij/start", DispatcherType.ASYNC, "/target", "/target", Boolean.TRUE, + "/test/target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", + "target", "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, + MappingMatch.EXACT, "/start", "start", "rd", "OK" }, + // Simple dispatch with path parameters on dispatch + { "/start", "/start", DispatcherType.INCLUDE, "/target", "/target;abcde=fghij", Boolean.TRUE, + "/test/start", "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", + "/test/target;abcde=fghij", "/test", "/target", null, null, MappingMatch.EXACT, "/target", + "target", "target", "OK" }, + { "/start", "/start", DispatcherType.FORWARD, "/target", "/target;abcde=fghij", Boolean.TRUE, + "/test/target;abcde=fghij", "/test", "/target", null, null, MappingMatch.EXACT, "/target", + "target", "target", "/test/start", "/test", "/start", null, null, MappingMatch.EXACT, "/start", + "start", "rd", "OK" }, + { "/start", "/start", DispatcherType.ASYNC, "/target", "/target;abcde=fghij", Boolean.TRUE, + "/test/target;abcde=fghij", "/test", "/target", null, null, MappingMatch.EXACT, "/target", + "target", "target", "/test/start", "/test", "/start", null, null, MappingMatch.EXACT, "/start", + "start", "rd", "OK" }, + // Simple dispatch with multiple path parameters on start and dispatch + { "/start", "/start;abcde=fghij", DispatcherType.INCLUDE, "/target", ";klmno=pqrst/target;uvwxy=z0123", + Boolean.TRUE, "/test/start;abcde=fghij", "/test", "/start", null, null, MappingMatch.EXACT, + "/start", "start", "rd", "/test/;klmno=pqrst/target;uvwxy=z0123", "/test", "/target", null, + null, MappingMatch.EXACT, "/target", "target", "target", "OK" }, + { "/start", "/start;abcde=fghij", DispatcherType.FORWARD, "/target", ";klmno=pqrst/target;uvwxy=z0123", + Boolean.TRUE, "/test/;klmno=pqrst/target;uvwxy=z0123", "/test", "/target", null, null, + MappingMatch.EXACT, "/target", "target", "target", "/test/start;abcde=fghij", "/test", "/start", + null, null, MappingMatch.EXACT, "/start", "start", "rd", "OK" }, + { "/start", "/start;abcde=fghij", DispatcherType.ASYNC, "/target", ";klmno=pqrst/target;uvwxy=z0123", + Boolean.TRUE, "/test/;klmno=pqrst/target;uvwxy=z0123", "/test", "/target", null, null, + MappingMatch.EXACT, "/target", "target", "target", "/test/start;abcde=fghij", "/test", "/start", + null, null, MappingMatch.EXACT, "/start", "start", "rd", "ASYNC-IAE" }, + // Simple dispatch with directory traversal + { "/start/*", "/start/foo", DispatcherType.INCLUDE, "/target", "../target", Boolean.TRUE, + "/test/start/foo", "/test", "/start", "/foo", null, MappingMatch.PATH, "/start/*", "foo", "rd", + "/test/start/../target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", + "target", "target", "OK" }, + { "/start/*", "/start/foo", DispatcherType.FORWARD, "/target", "../target", Boolean.TRUE, + "/test/start/../target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", + "target", "target", "/test/start/foo", "/test", "/start", "/foo", null, MappingMatch.PATH, + "/start/*", "foo", "rd", "OK" }, + { "/start/*", "/start/foo", DispatcherType.ASYNC, "/target", "../target", Boolean.TRUE, + "/test/start/../target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", + "target", "target", "/test/start/foo", "/test", "/start", "/foo", null, MappingMatch.PATH, + "/start/*", "foo", "rd", "ASYNC-IAE" }, + // Simple dispatch with directory traversal and path parameters + // Note comments in Request.getRequestDispatcher(String) that + // explain why the path parameter abcde=fghij is not present on the + // dispatched requestURI + { "/start/*", "/start;abcde=fghij/foo", DispatcherType.INCLUDE, "/target", "../target;klmno=pqrst", + Boolean.TRUE, "/test/start;abcde=fghij/foo", "/test", "/start", "/foo", null, MappingMatch.PATH, + "/start/*", "foo", "rd", "/test/start/../target;klmno=pqrst", "/test", "/target", null, null, + MappingMatch.EXACT, "/target", "target", "target", "OK" }, + { "/start/*", "/start;abcde=fghij/foo", DispatcherType.FORWARD, "/target", "../target;klmno=pqrst", + Boolean.TRUE, "/test/start/../target;klmno=pqrst", "/test", "/target", null, null, + MappingMatch.EXACT, "/target", "target", "target", "/test/start;abcde=fghij/foo", "/test", + "/start", "/foo", null, MappingMatch.PATH, "/start/*", "foo", "rd", "OK" }, + { "/start/*", "/start;abcde=fghij/foo", DispatcherType.ASYNC, "/target", "../target;klmno=pqrst", + Boolean.TRUE, "/test/start;abcde=fghij/../target;klmno=pqrst", "/test", "/target", null, null, + MappingMatch.EXACT, "/target", "target", "target", "/test/start;abcde=fghij/foo", "/test", + "/start", "/foo", null, MappingMatch.PATH, "/start/*", "foo", "rd", "ASYNC-IAE" }, + // Simple dispatch with invalid directory traversal + { "/start/*", "/start/foo", DispatcherType.INCLUDE, "/target", "../../target", Boolean.TRUE, + "/test/start/foo", "/test", "/start", "/foo", null, MappingMatch.PATH, "/start/*", "foo", "rd", + "/test/start/../target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", + "target", "target", "RD-NULL" }, + { "/start/*", "/start/foo", DispatcherType.FORWARD, "/target", "../../target", Boolean.TRUE, + "/test/start/../target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", + "target", "target", "/test/start/foo", "/test", "/start", "/foo", null, MappingMatch.PATH, + "/start/*", "foo", "rd", "RD-NULL" }, + { "/start/*", "/start/foo", DispatcherType.ASYNC, "/target", "../../target", Boolean.TRUE, + "/test/start/../target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", + "target", "target", "/test/start/foo", "/test", "/start", "/foo", null, MappingMatch.PATH, + "/start/*", "foo", "rd", "ASYNC-IAE" }, + // Simple dispatch with invalid target + { "/start", "/start", DispatcherType.INCLUDE, "/target", "/does-not-exist", Boolean.TRUE, "/test/start", + "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", "/test/target", + "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", "target", "RD-NULL" }, + { "/start", "/start", DispatcherType.FORWARD, "/target", "/does-not-exist", Boolean.TRUE, + "/test/target", "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", + "target", "/test/start", "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", + "rd", "RD-NULL" }, + { "/start", "/start", DispatcherType.ASYNC, "/target", "/does-not-exist", Boolean.TRUE, "/test/target", + "/test", "/target", null, null, MappingMatch.EXACT, "/target", "target", "target", + "/test/start", "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", + "ASYNC-RD-NULL" }, + // Welcome files + { "/start", "/start", DispatcherType.INCLUDE, "*.html", "/", Boolean.TRUE, "/test/start", "/test", + "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", "/test/", "/test", + "/index.html", null, null, MappingMatch.EXTENSION, "*.html", "index", "target", "OK" }, + { "/start", "/start", DispatcherType.FORWARD, "*.html", "/", Boolean.TRUE, "/test/", "/test", + "/index.html", null, null, MappingMatch.EXTENSION, "*.html", "index", "target", "/test/start", + "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", "OK" }, + { "/start", "/start", DispatcherType.ASYNC, "*.html", "/", Boolean.TRUE, "/test/", "/test", + "/index.html", null, null, MappingMatch.EXTENSION, "*.html", "index", "target", "/test/start", + "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", "OK" }, + // Welcome files with query strings + { "/start", "/start?abcde=fghij", DispatcherType.INCLUDE, "*.html", "/?zyxwv=utsrq", Boolean.TRUE, + "/test/start", "/test", "/start", null, "abcde=fghij", MappingMatch.EXACT, "/start", "start", + "rd", "/test/", "/test", "/index.html", null, "zyxwv=utsrq", MappingMatch.EXTENSION, "*.html", + "index", "target", "OK" }, + { "/start", "/start?abcde=fghij", DispatcherType.FORWARD, "*.html", "/?zyxwv=utsrq", Boolean.TRUE, + "/test/", "/test", "/index.html", null, "zyxwv=utsrq", MappingMatch.EXTENSION, "*.html", + "index", "target", "/test/start", "/test", "/start", null, "abcde=fghij", MappingMatch.EXACT, + "/start", "start", "rd", "OK" }, + { "/start", "/start?abcde=fghij", DispatcherType.ASYNC, "*.html", "/?zyxwv=utsrq", Boolean.TRUE, + "/test/", "/test", "/index.html", null, "zyxwv=utsrq", MappingMatch.EXTENSION, "*.html", + "index", "target", "/test/start", "/test", "/start", null, "abcde=fghij", MappingMatch.EXACT, + "/start", "start", "rd", "OK" }, + // Welcome files with trailing path parameters at start + { "/start", "/start;abcde=fghij", DispatcherType.INCLUDE, "*.html", "/", Boolean.TRUE, + "/test/start;abcde=fghij", "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", + "rd", "/test/", "/test", "/index.html", null, null, MappingMatch.EXTENSION, "*.html", "index", + "target", "OK" }, + { "/start", "/start;abcde=fghij", DispatcherType.FORWARD, "*.html", "/", Boolean.TRUE, "/test/", + "/test", "/index.html", null, null, MappingMatch.EXTENSION, "*.html", "index", "target", + "/test/start;abcde=fghij", "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", + "rd", "OK" }, + { "/start", "/start;abcde=fghij", DispatcherType.ASYNC, "*.html", "/", Boolean.TRUE, "/test/", "/test", + "/index.html", null, null, MappingMatch.EXTENSION, "*.html", "index", "target", + "/test/start;abcde=fghij", "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", + "rd", "OK" }, + // Welcome files with path parameters at start + { "/start", ";abcde=fghij/start", DispatcherType.INCLUDE, "*.html", "/", Boolean.TRUE, + "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, MappingMatch.EXACT, + "/start", "start", "rd", "/test/", "/test", "/index.html", null, null, MappingMatch.EXTENSION, + "*.html", "index", "target", "OK" }, + { "/start", ";abcde=fghij/start", DispatcherType.FORWARD, "*.html", "/", Boolean.TRUE, "/test/", + "/test", "/index.html", null, null, MappingMatch.EXTENSION, "*.html", "index", "target", + "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, MappingMatch.EXACT, + "/start", "start", "rd", "OK" }, + { "/start", ";abcde=fghij/start", DispatcherType.ASYNC, "*.html", "/", Boolean.TRUE, "/test/", "/test", + "/index.html", null, null, MappingMatch.EXTENSION, "*.html", "index", "target", + "/test;abcde=fghij/start", "/test;abcde=fghij", "/start", null, null, MappingMatch.EXACT, + "/start", "start", "rd", "OK" }, + // Welcome files with trailing path parameters on dispatch + { "/start", "/start", DispatcherType.INCLUDE, "*.html", "/;abcde=fghij", Boolean.TRUE, "/test/start", + "/test", "/start", null, null, MappingMatch.EXACT, "/start", "start", "rd", + "/test/;abcde=fghij", "/test", "/index.html", null, null, MappingMatch.EXTENSION, "*.html", + "index", "target", "OK" }, + { "/start", "/start", DispatcherType.FORWARD, "*.html", "/;abcde=fghij", Boolean.TRUE, + "/test/;abcde=fghij", "/test", "/index.html", null, null, MappingMatch.EXTENSION, "*.html", + "index", "target", "/test/start", "/test", "/start", null, null, MappingMatch.EXACT, "/start", + "start", "rd", "OK" }, + { "/start", "/start", DispatcherType.ASYNC, "*.html", "/;abcde=fghij", Boolean.TRUE, + "/test/;abcde=fghij", "/test", "/index.html", null, null, MappingMatch.EXTENSION, "*.html", + "index", "target", "/test/start", "/test", "/start", null, null, MappingMatch.EXACT, "/start", + "start", "rd", "OK" }, }); } // Inputs @@ -382,17 +283,14 @@ public TestApplicationContextGetRequestDispatcherB(String startMapping, String startUri, - DispatcherType dispatcherType, String targetMapping, String targetUri, - boolean useEncodedDispatchPaths, - String expectedRequestURI, String expectedContextPath, String expectedServletPath, - String expectedPathInfo, String expectedQueryString, MappingMatch expectedMappingMatch, - String expectedMappingPattern, String expectedMappingMatchValue, - String expectedMappingServletName, - String expectedDispatcherRequestURI, String expectedDispatcherContextPath, - String expectedDispatcherServletPath, String expectedDispatcherPathInfo, - String expectedDispatcherQueryString, MappingMatch expectedDispatcherMappingMatch, - String expectedDispatcherMappingPattern, String expectedDispatcherMappingMatchValue, - String expectedDispatcherMappingServletName, + DispatcherType dispatcherType, String targetMapping, String targetUri, boolean useEncodedDispatchPaths, + String expectedRequestURI, String expectedContextPath, String expectedServletPath, String expectedPathInfo, + String expectedQueryString, MappingMatch expectedMappingMatch, String expectedMappingPattern, + String expectedMappingMatchValue, String expectedMappingServletName, String expectedDispatcherRequestURI, + String expectedDispatcherContextPath, String expectedDispatcherServletPath, + String expectedDispatcherPathInfo, String expectedDispatcherQueryString, + MappingMatch expectedDispatcherMappingMatch, String expectedDispatcherMappingPattern, + String expectedDispatcherMappingMatchValue, String expectedDispatcherMappingServletName, String expectedBody) { this.startMapping = startMapping; this.startUri = startUri; @@ -419,7 +317,7 @@ this.expectedDispatcherMappingMatchValue = expectedDispatcherMappingMatchValue; this.expectedDispatcherMappingServletName = expectedDispatcherMappingServletName; this.expectedBody = expectedBody; - } + } @Test @@ -460,8 +358,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (dispatcherType == DispatcherType.INCLUDE) { RequestDispatcher rd = req.getRequestDispatcher(targetUri); @@ -520,8 +417,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Assert.assertEquals(expectedRequestURI, req.getRequestURI()); Assert.assertEquals(expectedContextPath, req.getContextPath()); @@ -548,17 +444,12 @@ Assert.assertEquals(expectedDispatcherQueryString, req.getAttribute("jakarta.servlet." + name + ".query_string")); HttpServletMapping dispatcherMapping = - (HttpServletMapping) req.getAttribute( - "jakarta.servlet." + name + ".mapping"); + (HttpServletMapping) req.getAttribute("jakarta.servlet." + name + ".mapping"); Assert.assertNotNull(dispatcherMapping); - Assert.assertEquals(expectedDispatcherMappingMatch, - dispatcherMapping.getMappingMatch()); - Assert.assertEquals(expectedDispatcherMappingPattern, - dispatcherMapping.getPattern()); - Assert.assertEquals(expectedDispatcherMappingMatchValue, - dispatcherMapping.getMatchValue()); - Assert.assertEquals(expectedDispatcherMappingServletName, - dispatcherMapping.getServletName()); + Assert.assertEquals(expectedDispatcherMappingMatch, dispatcherMapping.getMappingMatch()); + Assert.assertEquals(expectedDispatcherMappingPattern, dispatcherMapping.getPattern()); + Assert.assertEquals(expectedDispatcherMappingMatchValue, dispatcherMapping.getMatchValue()); + Assert.assertEquals(expectedDispatcherMappingServletName, dispatcherMapping.getServletName()); } else if (type == DispatcherType.ERROR || type == DispatcherType.REQUEST) { // Skip - not tested } else { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherC.java tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherC.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherC.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextGetRequestDispatcherC.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,158 @@ +/* + * 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.core; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; + +/* + * Tests interaction of ServletContext.getRequestDispatcher() and the Connector/Context attributes + * encodedSolidusHandling and encodedReverseSolidusHandling. + */ +@RunWith(value = Parameterized.class) +public class TestApplicationContextGetRequestDispatcherC extends TomcatBaseTest { + + @Parameters(name = "{index}: pathInfoRequest[{0}], pathInfoDispatcher[{1}]") + public static Collection data() { + return Arrays.asList(new Object[][] { + // Defaults + { "/a/b/c", "/a/b/c", null, null }, { "/a%2fb%5cc/%25", "/a/b/c/%", "decode", "decode" }, + { "/a%2fb%5cc/%25", "/a/b%5cc/%25", "decode", "passthrough" }, + { "/a%2fb%5cc/%25", "/a%2fb/c/%25", "passthrough", "decode" }, + { "/a%2fb%5cc/%25", "/a%2fb%5cc/%25", "passthrough", "passthrough" }, }); + } + + + @Parameter(0) + public String pathInfoRequest; + @Parameter(1) + public String pathInfoDispatcher; + @Parameter(2) + public String encodedSolidusHandling; + @Parameter(3) + public String encodedReverseSolidusHandling; + + + @Test + public void testSomething() throws Exception { + doTest(); + } + + + private void doTest() throws Exception { + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // Configure the Connector + tomcat.getConnector().setAllowBackslash(true); + if (encodedSolidusHandling != null) { + tomcat.getConnector().setEncodedSolidusHandling(encodedSolidusHandling); + } + if (encodedReverseSolidusHandling != null) { + tomcat.getConnector().setEncodedReverseSolidusHandling(encodedReverseSolidusHandling); + } + + // No file system docBase required + Context ctx = tomcat.addContext("/test", null); + ctx.addWelcomeFile("index.html"); + if (encodedSolidusHandling != null) { + ctx.setEncodedSolidusHandling(encodedSolidusHandling); + } + if (encodedReverseSolidusHandling != null) { + ctx.setEncodedReverseSolidusHandling(encodedReverseSolidusHandling); + } + + // Servlet that performs a dispatch to ... + Tomcat.addServlet(ctx, "rd", new Dispatch(pathInfoRequest)); + ctx.addServletMappingDecoded("/dispatch/*", "rd"); + + // ... this Servlet + Tomcat.addServlet(ctx, "target", new Target()); + ctx.addServletMappingDecoded("/target/*", "target"); + + tomcat.start(); + + StringBuilder url = new StringBuilder("http://localhost:"); + url.append(getPort()); + url.append("/test/dispatch"); + url.append(pathInfoRequest); + + ByteChunk bc = getUrl(url.toString()); + String body = bc.toString(); + + Assert.assertEquals(pathInfoDispatcher + pathInfoDispatcher, body); + } + + + private static class Dispatch extends HttpServlet { + + private static final long serialVersionUID = 1L; + + private final String dispatchPath; + + Dispatch(String dispatchPath) { + this.dispatchPath = dispatchPath; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + /* + * The original pathInfo as presented by the client and the dispatchPath are currently the same for this + * test, as is the handling configured in the Connector and Context. The test checks both the normal request + * processing and the dispatch processing for the correct handling of %2f and %5c. The test could be + * expanded so different settings are used for the Connector and the Context. + */ + // Handle an edge case - the application needs to encode any paths used to obtain a RequestDispatcher + String pathInfo = req.getPathInfo(); + if (pathInfo.endsWith("%")) { + pathInfo = pathInfo.replace("%", "%25"); + } + req.getServletContext().getRequestDispatcher("/target" + pathInfo + dispatchPath).forward(req, resp); + } + } + + + private static class Target extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("UTF-8"); + resp.getWriter().print(req.getPathInfo()); + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextStripPathParams.java tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextStripPathParams.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationContextStripPathParams.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationContextStripPathParams.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,42 +25,50 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.apache.catalina.connector.Request; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.catalina.util.RequestUtil; @RunWith(value = Parameterized.class) public class TestApplicationContextStripPathParams extends TomcatBaseTest { private final String input; private final String expectedOutput; + private final Boolean hasParameter; - public TestApplicationContextStripPathParams(String input, String expectedOutput) { + public TestApplicationContextStripPathParams(String input, String expectedOutput, Boolean hasParameter) { this.input = input; this.expectedOutput = expectedOutput; + this.hasParameter = hasParameter; } @Parameters(name = "{index}: input[{0}]") public static Collection data() { - return Arrays.asList(new Object[][]{ - { "/foo", "/foo"}, - { "/foo/", "/foo/"}, - { "/foo/bar", "/foo/bar"}, - { "/foo;", "/foo"}, - { "/foo;/", "/foo/"}, - { "/foo;/bar", "/foo/bar"}, - { "/foo;a=1", "/foo"}, - { "/foo;a=1/", "/foo/"}, - { "/foo;a=1/bar", "/foo/bar"}, - // Arguably not valid but does the right thing anyway - { ";/foo", "/foo"}, - { ";a=1/foo", "/foo"}, - { ";/foo/bar", "/foo/bar"}, - { ";/foo;a=1/bar", "/foo/bar"}, - }); + return Arrays.asList(new Object[][] { { "/foo", "/foo", Boolean.FALSE }, { "/foo/", "/foo/", Boolean.FALSE }, + { "/foo/bar", "/foo/bar", Boolean.FALSE }, { "/foo;", "/foo", Boolean.FALSE }, + { "/foo;/", "/foo/", Boolean.FALSE }, { "/foo;/bar", "/foo/bar", Boolean.FALSE }, + { "/foo;a=1", "/foo", Boolean.TRUE }, { "/foo;a=1/", "/foo/", Boolean.TRUE }, + { "/foo;a=1/bar", "/foo/bar", Boolean.TRUE }, + // Arguably not valid but does the right thing anyway + { ";/foo", "/foo", Boolean.FALSE }, { ";a=1/foo", "/foo", Boolean.TRUE }, + { ";/foo/bar", "/foo/bar", Boolean.FALSE }, { ";/foo;a=1/bar", "/foo/bar", Boolean.TRUE }, + { ";/foo;=/bar", "/foo/bar", Boolean.FALSE }, { ";/foo;a=/bar", "/foo/bar", Boolean.FALSE }, + { ";/foo;=1/bar", "/foo/bar", Boolean.FALSE }, { "/foo;a=1;b=1/bar", "/foo/bar", Boolean.TRUE }, + { ";/foo;a=1;b=1/bar", "/foo/bar", Boolean.TRUE }, { "/foo;b=1;a=1/bar", "/foo/bar", Boolean.TRUE }, + { ";/foo;b=1;a=1/bar", "/foo/bar", Boolean.TRUE }, }); } @Test public void testStringPathParams() { - String output = ApplicationContext.stripPathParams(input); + Request request = new Request(null); + request.setCoyoteRequest(new org.apache.coyote.Request()); + String output = RequestUtil.stripPathParams(input, request); Assert.assertEquals(expectedOutput, output); + String parameter = request.getPathParameter("a"); + if (hasParameter.booleanValue()) { + Assert.assertEquals("1", parameter); + } else { + Assert.assertNull(parameter); + } } } \ No newline at end of file diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationFilterConfig.java tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationFilterConfig.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationFilterConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationFilterConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -52,17 +52,14 @@ tomcat.start(); - final MBeanServer mbeanServer = - Registry.getRegistry(null, null).getMBeanServer(); + final MBeanServer mbeanServer = Registry.getRegistry(null).getMBeanServer(); // There should be one Servlet MBean registered - Set servlets = mbeanServer.queryNames( - new ObjectName("Tomcat:j2eeType=Servlet,*"), null); + Set servlets = mbeanServer.queryNames(new ObjectName("Tomcat:j2eeType=Servlet,*"), null); Assert.assertEquals(1, servlets.size()); // There should be one Filter MBean registered - Set filters = mbeanServer.queryNames( - new ObjectName("Tomcat:j2eeType=Filter,*"), null); + Set filters = mbeanServer.queryNames(new ObjectName("Tomcat:j2eeType=Filter,*"), null); Assert.assertEquals(1, filters.size()); } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationMapping.java tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationMapping.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationMapping.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationMapping.java 2026-01-23 19:33:36.000000000 +0000 @@ -98,8 +98,8 @@ doTestMapping("", "/foo/bar/*", "/foo/bar/foo2", "foo2", "PATH"); } - private void doTestMapping(String contextPath, String mapping, String requestPath, - String matchValue, String matchType) throws Exception { + private void doTestMapping(String contextPath, String mapping, String requestPath, String matchValue, + String matchType) throws Exception { doTestMappingDirect(contextPath, mapping, requestPath, matchValue, matchType); tearDown(); setUp(); @@ -118,8 +118,8 @@ doTestMappingAsync(contextPath, mapping, requestPath, matchValue, matchType); } - private void doTestMappingDirect(String contextPath, String mapping, String requestPath, - String matchValue, String matchType) throws Exception { + private void doTestMappingDirect(String contextPath, String mapping, String requestPath, String matchValue, + String matchType) throws Exception { Tomcat tomcat = getTomcatInstance(); // No file system docBase required @@ -139,8 +139,8 @@ Assert.assertTrue(body, body.contains("ServletName=[Mapping]")); } - private void doTestMappingInclude(String contextPath, String mapping, String requestPath, - String matchValue, String matchType) throws Exception { + private void doTestMappingInclude(String contextPath, String mapping, String requestPath, String matchValue, + String matchType) throws Exception { Tomcat tomcat = getTomcatInstance(); // No file system docBase required @@ -167,8 +167,8 @@ Assert.assertTrue(body, body.contains("IncludeServletName=[Mapping]")); } - private void doTestMappingNamedInclude(String contextPath, String mapping, String requestPath, - String matchValue, String matchType) throws Exception { + private void doTestMappingNamedInclude(String contextPath, String mapping, String requestPath, String matchValue, + String matchType) throws Exception { Tomcat tomcat = getTomcatInstance(); // No file system docBase required @@ -190,8 +190,8 @@ Assert.assertTrue(body, body.contains("ServletName=[Include]")); } - private void doTestMappingForward(String contextPath, String mapping, String requestPath, - String matchValue, String matchType) throws Exception { + private void doTestMappingForward(String contextPath, String mapping, String requestPath, String matchValue, + String matchType) throws Exception { Tomcat tomcat = getTomcatInstance(); // No file system docBase required @@ -218,8 +218,8 @@ Assert.assertTrue(body, body.contains("ForwardServletName=[Forward]")); } - private void doTestMappingNamedForward(String contextPath, String mapping, String requestPath, - String matchValue, String matchType) throws Exception { + private void doTestMappingNamedForward(String contextPath, String mapping, String requestPath, String matchValue, + String matchType) throws Exception { Tomcat tomcat = getTomcatInstance(); // No file system docBase required @@ -241,8 +241,8 @@ Assert.assertTrue(body, body.contains("ServletName=[Forward]")); } - private void doTestMappingAsync(String contextPath, String mapping, String requestPath, - String matchValue, String matchType) throws Exception { + private void doTestMappingAsync(String contextPath, String mapping, String requestPath, String matchValue, + String matchType) throws Exception { Tomcat tomcat = getTomcatInstance(); // No file system docBase required @@ -275,8 +275,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher rd = req.getRequestDispatcher("/mapping"); rd.include(req, resp); } @@ -287,8 +286,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher rd = req.getServletContext().getNamedDispatcher("Mapping"); rd.include(req, resp); } @@ -299,8 +297,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher rd = req.getServletContext().getNamedDispatcher("Mapping"); rd.forward(req, resp); } @@ -311,8 +308,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher rd = req.getRequestDispatcher("/mapping"); rd.forward(req, resp); } @@ -323,8 +319,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AsyncContext ac = req.startAsync(); ac.dispatch("/mapping"); } @@ -336,8 +331,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain;charset=UTF-8"); PrintWriter pw = resp.getWriter(); HttpServletMapping mapping = req.getHttpServletMapping(); @@ -362,8 +356,7 @@ pw.println("ForwardMatchType=[" + forwardMapping.getMappingMatch() + "]"); pw.println("ForwardServletName=[" + forwardMapping.getServletName() + "]"); } - HttpServletMapping asyncMapping = - (HttpServletMapping) req.getAttribute(AsyncContext.ASYNC_MAPPING); + HttpServletMapping asyncMapping = (HttpServletMapping) req.getAttribute(AsyncContext.ASYNC_MAPPING); if (asyncMapping != null) { pw.println("AsyncMatchValue=[" + asyncMapping.getMatchValue() + "]"); pw.println("AsyncPattern=[" + asyncMapping.getPattern() + "]"); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationSessionCookieConfig.java tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationSessionCookieConfig.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestApplicationSessionCookieConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestApplicationSessionCookieConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,7 @@ @Before public void setUp() throws Exception { - applicationSessionCookieConfig = new ApplicationSessionCookieConfig( - context); + applicationSessionCookieConfig = new ApplicationSessionCookieConfig(context); } @Test diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestAprLifecycleListener.java tomcat10-10.1.52/test/org/apache/catalina/core/TestAprLifecycleListener.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestAprLifecycleListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestAprLifecycleListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -56,7 +56,8 @@ } - private void doTestMultipleServerInstancesUsingTomcatNativeLibrary(boolean reverseShutdownOrder, boolean ffm) throws Exception { + private void doTestMultipleServerInstancesUsingTomcatNativeLibrary(boolean reverseShutdownOrder, boolean ffm) + throws Exception { Path tmpDir = Paths.get(System.getProperty("tomcat.test.temp", "output/tmp")); Files.createDirectories(tmpDir); @@ -66,7 +67,9 @@ tomcat1.setPort(0); TesterSupport.initSsl(tomcat1); TesterSupport.configureSSLImplementation(tomcat1, - ffm ? "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" : OpenSSLImplementation.class.getName(), true); + ffm ? "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" : + OpenSSLImplementation.class.getName(), + true); tomcat1.init(); Tomcat tomcat2 = new Tomcat(); @@ -75,7 +78,9 @@ tomcat2.setPort(0); TesterSupport.initSsl(tomcat2); TesterSupport.configureSSLImplementation(tomcat2, - ffm ? "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" : OpenSSLImplementation.class.getName(), true); + ffm ? "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" : + OpenSSLImplementation.class.getName(), + true); tomcat2.init(); // Start 1, then 2 diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextImpl.java tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextImpl.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextImpl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextImpl.java 2026-01-23 19:33:36.000000000 +0000 @@ -54,6 +54,7 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; import org.apache.catalina.connector.Request; @@ -179,8 +180,7 @@ // No file system docBase required Context ctx = getProgrammaticRootContext(); - AsyncStartNoCompleteServlet servlet = - new AsyncStartNoCompleteServlet(); + AsyncStartNoCompleteServlet servlet = new AsyncStartNoCompleteServlet(); Wrapper wrapper = Tomcat.addServlet(ctx, "servlet", servlet); wrapper.setAsyncSupported(true); @@ -204,10 +204,8 @@ // up to 5 seconds for the right response // Check the access log - alv.validateAccessLog(2, 500, - AsyncStartNoCompleteServlet.ASYNC_TIMEOUT, - AsyncStartNoCompleteServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + - REQUEST_TIME); + alv.validateAccessLog(2, 500, AsyncStartNoCompleteServlet.ASYNC_TIMEOUT, + AsyncStartNoCompleteServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + REQUEST_TIME); } @Test @@ -218,8 +216,7 @@ // No file system docBase required Context ctx = getProgrammaticRootContext(); - AsyncStartWithCompleteServlet servlet = - new AsyncStartWithCompleteServlet(); + AsyncStartWithCompleteServlet servlet = new AsyncStartWithCompleteServlet(); Wrapper wrapper = Tomcat.addServlet(ctx, "servlet", servlet); wrapper.setAsyncSupported(true); @@ -265,11 +262,10 @@ } @Override - protected void doGet(final HttpServletRequest req, - final HttpServletResponse resp) + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { - result = new StringBuilder(); + result = new StringBuilder(); result.append('1'); result.append(req.isAsyncStarted()); req.startAsync().setTimeout(10000); @@ -342,8 +338,7 @@ } @Override - protected void doGet(final HttpServletRequest req, - final HttpServletResponse resp) + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { result = new StringBuilder(); @@ -406,8 +401,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(final HttpServletRequest req, - final HttpServletResponse resp) + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { String echo = req.getParameter("echo"); @@ -426,8 +420,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(final HttpServletRequest req, - final HttpServletResponse resp) + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { AsyncContext actxt = req.startAsync(); @@ -457,8 +450,7 @@ } @Test - public void testTimeoutListenerNoCompleteNonAsyncDispatch() - throws Exception { + public void testTimeoutListenerNoCompleteNonAsyncDispatch() throws Exception { // Should work doTestTimeout(Boolean.FALSE, Boolean.FALSE); } @@ -481,8 +473,7 @@ doTestTimeout(null, null); } - private void doTestTimeout(Boolean completeOnTimeout, Boolean asyncDispatch) - throws Exception { + private void doTestTimeout(Boolean completeOnTimeout, Boolean asyncDispatch) throws Exception { resetTracker(); @@ -509,10 +500,8 @@ if (asyncDispatch != null) { if (asyncDispatch.booleanValue()) { - AsyncStartRunnable asyncStartRunnable = - new AsyncStartRunnable(); - Wrapper async = - Tomcat.addServlet(ctx, "async", asyncStartRunnable); + AsyncStartRunnable asyncStartRunnable = new AsyncStartRunnable(); + Wrapper async = Tomcat.addServlet(ctx, "async", asyncStartRunnable); async.setAsyncSupported(true); ctx.addServletMappingDecoded(dispatchUrl, "async"); } else { @@ -520,7 +509,7 @@ Tomcat.addServlet(ctx, "nonasync", nonAsync); ctx.addServletMappingDecoded(dispatchUrl, "nonasync"); } - } + } ctx.addApplicationListener(TrackingRequestListener.class.getName()); @@ -561,30 +550,24 @@ int count = 0; while (!expectedTrack.equals(getTrack()) && count < 100) { Thread.sleep(50); - count ++; + count++; } Assert.assertEquals(expectedTrack, getTrack()); // Check the access log - if (completeOnTimeout == null || - (!completeOnTimeout.booleanValue() && asyncDispatch == null)) { + if (completeOnTimeout == null || (!completeOnTimeout.booleanValue() && asyncDispatch == null)) { alvGlobal.validateAccessLog(1, 500, TimeoutServlet.ASYNC_TIMEOUT, - TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + - REQUEST_TIME); + TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + REQUEST_TIME); alv.validateAccessLog(1, 500, TimeoutServlet.ASYNC_TIMEOUT, - TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + - REQUEST_TIME); + TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + REQUEST_TIME); } else { long timeoutDelay = TimeoutServlet.ASYNC_TIMEOUT; - if (asyncDispatch != null && asyncDispatch.booleanValue() && - !completeOnTimeout.booleanValue()) { + if (asyncDispatch != null && asyncDispatch.booleanValue() && !completeOnTimeout.booleanValue()) { // The async dispatch includes a sleep timeoutDelay += AsyncStartRunnable.THREAD_SLEEP_TIME; } - alvGlobal.validateAccessLog(1, 200, timeoutDelay, - timeoutDelay + TIMEOUT_MARGIN + REQUEST_TIME); - alv.validateAccessLog(1, 200, timeoutDelay, - timeoutDelay + TIMEOUT_MARGIN + REQUEST_TIME); + alvGlobal.validateAccessLog(1, 200, timeoutDelay, timeoutDelay + TIMEOUT_MARGIN + REQUEST_TIME); + alv.validateAccessLog(1, 200, timeoutDelay, timeoutDelay + TIMEOUT_MARGIN + REQUEST_TIME); } Assert.assertTrue(timeout.isAsyncStartedCorrect()); @@ -608,8 +591,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (req.isAsyncSupported()) { track("TimeoutServletGet-"); final AsyncContext ac = req.startAsync(); @@ -710,7 +692,7 @@ int count = 0; while (!expectedTrack.equals(getTrack()) && count < 100) { Thread.sleep(50); - count ++; + count++; } Assert.assertEquals(expectedTrack, getTrack()); Assert.assertTrue(dispatch.isAsyncStartedCorrect()); @@ -726,8 +708,7 @@ private static final String DISPATCH_CHECK = "check"; private final transient TrackingListener trackingListener; - DispatchingServlet(boolean addTrackingListener, - boolean completeOnError) { + DispatchingServlet(boolean addTrackingListener, boolean completeOnError) { if (addTrackingListener) { trackingListener = new TrackingListener(completeOnError, true, null); } else { @@ -736,8 +717,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if ("y".equals(req.getParameter(DISPATCH_CHECK))) { if (req.getDispatcherType() != DispatcherType.ASYNC) { @@ -754,8 +734,7 @@ @Override public void run() { if (iter > 0) { - ctxt.dispatch("/stage1?" + ITER_PARAM + "=" + iter + - "&" + DISPATCH_CHECK + "=y"); + ctxt.dispatch("/stage1?" + ITER_PARAM + "=" + iter + "&" + DISPATCH_CHECK + "=y"); } else { ctxt.dispatch("/stage2"); } @@ -781,8 +760,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { track("NonAsyncServletGet-"); } } @@ -821,12 +799,11 @@ // Request may complete before listener has finished processing so wait // up to 5 seconds for the right response String expectedTrack = "DispatchingServletGet-DispatchingServletGet-" + - "onStartAsync-TimeoutServletGet-onStartAsync-onTimeout-" + - "onComplete-"; + "onStartAsync-TimeoutServletGet-onStartAsync-onTimeout-" + "onComplete-"; int count = 0; while (!expectedTrack.equals(getTrack()) && count < 100) { Thread.sleep(50); - count ++; + count++; } Assert.assertEquals(expectedTrack, getTrack()); @@ -842,8 +819,7 @@ private static volatile boolean first = true; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { track("DispatchingServletGet-"); resp.flushBuffer(); @@ -882,8 +858,7 @@ // has exited. private boolean asyncStartedCorrect = true; - public TrackingListener(boolean completeOnError, - boolean completeOnTimeout, String dispatchUrl) { + public TrackingListener(boolean completeOnError, boolean completeOnTimeout, String dispatchUrl) { this.completeOnError = completeOnError; this.completeOnTimeout = completeOnTimeout; this.dispatchUrl = dispatchUrl; @@ -899,7 +874,7 @@ boolean expectedAsyncStarted = true; track("onTimeout-"); - if (completeOnTimeout){ + if (completeOnTimeout) { event.getAsyncContext().complete(); expectedAsyncStarted = false; } @@ -938,8 +913,7 @@ private static class StickyTrackingListener extends TrackingListener { - StickyTrackingListener(boolean completeOnError, - boolean completeOnTimeout, String dispatchUrl) { + StickyTrackingListener(boolean completeOnError, boolean completeOnTimeout, String dispatchUrl) { super(completeOnError, completeOnTimeout, dispatchUrl); } @@ -951,8 +925,7 @@ } } - public static class TrackingRequestListener - implements ServletRequestListener { + public static class TrackingRequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { @@ -1011,26 +984,21 @@ } @Test - public void testDispatchErrorWithThreadSingleThenComplete() - throws Exception { + public void testDispatchErrorWithThreadSingleThenComplete() throws Exception { doTestDispatchError(1, true, true); } @Test - public void testDispatchErrorWithThreadDoubleThenComplete() - throws Exception { + public void testDispatchErrorWithThreadDoubleThenComplete() throws Exception { doTestDispatchError(2, true, true); } @Test - public void testDispatchErrorWithThreadMultipleThenComplete() - throws Exception { + public void testDispatchErrorWithThreadMultipleThenComplete() throws Exception { doTestDispatchError(5, true, true); } - private void doTestDispatchError(int iter, boolean useThread, - boolean completeOnError) - throws Exception { + private void doTestDispatchError(int iter, boolean useThread, boolean completeOnError) throws Exception { resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -1038,8 +1006,7 @@ // No file system docBase required Context ctx = getProgrammaticRootContext(); - DispatchingServlet dispatch = - new DispatchingServlet(true, completeOnError); + DispatchingServlet dispatch = new DispatchingServlet(true, completeOnError); Wrapper wrapper = Tomcat.addServlet(ctx, "dispatch", dispatch); wrapper.setAsyncSupported(true); ctx.addServletMappingDecoded("/stage1", "dispatch"); @@ -1081,7 +1048,7 @@ int count = 0; while (!expectedTrack.equals(getTrack()) && count < 100) { Thread.sleep(50); - count ++; + count++; } Assert.assertEquals(expectedTrack, getTrack()); Assert.assertTrue(dispatch.isAsyncStartedCorrect()); @@ -1095,8 +1062,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { track("ErrorServletGet-"); try { // Give the original thread a chance to exit the @@ -1136,7 +1102,7 @@ int count = 0; while (!expectedTrack.equals(getTrack()) && count < 100) { Thread.sleep(50); - count ++; + count++; } Assert.assertEquals(expectedTrack, getTrack()); @@ -1152,12 +1118,10 @@ public static final long THREAD_SLEEP_TIME = 3000; @Override - protected void doGet(HttpServletRequest request, - HttpServletResponse response) + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - final AsyncContext asyncContext = - request.startAsync(request, response); + final AsyncContext asyncContext = request.startAsync(request, response); asyncContext.addListener(new TrackingListener(false, false, null)); @@ -1203,7 +1167,7 @@ Assert.assertEquals(200, rc); Assert.assertEquals("OK", bc.toString()); String testHeader = getSingleHeader("A", headers); - Assert.assertEquals("xyz",testHeader); + Assert.assertEquals("xyz", testHeader); // Check the access log alv.validateAccessLog(1, 200, Bug50753Servlet.THREAD_SLEEP_TIME, @@ -1217,8 +1181,7 @@ public static final long THREAD_SLEEP_TIME = 5000; @Override - protected void doGet(HttpServletRequest req, - final HttpServletResponse resp) + protected void doGet(HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final AsyncContext ctx = req.startAsync(); ctx.start(new Runnable() { @@ -1282,10 +1245,8 @@ // No file system docBase required Context ctx = getProgrammaticRootContext(); - AsyncStatusServlet asyncStatusServlet = - new AsyncStatusServlet(HttpServletResponse.SC_BAD_REQUEST); - Wrapper wrapper = - Tomcat.addServlet(ctx, "asyncStatusServlet", asyncStatusServlet); + AsyncStatusServlet asyncStatusServlet = new AsyncStatusServlet(HttpServletResponse.SC_BAD_REQUEST); + Wrapper wrapper = Tomcat.addServlet(ctx, "asyncStatusServlet", asyncStatusServlet); wrapper.setAsyncSupported(true); ctx.addServletMappingDecoded("/asyncStatusServlet", "asyncStatusServlet"); @@ -1308,8 +1269,7 @@ Thread.sleep(REQUEST_TIME); // Check the access log - alv.validateAccessLog(1, HttpServletResponse.SC_BAD_REQUEST, 0, - REQUEST_TIME); + alv.validateAccessLog(1, HttpServletResponse.SC_BAD_REQUEST, 0, REQUEST_TIME); } @@ -1324,8 +1284,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AsyncContext actxt = req.startAsync(); resp.setStatus(status); @@ -1360,10 +1319,8 @@ // No file system docBase required Context ctx = getProgrammaticRootContext(); - AsyncErrorServlet asyncErrorServlet = - new AsyncErrorServlet(HttpServletResponse.SC_BAD_REQUEST, threaded); - Wrapper wrapper = - Tomcat.addServlet(ctx, "asyncErrorServlet", asyncErrorServlet); + AsyncErrorServlet asyncErrorServlet = new AsyncErrorServlet(HttpServletResponse.SC_BAD_REQUEST, threaded); + Wrapper wrapper = Tomcat.addServlet(ctx, "asyncErrorServlet", asyncErrorServlet); wrapper.setAsyncSupported(true); ctx.addServletMappingDecoded("/asyncErrorServlet", "asyncErrorServlet"); @@ -1411,8 +1368,7 @@ Thread.sleep(REQUEST_TIME); // Check the access log - alv.validateAccessLog(1, HttpServletResponse.SC_BAD_REQUEST, 0, - REQUEST_TIME); + alv.validateAccessLog(1, HttpServletResponse.SC_BAD_REQUEST, 0, REQUEST_TIME); } private static class CustomErrorServlet extends GenericServlet { @@ -1422,8 +1378,7 @@ public static final String ERROR_MESSAGE = "Custom error page"; @Override - public void service(ServletRequest req, ServletResponse res) - throws ServletException, IOException { + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { res.getWriter().print(ERROR_MESSAGE); } @@ -1457,7 +1412,7 @@ try { resp.sendError(status, ERROR_MESSAGE); actxt.complete(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } @@ -1505,8 +1460,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { RequestDispatcher rd = req.getRequestDispatcher("/ServletB"); rd.forward(req, resp); } @@ -1517,8 +1471,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(final HttpServletRequest req, - final HttpServletResponse resp) + protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException { final AsyncContext async = req.startAsync(); @@ -1542,8 +1495,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.getWriter().print("OK"); } @@ -1587,8 +1539,7 @@ private boolean isAsyncWhenExpected = true; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Should not be async at this point isAsyncWhenExpected = isAsyncWhenExpected && !req.isAsyncStarted(); @@ -1628,8 +1579,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.getWriter().print("OK"); } @@ -1647,8 +1597,7 @@ @Test public void testTimeoutErrorDispatchAsyncStart() throws Exception { - doTestTimeoutErrorDispatch( - Boolean.TRUE, ErrorPageAsyncMode.NO_COMPLETE); + doTestTimeoutErrorDispatch(Boolean.TRUE, ErrorPageAsyncMode.NO_COMPLETE); } @Test @@ -1661,8 +1610,7 @@ doTestTimeoutErrorDispatch(Boolean.TRUE, ErrorPageAsyncMode.DISPATCH); } - private void doTestTimeoutErrorDispatch(Boolean asyncError, - ErrorPageAsyncMode mode) throws Exception { + private void doTestTimeoutErrorDispatch(Boolean asyncError, ErrorPageAsyncMode mode) throws Exception { resetTracker(); // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -1717,7 +1665,7 @@ if (asyncError != null) { if (asyncError.booleanValue()) { expected.append("AsyncErrorPageGet-"); - if (mode == ErrorPageAsyncMode.NO_COMPLETE){ + if (mode == ErrorPageAsyncMode.NO_COMPLETE) { expected.append("NoOp-"); } else if (mode == ErrorPageAsyncMode.COMPLETE) { expected.append("Complete-"); @@ -1736,17 +1684,15 @@ int count = 0; while (!expectedTrack.equals(getTrack()) && count < 100) { Thread.sleep(50); - count ++; + count++; } Assert.assertEquals(expectedTrack, getTrack()); // Check the access log alvGlobal.validateAccessLog(1, 500, TimeoutServlet.ASYNC_TIMEOUT, - TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + - REQUEST_TIME); + TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + REQUEST_TIME); alv.validateAccessLog(1, 500, TimeoutServlet.ASYNC_TIMEOUT, - TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + - REQUEST_TIME); + TimeoutServlet.ASYNC_TIMEOUT + TIMEOUT_MARGIN + REQUEST_TIME); } private enum ErrorPageAsyncMode { @@ -1766,13 +1712,12 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { track("AsyncErrorPageGet-"); final AsyncContext ctxt = req.getAsyncContext(); - switch(mode) { + switch (mode) { case COMPLETE: track("Complete-"); ctxt.complete(); @@ -1800,8 +1745,7 @@ Context ctx = getProgrammaticRootContext(); Bug54178ServletA bug54178ServletA = new Bug54178ServletA(); - Wrapper wrapper = - Tomcat.addServlet(ctx, "bug54178ServletA", bug54178ServletA); + Wrapper wrapper = Tomcat.addServlet(ctx, "bug54178ServletA", bug54178ServletA); wrapper.setAsyncSupported(true); ctx.addServletMappingDecoded("/bug54178ServletA", "bug54178ServletA"); @@ -1815,8 +1759,7 @@ int rc = -1; try { - rc = getUrl("http://localhost:" + getPort() + "/bug54178ServletA?" + - Bug54178ServletA.PARAM_NAME + "=bar", + rc = getUrl("http://localhost:" + getPort() + "/bug54178ServletA?" + Bug54178ServletA.PARAM_NAME + "=bar", body, null); } catch (IOException ioe) { // This may happen if test fails. Output the exception in case it is @@ -1828,8 +1771,7 @@ body.recycle(); - rc = getUrl("http://localhost:" + getPort() + "/bug54178ServletB", - body, null); + rc = getUrl("http://localhost:" + getPort() + "/bug54178ServletB", body, null); Assert.assertEquals(HttpServletResponse.SC_OK, rc); Assert.assertEquals("OK", body.toString()); @@ -1841,8 +1783,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getParameter(PARAM_NAME); AsyncContext actxt = req.startAsync(); @@ -1856,8 +1797,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); PrintWriter pw = resp.getWriter(); @@ -1895,13 +1835,13 @@ @Test - public void testBug59219a() throws Exception{ + public void testBug59219a() throws Exception { doTestBug59219("", "doGet-onError-onComplete-"); } @Test - public void testBug59219b() throws Exception{ + public void testBug59219b() throws Exception { doTestBug59219("?loops=3", "doGet-doGet-onStartAsync-doGet-onStartAsync-onError-onComplete-"); } @@ -1922,7 +1862,7 @@ // Wait up to 5s for the response int count = 0; - while(!expectedTrack.equals(getTrack()) && count < 100) { + while (!expectedTrack.equals(getTrack()) && count < 100) { Thread.sleep(50); count++; } @@ -1936,9 +1876,9 @@ private static final long serialVersionUID = 1L; private final transient TrackingListener trackingListener = new TrackingListener(true, false, "/async"); + @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { track("doGet-"); AsyncContext ctx = req.startAsync(); @@ -1960,7 +1900,7 @@ req.setAttribute("loops", Integer.valueOf(loops)); ctx.dispatch(); } else { - throw new ServletException(); + throw new ServletException(); } } } @@ -1975,23 +1915,19 @@ Context ctx = getProgrammaticRootContext(); NonAsyncServlet nonAsyncServlet = new NonAsyncServlet(); - Wrapper wrapper = Tomcat.addServlet(ctx, "nonAsyncServlet", - nonAsyncServlet); + Wrapper wrapper = Tomcat.addServlet(ctx, "nonAsyncServlet", nonAsyncServlet); wrapper.setAsyncSupported(true); ctx.addServletMappingDecoded("/target", "nonAsyncServlet"); DispatchingGenericServlet forbiddenDispatchingServlet = new DispatchingGenericServlet(); - Wrapper wrapper1 = Tomcat.addServlet(ctx, - "forbiddenDispatchingServlet", forbiddenDispatchingServlet); + Wrapper wrapper1 = Tomcat.addServlet(ctx, "forbiddenDispatchingServlet", forbiddenDispatchingServlet); wrapper1.setAsyncSupported(true); - ctx.addServletMappingDecoded("/forbiddenDispatchingServlet", - "forbiddenDispatchingServlet"); + ctx.addServletMappingDecoded("/forbiddenDispatchingServlet", "forbiddenDispatchingServlet"); tomcat.start(); try { - getUrl("http://localhost:" + getPort() - + "/forbiddenDispatchingServlet"); + getUrl("http://localhost:" + getPort() + "/forbiddenDispatchingServlet"); } catch (IOException ioe) { // This may happen if test fails. Output the exception in case it is // useful and let asserts handle the failure @@ -2004,7 +1940,7 @@ int count = 0; while (!expectedTrack.equals(getTrack()) && count < 100) { Thread.sleep(50); - count ++; + count++; } Assert.assertEquals(expectedTrack, getTrack()); } @@ -2016,14 +1952,11 @@ private static final String EMPTY_DISPATCH = "empty"; @Override - public void service(ServletRequest req, ServletResponse resp) - throws ServletException, IOException { + public void service(ServletRequest req, ServletResponse resp) throws ServletException, IOException { if (DispatcherType.ASYNC != req.getDispatcherType()) { AsyncContext asyncContext; if ("y".equals(req.getParameter(CUSTOM_REQ_RESP))) { - asyncContext = req.startAsync( - new ServletRequestWrapper(req), - new ServletResponseWrapper(resp)); + asyncContext = req.startAsync(new ServletRequestWrapper(req), new ServletResponseWrapper(resp)); } else { asyncContext = req.startAsync(); } @@ -2073,8 +2006,7 @@ tomcat.start(); ByteChunk response = new ByteChunk(); - int rc = getUrl("http://localhost:" + getPort() +"/test", response, - null); + int rc = getUrl("http://localhost:" + getPort() + "/test", response, null); Assert.assertEquals(HttpServletResponse.SC_OK, rc); @@ -2084,7 +2016,7 @@ servlet.getAsyncContext().getRequest(); } else { servlet.getAsyncContext().getResponse(); - } + } } catch (IllegalStateException ise) { hasIse = true; } @@ -2094,10 +2026,8 @@ /** - * Accessing the AsyncContext in this way is an ugly hack that should never - * be used in a real application since it is not thread safe. That said, it - * is this sort of hack that the ISE is meant to be preventing. - * + * Accessing the AsyncContext in this way is an ugly hack that should never be used in a real application since it + * is not thread safe. That said, it is this sort of hack that the ISE is meant to be preventing. */ private static class AsyncISEServlet extends HttpServlet { @@ -2106,8 +2036,7 @@ private transient AsyncContext asyncContext; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain;UTF-8"); @@ -2134,8 +2063,7 @@ expected = new StringBuilder(); expected.append("OK"); expected.append("DispatchingGenericServletGet-"); - requestApplicationWithGenericServlet("/dispatch?crr=y&empty=y", - expected); + requestApplicationWithGenericServlet("/dispatch?crr=y&empty=y", expected); } private static class CustomGenericServlet extends GenericServlet { @@ -2143,10 +2071,8 @@ private static final long serialVersionUID = 1L; @Override - public void service(ServletRequest req, ServletResponse res) - throws ServletException, IOException { - if (req instanceof ServletRequestWrapper - && res instanceof ServletResponseWrapper) { + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { + if (req instanceof ServletRequestWrapper && res instanceof ServletResponseWrapper) { track("CustomGenericServletGet-"); } } @@ -2159,18 +2085,12 @@ StringBuilder expected = new StringBuilder(); expected.append("OK"); expected.append("DispatchingGenericServletGet-"); - requestApplicationWithGenericServlet("/fo%20o/dispatch?empty=y", - expected); - requestApplicationWithGenericServlet("//fo%20o/dispatch?empty=y", - expected); - requestApplicationWithGenericServlet("/./fo%20o/dispatch?empty=y", - expected); - requestApplicationWithGenericServlet("/fo%20o//dispatch?empty=y", - expected); - requestApplicationWithGenericServlet("/fo%20o/./dispatch?empty=y", - expected); - requestApplicationWithGenericServlet("/fo%20o/c/../dispatch?empty=y", - expected); + requestApplicationWithGenericServlet("/fo%20o/dispatch?empty=y", expected); + requestApplicationWithGenericServlet("//fo%20o/dispatch?empty=y", expected); + requestApplicationWithGenericServlet("/./fo%20o/dispatch?empty=y", expected); + requestApplicationWithGenericServlet("/fo%20o//dispatch?empty=y", expected); + requestApplicationWithGenericServlet("/fo%20o/./dispatch?empty=y", expected); + requestApplicationWithGenericServlet("/fo%20o/c/../dispatch?empty=y", expected); } @Test @@ -2179,22 +2099,15 @@ StringBuilder expected = new StringBuilder(); expected.append("OK"); expected.append("DispatchingGenericServletGet-"); - requestApplicationWithGenericServlet("/fo%20o/dispatch?crr=y&empty=y", - expected); - requestApplicationWithGenericServlet("//fo%20o/dispatch?crr=y&empty=y", - expected); - requestApplicationWithGenericServlet( - "/./fo%20o/dispatch?crr=y&empty=y", expected); - requestApplicationWithGenericServlet("/fo%20o//dispatch?crr=y&empty=y", - expected); - requestApplicationWithGenericServlet( - "/fo%20o/./dispatch?crr=y&empty=y", expected); - requestApplicationWithGenericServlet( - "/fo%20o/c/../dispatch?crr=y&empty=y", expected); + requestApplicationWithGenericServlet("/fo%20o/dispatch?crr=y&empty=y", expected); + requestApplicationWithGenericServlet("//fo%20o/dispatch?crr=y&empty=y", expected); + requestApplicationWithGenericServlet("/./fo%20o/dispatch?crr=y&empty=y", expected); + requestApplicationWithGenericServlet("/fo%20o//dispatch?crr=y&empty=y", expected); + requestApplicationWithGenericServlet("/fo%20o/./dispatch?crr=y&empty=y", expected); + requestApplicationWithGenericServlet("/fo%20o/c/../dispatch?crr=y&empty=y", expected); } - private void prepareApplicationWithGenericServlet(String contextPath) - throws Exception { + private void prepareApplicationWithGenericServlet(String contextPath) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -2208,16 +2121,14 @@ ctx.addServletMappingDecoded("/dispatch", "dispatch"); CustomGenericServlet customGeneric = new CustomGenericServlet(); - Wrapper wrapper2 = Tomcat.addServlet(ctx, "customGeneric", - customGeneric); + Wrapper wrapper2 = Tomcat.addServlet(ctx, "customGeneric", customGeneric); wrapper2.setAsyncSupported(true); ctx.addServletMappingDecoded("/target", "customGeneric"); tomcat.start(); } - private void requestApplicationWithGenericServlet(String path, - StringBuilder expectedContent) throws Exception { + private void requestApplicationWithGenericServlet(String path, StringBuilder expectedContent) throws Exception { resetTracker(); getUrl("http://localhost:" + getPort() + path); @@ -2227,7 +2138,7 @@ int count = 0; while (!expectedTrack.equals(getTrack()) && count < 100) { Thread.sleep(50); - count ++; + count++; } Assert.assertEquals(expectedTrack, getTrack()); } @@ -2263,9 +2174,10 @@ tomcat.start(); - getUrl("http://localhost:" + getPort()+ "/stage1"); + getUrl("http://localhost:" + getPort() + "/stage1"); - Assert.assertEquals("doGet-startAsync-doGet-startAsync-onStartAsync-NonAsyncServletGet-onComplete-", getTrack()); + Assert.assertEquals("doGet-startAsync-doGet-startAsync-onStartAsync-NonAsyncServletGet-onComplete-", + getTrack()); // Check the access log alv.validateAccessLog(1, 200, 0, REQUEST_TIME); @@ -2284,15 +2196,14 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { track("doGet-startAsync-"); AsyncContext ac = req.startAsync(); if (addTrackingListener) { ac.addListener(new StickyTrackingListener(false, false, null)); } ac.dispatch(target); - } + } } // https://bz.apache.org/bugzilla/show_bug.cgi?id=57559 @@ -2310,7 +2221,7 @@ } - private void doTestAsyncRequestURI(String uri) throws Exception{ + private void doTestAsyncRequestURI(String uri) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -2334,8 +2245,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (DispatcherType.ASYNC.equals(req.getDispatcherType())) { resp.setContentType("text/plain"); @@ -2379,8 +2289,8 @@ public void run() { try { getUrl("http://localhost:" + getPort() + "/asyncStashServlet"); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } } }); @@ -2388,8 +2298,7 @@ // Wait for first request to get as far as it can int count = 0; - while (count < 100 && getTrack() != null && - !getTrack().startsWith("AsyncStashServletGet-")) { + while (count < 100 && getTrack() != null && !getTrack().startsWith("AsyncStashServletGet-")) { count++; Thread.sleep(100); } @@ -2413,8 +2322,7 @@ private static final String DEFAULT_KEY = "DEFAULT"; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String key = req.getParameter("key"); if (key == null) { @@ -2431,8 +2339,7 @@ private static final String DEFAULT_KEY = "DEFAULT"; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String key = req.getParameter("key"); if (key == null) { @@ -2457,8 +2364,7 @@ public void testTimeoutDispatchCustomErrorPage() throws Exception { Tomcat tomcat = getTomcatInstance(); Context context = getProgrammaticRootContext(); - tomcat.addServlet("", "timeout", Bug58751AsyncServlet.class.getName()) - .setAsyncSupported(true); + tomcat.addServlet("", "timeout", Bug58751AsyncServlet.class.getName()).setAsyncSupported(true); CustomErrorServlet customErrorServlet = new CustomErrorServlet(); Tomcat.addServlet(context, "customErrorServlet", customErrorServlet); context.addServletMappingDecoded("/timeout", "timeout"); @@ -2481,8 +2387,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (req.getAttribute("timeout") != null) { resp.sendError(503); } else { @@ -2492,11 +2397,9 @@ @Override public void onTimeout(AsyncEvent event) throws IOException { - HttpServletResponse response = (HttpServletResponse) event - .getSuppliedResponse(); + HttpServletResponse response = (HttpServletResponse) event.getSuppliedResponse(); if (!response.isCommitted()) { - event.getSuppliedRequest() - .setAttribute("timeout", Boolean.TRUE); + event.getSuppliedRequest().setAttribute("timeout", Boolean.TRUE); context.dispatch(); } } @@ -2604,8 +2507,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Integer countObj = (Integer) req.getAttribute("count"); int count = 0; @@ -2641,8 +2543,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Integer countObj = (Integer) req.getAttribute("count"); int count = 0; @@ -2791,8 +2692,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (useComplete) { // Write expected body here @@ -2822,8 +2722,7 @@ private final boolean useComplete; private boolean invalidStateDetected = false; - AsyncIoEndWriteListener(AsyncContext ac, boolean useThread, - boolean useComplete) { + AsyncIoEndWriteListener(AsyncContext ac, boolean useThread, boolean useComplete) { this.ac = ac; this.useThread = useThread; this.useComplete = useComplete; @@ -2875,8 +2774,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Write expected body here resp.setContentType("text/plain"); @@ -2887,20 +2785,14 @@ /* - * Tests an error on an async thread before the container thread that called - * startAsync() has returned to the container. + * Tests an error on an async thread before the container thread that called startAsync() has returned to the + * container. * - * Required sequence is: - * - enter Servlet's service() method - * - startAsync() - * - start async thread - * - close client connection - * - write on async thread -> I/O error - * - exit Servlet's service() method + * Required sequence is: - enter Servlet's service() method - startAsync() - start async thread - close client + * connection - write on async thread -> I/O error - exit Servlet's service() method * - * This test makes extensive use of instance fields in the Servlet that - * would normally be considered very poor practice. It is only safe in this - * test as the Servlet only processes a single request. + * This test makes extensive use of instance fields in the Servlet that would normally be considered very poor + * practice. It is only safe in this test as the Servlet only processes a single request. */ @Test public void testBug63816() throws Exception { @@ -2925,9 +2817,13 @@ Bug63816Client client = new Bug63816Client(); client.setPort(getPort()); - client.setRequest(new String[] { "GET / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}); + // @formatter:off + client.setRequest(new String[] { + "GET / HTTP/1.1" + CRLF + + "Host: localhost:" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.sendRequest(); @@ -2962,8 +2858,8 @@ private final transient CountDownLatch threadCompleteLatch; private final AtomicBoolean ise; - Bug63816Servlet(CountDownLatch doGetLatch, CountDownLatch clientCloseLatch, - CountDownLatch threadCompleteLatch, AtomicBoolean ise) { + Bug63816Servlet(CountDownLatch doGetLatch, CountDownLatch clientCloseLatch, CountDownLatch threadCompleteLatch, + AtomicBoolean ise) { this.doGetLatch = doGetLatch; this.clientCloseLatch = clientCloseLatch; this.threadCompleteLatch = threadCompleteLatch; @@ -2971,8 +2867,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGetLatch.countDown(); @@ -3017,7 +2912,7 @@ resp.getWriter().write(TestCoyoteAdapter.TEXT_8K); resp.flushBuffer(); } - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } ise.set(false); @@ -3032,45 +2927,41 @@ @Test public void testCanceledPostChunked() throws Exception { + // @formatter:off doTestCanceledPost(new String[] { - "POST / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + SimpleHttpClient.CRLF + - "Transfer-Encoding: Chunked" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "10" + SimpleHttpClient.CRLF + - "This is 16 bytes" + SimpleHttpClient.CRLF + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + CRLF + + "Transfer-Encoding: Chunked" + CRLF + + CRLF + + "10" + CRLF + + "This is 16 bytes" + CRLF }); + // @formatter:on } @Test public void testCanceledPostNoChunking() throws Exception { + // @formatter:off doTestCanceledPost(new String[] { - "POST / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + SimpleHttpClient.CRLF + - "Content-Length: 100" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + CRLF + + "Content-Length: 100" + CRLF + + CRLF + "This is 16 bytes" }); + // @formatter:on } /* - * Tests an error on an async thread when the client closes the connection - * before fully writing the request body. + * Tests an error on an async thread when the client closes the connection before fully writing the request body. * - * Required sequence is: - * - enter Servlet's service() method - * - startAsync() - * - start async thread - * - read partial body - * - close client connection - * - read on async thread -> I/O error - * - exit Servlet's service() method + * Required sequence is: - enter Servlet's service() method - startAsync() - start async thread - read partial body + * - close client connection - read on async thread -> I/O error - exit Servlet's service() method * - * This test makes extensive use of instance fields in the Servlet that - * would normally be considered very poor practice. It is only safe in this - * test as the Servlet only processes a single request. + * This test makes extensive use of instance fields in the Servlet that would normally be considered very poor + * practice. It is only safe in this test as the Servlet only processes a single request. */ private void doTestCanceledPost(String[] request) throws Exception { CountDownLatch partialReadLatch = new CountDownLatch(1); @@ -3129,8 +3020,8 @@ private final transient CountDownLatch threadCompleteLatch; private final AtomicBoolean testFailed; - PostServlet(CountDownLatch doPostLatch, CountDownLatch clientCloseLatch, - CountDownLatch threadCompleteLatch, AtomicBoolean testFailed) { + PostServlet(CountDownLatch doPostLatch, CountDownLatch clientCloseLatch, CountDownLatch threadCompleteLatch, + AtomicBoolean testFailed) { this.partialReadLatch = doPostLatch; this.clientCloseLatch = clientCloseLatch; this.threadCompleteLatch = threadCompleteLatch; @@ -3138,8 +3029,7 @@ } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AsyncContext ac = req.startAsync(); Thread t = new PostServletThread(ac, partialReadLatch, clientCloseLatch, threadCompleteLatch, testFailed); @@ -3203,8 +3093,8 @@ // Read again try { is.read(); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); // Required. Clear the error marker. testFailed.set(false); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextImplDispatch.java tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextImplDispatch.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextImplDispatch.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextImplDispatch.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,9 +40,8 @@ import org.apache.tomcat.util.buf.ByteChunk; /** - * Written for the specific test case of async Servlet, dispatches to sync - * Servlet that then tries to call startAsync() but covers all combinations - * for completeness. + * Written for the specific test case of async Servlet, dispatches to sync Servlet that then tries to call startAsync() + * but covers all combinations for completeness. */ @RunWith(Parameterized.class) public class TestAsyncContextImplDispatch extends TomcatBaseTest { @@ -54,11 +53,12 @@ for (Boolean targetAsyncSupported : booleans) { for (Boolean dispatchAsyncSupported : booleans) { for (Boolean dispatchAsyncStart : booleans) { - Boolean allowed = Boolean.valueOf(!dispatchAsyncStart.booleanValue() || - targetAsyncSupported.booleanValue() && dispatchAsyncSupported.booleanValue() && - dispatchAsyncStart.booleanValue()); + Boolean allowed = + Boolean.valueOf(!dispatchAsyncStart.booleanValue() || targetAsyncSupported.booleanValue() && + dispatchAsyncSupported.booleanValue() && dispatchAsyncStart.booleanValue()); - parameterSets.add(new Object[] { targetAsyncSupported, dispatchAsyncSupported, dispatchAsyncStart, allowed} ); + parameterSets.add( + new Object[] { targetAsyncSupported, dispatchAsyncSupported, dispatchAsyncStart, allowed }); } } } @@ -115,8 +115,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/dispatch").forward(req, resp); } @@ -127,7 +126,7 @@ private static final long serialVersionUID = 1L; - private final boolean start; + private final boolean start; public TesterDispatchServlet(boolean start) { this.start = start; @@ -135,8 +134,7 @@ @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (start) { AsyncContext ac = req.startAsync(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextImplListenerOnComplete.java tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextImplListenerOnComplete.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextImplListenerOnComplete.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextImplListenerOnComplete.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,9 +37,9 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; -import org.apache.catalina.startup.SimpleHttpClient; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; @@ -96,9 +96,12 @@ socket.connect(new InetSocketAddress("localhost", port)); try (var writer = new OutputStreamWriter(socket.getOutputStream())) { - writer.write("GET /repro" + SimpleHttpClient.CRLF + - "Accept: text/event-stream" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF); + // @formatter:off + writer.write( + "GET /repro" + CRLF + + "Accept: text/event-stream" + CRLF + + CRLF); + // @formatter:on writer.flush(); } Thread.sleep(1_000); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextIoError.java tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextIoError.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextIoError.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextIoError.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,6 +30,7 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; import org.apache.catalina.startup.SimpleHttpClient; @@ -65,9 +66,13 @@ AsyncClient client = new AsyncClient(); client.setPort(getPort()); - client.setRequest(new String[] { "GET /async HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}); + // @formatter:off + client.setRequest(new String[] { + "GET /async HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.sendRequest(); @@ -112,7 +117,8 @@ } @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { AsyncContext ac = request.startAsync(); ac.setTimeout(0); @@ -198,7 +204,8 @@ } @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { invocationCount.incrementAndGet(); } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextStateChanges.java tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextStateChanges.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestAsyncContextStateChanges.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestAsyncContextStateChanges.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,6 +41,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; import org.apache.catalina.connector.TestCoyoteAdapter; @@ -110,9 +111,12 @@ Client client = new Client(); client.setPort(getPort()); - client.setRequest(new String[] { "GET / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}); + // @formatter:off + client.setRequest(new String[] { + "GET / HTTP/1.1" + CRLF + + "Host: localhost:" + CRLF + + CRLF}); + // @formatter:on client.connect(); client.sendRequest(); @@ -150,8 +154,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { servletStartLatch.countDown(); if (dispatch) { @@ -206,12 +209,10 @@ if (endTiming == EndTiming.THREAD_COMPLETES_AFTER_SERVLET_EXIT) { try { /* - * As much as I dislike it, I don't see any easy way around - * this hack. The thread is started as the Servlet exits but - * we need to wait for the post processing to complete for - * the test to work as intended. In real-world applications - * this does mean that there is a real chance of an ISE. We - * may need to increase this delay for some CI systems. + * As much as I dislike it, I don't see any easy way around this hack. The thread is started as the + * Servlet exits but we need to wait for the post processing to complete for the test to work as + * intended. In real-world applications this does mean that there is a real chance of an ISE. We may + * need to increase this delay for some CI systems. */ sleep(1000); } catch (InterruptedException e) { @@ -237,8 +238,8 @@ for (int i = 0; i < 64; i++) { os.write(TestCoyoteAdapter.TEXT_8K.getBytes(StandardCharsets.UTF_8)); } - } catch (IOException e) { - // Expected + } catch (IOException ignore) { + // Ignore - The test intends to trigger the IOE } } @@ -344,12 +345,12 @@ public enum AsyncEnd { - NONE ( true, false), - COMPLETE (false, false), - DISPATCH (false, false), - ERROR_NONE ( true, true), - ERROR_COMPLETE(false, true), - ERROR_DISPATCH(false, true); + NONE(true, false), + COMPLETE(false, false), + DISPATCH(false, false), + ERROR_NONE(true, true), + ERROR_COMPLETE(false, true), + ERROR_DISPATCH(false, true); final boolean none; final boolean error; diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestContextNamingInfoListener.java tomcat10-10.1.52/test/org/apache/catalina/core/TestContextNamingInfoListener.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestContextNamingInfoListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestContextNamingInfoListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,19 +42,27 @@ public static Collection parameters() { List parameterSets = new ArrayList<>(); - parameterSets.add(new Object[] { "", "", null, Boolean.FALSE, "/", "/", "ROOT" }); - parameterSets.add(new Object[] { "", "42", null, Boolean.FALSE, "/", "/", "ROOT##42" }); - parameterSets.add(new Object[] { "", "", null, Boolean.TRUE, "", "", "" }); - parameterSets.add(new Object[] { "", "42", null, Boolean.TRUE, "", "", "##42" }); - for (Boolean b: Arrays.asList(Boolean.FALSE, Boolean.TRUE)) { - parameterSets.add(new Object[] { "/foo", "", null, b, "/foo", "/foo", "/foo" }); - parameterSets.add(new Object[] { "/foo", "", "My Foo Webapp", b, "/foo", "/foo", "/foo" }); - parameterSets.add(new Object[] { "/foo", "42", "My Foo Webapp", b, "/foo", "/foo", "/foo##42" }); - parameterSets.add(new Object[] { "/foo/bar", "", null, b, "/foo/bar", "/foo/bar", "/foo/bar" }); - parameterSets.add(new Object[] { "/foo/bar", "", "My Foobar Webapp", b, "/foo/bar", "/foo/bar", "/foo/bar" }); - parameterSets.add(new Object[] { "/foo/bar", "42", "My Foobar Webapp", b, "/foo/bar", "/foo/bar", "/foo/bar##42" }); - parameterSets.add(new Object[] { "/\u0444\u0443/\u0431\u0430\u0440", "", "\u041C\u043E\u0439 \u0424\u0443\u0431\u0430\u0440 \u0412\u0435\u0431\u0430\u043F\u043F", b, "/\u0444\u0443/\u0431\u0430\u0440", "/%D1%84%D1%83/%D0%B1%D0%B0%D1%80", "/\u0444\u0443/\u0431\u0430\u0440" }); - parameterSets.add(new Object[] { "/\u0444\u0443/\u0431\u0430\u0440", "42", "\u041C\u043E\u0439 \u0424\u0443\u0431\u0430\u0440 \u0412\u0435\u0431\u0430\u043F\u043F", b, "/\u0444\u0443/\u0431\u0430\u0440", "/%D1%84%D1%83/%D0%B1%D0%B0%D1%80", "/\u0444\u0443/\u0431\u0430\u0440##42" }); + parameterSets.add(new Object[] { "", "", null, Boolean.FALSE, "/", "/", "ROOT" }); + parameterSets.add(new Object[] { "", "42", null, Boolean.FALSE, "/", "/", "ROOT##42" }); + parameterSets.add(new Object[] { "", "", null, Boolean.TRUE, "", "", "" }); + parameterSets.add(new Object[] { "", "42", null, Boolean.TRUE, "", "", "##42" }); + for (Boolean b : Arrays.asList(Boolean.FALSE, Boolean.TRUE)) { + parameterSets.add(new Object[] { "/foo", "", null, b, "/foo", "/foo", "/foo" }); + parameterSets.add(new Object[] { "/foo", "", "My Foo Webapp", b, "/foo", "/foo", "/foo" }); + parameterSets.add(new Object[] { "/foo", "42", "My Foo Webapp", b, "/foo", "/foo", "/foo##42" }); + parameterSets.add(new Object[] { "/foo/bar", "", null, b, "/foo/bar", "/foo/bar", "/foo/bar" }); + parameterSets + .add(new Object[] { "/foo/bar", "", "My Foobar Webapp", b, "/foo/bar", "/foo/bar", "/foo/bar" }); + parameterSets.add( + new Object[] { "/foo/bar", "42", "My Foobar Webapp", b, "/foo/bar", "/foo/bar", "/foo/bar##42" }); + parameterSets.add(new Object[] { "/\u0444\u0443/\u0431\u0430\u0440", "", + "\u041C\u043E\u0439 \u0424\u0443\u0431\u0430\u0440 \u0412\u0435\u0431\u0430\u043F\u043F", b, + "/\u0444\u0443/\u0431\u0430\u0440", "/%D1%84%D1%83/%D0%B1%D0%B0%D1%80", + "/\u0444\u0443/\u0431\u0430\u0440" }); + parameterSets.add(new Object[] { "/\u0444\u0443/\u0431\u0430\u0440", "42", + "\u041C\u043E\u0439 \u0424\u0443\u0431\u0430\u0440 \u0412\u0435\u0431\u0430\u043F\u043F", b, + "/\u0444\u0443/\u0431\u0430\u0440", "/%D1%84%D1%83/%D0%B1%D0%B0%D1%80", + "/\u0444\u0443/\u0431\u0430\u0440##42" }); } return parameterSets; diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestDefaultInstanceManager.java tomcat10-10.1.52/test/org/apache/catalina/core/TestDefaultInstanceManager.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestDefaultInstanceManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestDefaultInstanceManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -72,8 +72,7 @@ // Create the context (don't use addWebapp as we want to modify the // JSP Servlet settings). File appDir = new File("test/webapp"); - StandardContext ctxt = (StandardContext) tomcat.addContext( - null, "/test", appDir.getAbsolutePath()); + StandardContext ctxt = (StandardContext) tomcat.addContext(null, "/test", appDir.getAbsolutePath()); ctxt.addServletContainerInitializer(new JasperInitializer(), null); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestNamingContextListener.java tomcat10-10.1.52/test/org/apache/catalina/core/TestNamingContextListener.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestNamingContextListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestNamingContextListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -74,8 +74,7 @@ javax.naming.Context initCtx; try { initCtx = new InitialContext(); - javax.naming.Context envCtx = - (javax.naming.Context) initCtx.lookup("java:comp/env"); + javax.naming.Context envCtx = (javax.naming.Context) initCtx.lookup("java:comp/env"); String value = (String) envCtx.lookup(BUG49132_NAME); if (!BUG49132_VALUE.equals(value)) { throw new RuntimeException(); @@ -141,27 +140,23 @@ } } - public static final class Bug54096Listener implements - ServletContextListener { + public static final class Bug54096Listener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { javax.naming.Context initCtx; try { initCtx = new InitialContext(); - javax.naming.Context envCtx = - (javax.naming.Context) initCtx.lookup("java:comp/env"); + javax.naming.Context envCtx = (javax.naming.Context) initCtx.lookup("java:comp/env"); // Validate entry A - Bug54096EnvA valueA = - (Bug54096EnvA) envCtx.lookup(BUG54096_NameA); + Bug54096EnvA valueA = (Bug54096EnvA) envCtx.lookup(BUG54096_NameA); if (!BUG54096_ValueA.equals(valueA.getValue())) { throw new RuntimeException(); } // Validate entry B - Bug54096EnvB valueB = - (Bug54096EnvB) envCtx.lookup(BUG54096_NameB); + Bug54096EnvB valueB = (Bug54096EnvB) envCtx.lookup(BUG54096_NameB); if (BUG54096_ValueB.charAt(0) != valueB.getValue()) { throw new RuntimeException(); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestPropertiesRoleMappingListener.java tomcat10-10.1.52/test/org/apache/catalina/core/TestPropertiesRoleMappingListener.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestPropertiesRoleMappingListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestPropertiesRoleMappingListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -121,7 +121,7 @@ Tomcat.addServlet(ctx, "default", new DefaultServlet()); ctx.addServletMappingDecoded("/", "default"); - LoginConfig loginConfig = new LoginConfig(); + LoginConfig loginConfig = new LoginConfig(); loginConfig.setAuthMethod(HttpServletRequest.BASIC_AUTH); ctx.setLoginConfig(loginConfig); ctx.getPipeline().addValve(new BasicAuthenticator()); @@ -157,7 +157,7 @@ private void testRequest(String credentials, String path, int statusCode) throws IOException { ByteChunk out = new ByteChunk(); - Map> reqHead = new HashMap<>(); + Map> reqHead = new HashMap<>(); List head = new ArrayList<>(); head.add(HttpServletRequest.BASIC_AUTH + " " + Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.ISO_8859_1))); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardContext.java tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContext.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,7 +19,6 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; -import java.lang.reflect.Method; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -52,6 +51,7 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.ContainerEvent; import org.apache.catalina.ContainerListener; import org.apache.catalina.Context; @@ -75,15 +75,18 @@ import org.apache.tomcat.util.descriptor.web.FilterDef; import org.apache.tomcat.util.descriptor.web.FilterMap; import org.apache.tomcat.util.descriptor.web.LoginConfig; +import org.apache.tomcat.util.http.Method; public class TestStandardContext extends TomcatBaseTest { + // @formatter:off private static final String REQUEST = - "GET / HTTP/1.1\r\n" + - "Host: anything\r\n" + - "Connection: close\r\n" + - "\r\n"; + "GET / HTTP/1.1" + CRLF + + "Host: anything" + CRLF + + "Connection: close" + CRLF + + CRLF; + // @formatter:on @Test public void testBug46243() throws Exception { @@ -105,8 +108,7 @@ tomcat.start(); // Configure the client - Bug46243Client client = - new Bug46243Client(tomcat.getConnector().getLocalPort()); + Bug46243Client client = new Bug46243Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { REQUEST }); client.connect(); @@ -127,8 +129,7 @@ client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse200()); - Assert.assertEquals(Bug46243Filter.class.getName() - + HelloWorldServlet.RESPONSE_TEXT, client.getResponseBody()); + Assert.assertEquals(Bug46243Filter.class.getName() + HelloWorldServlet.RESPONSE_TEXT, client.getResponseBody()); } private static void configureTest46243Context(Context context, boolean fail) { @@ -167,8 +168,8 @@ private static final long serialVersionUID = 1L; @Override - public void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { PrintWriter out = response.getWriter(); out.print(getClass().getName()); chain.doFilter(request, response); @@ -214,8 +215,7 @@ Assert.assertEquals(LifecycleState.STARTED, context.getState()); // Using a test from testBug49922() to check that the webapp is running - ByteChunk result = getUrl("http://localhost:" + getPort() + - "/bug49922/target"); + ByteChunk result = getUrl("http://localhost:" + getPort() + "/bug49922/target"); Assert.assertEquals("Target", result.toString()); } @@ -250,16 +250,17 @@ Assert.assertEquals(LifecycleState.STARTED, context.getState()); // Using a test from testBug49922() to check that the webapp is running - ByteChunk result = getUrl("http://localhost:" + getPort() + - "/bug49922/target"); + ByteChunk result = getUrl("http://localhost:" + getPort() + "/bug49922/target"); Assert.assertEquals("Target", result.toString()); } private static class FailingWebappLoader extends WebappLoader { private boolean fail = true; + protected void setFail(boolean fail) { this.fail = fail; } + @Override protected void startInternal() throws LifecycleException { if (fail) { @@ -272,9 +273,11 @@ private static class FailingLifecycleListener implements LifecycleListener { private static final String failEvent = Lifecycle.CONFIGURE_START_EVENT; private boolean fail = true; + protected void setFail(boolean fail) { this.fail = fail; } + @Override public void lifecycleEvent(LifecycleEvent event) { if (fail && event.getType().equals(failEvent)) { @@ -294,8 +297,7 @@ ByteChunk result = new ByteChunk(); // Check filter and servlet aren't called - int rc = getUrl("http://localhost:" + getPort() + - "/test/bug49922/foo", result, null); + int rc = getUrl("http://localhost:" + getPort() + "/test/bug49922/foo", result, null); Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); Assert.assertTrue(result.getLength() > 0); @@ -312,22 +314,17 @@ Assert.assertEquals("FilterServlet", result.toString()); // Check filter is only called once - result = getUrl("http://localhost:" + getPort() + - "/test/bug49922/servlet/foo.do"); + result = getUrl("http://localhost:" + getPort() + "/test/bug49922/servlet/foo.do"); Assert.assertEquals("FilterServlet", result.toString()); - result = getUrl("http://localhost:" + getPort() + - "/test/bug49922/servlet/foo.od"); + result = getUrl("http://localhost:" + getPort() + "/test/bug49922/servlet/foo.od"); Assert.assertEquals("FilterServlet", result.toString()); // Check dispatcher mapping - result = getUrl("http://localhost:" + getPort() + - "/test/bug49922/target"); + result = getUrl("http://localhost:" + getPort() + "/test/bug49922/target"); Assert.assertEquals("Target", result.toString()); - result = getUrl("http://localhost:" + getPort() + - "/test/bug49922/forward"); + result = getUrl("http://localhost:" + getPort() + "/test/bug49922/forward"); Assert.assertEquals("FilterTarget", result.toString()); - result = getUrl("http://localhost:" + getPort() + - "/test/bug49922/include"); + result = getUrl("http://localhost:" + getPort() + "/test/bug49922/include"); Assert.assertEquals("IncludeFilterTarget", result.toString()); } @@ -337,8 +334,8 @@ private static final long serialVersionUID = 1L; @Override - public void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { response.setContentType("text/plain"); response.getWriter().print("Filter"); chain.doFilter(request, response); @@ -350,8 +347,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher("/bug49922/target").forward(req, resp); } @@ -362,8 +358,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.getWriter().print("Include"); req.getRequestDispatcher("/bug49922/target").include(req, resp); @@ -376,8 +371,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.getWriter().print("Target"); } @@ -389,8 +383,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.getWriter().print("Servlet"); } @@ -429,28 +422,24 @@ // Request the first servlet ByteChunk bc = new ByteChunk(); - int rc = getUrl("http://localhost:" + getPort() + "/bug50015", - bc, null); + int rc = getUrl("http://localhost:" + getPort() + "/bug50015", bc, null); // Check for a 401 Assert.assertNotSame("OK", bc.toString()); Assert.assertEquals(401, rc); } - public static final class Bug50015SCI - implements ServletContainerInitializer { + public static final class Bug50015SCI implements ServletContainerInitializer { @Override - public void onStartup(Set> c, ServletContext ctx) - throws ServletException { + public void onStartup(Set> c, ServletContext ctx) throws ServletException { // Register and map servlet Servlet s = new TesterServlet(); ServletRegistration.Dynamic sr = ctx.addServlet("bug50015", s); sr.addMapping("/bug50015"); // Limit access to users in the Tomcat role - HttpConstraintElement hce = new HttpConstraintElement( - TransportGuarantee.NONE, "tomcat"); + HttpConstraintElement hce = new HttpConstraintElement(TransportGuarantee.NONE, "tomcat"); ServletSecurityElement sse = new ServletSecurityElement(hce); sr.setServletSecurity(sse); } @@ -466,8 +455,7 @@ doTestDenyUncoveredHttpMethodsSCI(false); } - private void doTestDenyUncoveredHttpMethodsSCI(boolean enableDeny) - throws Exception { + private void doTestDenyUncoveredHttpMethodsSCI(boolean enableDeny) throws Exception { // Test that denying uncovered HTTP methods when adding servlet security // constraints programmatically does work. @@ -500,8 +488,7 @@ // Request the first servlet ByteChunk bc = new ByteChunk(); - int rc = getUrl("http://localhost:" + getPort() + "/test", - bc, null); + int rc = getUrl("http://localhost:" + getPort() + "/test", bc, null); // Check for a 401 if (enableDeny) { @@ -514,22 +501,18 @@ } } - public static final class DenyUncoveredHttpMethodsSCI - implements ServletContainerInitializer { + public static final class DenyUncoveredHttpMethodsSCI implements ServletContainerInitializer { @Override - public void onStartup(Set> c, ServletContext ctx) - throws ServletException { + public void onStartup(Set> c, ServletContext ctx) throws ServletException { // Register and map servlet Servlet s = new TesterServlet(); ServletRegistration.Dynamic sr = ctx.addServlet("test", s); sr.addMapping("/test"); // Add a constraint with uncovered methods - HttpConstraintElement hce = new HttpConstraintElement( - TransportGuarantee.NONE, "tomcat"); - HttpMethodConstraintElement hmce = - new HttpMethodConstraintElement("POST", hce); + HttpConstraintElement hce = new HttpConstraintElement(TransportGuarantee.NONE, "tomcat"); + HttpMethodConstraintElement hmce = new HttpMethodConstraintElement(Method.POST, hce); Set hmces = new HashSet<>(); hmces.add(hmce); ServletSecurityElement sse = new ServletSecurityElement(hmces); @@ -573,8 +556,7 @@ Assert.assertTrue(loadOnStartUp == sci.getServlet().isInitCalled()); } - public static final class Bug51376SCI - implements ServletContainerInitializer { + public static final class Bug51376SCI implements ServletContainerInitializer { private Bug51376Servlet s = null; private boolean loadOnStartUp; @@ -588,8 +570,7 @@ } @Override - public void onStartup(Set> c, ServletContext ctx) - throws ServletException { + public void onStartup(Set> c, ServletContext ctx) throws ServletException { // Register and map servlet s = new Bug51376Servlet(); ServletRegistration.Dynamic sr = ctx.addServlet("bug51376", s); @@ -626,15 +607,13 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.getWriter().write("OK"); } protected boolean isOk() { - if (initOk != null && initOk.booleanValue() && destroyOk != null && - destroyOk.booleanValue()) { + if (initOk != null && initOk.booleanValue() && destroyOk != null && destroyOk.booleanValue()) { return true; } else if (initOk == null && destroyOk == null) { return true; @@ -649,8 +628,7 @@ } /** - * Test case for bug 49711: HttpServletRequest.getParts does not work - * in a filter. + * Test case for bug 49711: HttpServletRequest.getParts does not work in a filter. */ @Test public void testBug49711() { @@ -667,9 +645,7 @@ // Make sure regular multipart works properly client.doRequest("/multipart", false, true); // send multipart request - Assert.assertEquals("Regular multipart doesn't work", - "parts=1", - client.getResponseBody()); + Assert.assertEquals("Regular multipart doesn't work", "parts=1", client.getResponseBody()); client.reset(); @@ -687,9 +663,8 @@ // there is no @MultipartConfig client.doRequest("/regular", true, true); // send multipart request - Assert.assertEquals("Incorrect response for configured casual multipart request", - "parts=1", - client.getResponseBody()); + Assert.assertEquals("Incorrect response for configured casual multipart request", "parts=1", + client.getResponseBody()); client.reset(); } @@ -698,17 +673,14 @@ private static final long serialVersionUID = 1L; @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Just echo the parameters and values back as plain text resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); PrintWriter out = resp.getWriter(); - out.print("parts=" + (null == req.getParts() - ? "null" - : Integer.valueOf(req.getParts().size()))); + out.print("parts=" + (null == req.getParts() ? "null" : Integer.valueOf(req.getParts().size()))); } } @@ -727,7 +699,7 @@ private synchronized void init() throws Exception { if (init) { - return; + return; } Tomcat tomcat = getTomcatInstance(); @@ -748,9 +720,7 @@ init = true; } - private Exception doRequest(String uri, - boolean allowCasualMultipart, - boolean makeMultipartRequest) { + private Exception doRequest(String uri, boolean allowCasualMultipart, boolean makeMultipartRequest) { try { init(); @@ -762,34 +732,22 @@ // Send specified request body using method String[] request; - if(makeMultipartRequest) { + if (makeMultipartRequest) { String boundary = "--simpleboundary"; - String content = "--" + boundary + CRLF - + "Content-Disposition: form-data; name=\"name\"" + CRLF + CRLF - + "value" + CRLF - + "--" + boundary + "--" + CRLF; + String content = "--" + boundary + CRLF + "Content-Disposition: form-data; name=\"name\"" + CRLF + + CRLF + "value" + CRLF + "--" + boundary + "--" + CRLF; // Re-encode the content so that bytes = characters content = new String(content.getBytes("UTF-8"), "ASCII"); - request = new String[] { - "POST http://localhost:" + getPort() + uri + " HTTP/1.1" + CRLF - + "Host: localhost:" + getPort() + CRLF - + "Connection: close" + CRLF - + "Content-Type: multipart/form-data; boundary=" + boundary + CRLF - + "Content-Length: " + content.length() + CRLF - + CRLF - + content - + CRLF - }; + request = new String[] { "POST http://localhost:" + getPort() + uri + " HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + "Connection: close" + CRLF + + "Content-Type: multipart/form-data; boundary=" + boundary + CRLF + "Content-Length: " + + content.length() + CRLF + CRLF + content + CRLF }; } else { - request = new String[] { - "GET http://localhost:" + getPort() + uri + " HTTP/1.1" + CRLF - + "Host: localhost:" + getPort() + CRLF - + "Connection: close" + CRLF - + CRLF - }; + request = new String[] { "GET http://localhost:" + getPort() + uri + " HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + "Connection: close" + CRLF + CRLF }; } setRequest(request); @@ -869,8 +827,7 @@ public void testFlagFailCtxIfServletStartFails() throws Exception { Tomcat tomcat = getTomcatInstance(); File docBase = new File(System.getProperty("java.io.tmpdir")); - StandardContext context = (StandardContext) tomcat.addContext("", - docBase.getAbsolutePath()); + StandardContext context = (StandardContext) tomcat.addContext("", docBase.getAbsolutePath()); // first we test the flag itself, which can be set on the Host and // Context @@ -884,26 +841,21 @@ context.getComputedFailCtxIfServletStartFails()); // second, we test the actual effect of the flag on the startup - Wrapper servlet = Tomcat.addServlet(context, "myservlet", - new FailingStartupServlet()); + Wrapper servlet = Tomcat.addServlet(context, "myservlet", new FailingStartupServlet()); servlet.setLoadOnStartup(1); tomcat.start(); - Assert.assertTrue("flag false should not fail deployment", context.getState() - .isAvailable()); + Assert.assertTrue("flag false should not fail deployment", context.getState().isAvailable()); tomcat.stop(); Assert.assertFalse(context.getState().isAvailable()); host.removeChild(context); - context = (StandardContext) tomcat.addContext("", - docBase.getAbsolutePath()); - servlet = Tomcat.addServlet(context, "myservlet", - new FailingStartupServlet()); + context = (StandardContext) tomcat.addContext("", docBase.getAbsolutePath()); + servlet = Tomcat.addServlet(context, "myservlet", new FailingStartupServlet()); servlet.setLoadOnStartup(1); tomcat.start(); - Assert.assertFalse("flag true should fail deployment", context.getState() - .isAvailable()); + Assert.assertFalse("flag true should fail deployment", context.getState().isAvailable()); } private static class FailingStartupServlet extends HttpServlet { @@ -927,8 +879,7 @@ } /* - * Check real path for directories ends with File.separator for consistency - * with previous major versions. + * Check real path for directories ends with File.separator for consistency with previous major versions. */ @Test public void testBug57556a() throws Exception { @@ -944,8 +895,8 @@ doTestBug57556(testContext, "", base + File.separatorChar); doTestBug57556(testContext, "/", base + File.separatorChar); - doTestBug57556(testContext, "/jsp", base + File.separatorChar+ "jsp"); - doTestBug57556(testContext, "/jsp/", base + File.separatorChar+ "jsp" + File.separatorChar); + doTestBug57556(testContext, "/jsp", base + File.separatorChar + "jsp"); + doTestBug57556(testContext, "/jsp/", base + File.separatorChar + "jsp" + File.separatorChar); doTestBug57556(testContext, "/index.html", base + File.separatorChar + "index.html"); doTestBug57556(testContext, "/foo", base + File.separatorChar + "foo"); doTestBug57556(testContext, "/foo/", base + File.separatorChar + "foo" + File.separatorChar); @@ -1027,23 +978,20 @@ public static class SCI implements ServletContainerInitializer { @Override - public void onStartup(Set> c, ServletContext ctx) - throws ServletException { + public void onStartup(Set> c, ServletContext ctx) throws ServletException { ServletRegistration.Dynamic sr = ctx.addServlet("Foo", Foo.class.getName()); sr.addMapping("/foo"); } } - @ServletSecurity(value=@HttpConstraint(ServletSecurity.EmptyRoleSemantic.DENY), - httpMethodConstraints=@HttpMethodConstraint("POST")) + @ServletSecurity(value = @HttpConstraint(ServletSecurity.EmptyRoleSemantic.DENY), httpMethodConstraints = @HttpMethodConstraint(Method.POST)) public static class Foo extends HttpServlet { private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().print("OK"); } } @@ -1062,7 +1010,7 @@ context.setName("context"); context.setParent(host); - Method m = StandardContext.class.getDeclaredMethod("getNamingContextName"); + java.lang.reflect.Method m = StandardContext.class.getDeclaredMethod("getNamingContextName"); m.setAccessible(true); String result = (String) m.invoke(context); @@ -1091,6 +1039,7 @@ } private static boolean customWrapperClassOk = false; + public static class MyWrapperClass extends StandardWrapper { @Override protected void startInternal() throws LifecycleException { @@ -1098,14 +1047,18 @@ customWrapperClassOk = true; } } + private static boolean containerListenerOk = false; + public static class MyWrapperContainerListener implements ContainerListener { @Override public void containerEvent(ContainerEvent event) { containerListenerOk = true; } } + private static boolean lifecycleListenerOk = false; + public static class MyWrapperLifecycleListener implements LifecycleListener { @Override public void lifecycleEvent(LifecycleEvent event) { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardContextAliases.java tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContextAliases.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardContextAliases.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContextAliases.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,8 +48,7 @@ File lib = new File("webapps/examples/WEB-INF/lib"); ctx.setResources(new StandardRoot(ctx)); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/lib", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/lib", lib.getAbsolutePath(), null, "/"); @@ -79,8 +78,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardContextResources.java tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContextResources.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardContextResources.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContextResources.java 2026-01-23 19:33:36.000000000 +0000 @@ -54,8 +54,7 @@ // present. The listener affects the JVM, and thus not only the current, // but also the subsequent tests that are run in the same JVM. So it is // fair to add it in every test. - tomcat.getServer().addLifecycleListener( - new JreMemoryLeakPreventionListener()); + tomcat.getServer().addLifecycleListener(new JreMemoryLeakPreventionListener()); } @Test @@ -68,31 +67,23 @@ tomcat.start(); - assertPageContains("/test/resourceA.jsp", - "

    resourceA.jsp in the web application

    "); - assertPageContains("/test/resourceB.jsp", - "

    resourceB.jsp in resources.jar

    "); - assertPageContains("/test/folder/resourceC.jsp", - "

    resourceC.jsp in the web application

    "); - assertPageContains("/test/folder/resourceD.jsp", - "

    resourceD.jsp in resources.jar

    "); - assertPageContains("/test/folder/resourceE.jsp", - "

    resourceE.jsp in the web application

    "); - assertPageContains("/test/resourceG.jsp", - "

    resourceG.jsp in WEB-INF/classes

    ", 404); + assertPageContains("/test/resourceA.jsp", "

    resourceA.jsp in the web application

    "); + assertPageContains("/test/resourceB.jsp", "

    resourceB.jsp in resources.jar

    "); + assertPageContains("/test/folder/resourceC.jsp", "

    resourceC.jsp in the web application

    "); + assertPageContains("/test/folder/resourceD.jsp", "

    resourceD.jsp in resources.jar

    "); + assertPageContains("/test/folder/resourceE.jsp", "

    resourceE.jsp in the web application

    "); + assertPageContains("/test/resourceG.jsp", "

    resourceG.jsp in WEB-INF/classes

    ", 404); // For BZ 54391. Relative ordering is specified in resources2.jar. // It is not absolute-ordering, so there may be other jars in the list @SuppressWarnings("unchecked") - List orderedLibs = (List) ctx.getServletContext() - .getAttribute(ServletContext.ORDERED_LIBS); + List orderedLibs = (List) ctx.getServletContext().getAttribute(ServletContext.ORDERED_LIBS); if (orderedLibs.size() > 2) { log.warn("testResources(): orderedLibs: " + orderedLibs); } int index = orderedLibs.indexOf("resources.jar"); int index2 = orderedLibs.indexOf("resources2.jar"); - Assert.assertTrue(orderedLibs.toString(), index >= 0 && index2 >= 0 - && index < index2); + Assert.assertTrue(orderedLibs.toString(), index >= 0 && index2 >= 0 && index < index2); } @Test @@ -103,24 +94,17 @@ File appDir = new File("test/webapp-fragments"); // Need to cast to be able to set StandardContext specific attribute - StandardContext ctxt = (StandardContext) - tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); + StandardContext ctxt = (StandardContext) tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); ctxt.setAddWebinfClassesResources(true); tomcat.start(); - assertPageContains("/test/resourceA.jsp", - "

    resourceA.jsp in the web application

    "); - assertPageContains("/test/resourceB.jsp", - "

    resourceB.jsp in resources.jar

    "); - assertPageContains("/test/folder/resourceC.jsp", - "

    resourceC.jsp in the web application

    "); - assertPageContains("/test/folder/resourceD.jsp", - "

    resourceD.jsp in resources.jar

    "); - assertPageContains("/test/folder/resourceE.jsp", - "

    resourceE.jsp in the web application

    "); - assertPageContains("/test/resourceG.jsp", - "

    resourceG.jsp in WEB-INF/classes

    "); + assertPageContains("/test/resourceA.jsp", "

    resourceA.jsp in the web application

    "); + assertPageContains("/test/resourceB.jsp", "

    resourceB.jsp in resources.jar

    "); + assertPageContains("/test/folder/resourceC.jsp", "

    resourceC.jsp in the web application

    "); + assertPageContains("/test/folder/resourceD.jsp", "

    resourceD.jsp in resources.jar

    "); + assertPageContains("/test/folder/resourceE.jsp", "

    resourceE.jsp in the web application

    "); + assertPageContains("/test/resourceG.jsp", "

    resourceG.jsp in WEB-INF/classes

    "); } @Test @@ -131,21 +115,19 @@ AbsoluteOrderContextConfig absoluteOrderConfig = new AbsoluteOrderContextConfig(); // app dir is relative to server home - StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test", - appDir.getAbsolutePath(), absoluteOrderConfig); + StandardContext ctx = + (StandardContext) tomcat.addWebapp(null, "/test", appDir.getAbsolutePath(), absoluteOrderConfig); Tomcat.addServlet(ctx, "getresource", new GetResourceServlet()); ctx.addServletMappingDecoded("/getresource", "getresource"); tomcat.start(); - assertPageContains("/test/getresource?path=/resourceF.jsp", - "

    resourceF.jsp in resources2.jar

    "); - assertPageContains("/test/getresource?path=/resourceB.jsp", - "

    resourceB.jsp in resources.jar

    "); + assertPageContains("/test/getresource?path=/resourceF.jsp", "

    resourceF.jsp in resources2.jar

    "); + assertPageContains("/test/getresource?path=/resourceB.jsp", "

    resourceB.jsp in resources.jar

    "); // Check ordering, for BZ 54391 - Assert.assertEquals(Arrays.asList("resources.jar", "resources2.jar"), ctx - .getServletContext().getAttribute(ServletContext.ORDERED_LIBS)); + Assert.assertEquals(Arrays.asList("resources.jar", "resources2.jar"), + ctx.getServletContext().getAttribute(ServletContext.ORDERED_LIBS)); tomcat.getHost().removeChild(ctx); tomcat.getHost().stop(); @@ -153,21 +135,18 @@ // change ordering absoluteOrderConfig.swap(); - ctx = (StandardContext) tomcat.addWebapp(null, "/test", - appDir.getAbsolutePath(), absoluteOrderConfig); + ctx = (StandardContext) tomcat.addWebapp(null, "/test", appDir.getAbsolutePath(), absoluteOrderConfig); Tomcat.addServlet(ctx, "getresource", new GetResourceServlet()); ctx.addServletMappingDecoded("/getresource", "getresource"); tomcat.getHost().start(); - assertPageContains("/test/getresource?path=/resourceF.jsp", - "

    resourceF.jsp in resources2.jar

    "); - assertPageContains("/test/getresource?path=/resourceB.jsp", - "

    resourceB.jsp in resources2.jar

    "); + assertPageContains("/test/getresource?path=/resourceF.jsp", "

    resourceF.jsp in resources2.jar

    "); + assertPageContains("/test/getresource?path=/resourceB.jsp", "

    resourceB.jsp in resources2.jar

    "); // Check ordering, for BZ 54391 - Assert.assertEquals(Arrays.asList("resources2.jar", "resources.jar"), ctx - .getServletContext().getAttribute(ServletContext.ORDERED_LIBS)); + Assert.assertEquals(Arrays.asList("resources2.jar", "resources.jar"), + ctx.getServletContext().getAttribute(ServletContext.ORDERED_LIBS)); } @@ -206,8 +185,7 @@ File appDir = new File("test/webapp-fragments"); // app dir is relative to server home - StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test", - appDir.getAbsolutePath()); + StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); skipTldsForResourceJars(ctx); Tomcat.addServlet(ctx, "getresource", new GetResourceServlet()); @@ -215,31 +193,26 @@ tomcat.start(); - assertPageContains("/test/getresource?path=/resourceF.jsp", - "

    resourceF.jsp in resources2.jar

    "); - assertPageContains("/test/getresource?path=/resourceA.jsp", - "

    resourceA.jsp in the web application

    "); - assertPageContains("/test/getresource?path=/resourceB.jsp", - "

    resourceB.jsp in resources.jar

    "); + assertPageContains("/test/getresource?path=/resourceF.jsp", "

    resourceF.jsp in resources2.jar

    "); + assertPageContains("/test/getresource?path=/resourceA.jsp", "

    resourceA.jsp in the web application

    "); + assertPageContains("/test/getresource?path=/resourceB.jsp", "

    resourceB.jsp in resources.jar

    "); assertPageContains("/test/getresource?path=/folder/resourceC.jsp", "

    resourceC.jsp in the web application

    "); - assertPageContains("/test/getresource?path=/folder/resourceD.jsp", - "

    resourceD.jsp in resources.jar

    "); + assertPageContains("/test/getresource?path=/folder/resourceD.jsp", "

    resourceD.jsp in resources.jar

    "); assertPageContains("/test/getresource?path=/folder/resourceE.jsp", "

    resourceE.jsp in the web application

    "); } /** - * A servlet that prints the requested resource. The path to the requested - * resource is passed as a parameter, path. + * A servlet that prints the requested resource. The path to the requested resource is passed as a parameter, + * path. */ public static class GetResourceServlet extends HttpServlet { private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); @@ -252,21 +225,18 @@ return; } - try (InputStream input = url.openStream(); - OutputStream output = resp.getOutputStream()) { + try (InputStream input = url.openStream(); OutputStream output = resp.getOutputStream()) { IOTools.flow(input, output); } } } - private void assertPageContains(String pageUrl, String expectedBody) - throws IOException { + private void assertPageContains(String pageUrl, String expectedBody) throws IOException { assertPageContains(pageUrl, expectedBody, 200); } - private void assertPageContains(String pageUrl, String expectedBody, - int expectedStatus) throws IOException { + private void assertPageContains(String pageUrl, String expectedBody, int expectedStatus) throws IOException { ByteChunk res = new ByteChunk(); int sc = getUrl("http://localhost:" + getPort() + pageUrl, res, null); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardContextValve.java tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContextValve.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardContextValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardContextValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -52,7 +52,7 @@ // Traces order of events across multiple components StringBuilder trace = new StringBuilder(); - //Add the error page + // Add the error page Tomcat.addServlet(ctx, "errorPage", new Bug51653ErrorPage(trace)); ctx.addServletMappingDecoded("/error", "errorPage"); // And the handling for 404 responses @@ -62,15 +62,13 @@ ctx.addErrorPage(errorPage); // Add the request listener - Bug51653RequestListener reqListener = - new Bug51653RequestListener(trace); + Bug51653RequestListener reqListener = new Bug51653RequestListener(trace); ((StandardContext) ctx).addApplicationEventListener(reqListener); tomcat.start(); // Request a page that does not exist - int rc = getUrl("http://localhost:" + getPort() + "/invalid", - new ByteChunk(), null); + int rc = getUrl("http://localhost:" + getPort() + "/invalid", new ByteChunk(), null); // Need to allow time (but not too long in case the test fails) for // ServletRequestListener to complete @@ -113,15 +111,13 @@ ctx.addErrorPage(errorPage); // Add the request listener - Bug51653RequestListener reqListener = - new Bug51653RequestListener(trace); + Bug51653RequestListener reqListener = new Bug51653RequestListener(trace); ((StandardContext) ctx).addApplicationEventListener(reqListener); tomcat.start(); // Request a page that does not exist - int rc = getUrl("http://localhost:" + getPort() + "/test", - new ByteChunk(), null); + int rc = getUrl("http://localhost:" + getPort() + "/test", new ByteChunk(), null); // Need to allow time (but not too long in case the test fails) for // ServletRequestListener to complete @@ -143,8 +139,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.sendError(Response.SC_NOT_FOUND); } } @@ -160,8 +155,7 @@ } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { sb.append("Error"); resp.setContentType("text/plain"); @@ -170,8 +164,7 @@ } - private static class Bug51653RequestListener - implements ServletRequestListener { + private static class Bug51653RequestListener implements ServletRequestListener { private StringBuilder sb; diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardHostValve.java tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardHostValve.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardHostValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardHostValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -74,8 +74,7 @@ } - private void doTestErrorPageHandling(int error, String exception, String report) - throws Exception { + private void doTestErrorPageHandling(int error, String exception, String report) throws Exception { // Set up a container Tomcat tomcat = getTomcatInstance(); @@ -118,8 +117,8 @@ // Request a page that triggers an error ByteChunk bc = new ByteChunk(); - int rc = getUrl("http://localhost:" + getPort() + "/error?errorCode=" + error + "&exception=" + exception, - bc, null); + int rc = getUrl("http://localhost:" + getPort() + "/error?errorCode=" + error + "&exception=" + exception, bc, + null); if (error > 399) { // Specific status code expected @@ -136,7 +135,7 @@ } - @Test(expected=IllegalArgumentException.class) + @Test(expected = IllegalArgumentException.class) public void testInvalidErrorPage() throws Exception { // No file system docBase required Context ctx = getProgrammaticRootContext(); @@ -238,8 +237,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int error = Integer.parseInt(req.getParameter("errorCode")); if (error > 399) { resp.sendError(error); @@ -273,8 +271,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.flushBuffer(); throw new IOException(); } @@ -286,8 +283,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String pathInfo = req.getPathInfo(); resp.setContentType("text/plain"); PrintWriter pw = resp.getWriter(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardWrapper.java tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardWrapper.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestStandardWrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestStandardWrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -50,6 +50,7 @@ import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.descriptor.web.LoginConfig; +import org.apache.tomcat.util.http.Method; public class TestStandardWrapper extends TomcatBaseTest { @@ -60,26 +61,22 @@ @Test public void testSecurityAnnotationsSubclass1() throws Exception { - doTest(SubclassDenyAllServlet.class.getName(), - false, false, false,false); + doTest(SubclassDenyAllServlet.class.getName(), false, false, false, false); } @Test public void testSecurityAnnotationsSubclass2() throws Exception { - doTest(SubclassAllowAllServlet.class.getName(), - false, false, true, false); + doTest(SubclassAllowAllServlet.class.getName(), false, false, true, false); } @Test public void testSecurityAnnotationsMethods1() throws Exception { - doTest(MethodConstraintServlet.class.getName(), - false, false, false, false); + doTest(MethodConstraintServlet.class.getName(), false, false, false, false); } @Test public void testSecurityAnnotationsMethods2() throws Exception { - doTest(MethodConstraintServlet.class.getName(), - true, false, true, false); + doTest(MethodConstraintServlet.class.getName(), true, false, true, false); } @Test @@ -154,9 +151,8 @@ ByteChunk bc = new ByteChunk(); int rc; - rc = getUrl("http://localhost:" + getPort() + - "/testStandardWrapper/securityAnnotationsWebXmlPriority", - bc, null, null); + rc = getUrl("http://localhost:" + getPort() + "/testStandardWrapper/securityAnnotationsWebXmlPriority", bc, + null, null); Assert.assertTrue(bc.getLength() > 0); Assert.assertEquals(403, rc); @@ -168,8 +164,7 @@ ByteChunk bc = new ByteChunk(); int rc; - rc = getUrl("http://localhost:" + getPort() + - "/test/testStandardWrapper/securityAnnotationsMetaDataPriority", + rc = getUrl("http://localhost:" + getPort() + "/test/testStandardWrapper/securityAnnotationsMetaDataPriority", bc, null, null); Assert.assertEquals("OK", bc.toString()); @@ -198,8 +193,7 @@ ByteChunk bc = new ByteChunk(); int rc; - rc = getUrl("http://localhost:" + getPort() + "/", - bc, null, null); + rc = getUrl("http://localhost:" + getPort() + "/", bc, null, null); Assert.assertTrue(bc.getLength() > 0); Assert.assertEquals(403, rc); @@ -217,16 +211,14 @@ ByteChunk bc = new ByteChunk(); int rc; - rc = getUrl("http://localhost:" + getPort() + "/protected.jsp", - bc, null, null); + rc = getUrl("http://localhost:" + getPort() + "/protected.jsp", bc, null, null); Assert.assertTrue(bc.getLength() > 0); Assert.assertEquals(403, rc); bc.recycle(); - rc = getUrl("http://localhost:" + getPort() + "/unprotected.jsp", - bc, null, null); + rc = getUrl("http://localhost:" + getPort() + "/unprotected.jsp", bc, null, null); Assert.assertEquals(200, rc); Assert.assertTrue(bc.toString().contains("00-OK")); @@ -247,8 +239,7 @@ doTestRoleMapping("context"); } - private void doTestRoleMapping(String realmContainer) - throws Exception { + private void doTestRoleMapping(String realmContainer) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -267,8 +258,9 @@ ch.setAlgorithm("SHA"); realm.setCredentialHandler(ch); - /* Attach the realm to the appropriate container, but role mapping must - * always succeed because it is evaluated at context level. + /* + * Attach the realm to the appropriate container, but role mapping must always succeed because it is evaluated + * at context level. */ if (realmContainer.equals("engine")) { tomcat.getEngine().setRealm(realm); @@ -301,21 +293,19 @@ // This now tests RealmBase#hasResourcePermission() because we need a wrapper // to be passed from an authenticator ByteChunk bc = new ByteChunk(); - Map> reqHeaders = new HashMap<>(); + Map> reqHeaders = new HashMap<>(); List authHeaders = new ArrayList<>(); // testUser, testPwd authHeaders.add("Basic dGVzdFVzZXI6dGVzdFB3ZA=="); reqHeaders.put("Authorization", authHeaders); - int rc = getUrl("http://localhost:" + getPort() + "/", bc, reqHeaders, - null); + int rc = getUrl("http://localhost:" + getPort() + "/", bc, reqHeaders, null); Assert.assertEquals("OK", bc.toString()); Assert.assertEquals(200, rc); } - private void doTestSecurityAnnotationsAddServlet(boolean useCreateServlet) - throws Exception { + private void doTestSecurityAnnotationsAddServlet(boolean useCreateServlet) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -342,9 +332,8 @@ } } - private void doTest(String servletClassName, boolean usePost, - boolean useRole, boolean expect200, boolean denyUncovered) - throws Exception { + private void doTest(String servletClassName, boolean usePost, boolean useRole, boolean expect200, + boolean denyUncovered) throws Exception { // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); @@ -382,11 +371,9 @@ int rc; if (usePost) { - rc = postUrl(null, "http://localhost:" + getPort() + "/", bc, - reqHeaders, null); + rc = postUrl(null, "http://localhost:" + getPort() + "/", bc, reqHeaders, null); } else { - rc = getUrl("http://localhost:" + getPort() + "/", bc, reqHeaders, - null); + rc = getUrl("http://localhost:" + getPort() + "/", bc, reqHeaders, null); } if (expect200) { @@ -402,16 +389,14 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.getWriter().print("OK"); } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } } @@ -430,20 +415,13 @@ private static final long serialVersionUID = 1L; } - @ServletSecurity(value= @HttpConstraint(EmptyRoleSemantic.PERMIT), - httpMethodConstraints = { - @HttpMethodConstraint(value="GET", - emptyRoleSemantic = EmptyRoleSemantic.DENY) - } - ) + @ServletSecurity(value = @HttpConstraint(EmptyRoleSemantic.PERMIT), httpMethodConstraints = { + @HttpMethodConstraint(value = Method.GET, emptyRoleSemantic = EmptyRoleSemantic.DENY) }) public static class MethodConstraintServlet extends TestServlet { private static final long serialVersionUID = 1L; } - @ServletSecurity(httpMethodConstraints = { - @HttpMethodConstraint(value="POST",rolesAllowed = "testRole") - } - ) + @ServletSecurity(httpMethodConstraints = { @HttpMethodConstraint(value = Method.POST, rolesAllowed = "testRole") }) public static class UncoveredGetServlet extends TestServlet { private static final long serialVersionUID = 1L; } @@ -469,8 +447,7 @@ } @Override - public void onStartup(Set> c, ServletContext ctx) - throws ServletException { + public void onStartup(Set> c, ServletContext ctx) throws ServletException { Servlet s; if (createServlet) { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/core/TestSwallowAbortedUploads.java tomcat10-10.1.52/test/org/apache/catalina/core/TestSwallowAbortedUploads.java --- tomcat10-10.1.34/test/org/apache/catalina/core/TestSwallowAbortedUploads.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/core/TestSwallowAbortedUploads.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,6 +23,7 @@ import java.io.PrintWriter; import java.io.Writer; import java.net.Socket; +import java.net.SocketException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; @@ -57,8 +58,7 @@ /* * Test whether size limited uploads correctly handle connection draining. */ - public Exception doAbortedUploadTest(AbortedUploadClient client, boolean limited, - boolean swallow) { + public Exception doAbortedUploadTest(AbortedUploadClient client, boolean limited, boolean swallow) { Exception ex = client.doRequest(limited, swallow); if (log.isDebugEnabled()) { log.debug("Response line: " + client.getResponseLine()); @@ -75,8 +75,7 @@ /* * Test whether aborted POST correctly handle connection draining. */ - public Exception doAbortedPOSTTest(AbortedPOSTClient client, int status, - boolean swallow) { + public Exception doAbortedPOSTTest(AbortedPOSTClient client, int status, boolean swallow) { Exception ex = client.doRequest(status, swallow); if (log.isDebugEnabled()) { log.debug("Response line: " + client.getResponseLine()); @@ -95,10 +94,8 @@ log.info("Unlimited, swallow enabled"); AbortedUploadClient client = new AbortedUploadClient(); Exception ex = doAbortedUploadTest(client, false, true); - Assert.assertNull("Unlimited upload with swallow enabled generates client exception", - ex); - Assert.assertTrue("Unlimited upload with swallow enabled returns error status code", - client.isResponse200()); + Assert.assertNull("Unlimited upload with swallow enabled generates client exception", ex); + Assert.assertTrue("Unlimited upload with swallow enabled returns error status code", client.isResponse200()); client.reset(); } @@ -107,10 +104,8 @@ log.info("Unlimited, swallow disabled"); AbortedUploadClient client = new AbortedUploadClient(); Exception ex = doAbortedUploadTest(client, false, false); - Assert.assertNull("Unlimited upload with swallow disabled generates client exception", - ex); - Assert.assertTrue("Unlimited upload with swallow disabled returns error status code", - client.isResponse200()); + Assert.assertNull("Unlimited upload with swallow disabled generates client exception", ex); + Assert.assertTrue("Unlimited upload with swallow disabled returns error status code", client.isResponse200()); client.reset(); } @@ -119,10 +114,8 @@ log.info("Limited, swallow enabled"); AbortedUploadClient client = new AbortedUploadClient(); Exception ex = doAbortedUploadTest(client, true, true); - Assert.assertNull("Limited upload with swallow enabled generates client exception", - ex); - Assert.assertTrue("Limited upload with swallow enabled returns non-500 status code", - client.isResponse500()); + Assert.assertNull("Limited upload with swallow enabled generates client exception", ex); + Assert.assertTrue("Limited upload with swallow enabled returns non-500 status code", client.isResponse500()); client.reset(); } @@ -131,8 +124,8 @@ log.info("Limited, swallow disabled"); AbortedUploadClient client = new AbortedUploadClient(); Exception ex = doAbortedUploadTest(client, true, false); - assertThat("Limited upload with swallow disabled does not generate client exception", - ex, instanceOf(java.net.SocketException.class)); + assertThat("Limited upload with swallow disabled does not generate client exception", ex, + instanceOf(java.net.SocketException.class)); client.reset(); } @@ -141,10 +134,8 @@ log.info("Aborted (OK), swallow enabled"); AbortedPOSTClient client = new AbortedPOSTClient(); Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_OK, true); - Assert.assertNull("Unlimited upload with swallow enabled generates client exception", - ex); - Assert.assertTrue("Unlimited upload with swallow enabled returns error status code", - client.isResponse200()); + Assert.assertNull("Unlimited upload with swallow enabled generates client exception", ex); + Assert.assertTrue("Unlimited upload with swallow enabled returns error status code", client.isResponse200()); client.reset(); } @@ -153,10 +144,8 @@ log.info("Aborted (OK), swallow disabled"); AbortedPOSTClient client = new AbortedPOSTClient(); Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_OK, false); - Assert.assertNull("Unlimited upload with swallow disabled generates client exception", - ex); - Assert.assertTrue("Unlimited upload with swallow disabled returns error status code", - client.isResponse200()); + Assert.assertNull("Unlimited upload with swallow disabled generates client exception", ex); + Assert.assertTrue("Unlimited upload with swallow disabled returns error status code", client.isResponse200()); client.reset(); } @@ -165,10 +154,8 @@ log.info("Aborted (413), swallow enabled"); AbortedPOSTClient client = new AbortedPOSTClient(); Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, true); - Assert.assertNull("Limited upload with swallow enabled generates client exception", - ex); - Assert.assertTrue("Limited upload with swallow enabled returns error status code", - client.isResponse413()); + Assert.assertNull("Limited upload with swallow enabled generates client exception", ex); + Assert.assertTrue("Limited upload with swallow enabled returns error status code", client.isResponse413()); client.reset(); } @@ -177,8 +164,8 @@ log.info("Aborted (413), swallow disabled"); AbortedPOSTClient client = new AbortedPOSTClient(); Exception ex = doAbortedPOSTTest(client, HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, false); - assertThat("Limited upload with swallow disabled does not generate client exception", - ex, instanceOf(java.net.SocketException.class)); + assertThat("Limited upload with swallow disabled does not generate client exception", ex, + instanceOf(java.net.SocketException.class)); client.reset(); } @@ -188,8 +175,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter out = resp.getWriter(); resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); @@ -203,10 +189,8 @@ log.debug("Count: " + c.size()); sb.append("Count: " + c.size() + "\n"); for (Part p : c) { - log.debug("Name: " + p.getName() + ", Size: " - + p.getSize()); - sb.append("Name: " + p.getName() + ", Size: " - + p.getSize() + "\n"); + log.debug("Name: " + p.getName() + ", Size: " + p.getSize()); + sb.append("Name: " + p.getName() + ", Size: " + p.getSize() + "\n"); } } } catch (IllegalStateException ex) { @@ -236,20 +220,17 @@ private Context context; - private synchronized void init(boolean limited, boolean swallow) - throws Exception { + private synchronized void init(boolean limited, boolean swallow) throws Exception { Tomcat tomcat = getTomcatInstance(); context = tomcat.addContext("", TEMP_DIR); Wrapper w; - w = Tomcat.addServlet(context, servletName, - new AbortedUploadServlet()); + w = Tomcat.addServlet(context, servletName, new AbortedUploadServlet()); // Tomcat.addServlet does not respect annotations, so we have // to set our own MultipartConfigElement. // Choose upload file size limit. if (limited) { - w.setMultipartConfigElement(new MultipartConfigElement("", - limitSize, -1, -1)); + w.setMultipartConfigElement(new MultipartConfigElement("", limitSize, -1, -1)); } else { w.setMultipartConfigElement(new MultipartConfigElement("")); } @@ -294,16 +275,19 @@ sb.append(CRLF); // Re-encode the content so that bytes = characters - String content = new String(sb.toString().getBytes("UTF-8"), - "ASCII"); + String content = new String(sb.toString().getBytes("UTF-8"), "ASCII"); - request = new String[] { "POST http://localhost:" + getPort() + URI + " HTTP/1.1" + CRLF + // @formatter:off + request = new String[] { + "POST http://localhost:" + getPort() + URI + " HTTP/1.1" + CRLF + "Host: localhost:" + getPort() + CRLF + "Connection: close" + CRLF + "Content-Type: multipart/form-data; boundary=" + boundary + CRLF + "Content-Length: " + content.length() + CRLF + CRLF - + content + CRLF }; + + content + CRLF + }; + // @formatter:on setRequest(request); processRequest(); // blocks until response has been read @@ -333,8 +317,7 @@ } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/plain"); resp.setCharacterEncoding("UTF-8"); resp.setStatus(status); @@ -356,8 +339,7 @@ private Context context; - private synchronized void init(int status, boolean swallow) - throws Exception { + private synchronized void init(int status, boolean swallow) throws Exception { Tomcat tomcat = getTomcatInstance(); context = tomcat.addContext("", TEMP_DIR); @@ -390,12 +372,15 @@ String content = new String(body); - request = new String[] { "POST http://localhost:" + getPort() + URI + " HTTP/1.1" + CRLF + // @formatter:off + request = new String[] { + "POST http://localhost:" + getPort() + URI + " HTTP/1.1" + CRLF + "Host: localhost:" + getPort() + CRLF + "Connection: close" + CRLF + "Content-Length: " + content.length() + CRLF + CRLF + content + CRLF }; + // @formatter:on setRequest(request); processRequest(); // blocks until response has been read @@ -439,13 +424,12 @@ tomcat.start(); - Exception writeEx = null; + SocketException writeEx = null; Exception readEx = null; String responseLine = null; try (Socket conn = new Socket("localhost", getPort())) { - Writer writer = new OutputStreamWriter( - conn.getOutputStream(), StandardCharsets.US_ASCII); + Writer writer = new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.US_ASCII); writer.write("PUT /does-not-exist HTTP/1.1\r\n"); writer.write("Host: any\r\n"); writer.write("Transfer-encoding: chunked\r\n"); @@ -459,17 +443,29 @@ writer.write("10\r\n"); writer.write("0123456789ABCDEF\r\n"); } - } catch (Exception e) { + writer.flush(); + } catch (SocketException e) { writeEx = e; } try { - BufferedReader reader = new BufferedReader(new InputStreamReader( - conn.getInputStream(), StandardCharsets.US_ASCII)); + BufferedReader reader = + new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.US_ASCII)); responseLine = reader.readLine(); - } catch (IOException e) { - readEx = e; + } catch (IOException ioe) { + readEx = ioe; + } + + // If no exception was thrown during the big write, + // write once more because the close may have not been observed + if (limit && writeEx == null) { + try { + writer.write("1\r\n"); + writer.flush(); + } catch (SocketException e) { + writeEx = e; + } } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TestAddCharSetFilter.java tomcat10-10.1.52/test/org/apache/catalina/filters/TestAddCharSetFilter.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TestAddCharSetFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TestAddCharSetFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -115,7 +115,7 @@ tomcat.start(); - Map> headers = new HashMap<>(); + Map> headers = new HashMap<>(); getUrl("http://localhost:" + getPort() + "/", new ByteChunk(), headers); String ct = getSingleHeader("Content-Type", headers).toLowerCase(Locale.ENGLISH); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TestCorsFilter.java tomcat10-10.1.52/test/org/apache/catalina/filters/TestCorsFilter.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TestCorsFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TestCorsFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,6 +32,7 @@ import org.junit.Assert; import org.junit.Test; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.RequestUtil; public class TestCorsFilter { @@ -50,7 +51,7 @@ public void testDoFilterSimpleGET() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setMethod("GET"); + request.setMethod(Method.GET); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -80,7 +81,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); request.setContentType("text/plain"); - request.setMethod("POST"); + request.setMethod(Method.POST); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -109,7 +110,7 @@ public void testDoFilterSimpleHEAD() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setMethod("HEAD"); + request.setMethod(Method.HEAD); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -136,7 +137,7 @@ public void testDoFilterSimpleSpecificHeader() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setMethod("POST"); + request.setMethod(Method.POST); request.setContentType("text/plain"); TesterHttpServletResponse response = new TesterHttpServletResponse(); @@ -177,7 +178,7 @@ public void testDoFilterSimpleAnyOriginAndSupportsCredentialsDisabled() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setMethod("GET"); + request.setMethod(Method.GET); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -206,7 +207,7 @@ public void testDoFilterSimpleWithExposedHeaders() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setMethod("POST"); + request.setMethod(Method.POST); request.setContentType("text/plain"); TesterHttpServletResponse response = new TesterHttpServletResponse(); @@ -236,9 +237,9 @@ public void testDoFilterPreflight() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "Content-Type"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -268,9 +269,9 @@ public void testDoFilterPreflightAnyOrigin() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "Content-Type"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -300,9 +301,9 @@ public void testDoFilterPreflightInvalidOrigin() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "http://www.example.com"); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "Content-Type"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -316,9 +317,9 @@ public void testDoFilterPreflightNegativeMaxAge() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "Content-Type"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -342,9 +343,9 @@ public void testDoFilterPreflightWithCredentials() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "Content-Type"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -369,9 +370,9 @@ public void testDoFilterPreflightWithoutCredentialsAndSpecificOrigin() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "Content-Type"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -398,7 +399,7 @@ public void testDoFilterNoOrigin() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); - request.setMethod("POST"); + request.setMethod(Method.POST); request.setContentType("text/plain"); TesterHttpServletResponse response = new TesterHttpServletResponse(); @@ -456,7 +457,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); - request.setMethod("POST"); + request.setMethod(Method.POST); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, origin); request.setScheme(scheme); request.setServerName(host); @@ -488,7 +489,7 @@ public void testDoFilterInvalidCORSOriginNotAllowed() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "www.google.com"); - request.setMethod("POST"); + request.setMethod(Method.POST); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -505,7 +506,7 @@ public void testDoFilterNullOriginAllowedByDefault() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); - request.setMethod("POST"); + request.setMethod(Method.POST); request.setContentType("text/plain"); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "null"); TesterHttpServletResponse response = new TesterHttpServletResponse(); @@ -528,7 +529,7 @@ public void testDoFilterNullOriginAllowedByConfiguration() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); - request.setMethod("POST"); + request.setMethod(Method.POST); request.setContentType("text/plain"); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "null"); TesterHttpServletResponse response = new TesterHttpServletResponse(); @@ -571,7 +572,7 @@ public void testInitDefaultFilterConfig() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setMethod("GET"); + request.setMethod(Method.GET); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -626,9 +627,9 @@ public void testNotSimple() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "Content-Type"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -647,7 +648,7 @@ public void testNotPreflight() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setMethod("GET"); + request.setMethod(Method.GET); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -690,7 +691,7 @@ public void testCheckSimpleRequestTypeAnyOrigin() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "http://www.w3.org"); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -706,7 +707,7 @@ public void testCheckSimpleRequestTypeGet() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -722,7 +723,7 @@ public void testCheckSimpleRequestTypePost() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setMethod("POST"); + request.setMethod(Method.POST); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -738,7 +739,7 @@ public void testCheckActualRequestType() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setMethod("PUT"); + request.setMethod(Method.PUT); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -754,7 +755,7 @@ public void testCheckActualRequestTypeMethodPOSTNotSimpleHeaders() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setMethod("POST"); + request.setMethod(Method.POST); request.setContentType("application/json"); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); @@ -771,9 +772,9 @@ public void testCheckPreFlightRequestType() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "Content-Type"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -788,7 +789,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -803,7 +804,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, ""); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -819,8 +820,8 @@ public void testCheckPreFlightRequestTypeNoHeaders() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); - request.setMethod("OPTIONS"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -840,7 +841,7 @@ TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "POLITE"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -859,8 +860,8 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "TRACE"); - request.setMethod("OPTIONS"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.TRACE); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -879,9 +880,9 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "X-ANSWER"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getSecureFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -900,9 +901,9 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, "Origin"); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getFilterConfigAnyOriginAndSupportsCredentialsDisabled()); corsFilter.doFilter(request, response, filterChain); @@ -915,8 +916,8 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "www.ebay.com"); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); - request.setMethod("OPTIONS"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getSecureFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -932,9 +933,9 @@ public void testCheckPreFlightRequestTypeEmptyHeaders() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTP_TOMCAT_APACHE_ORG); - request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, "PUT"); + request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_METHOD, Method.PUT); request.setHeader(CorsFilter.REQUEST_HEADER_ACCESS_CONTROL_REQUEST_HEADERS, ""); - request.setMethod("OPTIONS"); + request.setMethod(Method.OPTIONS); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -950,7 +951,7 @@ public void testCheckNotCORSRequestTypeEmptyOrigin() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, ""); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -969,7 +970,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "www.example.com"); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -984,7 +985,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "null"); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -1003,7 +1004,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "http://commons.apache.org"); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -1022,7 +1023,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "http://tomcat.apache.org"); - request.setMethod("PUT"); + request.setMethod(Method.PUT); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -1056,7 +1057,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "https://tomcat.apache.org"); - request.setMethod("POST"); + request.setMethod(Method.POST); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -1076,7 +1077,7 @@ TesterHttpServletRequest request = new TesterHttpServletRequest(); TesterHttpServletResponse response = new TesterHttpServletResponse(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "http://tomcat.apache.org:8080"); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getSpecificOriginFilterConfig()); corsFilter.doFilter(request, response, filterChain); @@ -1249,7 +1250,7 @@ public void testCheckInvalidCRLF1() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "http://www.w3.org\r\n"); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -1260,7 +1261,7 @@ public void testCheckInvalidCRLF2() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "http://www.w3.org\r\n"); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -1271,7 +1272,7 @@ public void testCheckInvalidCRLF3() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "http://www.w3.org%0d%0a"); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -1282,7 +1283,7 @@ public void testCheckInvalidCRLF4() throws ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "http://www.w3.org%0D%0A"); - request.setMethod("GET"); + request.setMethod(Method.GET); CorsFilter corsFilter = new CorsFilter(); corsFilter.init(TesterFilterConfigs.getDefaultFilterConfig()); CorsFilter.CORSRequestType requestType = corsFilter.checkRequestType(request); @@ -1293,7 +1294,7 @@ public void testDecorateRequestDisabled() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, TesterFilterConfigs.HTTPS_WWW_APACHE_ORG); - request.setMethod("GET"); + request.setMethod(Method.GET); TesterHttpServletResponse response = new TesterHttpServletResponse(); CorsFilter corsFilter = new CorsFilter(); @@ -1314,7 +1315,7 @@ public void testContentTypeWithParameter() throws IOException, ServletException { TesterHttpServletRequest request = new TesterHttpServletRequest(); - request.setMethod("POST"); + request.setMethod(Method.POST); request.setContentType("text/plain;charset=UTF-8"); request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN, "null"); TesterHttpServletResponse response = new TesterHttpServletResponse(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TestCsrfPreventionFilter.java tomcat10-10.1.52/test/org/apache/catalina/filters/TestCsrfPreventionFilter.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TestCsrfPreventionFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TestCsrfPreventionFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,6 +25,8 @@ import java.util.Collections; import java.util.Iterator; import java.util.function.Predicate; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import jakarta.servlet.http.HttpServletResponse; @@ -101,11 +103,15 @@ @Test public void testNoNonceBuilders() { - Assert.assertEquals(CsrfPreventionFilter.PrefixPredicate.class, CsrfPreventionFilter.createNoNoncePredicate(null, "/images/*").getClass()); - Assert.assertEquals(CsrfPreventionFilter.SuffixPredicate.class, CsrfPreventionFilter.createNoNoncePredicate(null, "*.png").getClass()); - Assert.assertEquals(CsrfPreventionFilter.PatternPredicate.class, CsrfPreventionFilter.createNoNoncePredicate(null, "/^(/images/.*|.*\\.png)$/").getClass()); + Assert.assertEquals(CsrfPreventionFilter.PrefixPredicate.class, + CsrfPreventionFilter.createNoNoncePredicate(null, "/images/*").getClass()); + Assert.assertEquals(CsrfPreventionFilter.SuffixPredicate.class, + CsrfPreventionFilter.createNoNoncePredicate(null, "*.png").getClass()); + Assert.assertEquals(CsrfPreventionFilter.PatternPredicate.class, + CsrfPreventionFilter.createNoNoncePredicate(null, "/^(/images/.*|.*\\.png)$/").getClass()); - Collection> chain = CsrfPreventionFilter.createNoNoncePredicates(null, "*.png,/js/*,*.jpg,/images/*,mime:*/png,mime:image/*"); + Collection> chain = CsrfPreventionFilter.createNoNoncePredicates(null, + "*.png,/js/*,*.jpg,/images/*,mime:*/png,mime:image/*"); Assert.assertEquals(6, chain.size()); Iterator> items = chain.iterator(); @@ -116,11 +122,13 @@ Assert.assertEquals(CsrfPreventionFilter.PrefixPredicate.class, items.next().getClass()); Predicate item = items.next(); Assert.assertEquals(CsrfPreventionFilter.MimePredicate.class, item.getClass()); - Assert.assertEquals(CsrfPreventionFilter.SuffixPredicate.class, ((CsrfPreventionFilter.MimePredicate)item).getPredicate().getClass()); + Assert.assertEquals(CsrfPreventionFilter.SuffixPredicate.class, + ((CsrfPreventionFilter.MimePredicate) item).getPredicate().getClass()); item = items.next(); Assert.assertEquals(CsrfPreventionFilter.MimePredicate.class, item.getClass()); - Assert.assertEquals(CsrfPreventionFilter.PrefixPredicate.class, ((CsrfPreventionFilter.MimePredicate)item).getPredicate().getClass()); + Assert.assertEquals(CsrfPreventionFilter.PrefixPredicate.class, + ((CsrfPreventionFilter.MimePredicate) item).getPredicate().getClass()); } @Test @@ -130,7 +138,7 @@ Predicate suffix = new CsrfPreventionFilter.SuffixPredicate(".png"); Predicate regex = new CsrfPreventionFilter.PatternPredicate("^(/images/.*|.*\\.png)$"); - for(String url : urls) { + for (String url : urls) { Assert.assertTrue("Prefix match fails", prefix.test(url)); Assert.assertTrue("Suffix match fails", suffix.test(url)); Assert.assertTrue("Pattern match fails", regex.test(url)); @@ -159,7 +167,8 @@ @Test public void testNoNonceMimeMatcher() { MimeTypeServletContext context = new MimeTypeServletContext(); - Predicate mime = new CsrfPreventionFilter.MimePredicate(context, new CsrfPreventionFilter.PrefixPredicate("image/")); + Predicate mime = + new CsrfPreventionFilter.MimePredicate(context, new CsrfPreventionFilter.PrefixPredicate("image/")); context.setMimeType("image/png"); Assert.assertTrue("MIME match fails", mime.test("/images/home.png")); @@ -189,8 +198,109 @@ Assert.assertEquals("/foo/images/home.jpg", response.encodeURL("/foo/images/home.jpg")); } + @Test + public void testMultipleTokens() { + String nonce = "TESTNONCE"; + String testURL = "/foo/bar?" + Constants.CSRF_NONCE_SESSION_ATTR_NAME + "=sample"; + CsrfPreventionFilter.CsrfResponseWrapper response = new CsrfPreventionFilter.CsrfResponseWrapper(new NonEncodingResponse(), + Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonce, null); + + Assert.assertTrue("Original URL does not contain CSRF token", + testURL.contains(Constants.CSRF_NONCE_SESSION_ATTR_NAME)); + + String result = response.encodeURL(testURL); + + int pos = result.indexOf(Constants.CSRF_NONCE_SESSION_ATTR_NAME); + Assert.assertTrue("Result URL does not contain CSRF token", + pos >= 0); + pos = result.indexOf(Constants.CSRF_NONCE_SESSION_ATTR_NAME, pos + 1); + Assert.assertFalse("Result URL contains multiple CSRF tokens: " + result, + pos >= 0); + } + + @Test + public void testURLCleansing() { + String[] urls = new String[] { + "/foo/bar", + "/foo/bar?", + "/foo/bar?csrf", + "/foo/bar?csrf&", + "/foo/bar?csrf=", + "/foo/bar?csrf=&", + "/foo/bar?csrf=abc", + "/foo/bar?csrf=abc&bar=foo", + "/foo/bar?bar=foo&csrf=abc", + "/foo/bar?bar=foo&csrf=abc&foo=bar", + "/foo/bar?csrfx=foil&bar=foo&csrf=abc&foo=bar", + "/foo/bar?csrfx=foil&bar=foo&csrf=abc&foo=bar&csrf=def", + "/foo/bar?csrf=&csrf&csrf&csrf&csrf=abc&csrf=", + "/foo/bar?xcsrf=&xcsrf&xcsrf&xcsrf&xcsrf=abc&xcsrf=", + "/foo/bar?xcsrf=&xcsrf&xcsrf&csrf=foo&xcsrf&xcsrf=abc&csrf=bar&xcsrf=&", + "/foo/bar?bar=fo?&csrf=abc", + "/foo/bar?bar=fo?&csrf=abc&baz=boh", + }; + + String csrfParameterName = "csrf"; + + for(String url : urls) { + String result = CsrfPreventionFilter.CsrfResponseWrapper.removeQueryParameters(url, csrfParameterName); + + Assert.assertEquals("Failed to cleanse URL '" + url + "' properly", dumbButAccurateCleanse(url, csrfParameterName), result); + } + + } + + private static String dumbButAccurateCleanse(String url, String csrfParameterName) { + // Get rid of [&csrf=value] with optional =value + Pattern p = Pattern.compile(Pattern.quote("&") + Pattern.quote(csrfParameterName) + "(=[^&]*)?(&|$)"); + + // Do this iteratively to get everything + Matcher m = p.matcher(url); + + while (m.find()) { + url = m.replaceFirst("$2"); + m = p.matcher(url); + } + + // Get rid of [?csrf=value] with optional =value + url = url.replaceAll(Pattern.quote("?") + csrfParameterName + "(=[^&]*)?(&|$)", "?"); + + if (url.endsWith("?")) { + // Special case: it's possible to have multiple ? in a URL: + // the query-string is technically allowed to contain ? characters. + // Only trim the trailing ? if it is actually the quest-string + // separator. + if(url.lastIndexOf('?', url.length() - 2) < 0) { + url = url.substring(0, url.length() - 1); + } + } + + return url; + } + + @Test + public void testDuplicateTokens() { + String nonce = "TESTNONCE"; + String testURL = "/foo/bar?" + Constants.CSRF_NONCE_SESSION_ATTR_NAME + "=" + nonce; + CsrfPreventionFilter.CsrfResponseWrapper response = new CsrfPreventionFilter.CsrfResponseWrapper(new NonEncodingResponse(), + Constants.CSRF_NONCE_SESSION_ATTR_NAME, nonce, null); + + Assert.assertTrue("Original URL does not contain CSRF token", + testURL.contains(Constants.CSRF_NONCE_SESSION_ATTR_NAME)); + + String result = response.encodeURL(testURL); + + int pos = result.indexOf(Constants.CSRF_NONCE_SESSION_ATTR_NAME); + Assert.assertTrue("Result URL does not contain CSRF token", + pos >= 0); + pos = result.indexOf(Constants.CSRF_NONCE_SESSION_ATTR_NAME, pos + 1); + Assert.assertFalse("Result URL contains multiple CSRF tokens: " + result, + pos >= 0); + } + private static class MimeTypeServletContext extends TesterServletContext { private String mimeType; + public void setMimeType(String type) { mimeType = type; } @@ -200,6 +310,7 @@ return mimeType; } } + private static class NonEncodingResponse extends TesterHttpServletResponse { @Override diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TestRateLimitFilterWithExactRateLimiter.java tomcat10-10.1.52/test/org/apache/catalina/filters/TestRateLimitFilterWithExactRateLimiter.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TestRateLimitFilterWithExactRateLimiter.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TestRateLimitFilterWithExactRateLimiter.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,221 @@ +/* + * 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.filters; + +import java.io.IOException; + +import jakarta.servlet.FilterChain; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.filters.TestRemoteIpFilter.MockFilterChain; +import org.apache.catalina.filters.TestRemoteIpFilter.MockHttpServletRequest; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.catalina.util.ExactRateLimiter; +import org.apache.tomcat.unittest.TesterResponse; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; + +public class TestRateLimitFilterWithExactRateLimiter extends TomcatBaseTest { + private void testRateLimitWith1Clients(boolean exposeHeaders, boolean enforce) throws Exception { + + int bucketRequests = 40; + int bucketDuration = 4; + + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("bucketRequests", String.valueOf(bucketRequests)); + filterDef.addInitParameter("bucketDuration", String.valueOf(bucketDuration)); + filterDef.addInitParameter("enforce", String.valueOf(enforce)); + filterDef.addInitParameter("exposeHeaders", String.valueOf(exposeHeaders)); + filterDef.addInitParameter("rateLimitClassName", "org.apache.catalina.util.ExactRateLimiter"); + + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", TEMP_DIR); + + MockFilterChain filterChain = new MockFilterChain(); + RateLimitFilter rateLimitFilter = testRateLimitFilter(filterDef, root); + tomcat.start(); + + ExactRateLimiter exactRateLimiter = (ExactRateLimiter) rateLimitFilter.rateLimiter; + + int allowedRequests = exactRateLimiter.getRequests(); + long sleepTime = exactRateLimiter.getBucketCounter().getMillisUntilNextBucket(); + System.out.printf("Sleeping %d millis for the next time bucket to start\n", Long.valueOf(sleepTime)); + Thread.sleep(sleepTime); + + TestClient tc1 = new TestClient(rateLimitFilter, filterChain, "10.20.20.5", 50, 5); // TPS: 5 + TestClient tc2 = new TestClient(rateLimitFilter, filterChain, "10.20.20.10", 100, 10); // TPS: 10 + + TestClient tc3 = new TestClient(rateLimitFilter, filterChain, "10.20.20.20", 200, 20); // TPS: 20 + TestClient tc4 = new TestClient(rateLimitFilter, filterChain, "10.20.20.40", 400, 40); // TPS: 40 + tc1.join(); + tc2.join(); + tc3.join(); + tc4.join(); + Assert.assertEquals(200, tc1.results[24]); // only 25 requests made in 5 seconds, all allowed + + Assert.assertEquals(200, tc2.results[49]); // only 50 requests made in 5 seconds, all allowed + + Assert.assertEquals(200, tc3.results[39]); // first allowedRequests allowed + + if (enforce) { + Assert.assertEquals(429, tc3.results[allowedRequests]); // subsequent requests dropped + } else { + Assert.assertEquals(200, tc3.results[allowedRequests]); + } + + Assert.assertEquals(200, tc4.results[allowedRequests - 1]); // first allowedRequests allowed + + if (enforce) { + Assert.assertEquals(429, tc4.results[allowedRequests]); // subsequent requests dropped + } else { + Assert.assertEquals(200, tc4.results[allowedRequests]); + } + + if (exposeHeaders) { + Assert.assertTrue(tc3.rlpHeader[24].contains("q=" + allowedRequests)); + Assert.assertTrue(tc3.rlpHeader[allowedRequests].contains("q=" + allowedRequests)); + if (enforce) { + Assert.assertTrue(tc3.rlHeader[24].contains("r=")); + Assert.assertFalse(tc3.rlHeader[24].contains("r=0")); + Assert.assertTrue(tc3.rlHeader[allowedRequests].contains("r=0")); + } + } else { + Assert.assertTrue(tc3.rlpHeader[24] == null); + Assert.assertTrue(tc3.rlHeader[24] == null); + Assert.assertTrue(tc3.rlpHeader[allowedRequests] == null); + Assert.assertTrue(tc3.rlHeader[allowedRequests] == null); + } + tomcat.stop(); + } + + @Test + public void testExposeHeaderAndReferenceRateLimitWith4Clients() throws Exception { + testRateLimitWith1Clients(true, false); + } + + @Test + public void testUnexposeHeaderAndReferenceRateLimitWith4Clients() throws Exception { + testRateLimitWith1Clients(false, false); + } + + @Test + public void testExposeHeaderAndEnforceRateLimitWith4Clients() throws Exception { + testRateLimitWith1Clients(true, true); + } + + @Test + public void testUnexposeHeaderAndEnforceRateLimitWith4Clients() throws Exception { + testRateLimitWith1Clients(false, true); + } + + private RateLimitFilter testRateLimitFilter(FilterDef filterDef, Context root) { + + RateLimitFilter rateLimitFilter = new RateLimitFilter(); + filterDef.setFilterClass(RateLimitFilter.class.getName()); + filterDef.setFilter(rateLimitFilter); + filterDef.setFilterName(RateLimitFilter.class.getName()); + root.addFilterDef(filterDef); + + FilterMap filterMap = new FilterMap(); + filterMap.setFilterName(RateLimitFilter.class.getName()); + filterMap.addURLPatternDecoded("*"); + root.addFilterMap(filterMap); + + return rateLimitFilter; + } + + static class TestClient extends Thread { + RateLimitFilter filter; + FilterChain filterChain; + String ip; + + int requests; + int timePerRequest; + + int[] results; + volatile String[] rlpHeader; + volatile String[] rlHeader; + + TestClient(RateLimitFilter filter, FilterChain filterChain, String ip, int requests, int rps) { + this.filter = filter; + this.filterChain = filterChain; + this.ip = ip; + this.requests = requests; + this.timePerRequest = 1000 / rps; + this.results = new int[requests]; + this.rlpHeader = new String[requests]; + this.rlHeader = new String[requests]; + super.setDaemon(true); + super.start(); + } + + @Override + public void run() { + long start = System.nanoTime(); + + try { + for (int i = 0; i < requests; i++) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRemoteAddr(ip); + TesterResponse response = new TesterResponseWithStatus(); + response.setRequest(request); + filter.doFilter(request, response, filterChain); + results[i] = response.getStatus(); + + rlpHeader[i] = response.getHeader(RateLimitFilter.HEADER_RATE_LIMIT_POLICY); + rlHeader[i] = response.getHeader(RateLimitFilter.HEADER_RATE_LIMIT); + + if (results[i] != 200) { + break; + } + /* + * Ensure requests are evenly spaced through time irrespective of how long each request takes to + * complete. Do comparisons in milliseconds. + */ + long expectedDuration = (i + 1) * timePerRequest; + long duration = (System.nanoTime() - start) / 1000000; + if (expectedDuration > duration) { + sleep(expectedDuration - duration); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + static class TesterResponseWithStatus extends TesterResponse { + + int status = 200; + String message = "OK"; + + @Override + public void sendError(int status, String message) throws IOException { + this.status = status; + this.message = message; + } + + @Override + public int getStatus() { + return status; + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TestRemoteCIDRFilter.java tomcat10-10.1.52/test/org/apache/catalina/filters/TestRemoteCIDRFilter.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TestRemoteCIDRFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TestRemoteCIDRFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -123,6 +123,96 @@ } } + @Test + public void testAllowDenySetAsNull() throws Exception { + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", TEMP_DIR); + tomcat.start(); + + TestRemoteIpFilter.MockFilterChain filterChain = new TestRemoteIpFilter.MockFilterChain(); + + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("allow", null); + filterDef.addInitParameter("deny", null); + Filter filter = createTestFilter(filterDef, RemoteCIDRFilter.class, root, "*"); + + String ipAddr; + Request request; + TesterResponse response; + int expected; + + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j += 11) { + ipAddr = String.format("192.168.%s.%s", Integer.valueOf(i), Integer.valueOf(j)); + request = new TestRemoteIpFilter.MockHttpServletRequest(ipAddr); + response = new TestRateLimitFilter.TesterResponseWithStatus(); + expected = HttpServletResponse.SC_FORBIDDEN; + filter.doFilter(request, response, filterChain); + Assert.assertEquals(expected, response.getStatus()); + } + } + + // Check getters + Assert.assertEquals("", ((RemoteCIDRFilter) filter).getAllow()); + Assert.assertEquals("", ((RemoteCIDRFilter) filter).getDeny()); + } + + @Test + public void testAllowDenySetAsEmptyString() throws Exception { + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", TEMP_DIR); + tomcat.start(); + + TestRemoteIpFilter.MockFilterChain filterChain = new TestRemoteIpFilter.MockFilterChain(); + + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("allow", ""); + filterDef.addInitParameter("deny", ""); + Filter filter = createTestFilter(filterDef, RemoteCIDRFilter.class, root, "*"); + + String ipAddr; + Request request; + TesterResponse response; + int expected; + + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j += 11) { + ipAddr = String.format("192.168.%s.%s", Integer.valueOf(i), Integer.valueOf(j)); + request = new TestRemoteIpFilter.MockHttpServletRequest(ipAddr); + response = new TestRateLimitFilter.TesterResponseWithStatus(); + expected = HttpServletResponse.SC_FORBIDDEN; + filter.doFilter(request, response, filterChain); + Assert.assertEquals(expected, response.getStatus()); + } + } + + // Check getters + Assert.assertEquals("", ((RemoteCIDRFilter) filter).getAllow()); + Assert.assertEquals("", ((RemoteCIDRFilter) filter).getDeny()); + } + + @Test(expected = ServletException.class) + public void testAllowInvalid() throws Exception { + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", TEMP_DIR); + tomcat.start(); + + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("allow", "this is not valid"); + createTestFilter(filterDef, RemoteCIDRFilter.class, root, "*"); + } + + @Test(expected = ServletException.class) + public void testDenyInvalid() throws Exception { + Tomcat tomcat = getTomcatInstance(); + Context root = tomcat.addContext("", TEMP_DIR); + tomcat.start(); + + FilterDef filterDef = new FilterDef(); + filterDef.addInitParameter("deny", "this is not valid"); + createTestFilter(filterDef, RemoteCIDRFilter.class, root, "*"); + } + private Filter createTestFilter(FilterDef filterDef, Class testFilterClass, Context root, String urlPattern) throws ServletException { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TestRemoteIpFilter.java tomcat10-10.1.52/test/org/apache/catalina/filters/TestRemoteIpFilter.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TestRemoteIpFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TestRemoteIpFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -739,7 +739,7 @@ public void testWithTomcatServer() throws Exception { // mostly default configuration : enable "x-forwarded-proto" - Map remoteIpFilterParameter = new HashMap<>(); + Map remoteIpFilterParameter = new HashMap<>(); remoteIpFilterParameter.put("protocolHeader", "x-forwarded-proto"); // SETUP @@ -766,8 +766,9 @@ getTomcatInstance().start(); // TEST - HttpURLConnection httpURLConnection = (HttpURLConnection) new URL( - "http://localhost:" + tomcat.getConnector().getLocalPort() + "/test").openConnection(); + HttpURLConnection httpURLConnection = + (HttpURLConnection) new URL("http://localhost:" + tomcat.getConnector().getLocalPort() + "/test") + .openConnection(); String expectedRemoteAddr = "my-remote-addr"; httpURLConnection.addRequestProperty("x-forwarded-for", expectedRemoteAddr); httpURLConnection.addRequestProperty("x-forwarded-proto", "https"); @@ -789,7 +790,7 @@ public void testJSessionIdSecureAttributeMissing() throws Exception { // mostly default configuration : enable "x-forwarded-proto" - Map remoteIpFilterParameter = new HashMap<>(); + Map remoteIpFilterParameter = new HashMap<>(); remoteIpFilterParameter.put("protocolHeader", "x-forwarded-proto"); // SETUP @@ -815,8 +816,8 @@ getTomcatInstance().start(); - Map> resHeaders = new HashMap<>(); - Map> reqHeaders = new HashMap<>(); + Map> resHeaders = new HashMap<>(); + Map> reqHeaders = new HashMap<>(); String expectedRemoteAddr = "my-remote-addr"; List forwardedFor = new ArrayList<>(1); forwardedFor.add(expectedRemoteAddr); @@ -862,6 +863,26 @@ doTestPattern(internalProxiesPattern, "100.127.255.255", true); doTestPattern(internalProxiesPattern, "100.128.0.0", false); doTestPattern(internalProxiesPattern, "100.130.0.0", false); + // Bug 69600 - IPv6 RFC 4193 Unique Local IPv6 Unicast Addresses + doTestPattern(internalProxiesPattern, "fe79:ffff:ffff:ffff:ffff:ffff:ffff:ffff", false); + doTestPattern(internalProxiesPattern, "fe80:0000:0000:0000:0000:0000:0000:0000", true); + doTestPattern(internalProxiesPattern, "fe80::", true); + doTestPattern(internalProxiesPattern, "fe80:0000:0000:0000:0000:0000:0000:0001", true); + doTestPattern(internalProxiesPattern, "fe80::1", true); + doTestPattern(internalProxiesPattern, "fe80:1234:5678:9abc:def0:1234:5678:9abc", true); + doTestPattern(internalProxiesPattern, "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true); + doTestPattern(internalProxiesPattern, "fec0:0000:0000:0000:0000:0000:0000:0000", false); + doTestPattern(internalProxiesPattern, "fec0::", false); + // Bug 69600 - IPv6 RFC 4291 Link Local IPv6 Unicast Addresses + doTestPattern(internalProxiesPattern, "fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", false); + doTestPattern(internalProxiesPattern, "fc00:0000:0000:0000:0000:0000:0000:0000", true); + doTestPattern(internalProxiesPattern, "fc00::", true); + doTestPattern(internalProxiesPattern, "fc00:0000:0000:0000:0000:0000:0000:0001", true); + doTestPattern(internalProxiesPattern, "fc00::1", true); + doTestPattern(internalProxiesPattern, "fc00:1234:5678:9abc:def0:1234:5678:9abc", true); + doTestPattern(internalProxiesPattern, "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true); + doTestPattern(internalProxiesPattern, "fe00:0000:0000:0000:0000:0000:0000:0000", false); + doTestPattern(internalProxiesPattern, "fe00::", false); } private void doTestPattern(Pattern pattern, String input, boolean expectedMatch) { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter.java tomcat10-10.1.52/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,6 +30,7 @@ import org.junit.Before; import org.junit.Test; +import org.apache.tomcat.util.http.Method; import org.easymock.EasyMock; public class TestRestCsrfPreventionFilter { @@ -38,10 +39,6 @@ private static final String INVALID_NONCE = "invalid-nonce"; - private static final String GET_METHOD = "GET"; - - private static final String POST_METHOD = "POST"; - public static final String ACCEPTED_PATH1 = "/accepted/index1.jsp"; public static final String ACCEPTED_PATH2 = "/accepted/index2.jsp"; @@ -74,45 +71,45 @@ @Test public void testGetRequestNoSessionNoNonce() throws Exception { - setRequestExpectations(GET_METHOD, null, null); + setRequestExpectations(Method.GET, null, null); filter.doFilter(request, response, filterChain); verifyContinueChain(); } @Test public void testPostRequestNoSessionNoNonce() throws Exception { - setRequestExpectations(POST_METHOD, null, null); + setRequestExpectations(Method.POST, null, null); filter.doFilter(request, response, filterChain); verifyDenyResponse(HttpServletResponse.SC_FORBIDDEN); } @Test public void testPostRequestSessionNoNonce1() throws Exception { - setRequestExpectations(POST_METHOD, session, null); + setRequestExpectations(Method.POST, session, null); testPostRequestHeaderScenarios(null, true); } @Test public void testPostRequestSessionNoNonce2() throws Exception { - setRequestExpectations(POST_METHOD, session, null); + setRequestExpectations(Method.POST, session, null); testPostRequestHeaderScenarios(NONCE, true); } @Test public void testPostRequestSessionInvalidNonce() throws Exception { - setRequestExpectations(POST_METHOD, session, INVALID_NONCE); + setRequestExpectations(Method.POST, session, INVALID_NONCE); testPostRequestHeaderScenarios(NONCE, true); } @Test public void testPostRequestSessionValidNonce() throws Exception { - setRequestExpectations(POST_METHOD, session, NONCE); + setRequestExpectations(Method.POST, session, NONCE); testPostRequestHeaderScenarios(NONCE, false); } @Test public void testGetFetchRequestSessionNoNonce() throws Exception { - setRequestExpectations(GET_METHOD, session, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE); + setRequestExpectations(Method.GET, session, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE); EasyMock.expect(session.getAttribute(Constants.CSRF_REST_NONCE_SESSION_ATTR_NAME)).andReturn(null); session.setAttribute(Constants.CSRF_REST_NONCE_SESSION_ATTR_NAME, NONCE); EasyMock.expectLastCall(); @@ -124,13 +121,13 @@ @Test public void testPostFetchRequestSessionNoNonce() throws Exception { - setRequestExpectations(POST_METHOD, session, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE); + setRequestExpectations(Method.POST, session, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE); testPostRequestHeaderScenarios(null, true); } @Test public void testGetFetchRequestSessionNonce() throws Exception { - setRequestExpectations(GET_METHOD, session, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE); + setRequestExpectations(Method.GET, session, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE); EasyMock.expect(session.getAttribute(Constants.CSRF_REST_NONCE_SESSION_ATTR_NAME)).andReturn(NONCE); EasyMock.replay(session); filter.doFilter(request, response, filterChain); @@ -140,13 +137,13 @@ @Test public void testPostFetchRequestSessionNonce() throws Exception { - setRequestExpectations(POST_METHOD, session, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE); + setRequestExpectations(Method.POST, session, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE); testPostRequestHeaderScenarios(NONCE, true); } @Test public void testPostRequestCustomDenyStatus() throws Exception { - setRequestExpectations(POST_METHOD, null, null); + setRequestExpectations(Method.POST, null, null); filter.setDenyStatus(HttpServletResponse.SC_BAD_REQUEST); filter.doFilter(request, response, filterChain); verifyDenyResponse(HttpServletResponse.SC_BAD_REQUEST); @@ -154,74 +151,74 @@ @Test public void testPostRequestValidNonceAsParameterValidPath1() throws Exception { - setRequestExpectations(POST_METHOD, session, null, new String[] { NONCE }, ACCEPTED_PATH1); + setRequestExpectations(Method.POST, session, null, new String[] { NONCE }, ACCEPTED_PATH1); testPostRequestParamsScenarios(NONCE, false, true); } @Test public void testPostRequestValidNonceAsParameterValidPath2() throws Exception { - setRequestExpectations(POST_METHOD, session, null, new String[] { NONCE }, ACCEPTED_PATH2); + setRequestExpectations(Method.POST, session, null, new String[] { NONCE }, ACCEPTED_PATH2); testPostRequestParamsScenarios(NONCE, false, true); } @Test public void testPostRequestInvalidNonceAsParameterValidPath() throws Exception { - setRequestExpectations(POST_METHOD, session, null, new String[] { INVALID_NONCE }, ACCEPTED_PATH1); + setRequestExpectations(Method.POST, session, null, new String[] { INVALID_NONCE }, ACCEPTED_PATH1); testPostRequestParamsScenarios(NONCE, true, true); } @Test public void testPostRequestValidNonceAsParameterInvalidPath() throws Exception { - setRequestExpectations(POST_METHOD, session, null, new String[] { NONCE }, ACCEPTED_PATH1 + "blah"); + setRequestExpectations(Method.POST, session, null, new String[] { NONCE }, ACCEPTED_PATH1 + "blah"); testPostRequestParamsScenarios(NONCE, true, true); } @Test public void testPostRequestValidNonceAsParameterNoPath() throws Exception { - setRequestExpectations(POST_METHOD, session, null, new String[] { NONCE }, ACCEPTED_PATH1); + setRequestExpectations(Method.POST, session, null, new String[] { NONCE }, ACCEPTED_PATH1); testPostRequestParamsScenarios(NONCE, true, false); } @Test public void testPostRequestValidNonceAsParameterNoNonceInSession() throws Exception { - setRequestExpectations(POST_METHOD, session, null, new String[] { NONCE }, ACCEPTED_PATH1); + setRequestExpectations(Method.POST, session, null, new String[] { NONCE }, ACCEPTED_PATH1); testPostRequestParamsScenarios(null, true, true); } @Test public void testPostRequestValidNonceAsParameterInvalidNonceAsHeader() throws Exception { - setRequestExpectations(POST_METHOD, session, INVALID_NONCE, new String[] { NONCE }, ACCEPTED_PATH1); + setRequestExpectations(Method.POST, session, INVALID_NONCE, new String[] { NONCE }, ACCEPTED_PATH1); testPostRequestParamsScenarios(NONCE, true, true); } @Test public void testPostRequestNoNonceAsParameterAndHeaderValidPath() throws Exception { - setRequestExpectations(POST_METHOD, session, null, null, ACCEPTED_PATH1); + setRequestExpectations(Method.POST, session, null, null, ACCEPTED_PATH1); testPostRequestParamsScenarios(NONCE, true, true); } @Test public void testPostRequestMultipleValidNoncesAsParameterValidPath() throws Exception { - setRequestExpectations(POST_METHOD, session, null, new String[] { NONCE, NONCE }, ACCEPTED_PATH1); + setRequestExpectations(Method.POST, session, null, new String[] { NONCE, NONCE }, ACCEPTED_PATH1); testPostRequestParamsScenarios(NONCE, false, true); } @Test public void testPostRequestMultipleNoncesAsParameterValidPath() throws Exception { - setRequestExpectations(POST_METHOD, session, null, new String[] { NONCE, INVALID_NONCE }, ACCEPTED_PATH1); + setRequestExpectations(Method.POST, session, null, new String[] { NONCE, INVALID_NONCE }, ACCEPTED_PATH1); testPostRequestParamsScenarios(NONCE, true, true); } @Test public void testPostRequestMultipleInvalidNoncesAsParameterValidPath() throws Exception { - setRequestExpectations(POST_METHOD, session, null, new String[] { INVALID_NONCE, INVALID_NONCE }, + setRequestExpectations(Method.POST, session, null, new String[] { INVALID_NONCE, INVALID_NONCE }, ACCEPTED_PATH1); testPostRequestParamsScenarios(NONCE, true, true); } @Test public void testGETRequestFetchNonceAsParameter() throws Exception { - setRequestExpectations(GET_METHOD, null, null, new String[] { Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE }, + setRequestExpectations(Method.GET, null, null, new String[] { Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE }, ACCEPTED_PATH1); filter.setPathsAcceptingParams(ACCEPTED_PATHS); filter.doFilter(request, response, filterChain); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter2.java tomcat10-10.1.52/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter2.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter2.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TestRestCsrfPreventionFilter2.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,14 +45,12 @@ import org.apache.tomcat.util.descriptor.web.LoginConfig; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; +import org.apache.tomcat.util.http.Method; public class TestRestCsrfPreventionFilter2 extends TomcatBaseTest { private static final boolean USE_COOKIES = true; private static final boolean NO_COOKIES = !USE_COOKIES; - private static final String METHOD_GET = "GET"; - private static final String METHOD_POST = "POST"; - private static final String HTTP_PREFIX = "http://localhost:"; private static final String CONTEXT_PATH_LOGIN = ""; private static final String URI_PROTECTED = "/services/*"; @@ -121,64 +119,64 @@ } private void testClearGet() throws Exception { - doTest(METHOD_GET, LIST_CUSTOMERS, CREDENTIALS, null, NO_COOKIES, HttpServletResponse.SC_OK, + doTest(Method.GET, LIST_CUSTOMERS, CREDENTIALS, null, NO_COOKIES, HttpServletResponse.SC_OK, CUSTOMERS_LIST_RESPONSE, null, false, null); } private void testClearPost() throws Exception { - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, null, NO_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, null, NO_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, null, true, Constants.CSRF_REST_NONCE_HEADER_REQUIRED_VALUE); } private void testGetFirstFetch() throws Exception { - doTest(METHOD_GET, LIST_CUSTOMERS, CREDENTIALS, null, NO_COOKIES, HttpServletResponse.SC_OK, + doTest(Method.GET, LIST_CUSTOMERS, CREDENTIALS, null, NO_COOKIES, HttpServletResponse.SC_OK, CUSTOMERS_LIST_RESPONSE, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE, true, null); } private void testValidPost() throws Exception { - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_OK, + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_OK, CUSTOMER_REMOVED_RESPONSE, validNonce, false, null); } private void testInvalidPost() throws Exception { - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE, true, Constants.CSRF_REST_NONCE_HEADER_REQUIRED_VALUE); - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, INVALID_NONCE_1, true, Constants.CSRF_REST_NONCE_HEADER_REQUIRED_VALUE); - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, INVALID_NONCE_2, true, Constants.CSRF_REST_NONCE_HEADER_REQUIRED_VALUE); - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, null, true, Constants.CSRF_REST_NONCE_HEADER_REQUIRED_VALUE); } private void testGetSecondFetch() throws Exception { - doTest(METHOD_GET, LIST_CUSTOMERS, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_OK, + doTest(Method.GET, LIST_CUSTOMERS, CREDENTIALS, null, USE_COOKIES, HttpServletResponse.SC_OK, CUSTOMERS_LIST_RESPONSE, Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE, true, validNonce); } private void testValidPostWithRequestParams() throws Exception { String validBody = Constants.CSRF_REST_NONCE_HEADER_NAME + "=" + validNonce; String invalidbody = Constants.CSRF_REST_NONCE_HEADER_NAME + "=" + INVALID_NONCE_1; - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, validBody.getBytes(StandardCharsets.ISO_8859_1), USE_COOKIES, + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, validBody.getBytes(StandardCharsets.ISO_8859_1), USE_COOKIES, HttpServletResponse.SC_OK, CUSTOMER_REMOVED_RESPONSE, null, false, null); - doTest(METHOD_POST, ADD_CUSTOMER, CREDENTIALS, validBody.getBytes(StandardCharsets.ISO_8859_1), USE_COOKIES, + doTest(Method.POST, ADD_CUSTOMER, CREDENTIALS, validBody.getBytes(StandardCharsets.ISO_8859_1), USE_COOKIES, HttpServletResponse.SC_OK, CUSTOMER_ADDED_RESPONSE, null, false, null); - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, invalidbody.getBytes(StandardCharsets.ISO_8859_1), + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, invalidbody.getBytes(StandardCharsets.ISO_8859_1), USE_COOKIES, HttpServletResponse.SC_OK, CUSTOMER_REMOVED_RESPONSE, validNonce, false, null); } private void testInvalidPostWithRequestParams() throws Exception { String validBody = Constants.CSRF_REST_NONCE_HEADER_NAME + "=" + validNonce; String invalidbody1 = Constants.CSRF_REST_NONCE_HEADER_NAME + "=" + INVALID_NONCE_1; - String invalidbody2 = Constants.CSRF_REST_NONCE_HEADER_NAME + "=" + - Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE; - doTest(METHOD_POST, REMOVE_ALL_CUSTOMERS, CREDENTIALS, validBody.getBytes(StandardCharsets.ISO_8859_1), + String invalidbody2 = + Constants.CSRF_REST_NONCE_HEADER_NAME + "=" + Constants.CSRF_REST_NONCE_HEADER_FETCH_VALUE; + doTest(Method.POST, REMOVE_ALL_CUSTOMERS, CREDENTIALS, validBody.getBytes(StandardCharsets.ISO_8859_1), USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, null, true, Constants.CSRF_REST_NONCE_HEADER_REQUIRED_VALUE); - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, invalidbody1.getBytes(StandardCharsets.ISO_8859_1), + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, invalidbody1.getBytes(StandardCharsets.ISO_8859_1), USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, null, true, Constants.CSRF_REST_NONCE_HEADER_REQUIRED_VALUE); - doTest(METHOD_POST, REMOVE_CUSTOMER, CREDENTIALS, invalidbody2.getBytes(StandardCharsets.ISO_8859_1), + doTest(Method.POST, REMOVE_CUSTOMER, CREDENTIALS, invalidbody2.getBytes(StandardCharsets.ISO_8859_1), USE_COOKIES, HttpServletResponse.SC_FORBIDDEN, null, null, true, Constants.CSRF_REST_NONCE_HEADER_REQUIRED_VALUE); } @@ -186,8 +184,8 @@ private void doTest(String method, String uri, BasicCredentials credentials, byte[] body, boolean useCookie, int expectedRC, String expectedResponse, String nonce, boolean expectCsrfRH, String expectedCsrfRHV) throws Exception { - Map> reqHeaders = new HashMap<>(); - Map> respHeaders = new HashMap<>(); + Map> reqHeaders = new HashMap<>(); + Map> respHeaders = new HashMap<>(); addNonce(reqHeaders, nonce, n -> Objects.nonNull(n)); @@ -199,7 +197,7 @@ ByteChunk bc = new ByteChunk(); int rc; - if (METHOD_GET.equals(method)) { + if (Method.GET.equals(method)) { rc = getUrl(HTTP_PREFIX + getPort() + uri, bc, reqHeaders, respHeaders); } else { rc = postUrl(body, HTTP_PREFIX + getPort() + uri, bc, reqHeaders, respHeaders); @@ -232,7 +230,7 @@ } } - private void addCookies(Map> reqHeaders, Predicate> tester) { + private void addCookies(Map> reqHeaders, Predicate> tester) { if (tester.test(cookies)) { StringBuilder cookieHeader = new StringBuilder(); boolean first = true; @@ -248,20 +246,20 @@ } } - private void addNonce(Map> reqHeaders, String nonce, Predicate tester) { + private void addNonce(Map> reqHeaders, String nonce, Predicate tester) { if (tester.test(nonce)) { addRequestHeader(reqHeaders, Constants.CSRF_REST_NONCE_HEADER_NAME, nonce); } } - private void addCredentials(Map> reqHeaders, BasicCredentials credentials, + private void addCredentials(Map> reqHeaders, BasicCredentials credentials, Predicate tester) { if (tester.test(credentials)) { addRequestHeader(reqHeaders, CLIENT_AUTH_HEADER, credentials.getCredentials()); } } - private void addRequestHeader(Map> reqHeaders, String key, String value) { + private void addRequestHeader(Map> reqHeaders, String key, String value) { List valueList = new ArrayList<>(1); valueList.add(value); reqHeaders.put(key, valueList); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TesterHttpServletRequest.java tomcat10-10.1.52/test/org/apache/catalina/filters/TesterHttpServletRequest.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TesterHttpServletRequest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TesterHttpServletRequest.java 2026-01-23 19:33:36.000000000 +0000 @@ -50,8 +50,8 @@ public class TesterHttpServletRequest implements HttpServletRequest { - private final Map attributes = new HashMap<>(); - private final Map> headers = new HashMap<>(); + private final Map attributes = new HashMap<>(); + private final Map> headers = new HashMap<>(); private String method; private String scheme; private String serverName; @@ -118,7 +118,7 @@ * This test implementation is hard coded to return an empty Hashmap. */ @Override - public Map getParameterMap() { + public Map getParameterMap() { return new HashMap<>(); } @@ -463,10 +463,11 @@ */ @Override public T upgrade(Class httpUpgradeHandlerClass) - throws IOException, ServletException { + throws IOException, ServletException { try { return httpUpgradeHandlerClass.getDeclaredConstructor().newInstance(); - }catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException ignore){ + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | + IllegalAccessException ignore) { } return null; @@ -483,7 +484,7 @@ } @Override - public Map getTrailerFields() { + public Map getTrailerFields() { throw new RuntimeException("Not implemented"); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/filters/TesterHttpServletResponse.java tomcat10-10.1.52/test/org/apache/catalina/filters/TesterHttpServletResponse.java --- tomcat10-10.1.34/test/org/apache/catalina/filters/TesterHttpServletResponse.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/filters/TesterHttpServletResponse.java 2026-01-23 19:33:36.000000000 +0000 @@ -366,11 +366,11 @@ /* NOOP */} @Override - public void setTrailerFields(Supplier> supplier) { + public void setTrailerFields(Supplier> supplier) { /* NOOP */ } @Override - public Supplier> getTrailerFields() { + public Supplier> getTrailerFields() { return null; } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/ha/context/TestReplicatedContext.java tomcat10-10.1.52/test/org/apache/catalina/ha/context/TestReplicatedContext.java --- tomcat10-10.1.34/test/org/apache/catalina/ha/context/TestReplicatedContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/ha/context/TestReplicatedContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -64,8 +64,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { getServletContext().setAttribute("NULL", null); resp.getWriter().print("OK"); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/ha/session/TestDeltaRequest.java tomcat10-10.1.52/test/org/apache/catalina/ha/session/TestDeltaRequest.java --- tomcat10-10.1.34/test/org/apache/catalina/ha/session/TestDeltaRequest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/ha/session/TestDeltaRequest.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,7 +44,7 @@ original.setAttribute("A", "One"); original.setAttribute("B", "Two"); - // Seralize original request + // Serialize original request byte[] bytes; try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos)) { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/loader/EchoTag.java tomcat10-10.1.52/test/org/apache/catalina/loader/EchoTag.java --- tomcat10-10.1.34/test/org/apache/catalina/loader/EchoTag.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/loader/EchoTag.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,8 +38,8 @@ public int doStartTag() throws JspException { try { pageContext.getOut().print("

    " + echo + "

    "); - } catch (IOException e) { - throw new JspException(e); + } catch (IOException ioe) { + throw new JspException(ioe); } return super.doStartTag(); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/loader/MyAnnotatedServlet.java tomcat10-10.1.52/test/org/apache/catalina/loader/MyAnnotatedServlet.java --- tomcat10-10.1.34/test/org/apache/catalina/loader/MyAnnotatedServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/loader/MyAnnotatedServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("test/plain"); resp.getWriter().println(MESSAGE); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/loader/TestVirtualContext.java tomcat10-10.1.52/test/org/apache/catalina/loader/TestVirtualContext.java --- tomcat10-10.1.34/test/org/apache/catalina/loader/TestVirtualContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/loader/TestVirtualContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,8 +49,7 @@ // present. The listener affects the JVM, and thus not only the current, // but also the subsequent tests that are run in the same JVM. So it is // fair to add it in every test. - tomcat.getServer().addLifecycleListener( - new JreMemoryLeakPreventionListener()); + tomcat.getServer().addLifecycleListener(new JreMemoryLeakPreventionListener()); } @Test @@ -60,36 +59,27 @@ File appDir = new File("test/webapp-virtual-webapp/src/main/webapp-a"); // app dir is relative to server home - StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test", - appDir.getAbsolutePath()); + StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); ctx.setResources(new StandardRoot(ctx)); File f1 = new File("test/webapp-virtual-webapp/target/classes"); File f2 = new File("test/webapp-virtual-library/target/WEB-INF"); - File f3 = new File( - "test/webapp-virtual-webapp/src/main/webapp-a/WEB-INF/classes"); - File f4 = new File( - "test/webapp-virtual-webapp/src/main/webapp-b/WEB-INF/classes"); + File f3 = new File("test/webapp-virtual-webapp/src/main/webapp-a/WEB-INF/classes"); + File f4 = new File("test/webapp-virtual-webapp/src/main/webapp-b/WEB-INF/classes"); File f5 = new File("test/webapp-virtual-webapp/src/main/misc"); File f6 = new File("test/webapp-virtual-webapp/src/main/webapp-b"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", f1.getAbsolutePath(), null, "/"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF", - f2.getAbsolutePath(), null, "/"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF", f2.getAbsolutePath(), + null, "/"); + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", f3.getAbsolutePath(), null, "/"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", f4.getAbsolutePath(), null, "/"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/other", - f5.getAbsolutePath(), null, "/"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/", - f6.getAbsolutePath(), null, "/"); + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/other", f5.getAbsolutePath(), + null, "/"); + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/", f6.getAbsolutePath(), null, + "/"); StandardJarScanner jarScanner = new StandardJarScanner(); jarScanner.setScanAllDirectories(true); @@ -98,77 +88,54 @@ tomcat.start(); - assertPageContains("/test/classpathGetResourceAsStream.jsp?path=nonexistent", - "resourceAInWebInfClasses=true", 404); + assertPageContains("/test/classpathGetResourceAsStream.jsp?path=nonexistent", "resourceAInWebInfClasses=true", + 404); - assertPageContains( - "/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceA.properties", - "resourceAInWebInfClasses=true"); - assertPageContains( - "/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceA.properties", - "resourceAInWebInfClasses=true"); - - assertPageContains( - "/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceB.properties", - "resourceBInTargetClasses=true"); - assertPageContains( - "/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceB.properties", - "resourceBInTargetClasses=true"); - - assertPageContains( - "/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceC.properties", - "resourceCInDependentLibraryTargetClasses=true"); - assertPageContains( - "/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceC.properties", - "resourceCInDependentLibraryTargetClasses=true"); - - assertPageContains( - "/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceD.properties", - "resourceDInPackagedJarInWebInfLib=true"); - assertPageContains( - "/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceD.properties", - "resourceDInPackagedJarInWebInfLib=true"); - - assertPageContains( - "/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceG.properties", - "resourceGInWebInfClasses=true"); - assertPageContains( - "/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceG.properties", - "resourceGInWebInfClasses=true"); + assertPageContains("/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceA.properties", + "resourceAInWebInfClasses=true"); + assertPageContains("/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceA.properties", + "resourceAInWebInfClasses=true"); + + assertPageContains("/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceB.properties", + "resourceBInTargetClasses=true"); + assertPageContains("/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceB.properties", + "resourceBInTargetClasses=true"); + + assertPageContains("/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceC.properties", + "resourceCInDependentLibraryTargetClasses=true"); + assertPageContains("/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceC.properties", + "resourceCInDependentLibraryTargetClasses=true"); + + assertPageContains("/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceD.properties", + "resourceDInPackagedJarInWebInfLib=true"); + assertPageContains("/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceD.properties", + "resourceDInPackagedJarInWebInfLib=true"); + + assertPageContains("/test/classpathGetResourceAsStream.jsp?path=rsrc/resourceG.properties", + "resourceGInWebInfClasses=true"); + assertPageContains("/test/classpathGetResourceUrlThenGetStream.jsp?path=rsrc/resourceG.properties", + "resourceGInWebInfClasses=true"); // test listing all possible paths for a classpath resource String allUrls = - getUrl( - "http://localhost:" + getPort() + - "/test/classpathGetResources.jsp?path=rsrc/").toString(); - Assert.assertTrue( - allUrls, - allUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp-a/WEB-INF/classes/rsrc") > 0); - Assert.assertTrue( - allUrls, - allUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp-b/WEB-INF/classes/rsrc") > 0); - Assert.assertTrue( - allUrls, - allUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp-a/WEB-INF/lib/rsrc.jar!/rsrc") > 0); - Assert.assertTrue( - allUrls, - allUrls.indexOf("/test/webapp-virtual-webapp/target/classes/rsrc") > 0); - Assert.assertTrue( - allUrls, - allUrls.indexOf("/test/webapp-virtual-library/target/WEB-INF/classes/rsrc") > 0); + getUrl("http://localhost:" + getPort() + "/test/classpathGetResources.jsp?path=rsrc/").toString(); + Assert.assertTrue(allUrls, + allUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp-a/WEB-INF/classes/rsrc") > 0); + Assert.assertTrue(allUrls, + allUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp-b/WEB-INF/classes/rsrc") > 0); + Assert.assertTrue(allUrls, + allUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp-a/WEB-INF/lib/rsrc.jar!/rsrc") > 0); + Assert.assertTrue(allUrls, allUrls.indexOf("/test/webapp-virtual-webapp/target/classes/rsrc") > 0); + Assert.assertTrue(allUrls, allUrls.indexOf("/test/webapp-virtual-library/target/WEB-INF/classes/rsrc") > 0); // check that there's no duplicate in the URLs String[] allUrlsArray = allUrls.split("\\s+"); - Assert.assertEquals(new HashSet<>(Arrays.asList(allUrlsArray)).size(), - allUrlsArray.length); + Assert.assertEquals(new HashSet<>(Arrays.asList(allUrlsArray)).size(), allUrlsArray.length); String allRsrsc2ClasspathUrls = - getUrl( - "http://localhost:" + getPort() + - "/test/classpathGetResources.jsp?path=rsrc-2/").toString(); - Assert.assertTrue( - allRsrsc2ClasspathUrls, - allRsrsc2ClasspathUrls.indexOf("/test/webapp-virtual-webapp/src/main/webapp-b/WEB-INF/classes/rsrc-2") > 0); + getUrl("http://localhost:" + getPort() + "/test/classpathGetResources.jsp?path=rsrc-2/").toString(); + Assert.assertTrue(allRsrsc2ClasspathUrls, allRsrsc2ClasspathUrls + .indexOf("/test/webapp-virtual-webapp/src/main/webapp-b/WEB-INF/classes/rsrc-2") > 0); // tests context.getRealPath @@ -179,71 +146,39 @@ // Real paths depend on the OS and this test has to work on all // platforms so use File to convert the path to a platform specific form - File f = new File( - "test/webapp-virtual-webapp/src/main/webapp-a/rsrc/resourceF.properties"); - assertPageContains( - "/test/contextGetRealPath.jsp?path=/rsrc/resourceF.properties", - f.getPath()); + File f = new File("test/webapp-virtual-webapp/src/main/webapp-a/rsrc/resourceF.properties"); + assertPageContains("/test/contextGetRealPath.jsp?path=/rsrc/resourceF.properties", f.getPath()); // tests context.getResource then the content - assertPageContains("/test/contextGetResource.jsp?path=/nonexistent", - "resourceAInWebInfClasses=true", 404); - assertPageContains( - "/test/contextGetResource.jsp?path=/WEB-INF/classes/rsrc/resourceA.properties", - "resourceAInWebInfClasses=true"); - assertPageContains( - "/test/contextGetResource.jsp?path=/WEB-INF/classes/rsrc/resourceG.properties", - "resourceGInWebInfClasses=true"); - assertPageContains( - "/test/contextGetResource.jsp?path=/rsrc/resourceE.properties", - "resourceEInDependentLibraryTargetClasses=true"); - assertPageContains( - "/test/contextGetResource.jsp?path=/other/resourceI.properties", - "resourceIInWebapp=true"); - assertPageContains( - "/test/contextGetResource.jsp?path=/rsrc-2/resourceJ.properties", - "resourceJInWebapp=true"); + assertPageContains("/test/contextGetResource.jsp?path=/nonexistent", "resourceAInWebInfClasses=true", 404); + assertPageContains("/test/contextGetResource.jsp?path=/WEB-INF/classes/rsrc/resourceA.properties", + "resourceAInWebInfClasses=true"); + assertPageContains("/test/contextGetResource.jsp?path=/WEB-INF/classes/rsrc/resourceG.properties", + "resourceGInWebInfClasses=true"); + assertPageContains("/test/contextGetResource.jsp?path=/rsrc/resourceE.properties", + "resourceEInDependentLibraryTargetClasses=true"); + assertPageContains("/test/contextGetResource.jsp?path=/other/resourceI.properties", "resourceIInWebapp=true"); + assertPageContains("/test/contextGetResource.jsp?path=/rsrc-2/resourceJ.properties", "resourceJInWebapp=true"); String allRsrcPaths = - getUrl( - "http://localhost:" + getPort() + - "/test/contextGetResourcePaths.jsp?path=/rsrc/").toString(); - Assert.assertTrue( - allRsrcPaths, - allRsrcPaths.indexOf("/rsrc/resourceF.properties") > 0); - Assert.assertTrue( - allRsrcPaths, - allRsrcPaths.indexOf("/rsrc/resourceE.properties") > 0); - Assert.assertTrue( - allRsrcPaths, - allRsrcPaths.indexOf("/rsrc/resourceH.properties") > 0); + getUrl("http://localhost:" + getPort() + "/test/contextGetResourcePaths.jsp?path=/rsrc/").toString(); + Assert.assertTrue(allRsrcPaths, allRsrcPaths.indexOf("/rsrc/resourceF.properties") > 0); + Assert.assertTrue(allRsrcPaths, allRsrcPaths.indexOf("/rsrc/resourceE.properties") > 0); + Assert.assertTrue(allRsrcPaths, allRsrcPaths.indexOf("/rsrc/resourceH.properties") > 0); // check that there's no duplicate in the URLs String[] allRsrcPathsArray = allRsrcPaths.split("\\s+"); - Assert.assertEquals(new HashSet<>(Arrays.asList(allRsrcPathsArray)).size(), - allRsrcPathsArray.length); + Assert.assertEquals(new HashSet<>(Arrays.asList(allRsrcPathsArray)).size(), allRsrcPathsArray.length); String allRsrc2Paths = - getUrl( - "http://localhost:" + getPort() + - "/test/contextGetResourcePaths.jsp?path=/rsrc-2/").toString(); - Assert.assertTrue( - allRsrc2Paths, - allRsrc2Paths.indexOf("/rsrc-2/resourceJ.properties") > 0); - - assertPageContains( - "/test/testTlds.jsp", - "worldA"); - assertPageContains( - "/test/testTlds.jsp", - "worldB"); - assertPageContains( - "/test/testTlds.jsp", - "worldC"); - assertPageContains( - "/test/testTlds.jsp", - "worldD"); + getUrl("http://localhost:" + getPort() + "/test/contextGetResourcePaths.jsp?path=/rsrc-2/").toString(); + Assert.assertTrue(allRsrc2Paths, allRsrc2Paths.indexOf("/rsrc-2/resourceJ.properties") > 0); + + assertPageContains("/test/testTlds.jsp", "worldA"); + assertPageContains("/test/testTlds.jsp", "worldB"); + assertPageContains("/test/testTlds.jsp", "worldC"); + assertPageContains("/test/testTlds.jsp", "worldD"); } @Test @@ -252,32 +187,28 @@ File appDir = new File("test/webapp-virtual-webapp/src/main/webapp-a"); // app dir is relative to server home - StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test", - appDir.getAbsolutePath()); + StandardContext ctx = (StandardContext) tomcat.addWebapp(null, "/test", appDir.getAbsolutePath()); File tempFile = File.createTempFile("virtualWebInfClasses", null); File additionWebInfClasses = new File(tempFile.getAbsolutePath() + ".dir"); Assert.assertTrue(additionWebInfClasses.mkdirs()); File targetPackageForAnnotatedClass = - new File(additionWebInfClasses, - MyAnnotatedServlet.class.getPackage().getName().replace('.', '/')); + new File(additionWebInfClasses, MyAnnotatedServlet.class.getPackage().getName().replace('.', '/')); Assert.assertTrue(targetPackageForAnnotatedClass.mkdirs()); - try (InputStream annotatedServletClassInputStream = this.getClass().getResourceAsStream( - MyAnnotatedServlet.class.getSimpleName() + ".class"); - FileOutputStream annotatedServletClassOutputStream = new FileOutputStream(new File( - targetPackageForAnnotatedClass, MyAnnotatedServlet.class.getSimpleName() - + ".class"))) { + try (InputStream annotatedServletClassInputStream = + this.getClass().getResourceAsStream(MyAnnotatedServlet.class.getSimpleName() + ".class"); + FileOutputStream annotatedServletClassOutputStream = + new FileOutputStream(new File(targetPackageForAnnotatedClass, + MyAnnotatedServlet.class.getSimpleName() + ".class"))) { IOTools.flow(annotatedServletClassInputStream, annotatedServletClassOutputStream); } ctx.setResources(new StandardRoot(ctx)); File f1 = new File("test/webapp-virtual-webapp/target/classes"); File f2 = new File("test/webapp-virtual-library/target/WEB-INF/classes"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", f1.getAbsolutePath(), null, "/"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", f2.getAbsolutePath(), null, "/"); tomcat.start(); @@ -290,14 +221,11 @@ // then test that if we configure StandardContext with the additional // path, the servlet is detected ctx.setResources(new StandardRoot(ctx)); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", f1.getAbsolutePath(), null, "/"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", f2.getAbsolutePath(), null, "/"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/classes", additionWebInfClasses.getAbsolutePath(), null, "/"); tomcat.start(); @@ -307,26 +235,23 @@ Assert.assertTrue("Failed to clean up [" + tempFile + "]", tempFile.delete()); } - private void assertPageContains(String pageUrl, String expectedBody) - throws IOException { + private void assertPageContains(String pageUrl, String expectedBody) throws IOException { assertPageContains(pageUrl, expectedBody, 200); } - private void assertPageContains(String pageUrl, String expectedBody, - int expectedStatus) throws IOException { + private void assertPageContains(String pageUrl, String expectedBody, int expectedStatus) throws IOException { ByteChunk res = new ByteChunk(); // Note: With a read timeout of 3s the ASF CI buildbot was consistently - // seeing failures with this test. The failures were due to the - // JSP initialisation taking longer than the read timeout. The - // root cause of this is the frequent poor IO performance of the - // VM running the buildbot instance. Increasing this to 10s should - // avoid these failures. - // With the additional of Travis CI, failures continued to - // observed with a 10s timeout. It was therefore increased to 20s - // and then 30s. - int sc = getUrl("http://localhost:" + getPort() + pageUrl, res, 30000, - null, null); + // seeing failures with this test. The failures were due to the + // JSP initialisation taking longer than the read timeout. The + // root cause of this is the frequent poor IO performance of the + // VM running the buildbot instance. Increasing this to 10s should + // avoid these failures. + // With the additional of Travis CI, failures continued to + // observed with a 10s timeout. It was therefore increased to 20s + // and then 30s. + int sc = getUrl("http://localhost:" + getPort() + pageUrl, res, 30000, null, null); Assert.assertEquals(expectedStatus, sc); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/loader/TestVirtualWebappLoader.java tomcat10-10.1.52/test/org/apache/catalina/loader/TestVirtualWebappLoader.java --- tomcat10-10.1.34/test/org/apache/catalina/loader/TestVirtualWebappLoader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/loader/TestVirtualWebappLoader.java 2026-01-23 19:33:36.000000000 +0000 @@ -48,8 +48,7 @@ Tomcat tomcat = getTomcatInstance(); File appDir = new File("test/webapp"); - StandardContext ctx = (StandardContext) tomcat.addContext("", - appDir.getAbsolutePath()); + StandardContext ctx = (StandardContext) tomcat.addContext("", appDir.getAbsolutePath()); loader.setContext(ctx); ctx.setLoader(loader); @@ -70,8 +69,7 @@ Tomcat tomcat = getTomcatInstance(); File appDir = new File("test/webapp"); - StandardContext ctx = (StandardContext) tomcat.addContext("", - appDir.getAbsolutePath()); + StandardContext ctx = (StandardContext) tomcat.addContext("", appDir.getAbsolutePath()); WebappLoader loader = new WebappLoader(); @@ -83,13 +81,12 @@ ctx.resourcesStart(); File f1 = new File("test/webapp-fragments/WEB-INF/lib"); - ctx.getResources().createWebResourceSet( - WebResourceRoot.ResourceSetType.POST, "/WEB-INF/lib", + ctx.getResources().createWebResourceSet(WebResourceRoot.ResourceSetType.POST, "/WEB-INF/lib", f1.getAbsolutePath(), null, "/"); loader.start(); String[] repos = loader.getLoaderRepositories(); - Assert.assertEquals(5,repos.length); + Assert.assertEquals(6, repos.length); loader.stop(); repos = loader.getLoaderRepositories(); @@ -98,7 +95,7 @@ // no leak loader.start(); repos = loader.getLoaderRepositories(); - Assert.assertEquals(5,repos.length); + Assert.assertEquals(6, repos.length); // clear loader ctx.setLoader(null); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/loader/TestWebappClassLoader.java tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoader.java --- tomcat10-10.1.34/test/org/apache/catalina/loader/TestWebappClassLoader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoader.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,6 +24,7 @@ import org.junit.Assert; import org.junit.Test; +import org.apache.catalina.Context; import org.apache.catalina.core.StandardContext; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; @@ -41,8 +42,7 @@ Tomcat tomcat = getTomcatInstance(); - StandardContext ctx = - (StandardContext)tomcat.addContext("", f.getAbsolutePath()); + StandardContext ctx = (StandardContext) tomcat.addContext("", f.getAbsolutePath()); tomcat.start(); @@ -62,49 +62,18 @@ @Test public void testFilter() throws IOException { - String[] classSuffixes = new String[]{ - "", - "some.package.Example" - }; - - String[] resourceSuffixes = new String[]{ - "", - "some/path/test.properties", - "some/path/test" - }; - - String[] prefixes = new String[]{ - "", - "resources", - "WEB-INF", - "WEB-INF.classes", - "WEB-INF.lib", - "org", - "org.apache", - "jakarta", - "javax", - "com.mycorp" - }; - - String[] prefixesPermit = new String[]{ - "org.apache.tomcat.jdbc", - "jakarta.servlet.jsp.jstl", - }; - - String[] prefixesDeny = new String[]{ - "org.apache.catalina", - "org.apache.coyote", - "org.apache.el", - "org.apache.jasper", - "org.apache.juli", - "org.apache.naming", - "org.apache.tomcat", - "jakarta.annotation", - "jakarta.el", - "jakarta.servlet", - "jakarta.websocket", - "jakarta.security.auth.message" - }; + String[] classSuffixes = new String[] { "", "some.package.Example" }; + + String[] resourceSuffixes = new String[] { "", "some/path/test.properties", "some/path/test" }; + + String[] prefixes = new String[] { "", "resources", "WEB-INF", "WEB-INF.classes", "WEB-INF.lib", "org", + "org.apache", "jakarta", "javax", "com.mycorp" }; + + String[] prefixesPermit = new String[] { "org.apache.tomcat.jdbc", "jakarta.servlet.jsp.jstl", }; + + String[] prefixesDeny = new String[] { "org.apache.catalina", "org.apache.coyote", "org.apache.el", + "org.apache.jasper", "org.apache.juli", "org.apache.naming", "org.apache.tomcat", "jakarta.annotation", + "jakarta.el", "jakarta.servlet", "jakarta.websocket", "jakarta.security.auth.message" }; try (WebappClassLoader loader = new WebappClassLoader()) { String name; @@ -112,33 +81,27 @@ for (String prefix : prefixes) { for (String suffix : classSuffixes) { name = prefix + "." + suffix; - Assert.assertTrue("Class '" + name + "' failed permit filter", - !loader.filter(name, true)); + Assert.assertTrue("Class '" + name + "' failed permit filter", !loader.filter(name, true)); if (prefix.equals("")) { name = suffix; - Assert.assertTrue("Class '" + name + "' failed permit filter", - !loader.filter(name, true)); + Assert.assertTrue("Class '" + name + "' failed permit filter", !loader.filter(name, true)); } if (suffix.equals("")) { name = prefix; - Assert.assertTrue("Class '" + name + "' failed permit filter", - !loader.filter(name, true)); + Assert.assertTrue("Class '" + name + "' failed permit filter", !loader.filter(name, true)); } } prefix = prefix.replace('.', '/'); for (String suffix : resourceSuffixes) { name = prefix + "/" + suffix; - Assert.assertTrue("Resource '" + name + "' failed permit filter", - !loader.filter(name, false)); + Assert.assertTrue("Resource '" + name + "' failed permit filter", !loader.filter(name, false)); if (prefix.equals("")) { name = suffix; - Assert.assertTrue("Resource '" + name + "' failed permit filter", - !loader.filter(name, false)); + Assert.assertTrue("Resource '" + name + "' failed permit filter", !loader.filter(name, false)); } if (suffix.equals("")) { name = prefix; - Assert.assertTrue("Resource '" + name + "' failed permit filter", - !loader.filter(name, false)); + Assert.assertTrue("Resource '" + name + "' failed permit filter", !loader.filter(name, false)); } } } @@ -146,30 +109,70 @@ for (String prefix : prefixesPermit) { for (String suffix : classSuffixes) { name = prefix + "." + suffix; - Assert.assertTrue("Class '" + name + "' failed permit filter", - !loader.filter(name, true)); + Assert.assertTrue("Class '" + name + "' failed permit filter", !loader.filter(name, true)); } prefix = prefix.replace('.', '/'); for (String suffix : resourceSuffixes) { name = prefix + "/" + suffix; - Assert.assertTrue("Resource '" + name + "' failed permit filter", - !loader.filter(name, false)); + Assert.assertTrue("Resource '" + name + "' failed permit filter", !loader.filter(name, false)); } } for (String prefix : prefixesDeny) { for (String suffix : classSuffixes) { name = prefix + "." + suffix; - Assert.assertTrue("Class '" + name + "' failed deny filter", - loader.filter(name, true)); + Assert.assertTrue("Class '" + name + "' failed deny filter", loader.filter(name, true)); } prefix = prefix.replace('.', '/'); for (String suffix : resourceSuffixes) { name = prefix + "/" + suffix; - Assert.assertTrue("Resource '" + name + "' failed deny filter", - loader.filter(name, false)); + Assert.assertTrue("Resource '" + name + "' failed deny filter", loader.filter(name, false)); } } } } + + + /* + * See https://github.com/apache/tomcat/pull/816 for details. + */ + @Test + public void testResourceName() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + getProgrammaticRootContext(); + tomcat.start(); + + // Add an external resource to the web application + WebappClassLoaderBase cl = + (WebappClassLoaderBase) ((Context) tomcat.getHost().findChildren()[0]).getLoader().getClassLoader(); + File f = new File("test/conf"); + cl.addURL(f.toURI().toURL()); + + /* + * External resources are loaded using URLClassLoader code so leading '/' characters are not permitted in + * resource names. + */ + URL u1 = cl.getResource("/jaspic-test-01.xml"); + Assert.assertNull(u1); + + // Should now be visible if the correct name is used. + URL u2 = cl.getResource("jaspic-test-01.xml"); + Assert.assertNotNull(u2); + } + + + @Test + public void testResourceNameEmptyString() throws Exception { + Tomcat tomcat = getTomcatInstance(); + getProgrammaticRootContext(); + tomcat.start(); + + // Add an external resource to the web application + WebappClassLoaderBase cl = + (WebappClassLoaderBase) ((Context) tomcat.getHost().findChildren()[0]).getLoader().getClassLoader(); + + URL u1 = cl.getResource(""); + Assert.assertNotNull(u1); + } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/loader/TestWebappClassLoaderExecutorMemoryLeak.java tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoaderExecutorMemoryLeak.java --- tomcat10-10.1.34/test/org/apache/catalina/loader/TestWebappClassLoaderExecutorMemoryLeak.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoaderExecutorMemoryLeak.java 2026-01-23 19:33:36.000000000 +0000 @@ -86,21 +86,17 @@ public transient volatile ThreadPoolExecutor tpe; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - resp.getWriter().println( - "The current thread served " + this + " servlet"); - tpe = new ThreadPoolExecutor(tpSize, tpSize, 50000L, - TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); + resp.getWriter().println("The current thread served " + this + " servlet"); + tpe = new ThreadPoolExecutor(tpSize, tpSize, 50000L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); Task[] tasks = new Task[nTasks]; for (int i = 0; i < nTasks; i++) { tasks[i] = new Task("Task " + i); tpe.execute(tasks[i]); } - resp.getWriter().println("Started " + nTasks + - " never ending tasks using the ThreadPoolExecutor"); + resp.getWriter().println("Started " + nTasks + " never ending tasks using the ThreadPoolExecutor"); resp.getWriter().flush(); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/loader/TestWebappClassLoaderMemoryLeak.java tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoaderMemoryLeak.java --- tomcat10-10.1.34/test/org/apache/catalina/loader/TestWebappClassLoaderMemoryLeak.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoaderMemoryLeak.java 2026-01-23 19:33:36.000000000 +0000 @@ -59,8 +59,7 @@ Thread[] threads = getThreads(); for (Thread thread : threads) { - if (thread != null && thread.isAlive() && - TaskServlet.TIMER_THREAD_NAME.equals(thread.getName())) { + if (thread != null && thread.isAlive() && TaskServlet.TIMER_THREAD_NAME.equals(thread.getName())) { thread.join(5000); if (thread.isAlive()) { Assert.fail("Timer thread still running"); @@ -70,12 +69,11 @@ } /* - * Get the set of current threads as an array. - * Copied from WebappClassLoaderBase + * Get the set of current threads as an array. Copied from WebappClassLoaderBase */ private Thread[] getThreads() { // Get the current thread group - ThreadGroup tg = Thread.currentThread( ).getThreadGroup( ); + ThreadGroup tg = Thread.currentThread().getThreadGroup(); // Find the root thread group while (tg.getParent() != null) { tg = tg.getParent(); @@ -86,7 +84,7 @@ int threadCountActual = tg.enumerate(threads); // Make sure we don't miss any threads while (threadCountActual == threadCountGuess) { - threadCountGuess *=2; + threadCountGuess *= 2; threads = new Thread[threadCountGuess]; // Note tg.enumerate(Thread[]) silently ignores any threads that // can't fit into the array @@ -102,8 +100,7 @@ private static final String TIMER_THREAD_NAME = "leaked-thread"; @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Timer timer = new Timer(TIMER_THREAD_NAME); timer.schedule(new LocalTask(), 0, 10000); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/loader/TestWebappClassLoaderWeaving.java tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoaderWeaving.java --- tomcat10-10.1.34/test/org/apache/catalina/loader/TestWebappClassLoaderWeaving.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/loader/TestWebappClassLoaderWeaving.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,8 +53,7 @@ copyResource(PACKAGE_PREFIX + "/TesterNeverWeavedClass.class", new File(classes, "TesterNeverWeavedClass.class")); - copyResource(PACKAGE_PREFIX + "/TesterUnweavedClass.class", - new File(classes, "TesterUnweavedClass.class")); + copyResource(PACKAGE_PREFIX + "/TesterUnweavedClass.class", new File(classes, "TesterUnweavedClass.class")); } @@ -269,11 +268,9 @@ Boolean.valueOf(copiedLoader.getClearReferencesStopTimerThreads())); Assert.assertEquals("getContextName did not match.", this.loader.getContextName(), copiedLoader.getContextName()); - Assert.assertEquals("getDelegate did not match.", - Boolean.valueOf(this.loader.getDelegate()), + Assert.assertEquals("getDelegate did not match.", Boolean.valueOf(this.loader.getDelegate()), Boolean.valueOf(copiedLoader.getDelegate())); - Assert.assertEquals("getURLs did not match.", this.loader.getURLs().length, - copiedLoader.getURLs().length); + Assert.assertEquals("getURLs did not match.", this.loader.getURLs().length, copiedLoader.getURLs().length); Assert.assertSame("getParent did not match.", this.loader.getParent(), copiedLoader.getParent()); } @@ -293,8 +290,7 @@ } } - private static String invokeDoMethodOnClass(WebappClassLoaderBase loader, String className) - throws Exception { + private static String invokeDoMethodOnClass(WebappClassLoaderBase loader, String className) throws Exception { Class c = loader.findClass("org.apache.catalina.loader." + className); Assert.assertNotNull("The loaded class should not be null.", c); @@ -317,8 +313,7 @@ } @Override - public byte[] transform(ClassLoader loader, String className, Class x, - ProtectionDomain y, byte[] b) { + public byte[] transform(ClassLoader loader, String className, Class x, ProtectionDomain y, byte[] b) { if (CLASS_TO_WEAVE.equals(className)) { return this.replacement; @@ -331,67 +326,55 @@ } /** - * Compiled version of org.apache.catalina.loader.TesterUnweavedClass, except that - * the doMethod method returns "Hello, Weaver #1!". Compiled with Oracle Java 1.6.0_51. + * Compiled version of org.apache.catalina.loader.TesterUnweavedClass, except that the doMethod method returns + * "Hello, Weaver #1!". Compiled with Oracle Java 1.6.0_51. */ - private static final byte[] WEAVED_REPLACEMENT_1 = new byte[] { - -54, -2, -70, -66, 0, 0, 0, 50, 0, 17, 10, 0, 4, 0, 13, 8, 0, 14, 7, 0, 15, 7, 0, 16, 1, - 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, - 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 8, 100, - 111, 77, 101, 116, 104, 111, 100, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, - 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, - 105, 108, 101, 1, 0, 24, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, 101, - 100, 67, 108, 97, 115, 115, 46, 106, 97, 118, 97, 12, 0, 5, 0, 6, 1, 0, 17, 72, 101, - 108, 108, 111, 44, 32, 87, 101, 97, 118, 101, 114, 32, 35, 49, 33, 1, 0, 46, 111, 114, - 103, 47, 97, 112, 97, 99, 104, 101, 47, 99, 97, 116, 97, 108, 105, 110, 97, 47, 108, - 111, 97, 100, 101, 114, 47, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, - 101, 100, 67, 108, 97, 115, 115, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, - 79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, - 0, 7, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 8, 0, 0, - 0, 6, 0, 1, 0, 0, 0, 19, 0, 1, 0, 9, 0, 10, 0, 1, 0, 7, 0, 0, 0, 27, 0, 1, 0, 1, 0, 0, - 0, 3, 18, 2, -80, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 22, 0, 1, 0, 11, 0, 0, 0, - 2, 0, 12 - }; + private static final byte[] WEAVED_REPLACEMENT_1 = new byte[] { -54, -2, -70, -66, 0, 0, 0, 50, 0, 17, 10, 0, 4, 0, + 13, 8, 0, 14, 7, 0, 15, 7, 0, 16, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, + 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 8, 100, + 111, 77, 101, 116, 104, 111, 100, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, + 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 24, 84, 101, + 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, 101, 100, 67, 108, 97, 115, 115, 46, 106, 97, 118, 97, 12, + 0, 5, 0, 6, 1, 0, 17, 72, 101, 108, 108, 111, 44, 32, 87, 101, 97, 118, 101, 114, 32, 35, 49, 33, 1, 0, 46, + 111, 114, 103, 47, 97, 112, 97, 99, 104, 101, 47, 99, 97, 116, 97, 108, 105, 110, 97, 47, 108, 111, 97, 100, + 101, 114, 47, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, 101, 100, 67, 108, 97, 115, 115, 1, + 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, + 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, 0, 7, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, + 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 19, 0, 1, 0, 9, 0, 10, 0, 1, 0, 7, 0, 0, 0, 27, 0, 1, 0, 1, 0, 0, 0, 3, 18, + 2, -80, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 22, 0, 1, 0, 11, 0, 0, 0, 2, 0, 12 }; /** - * Compiled version of org.apache.catalina.loader.TesterUnweavedClass, except that - * the doMethod method returns "Hello, Weaver #2!". Compiled with Oracle Java 1.6.0_51. + * Compiled version of org.apache.catalina.loader.TesterUnweavedClass, except that the doMethod method returns + * "Hello, Weaver #2!". Compiled with Oracle Java 1.6.0_51. */ - private static final byte[] WEAVED_REPLACEMENT_2 = new byte[] { - -54, -2, -70, -66, 0, 0, 0, 50, 0, 17, 10, 0, 4, 0, 13, 8, 0, 14, 7, 0, 15, 7, 0, 16, 1, - 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, - 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 8, 100, - 111, 77, 101, 116, 104, 111, 100, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, - 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, - 105, 108, 101, 1, 0, 24, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, 101, - 100, 67, 108, 97, 115, 115, 46, 106, 97, 118, 97, 12, 0, 5, 0, 6, 1, 0, 17, 72, 101, - 108, 108, 111, 44, 32, 87, 101, 97, 118, 101, 114, 32, 35, 50, 33, 1, 0, 46, 111, 114, - 103, 47, 97, 112, 97, 99, 104, 101, 47, 99, 97, 116, 97, 108, 105, 110, 97, 47, 108, - 111, 97, 100, 101, 114, 47, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, - 101, 100, 67, 108, 97, 115, 115, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, - 79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, - 0, 7, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 8, 0, 0, - 0, 6, 0, 1, 0, 0, 0, 19, 0, 1, 0, 9, 0, 10, 0, 1, 0, 7, 0, 0, 0, 27, 0, 1, 0, 1, 0, 0, - 0, 3, 18, 2, -80, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 22, 0, 1, 0, 11, 0, 0, 0, - 2, 0, 12 - }; + private static final byte[] WEAVED_REPLACEMENT_2 = new byte[] { -54, -2, -70, -66, 0, 0, 0, 50, 0, 17, 10, 0, 4, 0, + 13, 8, 0, 14, 7, 0, 15, 7, 0, 16, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, + 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 8, 100, + 111, 77, 101, 116, 104, 111, 100, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, + 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 24, 84, 101, + 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, 101, 100, 67, 108, 97, 115, 115, 46, 106, 97, 118, 97, 12, + 0, 5, 0, 6, 1, 0, 17, 72, 101, 108, 108, 111, 44, 32, 87, 101, 97, 118, 101, 114, 32, 35, 50, 33, 1, 0, 46, + 111, 114, 103, 47, 97, 112, 97, 99, 104, 101, 47, 99, 97, 116, 97, 108, 105, 110, 97, 47, 108, 111, 97, 100, + 101, 114, 47, 84, 101, 115, 116, 101, 114, 85, 110, 119, 101, 97, 118, 101, 100, 67, 108, 97, 115, 115, 1, + 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, + 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, 0, 7, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, + 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 19, 0, 1, 0, 9, 0, 10, 0, 1, 0, 7, 0, 0, 0, 27, 0, 1, 0, 1, 0, 0, 0, 3, 18, + 2, -80, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 22, 0, 1, 0, 11, 0, 0, 0, 2, 0, 12 }; /* - * The WEAVED_REPLACEMENT_1 and WEAVED_REPLACEMENT_2 field contents are generated using the - * following code. To regenerate them, alter the TesterUnweavedClass code as desired, recompile, - * and run this main method. + * The WEAVED_REPLACEMENT_1 and WEAVED_REPLACEMENT_2 field contents are generated using the following code. To + * regenerate them, alter the TesterUnweavedClass code as desired, recompile, and run this main method. */ public static void main(String... arguments) throws Exception { ClassLoader cl = TestWebappClassLoaderWeaving.class.getClassLoader(); - try (InputStream input = cl.getResourceAsStream( - "org/apache/catalina/loader/TesterUnweavedClass.class")) { + try (InputStream input = cl.getResourceAsStream("org/apache/catalina/loader/TesterUnweavedClass.class")) { StringBuilder builder = new StringBuilder(); builder.append(" "); System.out.println(" private static final byte[] WEAVED_REPLACEMENT_1 = new byte[] {"); for (int i = 0, b = input.read(); b >= 0; i++, b = input.read()) { - String value = "" + ((byte)b); + String value = "" + ((byte) b); if (builder.length() + value.length() > 97) { builder.append(','); System.out.println(builder.toString()); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/loader/TesterWebappClassLoaderThreadLocalMemoryLeak.java tomcat10-10.1.52/test/org/apache/catalina/loader/TesterWebappClassLoaderThreadLocalMemoryLeak.java --- tomcat10-10.1.34/test/org/apache/catalina/loader/TesterWebappClassLoaderThreadLocalMemoryLeak.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/loader/TesterWebappClassLoaderThreadLocalMemoryLeak.java 2026-01-23 19:33:36.000000000 +0000 @@ -55,14 +55,12 @@ Tomcat tomcat = getTomcatInstance(); // Need to make sure we see a leak for the right reasons - tomcat.getServer().addLifecycleListener( - new JreMemoryLeakPreventionListener()); + tomcat.getServer().addLifecycleListener(new JreMemoryLeakPreventionListener()); // No file system docBase required Context ctx = getProgrammaticRootContext(); - Tomcat.addServlet(ctx, "leakServlet1", - "org.apache.tomcat.unittest.TesterLeakingServlet1"); + Tomcat.addServlet(ctx, "leakServlet1", "org.apache.tomcat.unittest.TesterLeakingServlet1"); ctx.addServletMappingDecoded("/leak1", "leakServlet1"); tomcat.start(); @@ -71,20 +69,17 @@ ((ThreadPoolExecutor) executor).setThreadRenewalDelay(-1); // Configure logging filter to check leak message appears - TesterLogValidationFilter f = TesterLogValidationFilter.add(null, - "The web application [ROOT] created a ThreadLocal with key of", null, - "org.apache.catalina.loader.WebappClassLoaderBase"); + TesterLogValidationFilter f = + TesterLogValidationFilter.add(null, "The web application [ROOT] created a ThreadLocal with key of", + null, "org.apache.catalina.loader.WebappClassLoaderBase"); // Need to force loading of all web application classes via the web // application class loader - loadClass("TesterCounter", - (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); - loadClass("TesterLeakingServlet1", - (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); + loadClass("TesterCounter", (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); + loadClass("TesterLeakingServlet1", (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); // This will trigger the ThreadLocal creation - int rc = getUrl("http://localhost:" + getPort() + "/leak1", - new ByteChunk(), null); + int rc = getUrl("http://localhost:" + getPort() + "/leak1", new ByteChunk(), null); // Make sure request is OK Assert.assertEquals(HttpServletResponse.SC_OK, rc); @@ -95,8 +90,7 @@ ctx = null; // Make sure we have a memory leak - String[] leaks = ((StandardHost) tomcat.getHost()) - .findReloadedContextMemoryLeaks(); + String[] leaks = ((StandardHost) tomcat.getHost()).findReloadedContextMemoryLeaks(); Assert.assertNotNull(leaks); Assert.assertTrue(leaks.length > 0); @@ -110,14 +104,12 @@ Tomcat tomcat = getTomcatInstance(); // Need to make sure we see a leak for the right reasons - tomcat.getServer().addLifecycleListener( - new JreMemoryLeakPreventionListener()); + tomcat.getServer().addLifecycleListener(new JreMemoryLeakPreventionListener()); // No file system docBase required Context ctx = getProgrammaticRootContext(); - Tomcat.addServlet(ctx, "leakServlet2", - "org.apache.tomcat.unittest.TesterLeakingServlet2"); + Tomcat.addServlet(ctx, "leakServlet2", "org.apache.tomcat.unittest.TesterLeakingServlet2"); ctx.addServletMappingDecoded("/leak2", "leakServlet2"); tomcat.start(); @@ -126,22 +118,18 @@ ((ThreadPoolExecutor) executor).setThreadRenewalDelay(-1); // Configure logging filter to check leak message appears - TesterLogValidationFilter f = TesterLogValidationFilter.add(null, - "The web application [ROOT] created a ThreadLocal with key of", null, - "org.apache.catalina.loader.WebappClassLoaderBase"); + TesterLogValidationFilter f = + TesterLogValidationFilter.add(null, "The web application [ROOT] created a ThreadLocal with key of", + null, "org.apache.catalina.loader.WebappClassLoaderBase"); // Need to force loading of all web application classes via the web // application class loader - loadClass("TesterCounter", - (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); - loadClass("TesterThreadScopedHolder", - (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); - loadClass("TesterLeakingServlet2", - (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); + loadClass("TesterCounter", (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); + loadClass("TesterThreadScopedHolder", (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); + loadClass("TesterLeakingServlet2", (WebappClassLoaderBase) ctx.getLoader().getClassLoader()); // This will trigger the ThreadLocal creation - int rc = getUrl("http://localhost:" + getPort() + "/leak2", - new ByteChunk(), null); + int rc = getUrl("http://localhost:" + getPort() + "/leak2", new ByteChunk(), null); // Make sure request is OK Assert.assertEquals(HttpServletResponse.SC_OK, rc); @@ -152,8 +140,7 @@ ctx = null; // Make sure we have a memory leak - String[] leaks = ((StandardHost) tomcat.getHost()) - .findReloadedContextMemoryLeaks(); + String[] leaks = ((StandardHost) tomcat.getHost()).findReloadedContextMemoryLeaks(); Assert.assertNotNull(leaks); Assert.assertTrue(leaks.length > 0); @@ -163,23 +150,18 @@ /** - * Utility method to ensure that classes are loaded by the - * WebappClassLoader. We can't just create classes since they will be loaded - * by the current class loader rather than the WebappClassLoader. This would - * mean that no leak occurred making the test for a leak rather pointless - * So, we load the bytes via the current class loader but define the class - * with the WebappClassLoader. - * - * This method assumes that all classes are in the current package. + * Utility method to ensure that classes are loaded by the WebappClassLoader. We can't just create classes since + * they will be loaded by the current class loader rather than the WebappClassLoader. This would mean that no leak + * occurred making the test for a leak rather pointless So, we load the bytes via the current class loader but + * define the class with the WebappClassLoader. This method assumes that all classes are in the current package. */ private void loadClass(String name, WebappClassLoaderBase cl) throws Exception { - try (InputStream is = cl.getResourceAsStream( - "org/apache/tomcat/unittest/" + name + ".class")) { + try (InputStream is = cl.getResourceAsStream("org/apache/tomcat/unittest/" + name + ".class")) { // We know roughly how big the class will be (~ 1K) so allow 2k as a // starting point byte[] classBytes = new byte[2048]; int offset = 0; - int read = is.read(classBytes, offset, classBytes.length-offset); + int read = is.read(classBytes, offset, classBytes.length - offset); while (read > -1) { offset += read; if (offset == classBytes.length) { @@ -188,11 +170,10 @@ System.arraycopy(classBytes, 0, tmp, 0, classBytes.length); classBytes = tmp; } - read = is.read(classBytes, offset, classBytes.length-offset); + read = is.read(classBytes, offset, classBytes.length - offset); } - Class lpClass = cl.doDefineClass( - "org.apache.tomcat.unittest." + name, classBytes, 0, - offset, cl.getClass().getProtectionDomain()); + Class lpClass = cl.doDefineClass("org.apache.tomcat.unittest." + name, classBytes, 0, offset, + cl.getClass().getProtectionDomain()); // Make sure we can create an instance lpClass.getConstructor().newInstance(); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/manager/TestHostManagerWebapp.java tomcat10-10.1.52/test/org/apache/catalina/manager/TestHostManagerWebapp.java --- tomcat10-10.1.34/test/org/apache/catalina/manager/TestHostManagerWebapp.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/manager/TestHostManagerWebapp.java 2026-01-23 19:33:36.000000000 +0000 @@ -81,108 +81,152 @@ client.setPort(getPort()); String basicHeader = (new BasicAuthHeader("Basic", "admin", "sekr3t")).getHeader().toString(); + // @formatter:off client.setRequest(new String[] { "GET / HTTP/1.1" + CRLF + "Host: newhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + // @formatter:off client.setRequest(new String[] { "GET /host-manager/html HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/host-manager/css/manager.css")); + // @formatter:off client.setRequest(new String[] { "GET /host-manager/text/list HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("[localhost]:[]")); + // @formatter:off client.setRequest(new String[] { "GET /host-manager/text/add?name=newhost&aliases=bar&manager=true HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("[newhost]")); + // @formatter:off client.setRequest(new String[] { "GET /host-manager/text/list HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("[newhost]:[bar]")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/list HTTP/1.1" + CRLF + "Host: newhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/manager:running")); + // @formatter:off client.setRequest(new String[] { "GET /host-manager/text/stop?name=newhost HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("[newhost]")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/list HTTP/1.1" + CRLF + "Host: newhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("Hello")); + // @formatter:off client.setRequest(new String[] { "GET /host-manager/text/start?name=newhost HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("[newhost]")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/list HTTP/1.1" + CRLF + "Host: bar" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/manager:running")); + // @formatter:off client.setRequest(new String[] { "GET /host-manager/text/persist HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -191,46 +235,61 @@ Assert.assertTrue(serverXml.canRead()); addDeleteOnTearDown(serverXml); String serverXmlDump = ""; - try (FileReader reader = new FileReader(serverXml); - StringWriter writer = new StringWriter()) { + try (FileReader reader = new FileReader(serverXml); StringWriter writer = new StringWriter()) { IOTools.flow(reader, writer); serverXmlDump = writer.toString(); } Assert.assertTrue(serverXmlDump.contains("bar")); + // @formatter:off client.setRequest(new String[] { "GET /host-manager/text/remove?name=newhost HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("[newhost]")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/list HTTP/1.1" + CRLF + "Host: newhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("Hello")); + // @formatter:off client.setRequest(new String[] { "GET /host-manager/text/start?name=newhost HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("[newhost]")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/list HTTP/1.1" + CRLF + "Host: newhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -238,5 +297,4 @@ tomcat.stop(); } - } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/manager/TestManagerWebapp.java tomcat10-10.1.52/test/org/apache/catalina/manager/TestManagerWebapp.java --- tomcat10-10.1.34/test/org/apache/catalina/manager/TestManagerWebapp.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/manager/TestManagerWebapp.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import jakarta.servlet.http.HttpServletResponse; @@ -31,6 +30,9 @@ import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; +import org.apache.catalina.LifecycleListener; import org.apache.catalina.authenticator.TestBasicAuthParser.BasicAuthHeader; import org.apache.catalina.realm.MemoryRealm; import org.apache.catalina.realm.MessageDigestCredentialHandler; @@ -41,21 +43,23 @@ import org.apache.catalina.startup.TomcatBaseTest; import org.apache.catalina.storeconfig.StoreConfigLifecycleListener; import org.apache.catalina.util.IOTools; +import org.apache.catalina.util.ServerInfo; import org.apache.catalina.util.URLEncoder; + public class TestManagerWebapp extends TomcatBaseTest { - public static final String CONFIG = "" - + "" - + "" - + "" - + ""; + public static final String CONFIG = "" + + "" + + "" + + "" + + ""; /** * Integration test for the manager webapp (verify all main Servlets are working). + * * @throws Exception if an error occurs */ @Test @@ -87,109 +91,153 @@ client.setPort(getPort()); String basicHeader = (new BasicAuthHeader("Basic", "admin", "sekr3t")).getHeader().toString(); + // @formatter:off client.setRequest(new String[] { "GET /manager/html HTTP/1.1" + CRLF + "Host: localhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_UNAUTHORIZED, client.getStatusCode()); + // @formatter:off client.setRequest(new String[] { "GET /manager/html HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/manager/css/manager.css")); + // @formatter:off client.setRequest(new String[] { "GET /manager/status HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("MiB")); + // @formatter:off client.setRequest(new String[] { "GET /manager/jmxproxy HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("Tomcat:type=ThreadPool,name=")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains(" - ")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/sessions?path=/manager HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("[1]")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/resources HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains(" - ")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/serverinfo HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); - Assert.assertTrue(client.getResponseBody().contains("[Apache Tomcat")); + Assert.assertTrue(client.getResponseBody().contains("[" + ServerInfo.getServerInfo() + "]")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/vminfo HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("vmName: ")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/threaddump HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("http-")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/list HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -232,8 +280,8 @@ SimpleHttpClient client = new SimpleHttpClient() { // 10s default too low for some CI systems @Override - public void connect() throws UnknownHostException, IOException { - connect(30000,30000); + public void connect() throws IOException { + connect(30000, 30000); } @Override @@ -246,74 +294,106 @@ appDir = new File(webappDir, "examples"); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/deploy?war=" + URLEncoder.QUERY.encode(appDir.getAbsolutePath(), StandardCharsets.UTF_8) + " HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("OK - ")); + // @formatter:off client.setRequest(new String[] { "GET /examples/servlets/servlet/RequestInfoExample HTTP/1.1" + CRLF + "Host: localhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/examples/servlets/servlet/RequestInfoExample")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/stop?path=/examples HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + // @formatter:off client.setRequest(new String[] { "GET /examples/servlets/servlet/RequestInfoExample HTTP/1.1" + CRLF + "Host: localhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, client.getStatusCode()); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/start?path=/examples HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + // @formatter:off client.setRequest(new String[] { "GET /examples/servlets/servlet/RequestInfoExample HTTP/1.1" + CRLF + "Host: localhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/examples/servlets/servlet/RequestInfoExample")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/save HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/save?path=/examples HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -321,76 +401,103 @@ File serverXml = new File(tomcat.getServer().getCatalinaBase(), Catalina.SERVER_XML); Assert.assertTrue(serverXml.canRead()); addDeleteOnTearDown(serverXml); - String serverXmlDump = ""; - try (FileReader reader = new FileReader(serverXml); - StringWriter writer = new StringWriter()) { + String serverXmlDump; + try (FileReader reader = new FileReader(serverXml); StringWriter writer = new StringWriter()) { IOTools.flow(reader, writer); serverXmlDump = writer.toString(); } Assert.assertTrue(serverXmlDump.contains("StoreConfigLifecycleListener")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/reload?path=/examples HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/list HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/examples:running")); + // @formatter:off client.setRequest(new String[] { "GET /examples/servlets/servlet/RequestInfoExample HTTP/1.1" + CRLF + "Host: localhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/examples/servlets/servlet/RequestInfoExample")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/list HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/examples:running")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/undeploy?path=/examples HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/list HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertFalse(client.getResponseBody().contains("/examples:running")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/findleaks?statusLine=true HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -398,4 +505,125 @@ tomcat.stop(); } + + /* + * Test case for Bug 57700. + */ + @Test + public void testBug57700() throws Exception { + ignoreTearDown = true; + Tomcat tomcat = getTomcatInstance(); + tomcat.setAddDefaultWebXmlToWebapp(false); + File configFile = new File(getTemporaryDirectory(), "tomcat-users-manager-lifecycle.xml"); + try (PrintWriter writer = new PrintWriter(configFile)) { + writer.write(CONFIG); + } + addDeleteOnTearDown(configFile); + + MemoryRealm memoryRealm = new MemoryRealm(); + memoryRealm.setCredentialHandler(new MessageDigestCredentialHandler()); + memoryRealm.setPathname(configFile.getAbsolutePath()); + tomcat.getEngine().setRealm(memoryRealm); + + File webappDir = new File(getBuildDirectory(), "webapps"); + + File appDir = new File(webappDir, "manager"); + Context ctx = tomcat.addWebapp(null, "/manager", appDir.getAbsolutePath()); + + HostConfig hostConfig = new HostConfig(); + ctx.getParent().addLifecycleListener(hostConfig); + + File appRoot = new File(webappDir, "bug57700"); + Assert.assertTrue(appRoot.mkdirs() && appRoot.isDirectory()); + addDeleteOnTearDown(appRoot); + + try (@SuppressWarnings("unused") TomcatBaseTest.ContainerInjector ignored = + TomcatBaseTest.ContainerInjector.inject(ctx.getParent(), + c -> c.getPath().equals("/bug57700"), + c -> { + c.addLifecycleListener(new FailOnceListener()); + Tomcat.initWebappDefaults(c); + })) { + + tomcat.start(); + SimpleHttpClient client = new SimpleHttpClient() { + @Override + public void connect() throws IOException { + connect(30000, 30000); + } + + @Override + public boolean isResponseBodyOK() { + return true; + } + }; + + client.setPort(getPort()); + String basicHeader = (new BasicAuthHeader("Basic", "admin", "sekr3t")).getHeader().toString(); + + client.setRequest(new String[]{ + "GET /manager/text/deploy?war=" + URLEncoder.QUERY.encode(appRoot.getAbsolutePath(), StandardCharsets.UTF_8) + " HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Authorization: " + basicHeader + CRLF + + "Connection: Close" + CRLF + CRLF + }); + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + + client.setRequest(new String[]{ + "GET /bug57700 HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + CRLF + }); + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, client.getStatusCode()); + + client.setRequest(new String[]{ + "GET /manager/text/start?path=/bug57700 HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Authorization: " + basicHeader + CRLF + + "Connection: Close" + CRLF + CRLF + }); + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + + client.setRequest(new String[]{ + "GET /bug57700 HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + CRLF + }); + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_FOUND, client.getStatusCode()); + + client.setRequest(new String[] { + "GET /manager/text/undeploy?path=/bug57700 HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Authorization: " + basicHeader + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + + tomcat.stop(); + } + } + + private static class FailOnceListener implements LifecycleListener { + private volatile boolean firstRun = true; + @Override + public void lifecycleEvent(LifecycleEvent event) { + if (event.getLifecycle() instanceof Context) { + if (Lifecycle.CONFIGURE_START_EVENT.equals(event.getType()) && firstRun) { + firstRun = false; + throw new RuntimeException("Configuration failure in first run only"); + } + } + } + } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/manager/TestManagerWebappSsl.java tomcat10-10.1.52/test/org/apache/catalina/manager/TestManagerWebappSsl.java --- tomcat10-10.1.34/test/org/apache/catalina/manager/TestManagerWebappSsl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/manager/TestManagerWebappSsl.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,9 +42,8 @@ import org.apache.tomcat.websocket.server.WsContextListener; /** - * The keys and certificates used in this file are all available in svn and were - * generated using a test CA the files for which are in the Tomcat PMC private - * repository since not all of them are AL2 licensed. + * The keys and certificates used in this file are all available in svn and were generated using a test CA the files for + * which are in the Tomcat PMC private repository since not all of them are AL2 licensed. */ @RunWith(Parameterized.class) public class TestManagerWebappSsl extends TomcatBaseTest { @@ -52,12 +51,11 @@ @Parameterized.Parameters(name = "{0}") public static Collection parameters() { List parameterSets = new ArrayList<>(); - parameterSets.add(new Object[] { - "JSSE", Boolean.FALSE, "org.apache.tomcat.util.net.jsse.JSSEImplementation"}); - parameterSets.add(new Object[] { - "OpenSSL", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.OpenSSLImplementation"}); - parameterSets.add(new Object[] { - "OpenSSL-FFM", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation"}); + parameterSets.add(new Object[] { "JSSE", Boolean.FALSE, "org.apache.tomcat.util.net.jsse.JSSEImplementation" }); + parameterSets.add( + new Object[] { "OpenSSL", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.OpenSSLImplementation" }); + parameterSets.add(new Object[] { "OpenSSL-FFM", Boolean.TRUE, + "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" }); return parameterSets; } @@ -90,7 +88,7 @@ tomcat.addWebapp(null, "/manager", appDir.getAbsolutePath()); appDir = new File(webappDir, "examples"); - Context ctxt = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); + Context ctxt = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); ctxt.addApplicationListener(WsContextListener.class.getName()); TesterSupport.initSsl(tomcat); @@ -98,8 +96,7 @@ tomcat.start(); - ByteChunk res = getUrl("https://localhost:" + getPort() + - "/examples/servlets/servlet/HelloWorldExample"); + ByteChunk res = getUrl("https://localhost:" + getPort() + "/examples/servlets/servlet/HelloWorldExample"); Assert.assertTrue(res.toString().indexOf("") > 0); // Add a regular connector @@ -122,59 +119,77 @@ client.setPort(connector.getLocalPort()); String basicHeader = (new BasicAuthHeader("Basic", "admin", "sekr3t")).getHeader().toString(); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/sslConnectorCiphers HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains(" -")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/sslConnectorCerts HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("Subject: CN=localhost")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/sslConnectorTrustedCerts HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertFalse(client.getResponseBody().contains("Subject: CN=localhost")); Assert.assertTrue(client.getResponseBody().contains("Subject: CN=Apache Tomcat Test CA")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/sslReload HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains(" -")); + // @formatter:off client.setRequest(new String[] { "GET /manager/text/sslConnectorCerts HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Authorization: " + basicHeader + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("Subject: CN=localhost")); Assert.assertTrue(res.toString().indexOf("") > 0); - } - } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/manager/TestStatusTransformer.java tomcat10-10.1.52/test/org/apache/catalina/manager/TestStatusTransformer.java --- tomcat10-10.1.34/test/org/apache/catalina/manager/TestStatusTransformer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/manager/TestStatusTransformer.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,7 +38,9 @@ public class TestStatusTransformer extends TomcatBaseTest { enum Mode { - HTML, XML, JSON + HTML, + XML, + JSON } @Test @@ -63,8 +65,7 @@ File appDir = new File("test/webapp"); Context ctxt = tomcat.addContext("", appDir.getAbsolutePath()); ctxt.setPrivileged(true); - Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default", - "org.apache.catalina.servlets.DefaultServlet"); + Wrapper defaultServlet = Tomcat.addServlet(ctxt, "default", "org.apache.catalina.servlets.DefaultServlet"); defaultServlet.addInitParameter("fileEncoding", "ISO-8859-1"); ctxt.addServletMappingDecoded("/", "default"); Tomcat.addServlet(ctxt, "status", "org.apache.catalina.manager.StatusManagerServlet"); @@ -82,10 +83,14 @@ } }; client.setPort(getPort()); + // @formatter:off client.setRequest(new String[] { "GET /index.html HTTP/1.1" + CRLF + "Host: localhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); @@ -100,10 +105,14 @@ default: requestline = "GET /status/all HTTP/1.1"; } + // @formatter:off client.setRequest(new String[] { requestline + CRLF + "Host: localhost" + CRLF + - "Connection: Close" + CRLF + CRLF }); + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); String body = client.getResponseBody(); @@ -113,8 +122,10 @@ Assert.assertTrue(result.contains("name=localhost/")); } else if (mode.equals(Mode.XML)) { try (StringReader reader = new StringReader(body)) { - Document xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(reader)); - String serialized = ((DOMImplementationLS) xmlDocument.getImplementation()).createLSSerializer().writeToString(xmlDocument); + Document xmlDocument = + DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(reader)); + String serialized = ((DOMImplementationLS) xmlDocument.getImplementation()).createLSSerializer() + .writeToString(xmlDocument); // Verify that a request is being processed Assert.assertTrue(serialized.contains("stage=\"S\"")); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/mapper/TestMapper.java tomcat10-10.1.52/test/org/apache/catalina/mapper/TestMapper.java --- tomcat10-10.1.34/test/org/apache/catalina/mapper/TestMapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/mapper/TestMapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -37,7 +37,7 @@ protected Mapper mapper; - private HashMap hostMap = new HashMap<>(); + private HashMap hostMap = new HashMap<>(); private synchronized Host createHost(String name) { Host host = hostMap.get(name); @@ -524,16 +524,16 @@ mapper.addContextVersion("aaaaaa", createHost("a6"), "", "0", createContext("c6"), new String[0], null, null); mapper.addContextVersion("aaaaaaa", createHost("a7"), "", "0", createContext("c7"), new String[0], null, null); - mapper.addWrappers("aaa", "", "0", Arrays.asList(new WrapperMappingInfo[] { - new WrapperMappingInfo("/", createWrapper("c3-default"), false, false) })); - mapper.addWrappers("aaaa", "", "0", Arrays.asList(new WrapperMappingInfo[] { - new WrapperMappingInfo("/", createWrapper("c4-default"), false, false) })); - mapper.addWrappers("aaaaa", "", "0", Arrays.asList(new WrapperMappingInfo[] { - new WrapperMappingInfo("/", createWrapper("c5-default"), false, false) })); - mapper.addWrappers("aaaaaa", "", "0", Arrays.asList(new WrapperMappingInfo[] { - new WrapperMappingInfo("/", createWrapper("c6-default"), false, false) })); - mapper.addWrappers("aaaaaaa", "", "0", Arrays.asList(new WrapperMappingInfo[] { - new WrapperMappingInfo("/", createWrapper("c7-default"), false, false) })); + mapper.addWrappers("aaa", "", "0", Arrays.asList( + new WrapperMappingInfo[] { new WrapperMappingInfo("/", createWrapper("c3-default"), false, false) })); + mapper.addWrappers("aaaa", "", "0", Arrays.asList( + new WrapperMappingInfo[] { new WrapperMappingInfo("/", createWrapper("c4-default"), false, false) })); + mapper.addWrappers("aaaaa", "", "0", Arrays.asList( + new WrapperMappingInfo[] { new WrapperMappingInfo("/", createWrapper("c5-default"), false, false) })); + mapper.addWrappers("aaaaaa", "", "0", Arrays.asList( + new WrapperMappingInfo[] { new WrapperMappingInfo("/", createWrapper("c6-default"), false, false) })); + mapper.addWrappers("aaaaaaa", "", "0", Arrays.asList( + new WrapperMappingInfo[] { new WrapperMappingInfo("/", createWrapper("c7-default"), false, false) })); MappingData mappingData = new MappingData(); MessageBytes hostMB = MessageBytes.newInstance(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/mapper/TestMapperWebapps.java tomcat10-10.1.52/test/org/apache/catalina/mapper/TestMapperWebapps.java --- tomcat10-10.1.34/test/org/apache/catalina/mapper/TestMapperWebapps.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/mapper/TestMapperWebapps.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,7 +31,7 @@ import org.apache.catalina.core.StandardContext; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; -import org.apache.catalina.valves.RemoteAddrValve; +import org.apache.catalina.valves.RemoteHostValve; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; @@ -226,7 +226,7 @@ org.apache.catalina.Context examples = tomcat.addWebapp(null, "/examples", examplesDir.getAbsolutePath()); examples.setMapperContextRootRedirectEnabled(false); // Then block access to the examples to test redirection - RemoteAddrValve rav = new RemoteAddrValve(); + RemoteHostValve rav = new RemoteHostValve(); rav.setDeny(".*"); rav.setDenyStatus(404); examples.getPipeline().addValve(rav); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/mbeans/TestRegistration.java tomcat10-10.1.52/test/org/apache/catalina/mbeans/TestRegistration.java --- tomcat10-10.1.34/test/org/apache/catalina/mbeans/TestRegistration.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/mbeans/TestRegistration.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,9 +41,8 @@ import org.apache.tomcat.util.modeler.Registry; /** - * General tests around the process of registration and de-registration that - * don't necessarily apply to one specific Tomcat class. - * + * General tests around the process of registration and de-registration that don't necessarily apply to one specific + * Tomcat class. */ public class TestRegistration extends TomcatBaseTest { @@ -63,91 +62,61 @@ private static String[] basicMBeanNames() { - return new String[] { - "Tomcat:type=Engine", - "Tomcat:type=Realm,realmPath=/realm0", - "Tomcat:type=Mapper", - "Tomcat:type=MBeanFactory", - "Tomcat:type=NamingResources", - "Tomcat:type=Server", - "Tomcat:type=Service", - "Tomcat:type=StringCache", - "Tomcat:type=UtilityExecutor", - "Tomcat:type=Valve,name=StandardEngineValve", - }; + return new String[] { "Tomcat:type=Engine", "Tomcat:type=Realm,realmPath=/realm0", "Tomcat:type=Mapper", + "Tomcat:type=MBeanFactory", "Tomcat:type=NamingResources", "Tomcat:type=Server", "Tomcat:type=Service", + "Tomcat:type=StringCache", "Tomcat:type=UtilityExecutor", + "Tomcat:type=Valve,name=StandardEngineValve", }; } private static String[] hostMBeanNames(String host) { - return new String[] { - "Tomcat:type=Host,host=" + host, - "Tomcat:type=Valve,host=" + host + ",name=ErrorReportValve", - "Tomcat:type=Valve,host=" + host + ",name=StandardHostValve", - }; + return new String[] { "Tomcat:type=Host,host=" + host, + "Tomcat:type=Valve,host=" + host + ",name=ErrorReportValve", + "Tomcat:type=Valve,host=" + host + ",name=StandardHostValve", }; } private String[] optionalMBeanNames(String host) { if (isAccessLogEnabled()) { - return new String[] { - "Tomcat:type=Valve,host=" + host + ",name=AccessLogValve", - }; + return new String[] { "Tomcat:type=Valve,host=" + host + ",name=AccessLogValve", }; } else { - return new String[] { }; + return new String[] {}; } } private static String[] requestMBeanNames(String port, String type) { - return new String[] { - "Tomcat:type=RequestProcessor,worker=" + - ObjectName.quote("http-" + type + "-" + ADDRESS + "-" + port) + - ",name=HttpRequest1", - }; + return new String[] { "Tomcat:type=RequestProcessor,worker=" + + ObjectName.quote("http-" + type + "-" + ADDRESS + "-" + port) + ",name=HttpRequest1", }; } private static String[] contextMBeanNames(String host, String context) { return new String[] { - "Tomcat:j2eeType=WebModule,name=//" + host + context + - ",J2EEApplication=none,J2EEServer=none", - "Tomcat:type=Loader,host=" + host + ",context=" + context, - "Tomcat:type=Manager,host=" + host + ",context=" + context, - "Tomcat:type=NamingResources,host=" + host + ",context=" + context, - "Tomcat:type=Valve,host=" + host + ",context=" + context + - ",name=NonLoginAuthenticator", - "Tomcat:type=Valve,host=" + host + ",context=" + context + - ",name=StandardContextValve", - "Tomcat:type=ParallelWebappClassLoader,host=" + host + ",context=" + context, - "Tomcat:type=WebResourceRoot,host=" + host + ",context=" + context, - "Tomcat:type=WebResourceRoot,host=" + host + ",context=" + context + - ",name=Cache", - "Tomcat:type=Realm,realmPath=/realm0,host=" + host + - ",context=" + context, - "Tomcat:type=Realm,realmPath=/realm0/realm0,host=" + host + - ",context=" + context - }; + "Tomcat:j2eeType=WebModule,name=//" + host + context + ",J2EEApplication=none,J2EEServer=none", + "Tomcat:type=Loader,host=" + host + ",context=" + context, + "Tomcat:type=Manager,host=" + host + ",context=" + context, + "Tomcat:type=NamingResources,host=" + host + ",context=" + context, + "Tomcat:type=Valve,host=" + host + ",context=" + context + ",name=NonLoginAuthenticator", + "Tomcat:type=Valve,host=" + host + ",context=" + context + ",name=StandardContextValve", + "Tomcat:type=ParallelWebappClassLoader,host=" + host + ",context=" + context, + "Tomcat:type=WebResourceRoot,host=" + host + ",context=" + context, + "Tomcat:type=WebResourceRoot,host=" + host + ",context=" + context + ",name=Cache", + "Tomcat:type=Realm,realmPath=/realm0,host=" + host + ",context=" + context, + "Tomcat:type=Realm,realmPath=/realm0/realm0,host=" + host + ",context=" + context }; } private static String[] connectorMBeanNames(String port, String type) { - return new String[] { - "Tomcat:type=Connector,port=" + port + ",address=" - + ObjectName.quote(ADDRESS), - "Tomcat:type=GlobalRequestProcessor,name=" - + ObjectName.quote("http-" + type + "-" + ADDRESS + "-" + port), - "Tomcat:type=ProtocolHandler,port=" + port + ",address=" - + ObjectName.quote(ADDRESS), - "Tomcat:type=ThreadPool,name=" - + ObjectName.quote("http-" + type + "-" + ADDRESS + "-" + port), - "Tomcat:type=SocketProperties,name=" - + ObjectName.quote("http-" + type + "-" + ADDRESS + "-" + port), - }; + return new String[] { "Tomcat:type=Connector,port=" + port + ",address=" + ObjectName.quote(ADDRESS), + "Tomcat:type=GlobalRequestProcessor,name=" + + ObjectName.quote("http-" + type + "-" + ADDRESS + "-" + port), + "Tomcat:type=ProtocolHandler,port=" + port + ",address=" + ObjectName.quote(ADDRESS), + "Tomcat:type=ThreadPool,name=" + ObjectName.quote("http-" + type + "-" + ADDRESS + "-" + port), + "Tomcat:type=SocketProperties,name=" + ObjectName.quote("http-" + type + "-" + ADDRESS + "-" + port), }; } /* - * Test verifying that Tomcat correctly de-registers the MBeans it has - * registered. - * @author Marc Guillemot + * Test verifying that Tomcat correctly de-registers the MBeans it has registered. */ @Test public void testMBeanDeregistration() throws Exception { - final MBeanServer mbeanServer = Registry.getRegistry(null, null).getMBeanServer(); + final MBeanServer mbeanServer = Registry.getRegistry(null).getMBeanServer(); // Verify there are no Catalina or Tomcat MBeans Set onames = mbeanServer.queryNames(new ObjectName("Catalina:*"), null); log.info(MBeanDumper.dumpBeans(mbeanServer, onames)); @@ -181,7 +150,7 @@ // Verify there are the correct Tomcat MBeans onames = mbeanServer.queryNames(new ObjectName("Tomcat:*"), null); ArrayList found = new ArrayList<>(onames.size()); - for (ObjectName on: onames) { + for (ObjectName on : onames) { found.add(on.toString()); } @@ -198,8 +167,7 @@ expected.addAll(Arrays.asList(contextMBeanNames("localhost", contextName))); expected.addAll(Arrays.asList(connectorMBeanNames("auto-" + index, protocol))); expected.addAll(Arrays.asList(optionalMBeanNames("localhost"))); - expected.addAll(Arrays.asList(requestMBeanNames( - "auto-" + index + "-" + getPort(), protocol))); + expected.addAll(Arrays.asList(requestMBeanNames("auto-" + index + "-" + getPort(), protocol))); // Did we find all expected MBeans? ArrayList missing = new ArrayList<>(expected); @@ -248,8 +216,7 @@ } /* - * Confirm that, as far as ObjectName is concerned, the order of the key - * properties is not significant. + * Confirm that, as far as ObjectName is concerned, the order of the key properties is not significant. */ @Test public void testNames() throws MalformedObjectNameException { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java tomcat10-10.1.52/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java --- tomcat10-10.1.34/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/nonblocking/TestNonBlockingAPI.java 2026-01-23 19:33:36.000000000 +0000 @@ -55,6 +55,7 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; import org.apache.catalina.core.AsyncContextImpl; @@ -77,7 +78,7 @@ private static String TRAILER_HEADER_VALUE = "abcde"; private static final int CHUNK_SIZE = 1024 * 1024; - private static final int WRITE_SIZE = CHUNK_SIZE * 10; + private static final int WRITE_SIZE = CHUNK_SIZE * 10; private static final byte[] DATA = new byte[WRITE_SIZE]; private static final int WRITE_PAUSE_MS = 500; @@ -85,8 +86,7 @@ static { // Use this sequence for padding to make it easier to spot errors - byte[] padding = new byte[] {'z', 'y', 'x', 'w', 'v', 'u', 't', 's', - 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k'}; + byte[] padding = new byte[] { 'z', 'y', 'x', 'w', 'v', 'u', 't', 's', 'r', 'q', 'p', 'o', 'n', 'm', 'l', 'k' }; int blockSize = padding.length; for (int i = 0; i < WRITE_SIZE / blockSize; i++) { @@ -95,8 +95,7 @@ int padSize = blockSize - hexSize; System.arraycopy(padding, 0, DATA, i * blockSize, padSize); - System.arraycopy( - hex.getBytes(), 0, DATA, i * blockSize + padSize, hexSize); + System.arraycopy(hex.getBytes(), 0, DATA, i * blockSize + padSize, hexSize); } Field f = null; @@ -122,7 +121,7 @@ } - @Test(expected=IOException.class) + @Test(expected = IOException.class) public void testNonBlockingReadIgnoreIsReady() throws Exception { doTestNonBlockingRead(true, false); } @@ -141,7 +140,7 @@ tomcat.start(); - Map> reqHeaders = new HashMap<>(); + Map> reqHeaders = new HashMap<>(); int rc = postUrl(true, new DataWriter(async ? 0 : 500, async ? 2000000 : 5), "http://localhost:" + getPort() + "/", new ByteChunk(), reqHeaders, null); @@ -149,7 +148,7 @@ if (async) { Assert.assertEquals(2000000 * 8, servlet.listener.body.length()); TestAsyncReadListener listener = (TestAsyncReadListener) servlet.listener; - Assert.assertTrue(Math.abs(listener.containerThreadCount.get() - listener.notReadyCount.get()) <= 1); + Assert.assertTrue(Math.abs(listener.containerThreadCount.get() - listener.notReadyCount.get()) <= 1); Assert.assertEquals(listener.isReadyCount.get(), listener.nonContainerThreadCount.get()); } else { Assert.assertEquals(5 * 8, servlet.listener.body.length()); @@ -159,11 +158,14 @@ @Test public void testNonBlockingReadChunkedNoSplits() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -171,12 +173,15 @@ @Test public void testNonBlockingReadChunkedSplitBeforeChunkHeader() throws Exception { + // @formatter:off String[] requestBody = new String[] { "", - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -184,12 +189,15 @@ @Test public void testNonBlockingReadChunkedSplitInChunkHeader() throws Exception { + // @formatter:off String[] requestBody = new String[] { "1", - "4" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "4" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -197,12 +205,15 @@ @Test public void testNonBlockingReadChunkedSplitAfterChunkHeader() throws Exception { + // @formatter:off String[] requestBody = new String[] { "14", - SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -210,12 +221,15 @@ @Test public void testNonBlockingReadChunkedSplitInHeaderCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { "14\r", "\n" + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -223,37 +237,46 @@ @Test public void testNonBlockingReadChunkedSplitAfterHeaderCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF, - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "14" + CRLF, + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @Test - public void testNonBlockingReadChunkedSplitBeforeExtensionDelimter() throws Exception { + public void testNonBlockingReadChunkedSplitBeforeExtensionDelimiter() throws Exception { + // @formatter:off String[] requestBody = new String[] { "14", - ";a=b" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + ";a=b" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @Test - public void testNonBlockingReadChunkedSplitAfterExtensionDelimter() throws Exception { + public void testNonBlockingReadChunkedSplitAfterExtensionDelimiter() throws Exception { + // @formatter:off String[] requestBody = new String[] { "14;", - "a=b" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "a=b" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -261,12 +284,15 @@ @Test public void testNonBlockingReadChunkedSplitInExtension() throws Exception { + // @formatter:off String[] requestBody = new String[] { "14;a", - "=b" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "=b" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -274,12 +300,15 @@ @Test public void testNonBlockingReadChunkedSplitAfterExtension() throws Exception { + // @formatter:off String[] requestBody = new String[] { "14;a=b", - SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -287,12 +316,15 @@ @Test public void testNonBlockingReadChunkedSplitInChunkBody() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + + "14" + CRLF + "012345", - "678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "678901FINISHED" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -300,12 +332,15 @@ @Test public void testNonBlockingReadChunkedSplitBeforeChunkBodyCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + + "14" + CRLF + "012345678901FINISHED", - SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -313,12 +348,15 @@ @Test public void testNonBlockingReadChunkedSplitInChunkBodyCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + + "14" + CRLF + "012345678901FINISHED\r", "\n" + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -326,11 +364,14 @@ @Test public void testNonBlockingReadChunkedSplitAfterChunkBodyCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF, - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "14" + CRLF + + "012345678901FINISHED" + CRLF, + "0" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -338,12 +379,15 @@ @Test public void testNonBlockingReadChunkedSplitBeforeEndChunkCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + "0", - SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -351,13 +395,16 @@ @Test public void testNonBlockingReadChunkedSplitInEndChunkCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + "0" + "\r", "\n" + - SimpleHttpClient.CRLF}; + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -365,12 +412,15 @@ @Test public void testNonBlockingReadChunkedSplitAfterEndChunkCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + "0" + - SimpleHttpClient.CRLF, - SimpleHttpClient.CRLF}; + CRLF, + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -378,12 +428,15 @@ @Test public void testNonBlockingReadChunkedSplitBeforeTrailer() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF, - TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF, + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); } @@ -391,13 +444,16 @@ @Test public void testNonBlockingReadChunkedSplitInTrailerName() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + "x-te", - "st" + ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "st" + ": " + TRAILER_HEADER_VALUE + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); } @@ -405,27 +461,33 @@ @Test public void testNonBlockingReadChunkedSplitAfterTrailerName() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + TRAILER_HEADER_NAME, - ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + ": " + TRAILER_HEADER_VALUE + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); } @Test - public void testNonBlockingReadChunkedSplitAfterTrailerDelimter() throws Exception { + public void testNonBlockingReadChunkedSplitAfterTrailerDelimiter() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + TRAILER_HEADER_NAME + ":", - " " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + " " + TRAILER_HEADER_VALUE + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); } @@ -433,13 +495,16 @@ @Test public void testNonBlockingReadChunkedSplitBeforeTrailerValue() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + TRAILER_HEADER_NAME + ": ", - TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + TRAILER_HEADER_VALUE + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); } @@ -447,13 +512,16 @@ @Test public void testNonBlockingReadChunkedSplitInTrailerValue() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + TRAILER_HEADER_NAME + ": abc", - "de" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "de" + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); } @@ -461,13 +529,16 @@ @Test public void testNonBlockingReadChunkedSplitAfterTrailerValue() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE, - SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + CRLF + + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); } @@ -475,13 +546,16 @@ @Test public void testNonBlockingReadChunkedSplitInTrailerCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + "\r", "\n" + - SimpleHttpClient.CRLF}; + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); } @@ -489,12 +563,15 @@ @Test public void testNonBlockingReadChunkedSplitAfterTrailerCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF, - SimpleHttpClient.CRLF}; + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + CRLF, + CRLF + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody, TRAILER_HEADER_VALUE); } @@ -502,13 +579,16 @@ @Test public void testNonBlockingReadChunkedSplitInFinalCrlf() throws Exception { + // @formatter:off String[] requestBody = new String[] { - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + + "14" + CRLF + + "012345678901FINISHED" + CRLF + "0" + - SimpleHttpClient.CRLF + + CRLF + "\r", - "\n"}; + "\n" + }; + // @formatter:on doTestNonBlockingReadChunked(requestBody); } @@ -516,12 +596,14 @@ @Test public void testNonBlockingReadChunkedSplitMaximum() throws Exception { + // @formatter:off String requestBody = new String( - "14" + SimpleHttpClient.CRLF + - "012345678901FINISHED" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF); + "14" + CRLF + + "012345678901FINISHED" + CRLF + + "0" + CRLF + + TRAILER_HEADER_NAME + ": " + TRAILER_HEADER_VALUE + CRLF + + CRLF); + // @formatter:on String[] requestBodySplit = new String[requestBody.length()]; for (int i = 0; i < requestBody.length(); i++) { @@ -553,19 +635,21 @@ tomcat.start(); // Add the headers to the first part of the chunked body + // @formatter:off requestBody[0] = - "GET / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost" + getPort() + SimpleHttpClient.CRLF + - "Transfer-Encoding: chunked" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + + "GET / HTTP/1.1" + CRLF + + "Host: localhost" + getPort() + CRLF + + "Transfer-Encoding: chunked" + CRLF + + CRLF + requestBody[0]; + // @formatter:on Client client = new Client(); client.setPort(getPort()); client.setRequest(requestBody); /* - * Reduce default pause to speed up test execution. Pause only needs to be long enough that each part of the - * request is read separately. + * Reduce default pause to speed up test execution. Pause only needs to be long enough that each part of the + * request is read separately. */ client.setRequestPause(200); client.connect(); @@ -609,8 +693,8 @@ Tomcat.addServlet(ctx, servletName, servlet); ctx.addServletMappingDecoded("/", servletName); // Note: Low values of socket.txBufSize can trigger very poor - // performance. Set it just low enough to ensure that the - // non-blocking write servlet will see isReady() == false + // performance. Set it just low enough to ensure that the + // non-blocking write servlet will see isReady() == false Assert.assertTrue(tomcat.getConnector().setProperty("socket.txBufSize", "1048576")); tomcat.start(); @@ -624,9 +708,8 @@ OutputStream os = s.getOutputStream(); if (keepAlive) { - os.write(("OPTIONS * HTTP/1.1\r\n" + - "Host: localhost:" + getPort() + "\r\n" + - "\r\n").getBytes(StandardCharsets.ISO_8859_1)); + os.write(("OPTIONS * HTTP/1.1\r\n" + "Host: localhost:" + getPort() + "\r\n" + "\r\n") + .getBytes(StandardCharsets.ISO_8859_1)); os.flush(); // Make sure the entire response has been read. int read = is.read(buffer); @@ -636,10 +719,8 @@ Assert.assertEquals(buffer[read - 2], '\r'); Assert.assertEquals(buffer[read - 1], '\n'); } - os.write(("GET / HTTP/1.1\r\n" + - "Host: localhost:" + getPort() + "\r\n" + - "Connection: close\r\n" + - "\r\n").getBytes(StandardCharsets.ISO_8859_1)); + os.write(("GET / HTTP/1.1\r\n" + "Host: localhost:" + getPort() + "\r\n" + "Connection: close\r\n" + "\r\n") + .getBytes(StandardCharsets.ISO_8859_1)); os.flush(); int read = 0; @@ -703,7 +784,7 @@ // Read the chunk lineStart = lineEnd + 1; lineEnd = resultString.indexOf('\n', lineStart); - log.info("Start : " + lineStart + ", End: " + lineEnd); + log.info("Start : " + lineStart + ", End: " + lineEnd); if (lineEnd > lineStart) { line = resultString.substring(lineStart, lineEnd + 1); } else { @@ -715,8 +796,7 @@ log.info(line); } if (chunkSize + 2 != line.length()) { - log.error("Chunk wrong length. Was " + line.length() + - " Expected " + (chunkSize + 2)); + log.error("Chunk wrong length. Was " + line.length() + " Expected " + (chunkSize + 2)); byte[] resultBytes = resultString.getBytes(); @@ -740,10 +820,8 @@ if (resultEnd > resultString.length()) { resultEnd = resultString.length(); } - log.error("Mismatch tx: " + new String( - DATA, dataStart, dataEnd - dataStart)); - log.error("Mismatch rx: " + - resultString.substring(resultStart, resultEnd)); + log.error("Mismatch tx: " + new String(DATA, dataStart, dataEnd - dataStart)); + log.error("Mismatch rx: " + resultString.substring(resultStart, resultEnd)); found = true; break; } @@ -795,8 +873,8 @@ Tomcat.addServlet(ctx, servletName, servlet); ctx.addServletMappingDecoded("/", servletName); // Note: Low values of socket.txBufSize can trigger very poor - // performance. Set it just low enough to ensure that the - // non-blocking write servlet will see isReady() == false + // performance. Set it just low enough to ensure that the + // non-blocking write servlet will see isReady() == false Assert.assertTrue(tomcat.getConnector().setProperty("socket.txBufSize", "524228")); tomcat.start(); @@ -805,10 +883,8 @@ ByteChunk result = new ByteChunk(); OutputStream os = s.getOutputStream(); - os.write(("GET / HTTP/1.1\r\n" + - "Host: localhost:" + getPort() + "\r\n" + - "Connection: close\r\n" + - "\r\n").getBytes(StandardCharsets.ISO_8859_1)); + os.write(("GET / HTTP/1.1\r\n" + "Host: localhost:" + getPort() + "\r\n" + "Connection: close\r\n" + "\r\n") + .getBytes(StandardCharsets.ISO_8859_1)); os.flush(); InputStream is = s.getInputStream(); @@ -821,8 +897,7 @@ long start = System.currentTimeMillis(); read = is.read(buffer); long end = System.currentTimeMillis(); - log.info("Client read [" + read + "] bytes in [" + (end - start) + - "] ms"); + log.info("Client read [" + read + "] bytes in [" + (end - start) + "] ms"); if (read > 0) { result.append(buffer, 0, read); } @@ -850,17 +925,17 @@ int count = 0; while (count < 100 && !servlet.wlistener.onErrorInvoked) { Thread.sleep(100); - count ++; + count++; } while (count < 100 && !asyncContextIsComplete.get()) { Thread.sleep(100); - count ++; + count++; } while (count < 100 && alv.getEntryCount() < 1) { Thread.sleep(100); - count ++; + count++; } Assert.assertTrue("Error listener should have been invoked.", servlet.wlistener.onErrorInvoked); @@ -868,8 +943,7 @@ // TODO Figure out why non-blocking writes with the NIO connector appear // to be slower on Linux - alv.validateAccessLog(1, 500, WRITE_PAUSE_MS, - WRITE_PAUSE_MS + 30 * 1000); + alv.validateAccessLog(1, 500, WRITE_PAUSE_MS, WRITE_PAUSE_MS + 30 * 1000); } @@ -887,7 +961,7 @@ tomcat.start(); - Map> resHeaders = new HashMap<>(); + Map> resHeaders = new HashMap<>(); int rc = postUrl(false, new BytesStreamer() { @Override public byte[] next() { @@ -903,8 +977,7 @@ public int available() { return 0; } - }, "http://localhost:" + - getPort() + "/", new ByteChunk(), resHeaders, null); + }, "http://localhost:" + getPort() + "/", new ByteChunk(), resHeaders, null); Assert.assertEquals(HttpServletResponse.SC_OK, rc); } @@ -939,18 +1012,18 @@ public byte[] next() { if (count < max) { if (count > 0) { - try { - if (delay > 0) { - Thread.sleep(delay); - } - } catch (Exception x) { - } + try { + if (delay > 0) { + Thread.sleep(delay); + } + } catch (Exception x) { + } } count++; if (count < max) { - return b; + return b; } else { - return f; + return f; } } else { return null; @@ -1028,7 +1101,8 @@ } - public NBWriteServlet(AtomicBoolean asyncContextIsComplete, boolean unlimited, boolean listenerCompletesOnError) { + public NBWriteServlet(AtomicBoolean asyncContextIsComplete, boolean unlimited, + boolean listenerCompletesOnError) { this.asyncContextIsComplete = asyncContextIsComplete; this.unlimited = unlimited; this.listenerCompletesOnError = listenerCompletesOnError; @@ -1102,9 +1176,7 @@ protected final StringBuilder body = new StringBuilder(); - TestReadListener(AsyncContext ctx, - boolean usingNonBlockingWrite, - boolean ignoreIsReady, + TestReadListener(AsyncContext ctx, boolean usingNonBlockingWrite, boolean ignoreIsReady, String expectedTrailerFieldValue) { this.ctx = ctx; this.usingNonBlockingWrite = usingNonBlockingWrite; @@ -1210,8 +1282,8 @@ if (isReady) { onDataAvailable(); } - } catch (IOException e) { - onError(e); + } catch (IOException ioe) { + onError(ioe); } } }.start(); @@ -1220,17 +1292,15 @@ @Override public void onAllDataRead() { super.onAllDataRead(); - log.info("isReadyCount=" + isReadyCount + " notReadyCount=" + notReadyCount - + " containerThreadCount=" + containerThreadCount - + " nonContainerThreadCount=" + nonContainerThreadCount); + log.info("isReadyCount=" + isReadyCount + " notReadyCount=" + notReadyCount + " containerThreadCount=" + + containerThreadCount + " nonContainerThreadCount=" + nonContainerThreadCount); } @Override public void onError(Throwable throwable) { super.onError(throwable); - log.info("isReadyCount=" + isReadyCount + " notReadyCount=" + notReadyCount - + " containerThreadCount=" + containerThreadCount - + " nonContainerThreadCount=" + nonContainerThreadCount); + log.info("isReadyCount=" + isReadyCount + " notReadyCount=" + notReadyCount + " containerThreadCount=" + + containerThreadCount + " nonContainerThreadCount=" + nonContainerThreadCount); } } @@ -1249,10 +1319,8 @@ public void onWritePossible() throws IOException { long start = System.currentTimeMillis(); int before = written; - while ((written < WRITE_SIZE || unlimited) && - ctx.getResponse().getOutputStream().isReady()) { - ctx.getResponse().getOutputStream().write( - DATA, written, CHUNK_SIZE); + while ((written < WRITE_SIZE || unlimited) && ctx.getResponse().getOutputStream().isReady()) { + ctx.getResponse().getOutputStream().write(DATA, written, CHUNK_SIZE); written += CHUNK_SIZE; } if (written == WRITE_SIZE) { @@ -1260,11 +1328,10 @@ // calling complete ctx.getResponse().flushBuffer(); } - log.info("Write took: " + (System.currentTimeMillis() - start) + - " ms. Bytes before=" + before + " after=" + written); + log.info("Write took: " + (System.currentTimeMillis() - start) + " ms. Bytes before=" + before + " after=" + + written); // only call complete if we have emptied the buffer - if (ctx.getResponse().getOutputStream().isReady() && - written == WRITE_SIZE) { + if (ctx.getResponse().getOutputStream().isReady() && written == WRITE_SIZE) { // it is illegal to call complete // if there is a write in progress ctx.complete(); @@ -1337,14 +1404,14 @@ } public static int postUrlWithDisconnect(boolean stream, BytesStreamer streamer, String path, - Map> reqHead, Map> resHead) throws IOException { + Map> reqHead, Map> resHead) throws IOException { URL url = new URL(path); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setReadTimeout(1000000); if (reqHead != null) { - for (Map.Entry> entry : reqHead.entrySet()) { + for (Map.Entry> entry : reqHead.entrySet()) { StringBuilder valueList = new StringBuilder(); for (String value : entry.getValue()) { if (valueList.length() > 0) { @@ -1376,7 +1443,7 @@ int rc = connection.getResponseCode(); if (resHead != null) { - Map> head = connection.getHeaderFields(); + Map> head = connection.getHeaderFields(); resHead.putAll(head); } try { @@ -1408,14 +1475,13 @@ CountDownLatch latch2 = new CountDownLatch(2); List exceptions = new ArrayList<>(); - Thread t = new Thread( - new RequestExecutor("http://localhost:" + getPort() + "/", latch2, exceptions)); + Thread t = new Thread(new RequestExecutor("http://localhost:" + getPort() + "/", latch2, exceptions)); t.start(); latch1.await(3000, TimeUnit.MILLISECONDS); - Thread t1 = new Thread(new RequestExecutor( - "http://localhost:" + getPort() + "/?notify=true", latch2, exceptions)); + Thread t1 = + new Thread(new RequestExecutor("http://localhost:" + getPort() + "/?notify=true", latch2, exceptions)); t1.start(); latch2.await(3000, TimeUnit.MILLISECONDS); @@ -1441,8 +1507,7 @@ CountDownLatch latch2 = new CountDownLatch(1); List exceptions = new ArrayList<>(); - Thread t = new Thread( - new RequestPostExecutor("http://localhost:" + getPort() + "/", latch2, exceptions)); + Thread t = new Thread(new RequestPostExecutor("http://localhost:" + getPort() + "/", latch2, exceptions)); t.start(); latch1.await(3000, TimeUnit.MILLISECONDS); @@ -1551,11 +1616,11 @@ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) - throws ServletException, IOException { + throws ServletException, IOException { final AsyncContext ctx = request.startAsync(); ctx.setTimeout(1000); - Thread readWriteListener = new Thread(new ReadWriteListener(latch, ctx)); + Thread readWriteListener = new Thread(new ReadWriteListener(latch, ctx)); readWriteListener.start(); } } @@ -1564,7 +1629,7 @@ private final transient CountDownLatch latch; private final transient AsyncContext ctx; - ReadWriteListener(CountDownLatch latch, AsyncContext ctx){ + ReadWriteListener(CountDownLatch latch, AsyncContext ctx) { this.latch = latch; this.ctx = ctx; } @@ -1573,8 +1638,8 @@ public void run() { try { setListeners(); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } } @@ -1595,8 +1660,8 @@ String body = new String(buffer, StandardCharsets.UTF_8); Assert.assertTrue(body.equals("body")); - } catch (IOException ex) { - ex.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } } @@ -1696,9 +1761,9 @@ tomcat.start(); - Map> resHeaders = new HashMap<>(); - int rc = postUrl(true, new DataWriter(500, 5), "http://localhost:" + - getPort() + "/", new ByteChunk(), resHeaders, null); + Map> resHeaders = new HashMap<>(); + int rc = postUrl(true, new DataWriter(500, 5), "http://localhost:" + getPort() + "/", new ByteChunk(), + resHeaders, null); Assert.assertEquals(HttpServletResponse.SC_OK, rc); } @@ -1710,8 +1775,7 @@ private static final long serialVersionUID = 1L; @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final CountDownLatch latch = new CountDownLatch(1); @@ -1735,8 +1799,8 @@ is.read(buffer); } - } catch (IOException ex) { - ex.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } } @@ -1764,45 +1828,42 @@ @Test public void testCanceledPostChunked() throws Exception { + // @formatter:off doTestCanceledPost(new String[] { - "POST / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + SimpleHttpClient.CRLF + - "Transfer-Encoding: Chunked" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "10" + SimpleHttpClient.CRLF + - "This is 16 bytes" + SimpleHttpClient.CRLF + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + CRLF + + "Transfer-Encoding: Chunked" + CRLF + + CRLF + + "10" + CRLF + + "This is 16 bytes" + CRLF }); + // @formatter:on } @Test public void testCanceledPostNoChunking() throws Exception { + // @formatter:off doTestCanceledPost(new String[] { - "POST / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + SimpleHttpClient.CRLF + - "Content-Length: 100" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + CRLF + + "Content-Length: 100" + CRLF + + CRLF + "This is 16 bytes" }); + // @formatter:on } /* - * Tests an error on an non-blocking read when the client closes the - * connection before fully writing the request body. + * Tests an error on an non-blocking read when the client closes the connection before fully writing the request + * body. * - * Required sequence is: - * - enter Servlet's service() method - * - startAsync() - * - configure non-blocking read - * - read partial body - * - close client connection - * - error is triggered - * - exit Servlet's service() method + * Required sequence is: - enter Servlet's service() method - startAsync() - configure non-blocking read - read + * partial body - close client connection - error is triggered - exit Servlet's service() method * - * This test makes extensive use of instance fields in the Servlet that - * would normally be considered very poor practice. It is only safe in this - * test as the Servlet only processes a single request. + * This test makes extensive use of instance fields in the Servlet that would normally be considered very poor + * practice. It is only safe in this test as the Servlet only processes a single request. */ private void doTestCanceledPost(String[] request) throws Exception { @@ -1865,8 +1926,7 @@ } @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) - throws ServletException, IOException { + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { AsyncContext ac = req.startAsync(); ac.setTimeout(-1); @@ -1939,9 +1999,8 @@ } totalRead += bytesRead; isReady = sis.isReady(); - System.out.println("Read [" + bytesRead + - "], buffer [" + new String(buffer, 0, bytesRead, StandardCharsets.UTF_8) + - "], total read [" + totalRead + + System.out.println("Read [" + bytesRead + "], buffer [" + + new String(buffer, 0, bytesRead, StandardCharsets.UTF_8) + "], total read [" + totalRead + "], isReady [" + isReady + "]"); } while (isReady); if (totalRead == 16) { @@ -1977,12 +2036,8 @@ /* - * Tests client disconnect in the following scenario: - * - async with non-blocking IO - * - response has been committed - * - no data in buffers - * - client disconnects - * - server attempts a write + * Tests client disconnect in the following scenario: - async with non-blocking IO - response has been committed - + * no data in buffers - client disconnects - server attempts a write */ private void doTestNonBlockingWriteError02(boolean swallowIoException) throws Exception { CountDownLatch responseCommitLatch = new CountDownLatch(1); @@ -2005,11 +2060,13 @@ ResponseOKClient client = new ResponseOKClient(); client.setPort(getPort()); + // @formatter:off client.setRequest(new String[] { - "GET / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + "GET / HTTP/1.1" + CRLF + + "Host: localhost:" + CRLF + + CRLF }); + // @formatter:on client.connect(); client.sendRequest(); @@ -2093,8 +2150,8 @@ private final boolean swallowIoException; private volatile AtomicInteger stage = new AtomicInteger(0); - TestWriteListener02(AsyncContext ac, CountDownLatch responseCommitLatch, - CountDownLatch clientCloseLatch, boolean swallowIoException) { + TestWriteListener02(AsyncContext ac, CountDownLatch responseCommitLatch, CountDownLatch clientCloseLatch, + boolean swallowIoException) { this.ac = ac; this.responseCommitLatch = responseCommitLatch; this.clientCloseLatch = clientCloseLatch; diff -Nru tomcat10-10.1.34/test/org/apache/catalina/nonblocking/TesterAjpNonBlockingClient.java tomcat10-10.1.52/test/org/apache/catalina/nonblocking/TesterAjpNonBlockingClient.java --- tomcat10-10.1.34/test/org/apache/catalina/nonblocking/TesterAjpNonBlockingClient.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/nonblocking/TesterAjpNonBlockingClient.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,22 +36,19 @@ import org.apache.tomcat.util.buf.ByteChunk; /** - * This is not a standard set of unit tests. This is a set of test clients for - * AJP support of Servlet 3.1 non-blocking IO. It assumes that there is an httpd - * instance listening on localhost:80 that is redirecting all traffic to a - * default Tomcat instance of version 8 or above that includes the examples - * web application. + * This is not a standard set of unit tests. This is a set of test clients for AJP support of Servlet 3.1 non-blocking + * IO. It assumes that there is an httpd instance listening on localhost:80 that is redirecting all traffic to a default + * Tomcat instance of version 8 or above that includes the examples web application. */ public class TesterAjpNonBlockingClient extends TomcatBaseTest { @Test public void doTestAJPNonBlockingRead() throws Exception { - Map> resHeaders = new HashMap<>(); + Map> resHeaders = new HashMap<>(); ByteChunk out = new ByteChunk(); - int rc = postUrl(true, new DataWriter(2000, 5), "http://localhost" + - "/examples/servlets/nonblocking/bytecounter", - out, resHeaders, null); + int rc = postUrl(true, new DataWriter(2000, 5), + "http://localhost" + "/examples/servlets/nonblocking/bytecounter", out, resHeaders, null); System.out.println(out.toString()); @@ -67,10 +64,8 @@ ByteChunk result = new ByteChunk(); OutputStream os = s.getOutputStream(); - os.write(("GET /examples/servlets/nonblocking/numberwriter HTTP/1.1\r\n" + - "Host: localhost\r\n" + - "Connection: close\r\n" + - "\r\n").getBytes(StandardCharsets.ISO_8859_1)); + os.write(("GET /examples/servlets/nonblocking/numberwriter HTTP/1.1\r\n" + "Host: localhost\r\n" + + "Connection: close\r\n" + "\r\n").getBytes(StandardCharsets.ISO_8859_1)); os.flush(); InputStream is = s.getInputStream(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/realm/TestDataSourceRealm.java tomcat10-10.1.52/test/org/apache/catalina/realm/TestDataSourceRealm.java --- tomcat10-10.1.34/test/org/apache/catalina/realm/TestDataSourceRealm.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/realm/TestDataSourceRealm.java 2026-01-23 19:33:36.000000000 +0000 @@ -147,7 +147,7 @@ Assert.assertFalse(gp.hasRole("manager")); String pass = db.getPassword("tomcat"); - Assert.assertEquals(pass, "password"); + Assert.assertEquals("password", pass); List roles = db.getRoles("tomcat"); Assert.assertEquals(2, roles.size()); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java tomcat10-10.1.52/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java --- tomcat10-10.1.34/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/realm/TestJNDIRealmIntegration.java 2026-01-23 19:33:36.000000000 +0000 @@ -118,7 +118,7 @@ public int poolSize; @Test - public void testAuthenication() throws Exception { + public void testAuthentication() throws Exception { JNDIRealm realm = new JNDIRealm(); realm.containerLog = LogFactory.getLog(TestJNDIRealmIntegration.class); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/realm/TestRealmBase.java tomcat10-10.1.52/test/org/apache/catalina/realm/TestRealmBase.java --- tomcat10-10.1.34/test/org/apache/catalina/realm/TestRealmBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/realm/TestRealmBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -36,6 +36,7 @@ import org.apache.tomcat.unittest.TesterResponse; import org.apache.tomcat.util.descriptor.web.SecurityCollection; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; +import org.apache.tomcat.util.http.Method; public class TestRealmBase { @@ -659,7 +660,7 @@ SecurityConstraint deleteConstraint = new SecurityConstraint(); deleteConstraint.addAuthRole(ROLE1); SecurityCollection deleteCollection = new SecurityCollection(); - deleteCollection.addMethod("DELETE"); + deleteCollection.addMethod(Method.DELETE); deleteCollection.addPatternDecoded("/*"); deleteConstraint.addCollection(deleteCollection); @@ -692,7 +693,7 @@ context.addConstraint(deleteConstraint); // All users should be able to perform a GET - request.setMethod("GET"); + request.setMethod(Method.GET); SecurityConstraint[] constraintsGet = mapRealm.findSecurityConstraints(request, context); @@ -712,7 +713,7 @@ // Only user1 should be able to perform a POST as only that user has // role1. - request.setMethod("POST"); + request.setMethod(Method.POST); SecurityConstraint[] constraintsPost = mapRealm.findSecurityConstraints(request, context); @@ -732,7 +733,7 @@ // Only users with application roles (role1 or role2 so user1 or user2) // should be able to perform a PUT. - request.setMethod("PUT"); + request.setMethod(Method.PUT); SecurityConstraint[] constraintsPut = mapRealm.findSecurityConstraints(request, context); @@ -751,7 +752,7 @@ request, response, constraintsPut, null)); // Any authenticated user should be able to perform a TRACE. - request.setMethod("TRACE"); + request.setMethod(Method.TRACE); SecurityConstraint[] constraintsTrace = mapRealm.findSecurityConstraints(request, context); @@ -771,7 +772,7 @@ // Only user1 should be able to perform a DELETE as only that user has // role1. - request.setMethod("DELETE"); + request.setMethod(Method.DELETE); SecurityConstraint[] constraintsDelete = mapRealm.findSecurityConstraints(request, context); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/realm/TesterLoginModule.java tomcat10-10.1.52/test/org/apache/catalina/realm/TesterLoginModule.java --- tomcat10-10.1.34/test/org/apache/catalina/realm/TesterLoginModule.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/realm/TesterLoginModule.java 2026-01-23 19:33:36.000000000 +0000 @@ -119,8 +119,8 @@ return true; - } catch (IOException e) { - throw new LoginException(e.getMessage()); + } catch (IOException ioe) { + throw new LoginException(ioe.getMessage()); } catch (UnsupportedCallbackException e) { throw new LoginException(e.getMessage()); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/realm/TesterServletSecurity01.java tomcat10-10.1.52/test/org/apache/catalina/realm/TesterServletSecurity01.java --- tomcat10-10.1.34/test/org/apache/catalina/realm/TesterServletSecurity01.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/realm/TesterServletSecurity01.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,14 +21,15 @@ import jakarta.servlet.annotation.ServletSecurity; import org.apache.tomcat.util.descriptor.web.SecurityConstraint; +import org.apache.tomcat.util.http.Method; @ServletSecurity(value=@HttpConstraint, httpMethodConstraints={ - @HttpMethodConstraint(value="POST", + @HttpMethodConstraint(value=Method.POST, rolesAllowed=TestRealmBase.ROLE1), - @HttpMethodConstraint(value="PUT", + @HttpMethodConstraint(value=Method.PUT, rolesAllowed=SecurityConstraint.ROLE_ALL_ROLES), - @HttpMethodConstraint(value="TRACE", + @HttpMethodConstraint(value=Method.TRACE, rolesAllowed=SecurityConstraint.ROLE_ALL_AUTHENTICATED_USERS)}) public class TesterServletSecurity01 { // Class is NO-OP. It is only used to 'host' the annotation. diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/ServletOptionsBaseTest.java tomcat10-10.1.52/test/org/apache/catalina/servlets/ServletOptionsBaseTest.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/ServletOptionsBaseTest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/ServletOptionsBaseTest.java 2026-01-23 19:33:36.000000000 +0000 @@ -94,11 +94,14 @@ OptionsHttpClient client = new OptionsHttpClient(); client.setPort(getPort()); + // @formatter:off client.setRequest(new String[] { "OPTIONS /webdav/" + url + " HTTP/1.1" + CRLF + "Host: localhost:" + getPort() + CRLF + "Connection: close" + CRLF + - CRLF }); + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); @@ -109,11 +112,14 @@ client.disconnect(); client.reset(); + // @formatter:off client.setRequest(new String[] { method + " /webdav/" + url + " HTTP/1.1" + CRLF + "Host: localhost:" + getPort() + CRLF + "Connection: close" + CRLF + - CRLF }); + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServlet.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServlet.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -211,11 +211,15 @@ TestCompressedClient client = new TestCompressedClient(getPort()); client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /index.html HTTP/1.1" + CRLF + - "Host: localhost" + CRLF + - "Connection: Close" + CRLF + - "Accept-Encoding: br, gzip" + CRLF + CRLF }); + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + + "Accept-Encoding: br, gzip" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse200()); @@ -225,10 +229,14 @@ Assert.assertTrue(responseHeaders.contains("vary: accept-encoding")); client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /index.html HTTP/1.1" + CRLF + - "Host: localhost" + CRLF + - "Connection: Close" + CRLF+ CRLF }); + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse200()); @@ -266,11 +274,15 @@ TestCompressedClient client = new TestCompressedClient(getPort()); client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /index.html HTTP/1.1" + CRLF + - "Host: localhost" + CRLF + - "Connection: Close" + CRLF + - "Accept-Encoding: br, gzip ; q = 0.5 , custom" + CRLF + CRLF }); + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + + "Accept-Encoding: br, gzip ; q = 0.5 , custom" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse200()); @@ -280,11 +292,14 @@ Assert.assertTrue(responseHeaders.contains("vary: accept-encoding")); client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /index.html HTTP/1.1" + CRLF + - "Host: localhost" + CRLF + - "Connection: Close" + CRLF + - "Accept-Encoding: br;q=1,gzip,custom" + CRLF + CRLF }); + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + + "Accept-Encoding: br;q=1,gzip,custom" + CRLF + + CRLF }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse200()); @@ -322,11 +337,15 @@ TestCompressedClient client = new TestCompressedClient(getPort()); client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /index.html HTTP/1.1" + CRLF + - "Host: localhost" + CRLF + - "Connection: Close" + CRLF + - "Accept-Encoding: gzip;q=0.9,*" + CRLF + CRLF }); + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + + "Accept-Encoding: gzip;q=0.9,*" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse200()); @@ -336,11 +355,15 @@ Assert.assertTrue(responseHeaders.contains("vary: accept-encoding")); client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /index.html HTTP/1.1" + CRLF + - "Host: localhost" + CRLF + - "Connection: Close" + CRLF + - "Accept-Encoding: gzip;q=0.9,br;q=0,identity," + CRLF + CRLF }); + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + + "Accept-Encoding: gzip;q=0.9,br;q=0,identity," + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse200()); @@ -377,11 +400,15 @@ // Firefox 45 Accept-Encoding client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /index.html HTTP/1.1" + CRLF + - "Host: localhost" + CRLF + - "Connection: Close" + CRLF + - "Accept-Encoding: gzip, deflate, br" + CRLF + CRLF }); + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + + "Accept-Encoding: gzip, deflate, br" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse200()); @@ -392,11 +419,15 @@ // Chrome 50 Accept-Encoding client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /index.html HTTP/1.1" + CRLF + - "Host: localhost" + CRLF + - "Connection: Close" + CRLF + - "Accept-Encoding: gzip, deflate, sdch, br" + CRLF + CRLF }); + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + + "Accept-Encoding: gzip, deflate, sdch, br" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse200()); @@ -506,8 +537,12 @@ new TestCustomErrorClient(tomcat.getConnector().getLocalPort()); client.reset(); + // @formatter:off client.setRequest(new String[] { - "GET /MyApp/missing HTTP/1.0" +CRLF + CRLF }); + "GET /MyApp/missing HTTP/1.0" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse404()); @@ -522,11 +557,15 @@ // https://bz.apache.org/bugzilla/show_bug.cgi?id=50413 // client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /MyApp/missing HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Connection: close" + CRLF + - "If-Modified-Since: " + tomorrow + CRLF + CRLF }); + "If-Modified-Since: " + tomorrow + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse404()); @@ -535,11 +574,15 @@ // https://bz.apache.org/bugzilla/show_bug.cgi?id=50413#c6 // client.reset(); + // @formatter:off client.setRequest(new String[] { "GET /MyApp/missing HTTP/1.1" + CRLF + "Host: localhost" + CRLF + "Connection: close" + CRLF + - "Range: bytes=0-100" + CRLF + CRLF }); + "Range: bytes=0-100" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse404()); @@ -562,15 +605,17 @@ File webxml = new File(appDir, "WEB-INF/web.xml"); try (FileOutputStream fos = new FileOutputStream(webxml); Writer w = new OutputStreamWriter(fos, "UTF-8")) { - w.write("\n" - + "\n" - + "\n404\n" - + "/404-absent.html\n\n" - + "\n"); + // @formatter:off + w.write("\n" + + "\n" + + "\n404\n" + + "/404-absent.html\n\n" + + "\n"); + // @formatter:on } Tomcat tomcat = getTomcatInstance(); @@ -582,8 +627,12 @@ new TestCustomErrorClient(tomcat.getConnector().getLocalPort()); client.reset(); + // @formatter:off client.setRequest(new String[] { - "GET /MyApp/missing HTTP/1.0" + CRLF + CRLF }); + "GET /MyApp/missing HTTP/1.0" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(); Assert.assertTrue(client.isResponse404()); @@ -690,25 +739,32 @@ TestCompressedClient client = new TestCompressedClient(getPort()); - client.setRequest(new String[] { "GET / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Accept-Language: fr-FR, fr, en" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "GET / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Accept-Language: fr-FR, fr, en" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("Taille")); Assert.assertTrue(client.getResponseBody().contains("bug43nnn/")); - client.setRequest(new String[] { "GET /bug43nnn/ HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Accept-Language: fr-FR, fr, en" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "GET /bug43nnn/ HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Accept-Language: fr-FR, fr, en" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); - } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,7 +19,6 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.security.MessageDigest; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -40,6 +39,7 @@ import org.apache.catalina.util.IOTools; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.buf.HexUtils; +import org.apache.tomcat.util.security.ConcurrentMessageDigest; @RunWith(Parameterized.class) public class TestDefaultServletIfMatchRequests extends TomcatBaseTest { @@ -62,7 +62,7 @@ try (FileInputStream is = new FileInputStream(index)) { ByteArrayOutputStream os = new ByteArrayOutputStream(); IOTools.flow(is, os); - resourceETagStrong = "\"" + HexUtils.toHexString(MessageDigest.getInstance("SHA-1").digest(os.toByteArray())) + "\""; + resourceETagStrong = "\"" + HexUtils.toHexString(ConcurrentMessageDigest.digestSHA256(os.toByteArray())) + "\""; } catch (Exception e) { } resourceETagWeak = "W/" + "\"" + index.length() + "-" + index.lastModified() + "\""; diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletOptions.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletOptions.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletOptions.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletOptions.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,13 +26,15 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.apache.tomcat.util.http.Method; + @RunWith(Parameterized.class) public class TestDefaultServletOptions extends ServletOptionsBaseTest { @Parameters public static Collection inputs() { String[] urls = new String[] { COLLECTION_NAME, FILE_NAME, UNKNOWN_NAME }; - String[] methods = new String[] { "GET", "POST", "HEAD", "TRACE", "PUT", "DELETE" }; + String[] methods = new String[] { Method.GET, Method.POST, Method.HEAD, Method.TRACE, Method.PUT, Method.DELETE }; List result = new ArrayList<>(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletPut.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletPut.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletPut.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletPut.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,9 +41,9 @@ public class TestDefaultServletPut extends TomcatBaseTest { private static final String START_TEXT= "Starting text"; - private static final String START_LEN = Integer.toString(START_TEXT.length()); + private static final int START_LEN = START_TEXT.length(); private static final String PATCH_TEXT= "Ending *"; - private static final String PATCH_LEN = Integer.toString(PATCH_TEXT.length()); + private static final int PATCH_LEN = PATCH_TEXT.length(); private static final String END_TEXT= "Ending * text"; @Parameterized.Parameters(name = "{index} rangeHeader [{0}]") @@ -52,23 +52,23 @@ // Valid partial PUT parameterSets.add(new Object[] { - "Content-Range: bytes 0-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.TRUE, END_TEXT, Boolean.TRUE }); + "Content-Range: bytes 0-" + (PATCH_LEN-1) + "/" + START_LEN + CRLF, Boolean.TRUE, END_TEXT, Boolean.TRUE }); parameterSets.add(new Object[] { - "Content-Range: ByTeS 0-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.TRUE, END_TEXT, Boolean.TRUE }); + "Content-Range: ByTeS 0-" + (PATCH_LEN-1) + "/" + START_LEN + CRLF, Boolean.TRUE, END_TEXT, Boolean.TRUE }); // Full PUT parameterSets.add(new Object[] { "", null, PATCH_TEXT, Boolean.TRUE }); // Invalid range parameterSets.add(new Object[] { - "Content-Range: apples 0-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); + "Content-Range: apples 0-" + (PATCH_LEN-1) + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); parameterSets.add(new Object[] { - "Content-Range: bytes00-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); + "Content-Range: bytes00-" + (PATCH_LEN-1) + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); parameterSets.add(new Object[] { - "Content-Range: bytes0-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); + "Content-Range: bytes0-" + (PATCH_LEN-1) + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); parameterSets.add(new Object[] { - "Content-Range: bytes=0-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); + "Content-Range: bytes=0-" + (PATCH_LEN-1) + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); parameterSets.add(new Object[] { - "Content-Range: bytes@0-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); + "Content-Range: bytes@0-" + (PATCH_LEN-1) + "/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); parameterSets.add(new Object[] { "Content-Range: bytes 9-7/" + START_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); parameterSets.add(new Object[] { @@ -82,11 +82,17 @@ parameterSets.add(new Object[] { "Content-Range: bytes 0-5/0x5" + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); parameterSets.add(new Object[] { - "Content-Range: bytes 0-" + PATCH_LEN + "/" + PATCH_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); + "Content-Range: bytes 0-" + (PATCH_LEN) + "/" + PATCH_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); // Valid partial PUT but partial PUT is disabled parameterSets.add(new Object[] { - "Content-Range: bytes 0-" + PATCH_LEN + "/" + START_LEN + CRLF, Boolean.TRUE, START_TEXT, Boolean.FALSE }); - + "Content-Range: bytes 0-" + (PATCH_LEN-1) + "/" + START_LEN + CRLF, Boolean.TRUE, START_TEXT, Boolean.FALSE }); + // Errors due to incorrect length + parameterSets.add(new Object[] { + "Content-Range: bytes 0-1/" + PATCH_LEN + CRLF, Boolean.FALSE, START_TEXT, Boolean.TRUE }); + parameterSets.add(new Object[] { "Content-Range: bytes 0-" + PATCH_LEN + "/20" + CRLF, Boolean.FALSE, + START_TEXT, Boolean.TRUE }); + parameterSets.add(new Object[] { "Content-Range: bytes 0-" + (PATCH_LEN - 2) + "/20" + CRLF, Boolean.FALSE, + START_TEXT, Boolean.TRUE }); return parameterSets; } @@ -134,13 +140,15 @@ // Full PUT PutClient putClient = new PutClient(getPort()); + // @formatter:off putClient.setRequest(new String[] { "PUT /test.txt HTTP/1.1" + CRLF + "Host: localhost:" + getPort() + CRLF + "Content-Length: " + START_LEN + CRLF + CRLF + START_TEXT - }); + }); + // @formatter:on putClient.connect(); putClient.processRequest(false); Assert.assertTrue(putClient.isResponse201()); @@ -150,6 +158,7 @@ // Partial PUT putClient.connect(); + // @formatter:off putClient.setRequest(new String[] { "PUT /test.txt HTTP/1.1" + CRLF + "Host: localhost:" + getPort() + CRLF + @@ -157,7 +166,8 @@ "Content-Length: " + PATCH_LEN + CRLF + CRLF + PATCH_TEXT - }); + }); + // @formatter:on putClient.processRequest(false); if (contentRangeHeaderValid == null) { // Not present (so will do a full PUT, replacing the existing) diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletRangeRequests.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletRangeRequests.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletRangeRequests.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletRangeRequests.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,7 +16,9 @@ */ package org.apache.catalina.servlets; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -30,10 +32,14 @@ import org.junit.runners.Parameterized.Parameter; import org.apache.catalina.Context; +import org.apache.catalina.Wrapper; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.catalina.util.IOTools; import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.buf.HexUtils; import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.security.ConcurrentMessageDigest; @RunWith(Parameterized.class) public class TestDefaultServletRangeRequests extends TomcatBaseTest { @@ -47,6 +53,14 @@ long len = index.length(); String strLen = Long.toString(len); String lastModified = FastHttpDateFormat.formatDate(index.lastModified()); + String weakETag = "W/\"" + strLen + "-" + Long.toString(index.lastModified()) + "\""; + String strongETag = "\"\""; + try (FileInputStream is = new FileInputStream(index)) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + IOTools.flow(is, os); + strongETag = "\"" + HexUtils.toHexString(ConcurrentMessageDigest.digestSHA256(os.toByteArray())) + "\""; + } catch (Exception e) { + } List parameterSets = new ArrayList<>(); @@ -97,12 +111,25 @@ // Valid parameterSets.add(new Object[] { "bytes=0-9", lastModified, Integer.valueOf(206), "10", "0-9/" + len }); - // Nonsense date (return whole entity) + // Nonsense data (request rejected) + parameterSets.add(new Object[] { "bytes=0-9", "a-b-c", Integer.valueOf(400), null, "" }); + parameterSets.add(new Object[] { "bytes=0-9", "W\"123\"", Integer.valueOf(400), null, "" }); + parameterSets.add(new Object[] { "bytes=0-9", "\"123\"W", Integer.valueOf(400), null, "" }); + // More than one (request rejected) parameterSets.add(new Object[] { - "bytes=0-9", "a-b-c", Integer.valueOf(200), strLen, ""}); + "bytes=0-9", "\"46273648\", " + weakETag, Integer.valueOf(400), null, "" }); // Different date (return whole entity) parameterSets.add(new Object[] { - "bytes=0-9", FastHttpDateFormat.formatDate(1000), Integer.valueOf(200), strLen, ""}); + "bytes=0-9", FastHttpDateFormat.formatDate(1000), Integer.valueOf(200), strLen, "" }); + // Valid weak etag + parameterSets.add(new Object[] { + "bytes=0-9", weakETag, Integer.valueOf(200), strLen, "" }); + // Invalid strong etag + parameterSets.add(new Object[] { + "bytes=0-9", "\"46273648\"", Integer.valueOf(200), strLen, "" }); + // Valid strong etag + parameterSets.add(new Object[] { + "bytes=0-9", strongETag, Integer.valueOf(206), "10", "0-9/" + len }); return parameterSets; } @@ -126,7 +153,10 @@ File appDir = new File("test/webapp"); Context ctxt = tomcat.addContext("", appDir.getAbsolutePath()); - Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName()); + Wrapper wrapper = Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName()); + if (ifRangeHeader != null && ifRangeHeader.startsWith("\"")) { + wrapper.addInitParameter("useStrongETags", "true"); + } ctxt.addServletMappingDecoded("/", "default"); tomcat.start(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section13.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section13.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section13.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section13.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,648 @@ +/* + * 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.servlets; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +import org.apache.catalina.Context; +import org.apache.catalina.Wrapper; +import org.apache.catalina.startup.SimpleHttpClient; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.http.FastHttpDateFormat; + +/** + * This test case is used to verify RFC 9110 Section 13. Conditional Requests. + */ +@RunWith(Parameterized.class) +public class TestDefaultServletRfc9110Section13 extends TomcatBaseTest { + + @Parameter(0) + public boolean useStrongETags; + @Parameter(1) + public Task task; + @Parameter(2) + public EtagPrecondition ifMatchPrecondition; + @Parameter(3) + public DatePrecondition ifUnmodifiedSincePrecondition; + @Parameter(4) + public EtagPrecondition ifNoneMatchPrecondition; + @Parameter(5) + public DatePrecondition ifModifiedSincePrecondition; + @Parameter(6) + public EtagPrecondition ifRangeEtagPrecondition; + @Parameter(7) + public DatePrecondition ifRangeDatePrecondition; + @Parameter(8) + public boolean addRangeHeader; + @Parameter(9) + public Integer scExpected; + + @Parameterized.Parameters(name = "{index} resource-strong [{0}], matchHeader [{1}]") + public static Collection parameters() { + List parameterSets = new ArrayList<>(); + for (Boolean useStrongEtag : booleans) { + for (Task task : Arrays.asList(Task.HEAD_INDEX_HTML, Task.GET_INDEX_HTML, Task.POST_INDEX_HTML)) { + // RFC 9110, Section 13.2.2, Step 1, HEAD: If-Match with and without If-Unmodified-Since + for (DatePrecondition dateCondition : DatePrecondition.values()) { + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.ALL, dateCondition, null, + null, null, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.EXACTLY, dateCondition, null, + null, null, null, Boolean.FALSE, useStrongEtag.booleanValue() ? SC_200 : SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.IN, dateCondition, null, + null, null, null, Boolean.FALSE, useStrongEtag.booleanValue() ? SC_200 : SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.NOT_IN, dateCondition, null, + null, null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.INVALID, dateCondition, null, + null, null, null, Boolean.FALSE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.INVALID_ALL_PLUS_OTHER, + dateCondition, null, null, null, null, Boolean.FALSE, SC_400 }); + } + + // RFC 9110, Section 13.2.2, Step 2, HEAD: If-Unmodified-Since only + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.EQ, null, null, null, null, + Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.LT, null, null, null, null, + Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.GT, null, null, null, null, + Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.MULTI_IN, null, null, null, + null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.MULTI_IN_REV, null, null, + null, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.INVALID, null, null, null, + null, Boolean.FALSE, SC_200 }); + + // Ensure If-Unmodified-Since takes precedence over If-Modified-Since + // If-Unmodified-Since only + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.LT, null, null, null, null, + Boolean.FALSE, SC_412 }); + // If-Modified-Since only + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.GT, null, null, + Boolean.FALSE, task.equals(Task.POST_INDEX_HTML) ? SC_200 : SC_304 }); + // Both + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.LT, null, + DatePrecondition.GT, null, null, Boolean.FALSE, SC_412 }); + + // RFC 9110, Section 13.2.2, Step 3, HEAD: If-None-Match with and without If-Modified-Since + for (DatePrecondition dateCondition : DatePrecondition.values()) { + parameterSets + .add(new Object[] { useStrongEtag, task, null, null, EtagPrecondition.ALL, dateCondition, + null, null, Boolean.FALSE, task.equals(Task.POST_INDEX_HTML) ? SC_412 : SC_304 }); + parameterSets.add( + new Object[] { useStrongEtag, task, null, null, EtagPrecondition.EXACTLY, dateCondition, + null, null, Boolean.FALSE, task.equals(Task.POST_INDEX_HTML) ? SC_412 : SC_304 }); + parameterSets + .add(new Object[] { useStrongEtag, task, null, null, EtagPrecondition.IN, dateCondition, + null, null, Boolean.FALSE, task.equals(Task.POST_INDEX_HTML) ? SC_412 : SC_304 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, EtagPrecondition.NOT_IN, + dateCondition, null, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, EtagPrecondition.INVALID, + dateCondition, null, null, Boolean.FALSE, SC_400 }); + parameterSets.add( + new Object[] { useStrongEtag, task, null, null, EtagPrecondition.INVALID_ALL_PLUS_OTHER, + dateCondition, null, null, Boolean.FALSE, SC_400 }); + } + + // RFC 9110, Section 13.2.2, Step 4, HEAD: If-Modified-Since only + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.EQ, null, null, + Boolean.FALSE, task.equals(Task.POST_INDEX_HTML) ? SC_200 : SC_304 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.LT, null, null, + Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.GT, null, null, + Boolean.FALSE, task.equals(Task.POST_INDEX_HTML) ? SC_200 : SC_304 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.MULTI_IN, null, + null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.MULTI_IN_REV, + null, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.INVALID, null, + null, Boolean.FALSE, SC_200 }); + } + + for (Task task : Arrays.asList(Task.HEAD_404_HTML, Task.GET_404_HTML, Task.POST_404_HTML)) { + // RFC 9110, Section 13.2.2, Step 1, HEAD: If-Match with and without If-Unmodified-Since + for (DatePrecondition dateCondition : DatePrecondition.values()) { + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.ALL, dateCondition, null, + null, null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.IN, dateCondition, null, + null, null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.NOT_IN, dateCondition, null, + null, null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.INVALID, dateCondition, null, + null, null, null, Boolean.FALSE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, task, EtagPrecondition.INVALID_ALL_PLUS_OTHER, + dateCondition, null, null, null, null, Boolean.FALSE, SC_400 }); + } + + // RFC 9110, Section 13.2.2, Step 2, HEAD: If-Unmodified-Since only + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.EQ, null, null, null, null, + Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.LT, null, null, null, null, + Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.GT, null, null, null, null, + Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.MULTI_IN, null, null, null, + null, Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.MULTI_IN_REV, null, null, + null, null, Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, DatePrecondition.INVALID, null, null, null, + null, Boolean.FALSE, SC_404 }); + + // RFC 9110, Section 13.2.2, Step 3, HEAD: If-None-Match with and without If-Modified-Since + for (DatePrecondition dateCondition : DatePrecondition.values()) { + parameterSets.add(new Object[] { useStrongEtag, task, null, null, EtagPrecondition.ALL, + dateCondition, null, null, Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, EtagPrecondition.IN, + dateCondition, null, null, Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, EtagPrecondition.NOT_IN, + dateCondition, null, null, Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, EtagPrecondition.INVALID, + dateCondition, null, null, Boolean.FALSE, SC_400 }); + parameterSets.add( + new Object[] { useStrongEtag, task, null, null, EtagPrecondition.INVALID_ALL_PLUS_OTHER, + dateCondition, null, null, Boolean.FALSE, SC_400 }); + } + + // RFC 9110, Section 13.2.2, Step 4, HEAD: If-Modified-Since only + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.EQ, null, null, + Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.LT, null, null, + Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.GT, null, null, + Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.MULTI_IN, null, + null, Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.MULTI_IN_REV, + null, null, Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, task, null, null, null, DatePrecondition.INVALID, null, + null, Boolean.FALSE, SC_404 }); + } + + // RFC 9110, Section 13.2.2, Step 5, GET: If-Range only + // entity-tag + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.ALL, null, Boolean.TRUE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.EXACTLY, null, Boolean.TRUE, useStrongEtag.booleanValue() ? SC_206 : SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.IN, null, Boolean.TRUE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.NOT_IN, null, Boolean.TRUE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.INVALID, null, Boolean.TRUE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.INVALID_ALL_PLUS_OTHER, null, Boolean.TRUE, SC_400 }); + // HTTP-date + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.EQ, Boolean.TRUE, SC_206 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.LT, Boolean.TRUE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.GT, Boolean.TRUE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.MULTI_IN, Boolean.TRUE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.MULTI_IN_REV, Boolean.TRUE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.INVALID, Boolean.TRUE, SC_400 }); + // Range header without If-Range + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, EtagPrecondition.ALL, null, null, null, + null, null, Boolean.TRUE, SC_206 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, EtagPrecondition.EXACTLY, null, null, + null, null, null, Boolean.TRUE, useStrongEtag.booleanValue() ? SC_206 : SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, EtagPrecondition.IN, null, null, null, + null, null, Boolean.TRUE, useStrongEtag.booleanValue() ? SC_206 : SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, EtagPrecondition.NOT_IN, null, null, + null, null, null, Boolean.TRUE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, EtagPrecondition.INVALID, null, null, + null, null, null, Boolean.TRUE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, + EtagPrecondition.INVALID_ALL_PLUS_OTHER, null, null, null, null, null, Boolean.TRUE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, DatePrecondition.EQ, null, null, + null, null, Boolean.TRUE, SC_206 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, DatePrecondition.LT, null, null, + null, null, Boolean.TRUE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, DatePrecondition.GT, null, null, + null, null, Boolean.TRUE, SC_206 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, DatePrecondition.MULTI_IN, null, + null, null, null, Boolean.TRUE, SC_206 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, DatePrecondition.MULTI_IN_REV, + null, null, null, null, Boolean.TRUE, SC_206 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, DatePrecondition.INVALID, null, + null, null, null, Boolean.TRUE, SC_206 }); + // If-Range header without Range + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.ALL, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.EXACTLY, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.IN, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.NOT_IN, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.INVALID, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, + EtagPrecondition.INVALID_ALL_PLUS_OTHER, null, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.EQ, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.LT, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.GT, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.MULTI_IN, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.MULTI_IN_REV, Boolean.FALSE, SC_200 }); + parameterSets.add(new Object[] { useStrongEtag, Task.GET_INDEX_HTML, null, null, null, null, null, + DatePrecondition.INVALID, Boolean.FALSE, SC_200 }); + + // PUT tests + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_EXIST_TXT, null, null, null, null, null, null, + Boolean.FALSE, SC_204 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_EXIST_TXT, EtagPrecondition.ALL, null, null, null, + null, null, Boolean.FALSE, SC_204 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_EXIST_TXT, EtagPrecondition.EXACTLY, null, null, + null, null, null, Boolean.FALSE, useStrongEtag.booleanValue() ? SC_204 : SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_EXIST_TXT, EtagPrecondition.IN, null, null, null, + null, null, Boolean.FALSE, useStrongEtag.booleanValue() ? SC_204 : SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_EXIST_TXT, EtagPrecondition.NOT_IN, null, null, + null, null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_EXIST_TXT, EtagPrecondition.INVALID, null, null, + null, null, null, Boolean.FALSE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_EXIST_TXT, EtagPrecondition.INVALID_ALL_PLUS_OTHER, + null, null, null, null, null, Boolean.FALSE, SC_400 }); + + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_NEW_TXT, null, null, null, null, null, null, + Boolean.FALSE, SC_201 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_NEW_TXT, EtagPrecondition.ALL, null, null, null, + null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_NEW_TXT, EtagPrecondition.IN, null, null, null, + null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_NEW_TXT, EtagPrecondition.NOT_IN, null, null, null, + null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_NEW_TXT, EtagPrecondition.INVALID, null, null, + null, null, null, Boolean.FALSE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.PUT_NEW_TXT, EtagPrecondition.INVALID_ALL_PLUS_OTHER, + null, null, null, null, null, Boolean.FALSE, SC_400 }); + + // DELETE TESTS + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_EXIST_TXT, null, null, null, null, null, null, + Boolean.FALSE, SC_204 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_EXIST_TXT, EtagPrecondition.ALL, null, null, + null, null, null, Boolean.FALSE, SC_204 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_EXIST_TXT, EtagPrecondition.EXACTLY, null, null, + null, null, null, Boolean.FALSE, useStrongEtag.booleanValue() ? SC_204 : SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_EXIST_TXT, EtagPrecondition.IN, null, null, + null, null, null, Boolean.FALSE, useStrongEtag.booleanValue() ? SC_204 : SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_EXIST_TXT, EtagPrecondition.NOT_IN, null, null, + null, null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_EXIST_TXT, EtagPrecondition.INVALID, null, null, + null, null, null, Boolean.FALSE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_EXIST_TXT, + EtagPrecondition.INVALID_ALL_PLUS_OTHER, null, null, null, null, null, Boolean.FALSE, SC_400 }); + + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_NOT_EXIST_TXT, null, null, null, null, null, + null, Boolean.FALSE, SC_404 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_NOT_EXIST_TXT, EtagPrecondition.ALL, null, null, + null, null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_NOT_EXIST_TXT, EtagPrecondition.IN, null, null, + null, null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_NOT_EXIST_TXT, EtagPrecondition.NOT_IN, null, + null, null, null, null, Boolean.FALSE, SC_412 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_NOT_EXIST_TXT, EtagPrecondition.INVALID, null, + null, null, null, null, Boolean.FALSE, SC_400 }); + parameterSets.add(new Object[] { useStrongEtag, Task.DELETE_NOT_EXIST_TXT, + EtagPrecondition.INVALID_ALL_PLUS_OTHER, null, null, null, null, null, Boolean.FALSE, SC_400 }); + } + + return parameterSets; + } + + + private static Integer SC_200 = Integer.valueOf(HttpServletResponse.SC_OK); + private static Integer SC_201 = Integer.valueOf(HttpServletResponse.SC_CREATED); + private static Integer SC_204 = Integer.valueOf(HttpServletResponse.SC_NO_CONTENT); + private static Integer SC_206 = Integer.valueOf(HttpServletResponse.SC_PARTIAL_CONTENT); + private static Integer SC_304 = Integer.valueOf(HttpServletResponse.SC_NOT_MODIFIED); + private static Integer SC_400 = Integer.valueOf(HttpServletResponse.SC_BAD_REQUEST); + private static Integer SC_404 = Integer.valueOf(HttpServletResponse.SC_NOT_FOUND); + private static Integer SC_412 = Integer.valueOf(HttpServletResponse.SC_PRECONDITION_FAILED); + + + private enum HTTP_METHOD { + GET, + PUT, + DELETE, + POST, + HEAD + } + + + private enum Task { + HEAD_INDEX_HTML(HTTP_METHOD.HEAD, "/index.html"), + HEAD_404_HTML(HTTP_METHOD.HEAD, "/sc_404.html"), + + GET_INDEX_HTML(HTTP_METHOD.GET, "/index.html"), + GET_404_HTML(HTTP_METHOD.GET, "/sc_404.html"), + + POST_INDEX_HTML(HTTP_METHOD.POST, "/index.html"), + POST_404_HTML(HTTP_METHOD.POST, "/sc_404.html"), + + PUT_EXIST_TXT(HTTP_METHOD.PUT, "/put_exist.txt"), + PUT_NEW_TXT(HTTP_METHOD.PUT, "/put_new.txt"), + + DELETE_EXIST_TXT(HTTP_METHOD.DELETE, "/delete_exist.txt"), + DELETE_NOT_EXIST_TXT(HTTP_METHOD.DELETE, "/delete_404.txt"); + + HTTP_METHOD m; + String uri; + + Task(HTTP_METHOD m, String uri) { + this.m = m; + this.uri = uri; + } + + @Override + public String toString() { + return m.name() + " " + uri; + } + } + + + private enum EtagPrecondition { + EXACTLY, + IN, + ALL, + NOT_IN, + INVALID, + INVALID_ALL_PLUS_OTHER + } + + + private enum DatePrecondition { + /** + * Condition header value of http date is equivalent to actual resource lastModified date + */ + EQ, + /** + * Condition header value of http date is greater(later) than actual resource lastModified date + */ + GT, + /** + * Condition header value of http date is less(earlier) than actual resource lastModified date + */ + LT, + MULTI_IN, + MULTI_IN_REV, + /** + * not a valid HTTP-date + */ + INVALID, + /** + * None. + */ + NONE; + } + + + protected void genETagPrecondition(String strongETag, String weakETag, EtagPrecondition condition, + String headerName, Map> requestHeaders) { + if (condition == null) { + return; + } + List headerValues = new ArrayList<>(); + switch (condition) { + case ALL: + headerValues.add("*"); + break; + case EXACTLY: + if (strongETag != null) { + headerValues.add(strongETag); + } else { + // Should not happen + throw new IllegalArgumentException("strong etag not found!"); + } + break; + case IN: + headerValues.add("\"1a2b3c4d\""); + headerValues.add((weakETag != null ? weakETag + "," : "") + + (strongETag != null ? strongETag + "," : "") + "W/\"*\""); + headerValues.add("\"abcdefg\""); + break; + case NOT_IN: + headerValues.add("\"1a2b3c4d\""); + if (weakETag != null && weakETag.length() > 8) { + headerValues.add(weakETag.substring(0, 3) + "XXXXX" + weakETag.substring(8)); + } + if (strongETag != null && strongETag.length() > 6) { + headerValues.add(strongETag.substring(0, 1) + "XXXXX" + strongETag.substring(6)); + } + headerValues.add("\"abcdefg\""); + break; + case INVALID: + headerValues.add("invalid-no-quotes"); + break; + case INVALID_ALL_PLUS_OTHER: + headerValues.add("*"); + headerValues.add("W/\"1abcd\""); + break; + } + if (!headerValues.isEmpty()) { + requestHeaders.put(headerName, headerValues); + } + } + + + protected void genDatePrecondition(long lastModifiedTimestamp, DatePrecondition condition, String headerName, + Map> requestHeaders) { + if (condition == null || lastModifiedTimestamp <= 0) { + return; + } + List headerValues = new ArrayList<>(); + switch (condition) { + case EQ: + headerValues.add(FastHttpDateFormat.formatDate(lastModifiedTimestamp)); + break; + case GT: + headerValues.add(FastHttpDateFormat.formatDate(lastModifiedTimestamp + 30000L)); + break; + case LT: + headerValues.add(FastHttpDateFormat.formatDate(lastModifiedTimestamp - 30000L)); + break; + case MULTI_IN: + headerValues.add(FastHttpDateFormat.formatDate(lastModifiedTimestamp - 30000L)); + headerValues.add(FastHttpDateFormat.formatDate(lastModifiedTimestamp)); + headerValues.add(FastHttpDateFormat.formatDate(lastModifiedTimestamp + 30000L)); + break; + case MULTI_IN_REV: + headerValues.add(FastHttpDateFormat.formatDate(lastModifiedTimestamp + 30000L)); + headerValues.add(FastHttpDateFormat.formatDate(lastModifiedTimestamp)); + headerValues.add(FastHttpDateFormat.formatDate(lastModifiedTimestamp - 30000L)); + break; + case INVALID: + headerValues.add("2024.12.09 GMT"); + break; + case NONE: + // NO-OP + break; + } + if (!headerValues.isEmpty()) { + requestHeaders.put(headerName, headerValues); + } + } + + protected void addPreconditionHeaders(Map> requestHeaders, String resourceETag, + long lastModified) { + + String weakETag = resourceETag; + String strongETag = resourceETag; + if (resourceETag != null) { + if (resourceETag.startsWith("W/")) { + strongETag = resourceETag.substring(2); + } else { + weakETag = "W/" + resourceETag; + } + } + + genETagPrecondition(strongETag, weakETag, ifMatchPrecondition, "If-Match", requestHeaders); + genDatePrecondition(lastModified, ifUnmodifiedSincePrecondition, "If-Unmodified-Since", requestHeaders); + genETagPrecondition(strongETag, weakETag, ifNoneMatchPrecondition, "If-None-Match", requestHeaders); + genDatePrecondition(lastModified, ifModifiedSincePrecondition, "If-Modified-Since", requestHeaders); + genETagPrecondition(strongETag, weakETag, ifRangeEtagPrecondition, "If-Range", requestHeaders); + genDatePrecondition(lastModified, ifRangeDatePrecondition, "If-Range", requestHeaders); + } + + private File tempDocBase = null; + + @Override + public void setUp() throws Exception { + super.setUp(); + tempDocBase = Files.createTempDirectory(getTemporaryDirectory().toPath(), "conditional").toFile(); + long lastModified = FastHttpDateFormat.parseDate("Fri, 06 Dec 2024 00:00:00 GMT"); + Files.write(Path.of(tempDocBase.getAbsolutePath(), "index.html"), "Index".getBytes(), + StandardOpenOption.CREATE); + Path.of(tempDocBase.getAbsolutePath(), "index.html").toFile().setLastModified(lastModified); + + if (task.m.equals(HTTP_METHOD.PUT)) { + Files.write(Path.of(tempDocBase.getAbsolutePath(), "put_exist.txt"), "put_exist_v0".getBytes(), + StandardOpenOption.CREATE); + Path.of(tempDocBase.getAbsolutePath(), "put_exist.txt").toFile().setLastModified(lastModified); + } + + if (task.m.equals(HTTP_METHOD.DELETE)) { + Files.write(Path.of(tempDocBase.getAbsolutePath(), "delete_exist.txt"), "delete_exist_v0".getBytes(), + StandardOpenOption.CREATE); + Path.of(tempDocBase.getAbsolutePath(), "delete_exist.txt").toFile().setLastModified(lastModified); + } + } + + @Test + public void testPreconditions() throws Exception { + Tomcat tomcat = getTomcatInstance(); + Context ctxt = tomcat.addContext("", tempDocBase.getAbsolutePath()); + + Wrapper w = Tomcat.addServlet(ctxt, "default", DefaultServlet.class.getName()); + w.addInitParameter("readonly", "false"); + w.addInitParameter("allowPartialPut", "true"); + w.addInitParameter("useStrongETags", Boolean.toString(useStrongETags)); + ctxt.addServletMappingDecoded("/", "default"); + + tomcat.start(); + + Assert.assertNotNull(task); + + Map> requestHeaders = new HashMap<>(); + Map> responseHeaders = new HashMap<>(); + String etag = null; + long lastModified = -1; + String uri = "http://localhost:" + getPort() + task.uri; + + // Try head to receives etag and lastModified Date + int sc = headUrl(uri, new ByteChunk(), responseHeaders); + if (sc == 200) { + etag = getSingleHeader("ETag", responseHeaders); + String dt = getSingleHeader("Last-Modified", responseHeaders); + if (dt != null && dt.length() > 0) { + lastModified = FastHttpDateFormat.parseDate(dt); + } + } + + addPreconditionHeaders(requestHeaders, etag, lastModified); + + responseHeaders.clear(); + sc = 0; + SimpleHttpClient client = null; + client = new SimpleHttpClient() { + + @Override + public boolean isResponseBodyOK() { + return true; + } + }; + client.setPort(getPort()); + StringBuffer curl = new StringBuffer(); + curl.append(task.m.name() + " " + task.uri + " HTTP/1.1" + SimpleHttpClient.CRLF + "Host: localhost" + + SimpleHttpClient.CRLF + "Connection: Close" + SimpleHttpClient.CRLF); + + for (Entry> e : requestHeaders.entrySet()) { + for (String v : e.getValue()) { + curl.append(e.getKey() + ": " + v + SimpleHttpClient.CRLF); + } + } + if (addRangeHeader) { + curl.append("Range: bytes=0-10" + SimpleHttpClient.CRLF); + } + curl.append("Content-Length: 6" + SimpleHttpClient.CRLF); + curl.append(SimpleHttpClient.CRLF); + + curl.append("PUT_v2"); + client.setRequest(new String[] { curl.toString() }); + client.connect(); + client.processRequest(); + for (String e : client.getResponseHeaders()) { + Assert.assertTrue("Separator ':' expected and not the last char of response header field `" + e + "`", + e.contains(":") && e.indexOf(':') < e.length() - 1); + String name = e.substring(0, e.indexOf(':')); + String value = e.substring(e.indexOf(':') + 1); + responseHeaders.computeIfAbsent(name, k -> new ArrayList()).add(value); + } + sc = client.getStatusCode(); + boolean test = scExpected.intValue() == sc; + Assert.assertTrue("Failure - sc expected:" + String.valueOf(scExpected) + ", sc actual:" + String.valueOf(sc) + ", task:" + + task + ", \ntarget resource:(" + etag + "," + FastHttpDateFormat.formatDate(lastModified) + + "), \nreq headers: " + requestHeaders.toString() + ", \nresp headers: " + responseHeaders.toString(), test); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section14.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section14.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section14.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestDefaultServletRfc9110Section14.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,6 +30,7 @@ import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.http.Method; public class TestDefaultServletRfc9110Section14 extends TomcatBaseTest { @@ -62,7 +63,7 @@ Assert.assertTrue("Range requests is turn on, header `Accept-Ranges: bytes` is expected", responseHeaders.containsKey("Accept-Ranges") && responseHeaders.get("Accept-Ranges").contains("bytes")); - rc = methodUrl(path, responseBody, DEFAULT_CLIENT_TIMEOUT_MS, requestHeaders, responseHeaders, "HEAD"); + rc = methodUrl(path, responseBody, DEFAULT_CLIENT_TIMEOUT_MS, requestHeaders, responseHeaders, Method.HEAD); Assert.assertEquals("Range requests is turn on, SC_OK of HEAD is expected", HttpServletResponse.SC_OK, rc); Assert.assertTrue("Range requests is turn on, header `Accept-Ranges: bytes` is expected", responseHeaders.containsKey("Accept-Ranges") && responseHeaders.get("Accept-Ranges").contains("bytes")); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavPropertyStore.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,6 +34,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -44,6 +45,7 @@ import org.apache.catalina.servlets.WebdavServlet.ProppatchOperation; import org.apache.catalina.startup.LoggingBaseTest; import org.apache.catalina.util.XMLWriter; +import org.apache.tomcat.util.compat.JreCompat; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.InputSource; @@ -70,11 +72,12 @@ ""; public static final String SIMPLE_SCHEMA = - "create table properties (\n" + - " path varchar(256) not null,\n" + - " namespace varchar(64) not null,\n" + - " name varchar(64) not null,\n" + - " node varchar(1024) not null" + + "CREATE TABLE webdavproperties (\n" + + " path VARCHAR(1024) NOT NULL,\n" + + " namespace VARCHAR(64) NOT NULL,\n" + + " name VARCHAR(64) NOT NULL,\n" + + " node VARCHAR(2048) NOT NULL,\n" + + " PRIMARY KEY (path, namespace, name)\n" + ")"; public static class CustomDataSourcePropertyStore extends DataSourcePropertyStore { @@ -162,6 +165,10 @@ @Test public void testStore() throws Exception { + if (storeName.contains("DataSource")) { + Assume.assumeTrue(JreCompat.isJre16Available()); + } + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); documentBuilderFactory.setExpandEntityReferences(false); @@ -176,7 +183,9 @@ PropertyStore propertyStore = (PropertyStore) Class.forName(storeName).getDeclaredConstructor().newInstance(); if (propertyStore instanceof CustomDataSourcePropertyStore) { ((CustomDataSourcePropertyStore) propertyStore).setDataSource(new DerbyDataSource()); + ((CustomDataSourcePropertyStore) propertyStore).setTableName("webdavproperties"); } + propertyStore.init(); // Add properties ArrayList operations = new ArrayList<>(); @@ -241,5 +250,7 @@ Assert.assertFalse(propertyStore.propfind("/other/path2", node1, false, xmlWriter9)); Assert.assertTrue(xmlWriter9.toString().isEmpty()); + propertyStore.destroy(); + } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavServlet.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServlet.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,11 +18,13 @@ import java.io.File; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; import javax.xml.parsers.SAXParserFactory; @@ -31,6 +33,7 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; import org.apache.catalina.servlets.WebdavServlet.PropertyStore; @@ -46,46 +49,85 @@ public class TestWebdavServlet extends TomcatBaseTest { - /* - * Test attempting to access special paths (WEB-INF/META-INF) using WebdavServlet - */ @Test - public void testGetSpecials() throws Exception { + public void testGetSpecial_allowSpecialPaths_rootdavcontext() throws Exception { + testGetSpecials(true, false); + } + + @Test + public void testGetSpecial_allowSpecialPaths_nonrootdavcontext() throws Exception { + testGetSpecials(true, true); + } + + @Test + public void testGetSpecial_disallowSpecialPaths_rootdavcontext() throws Exception { + testGetSpecials(false, false); + } + + @Test + public void testGetSpecial_disallowSpecialPaths_nonrootdavcontext() throws Exception { + testGetSpecials(false, true); + } + + /* Test attempting to access special paths (WEB-INF/META-INF) using WebdavServlet */ + private void testGetSpecials(boolean allowSpecialPaths, boolean useSubpathWebdav) throws Exception { Tomcat tomcat = getTomcatInstance(); - String contextPath = "/examples"; + // Create a temp webapp that can be safely written to + File tempWebapp = new File(getTemporaryDirectory(), "webdav-specialpath"+UUID.randomUUID()); + Assert.assertTrue("Failed to mkdirs on "+tempWebapp.getCanonicalPath(),tempWebapp.mkdirs()); + Assert.assertTrue(new File(tempWebapp,"WEB-INF").mkdir()); + Assert.assertTrue(new File(tempWebapp,"META-INF").mkdir()); + try (FileWriter fw = new FileWriter(new File(tempWebapp, "WEB-INF-desc.xml"))) { + fw.write("This is not a special file"); + } + try (FileWriter fw = new FileWriter(new File(tempWebapp, "WEB-INF/web.xml"))) { + fw.write("..."); + } + try (FileWriter fw = new FileWriter(new File(tempWebapp, "META-INF/context.xml"))) { + fw.write("..."); + } - File appDir = new File(getBuildDirectory(), "webapps" + contextPath); - // app dir is relative to server home - Context ctx = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); + Context ctx = tomcat.addContext("", tempWebapp.getAbsolutePath()); + Wrapper webdavServlet = Tomcat.addServlet(ctx, "webdav", new WebdavServlet()); - Tomcat.addServlet(ctx, "webdav", new WebdavServlet()); - ctx.addServletMappingDecoded("/*", "webdav"); + webdavServlet.addInitParameter("listings", "true"); + webdavServlet.addInitParameter("allowSpecialPaths", allowSpecialPaths ? "true" : "false"); + + String contextPath=""; + if (useSubpathWebdav) { + ctx.addServletMappingDecoded("/webdav/*", "webdav"); + contextPath = "/webdav"; + } else { + ctx.addServletMappingDecoded("/*", "webdav"); + } tomcat.start(); final ByteChunk res = new ByteChunk(); - int rc =getUrl("http://localhost:" + getPort() + contextPath + - "/WEB-INF/web.xml", res, null); - Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); + int rc = 0; - rc =getUrl("http://localhost:" + getPort() + contextPath + - "/WEB-INF/doesntexistanywhere", res, null); - Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); + // Notice: Special paths /WEB-INF and /META-INF are protected by StandardContextValve. + // allowSpecialPaths works only when webdav re-mount to a non-root path. + rc = getUrl("http://localhost:" + getPort() + contextPath + "/WEB-INF/web.xml", res, null); + Assert.assertEquals( + useSubpathWebdav && allowSpecialPaths ? HttpServletResponse.SC_OK : HttpServletResponse.SC_NOT_FOUND, + rc); - rc =getUrl("http://localhost:" + getPort() + contextPath + - "/WEB-INF/", res, null); + rc = getUrl("http://localhost:" + getPort() + contextPath + "/WEB-INF/doesntexistanywhere", res, null); Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); - rc =getUrl("http://localhost:" + getPort() + contextPath + - "/META-INF/MANIFEST.MF", res, null); - Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); + rc = getUrl("http://localhost:" + getPort() + contextPath + "/META-INF/context.xml", res, null); + Assert.assertEquals( + useSubpathWebdav && allowSpecialPaths ? HttpServletResponse.SC_OK : HttpServletResponse.SC_NOT_FOUND, + rc); - rc =getUrl("http://localhost:" + getPort() + contextPath + - "/META-INF/doesntexistanywhere", res, null); + rc = getUrl("http://localhost:" + getPort() + contextPath + "/META-INF/doesntexistanywhere", res, null); Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, rc); + rc = getUrl("http://localhost:" + getPort() + contextPath + "/WEB-INF-desc.xml", res, null); + Assert.assertEquals(HttpServletResponse.SC_OK, rc); } /* @@ -180,9 +222,13 @@ Client client = new Client(); client.setPort(getPort()); - client.setRequest(new String[] { "PROPFIND /bug66609/ HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}); + // @formatter:off + client.setRequest(new String[] { + "PROPFIND /bug66609/ HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.sendRequest(); @@ -195,6 +241,7 @@ private static final String CONTENT = "FOOBAR"; + // @formatter:off private static final String LOCK_BODY = "\n" + "\n" + @@ -242,6 +289,7 @@ " \n" + " \n" + ""; + // @formatter:on @Test public void testBasicProperties() throws Exception { @@ -263,61 +311,90 @@ client.setPort(getPort()); // Create a test file - client.setRequest(new String[] { "PUT /file1.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); // Add lock to check for lock discovery - client.setRequest(new String[] { "LOCK /file2.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /file2.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("urn:uuid:")); - client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "PROPFIND / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("urn:uuid:")); - client.setRequest(new String[] { "PROPPATCH /file1.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + PROPPATCH_PROPNAME.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + PROPPATCH_PROPNAME }); + // @formatter:off + client.setRequest(new String[] { + "PROPPATCH /file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + PROPPATCH_PROPNAME.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + PROPPATCH_PROPNAME + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("")); - client.setRequest(new String[] { "PROPFIND /file1.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + PROPFIND_PROP.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + PROPFIND_PROP }); + // @formatter:off + client.setRequest(new String[] { + "PROPFIND /file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + PROPFIND_PROP.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + PROPFIND_PROP + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); @@ -325,20 +402,29 @@ Assert.assertFalse(client.getResponseBody().contains("")); Assert.assertTrue(client.getResponseBody().contains("")); - client.setRequest(new String[] { "MOVE /file1.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Destination: /file3.txt" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "MOVE /file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Destination: /file3.txt" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "PROPFIND /file3.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + PROPFIND_PROP.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + PROPFIND_PROP }); + // @formatter:off + client.setRequest(new String[] { + "PROPFIND /file3.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + PROPFIND_PROP.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + PROPFIND_PROP + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); @@ -372,20 +458,32 @@ client.setPort(getPort()); // Create a few files - client.setRequest(new String[] { "PUT /file1.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "PUT /file2.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 12" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT + CONTENT }); + // @formatter:off + + client.setRequest(new String[] { + "PUT /file2.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 12" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); @@ -394,48 +492,71 @@ for (int i = 0; i < 100; i++) { sb.append(CONTENT); } - client.setRequest(new String[] { "PUT /file12.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + String.valueOf(sb.length()) + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + sb.toString() }); + // @formatter:off + client.setRequest(new String[] { + "PUT /file12.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + String.valueOf(sb.length()) + CRLF + + "Connection: Close" + CRLF + + CRLF + + sb.toString() + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "MKCOL /myfolder HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "MKCOL /myfolder HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "PUT /myfolder/file3.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/file3.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); // Verify that listing the file works - client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "PROPFIND / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("12<")); // Lock /myfolder - client.setRequest(new String[] { "LOCK /myfolder HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -449,61 +570,88 @@ Assert.assertNotNull(lockToken); // Try to add /myfolder/file4.txt to myfolder without lock token - client.setRequest(new String[] { "PUT /myfolder/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_LOCKED, client.getStatusCode()); // Same but provide the lock token - client.setRequest(new String[] { "PUT /myfolder/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "If: (" + lockToken + ")" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] {"PUT /myfolder/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "If: (" + lockToken + ")" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); // Add lock for /myfolder/file5.txt - client.setRequest(new String[] { "LOCK /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_LOCKED, client.getStatusCode()); - client.setRequest(new String[] { "UNLOCK /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Lock-Token: " + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "UNLOCK /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Lock-Token: " + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CONFLICT, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/myfolder")); // Unlock /myfolder - client.setRequest(new String[] { "UNLOCK /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Lock-Token: " + lockToken + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "UNLOCK /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Lock-Token: " + lockToken + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); - client.setRequest(new String[] { "LOCK /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); @@ -516,63 +664,93 @@ } Assert.assertNotNull(lockTokenFile); - client.setRequest(new String[] { "LOCK /myfolder HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("/myfolder/file5.txt")); Assert.assertTrue(client.getResponseBody().contains("HTTP/1.1 423")); - client.setRequest(new String[] { "PUT /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + + client.setRequest(new String[] { + "PUT /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_LOCKED, client.getStatusCode()); // Same but with lock token and lock null - client.setRequest(new String[] { "PUT /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "If: (" + lockTokenFile + ")" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "If: (" + lockTokenFile + ")" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); // Verify that this also removes the lock by doing another PUT without the token - client.setRequest(new String[] { "DELETE /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "If: (" + lockTokenFile + ")" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "DELETE /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "If: (" + lockTokenFile + ")" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); - client.setRequest(new String[] { "PUT /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); // Lock /myfolder again - client.setRequest(new String[] { "LOCK /myfolder HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Timeout: Second-20" + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Timeout: Second-20" + CRLF + + "Content-Length: " + LOCK_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -586,40 +764,57 @@ Assert.assertNotNull(lockToken); // Copy /myfolder/file5.txt to /myfolder/file6.txt without lock (should not work) - client.setRequest(new String[] { "COPY /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Destination: http://localhost:" + getPort() + "/myfolder/file6.txt" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "COPY /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Destination: http://localhost:" + getPort() + "/myfolder/file6.txt" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_LOCKED, client.getStatusCode()); - client.setRequest(new String[] { "COPY /myfolder HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Destination: /myfolder2" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + + client.setRequest(new String[] { + "COPY /myfolder HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Destination: /myfolder2" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_CREATED, client.getStatusCode()); // Delete /myfolder/file4.txt - client.setRequest(new String[] { "DELETE /myfolder/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "If: (" + lockToken + ")" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "DELETE /myfolder/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "If: (" + lockToken + ")" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); // Copy /myfolder/file5.txt to /file7.txt without lock (should work) - client.setRequest(new String[] { "COPY /myfolder/file5.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Destination: http://localhost:" + getPort() + "/file7.txt" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "COPY /myfolder/file5.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Destination: http://localhost:" + getPort() + "/file7.txt" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); @@ -628,71 +823,195 @@ for (int i = 0; i < 3000; i++) { sb2.append(CONTENT); } - client.setRequest(new String[] { "PUT /file6.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + String.valueOf(sb2.length()) + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + sb2.toString() }); + // @formatter:off + client.setRequest(new String[] { + "PUT /file6.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + String.valueOf(sb2.length()) + CRLF + + "Connection: Close" + CRLF + + CRLF + + sb2.toString() + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); // Verify that everything created is there - client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "PROPFIND / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); Assert.assertFalse(client.getResponseBody().contains("/myfolder/file4.txt")); Assert.assertTrue(client.getResponseBody().contains("/file7.txt")); Assert.assertTrue(client.getResponseBody().contains("Second-")); - Assert.assertTrue(client.getResponseBody().contains("d1dc021f456864e84f9a37b7a6f51c51301128a0")); - Assert.assertTrue(client.getResponseBody().contains("f3390fe2e5546dac3d1968970df1a222a3a39c00")); + // SHA-256 hash for "FOOBAR...FOOBAR" (repeats 3000 times) + Assert.assertTrue(client.getResponseBody().contains( + "bb94e8d310800b24310036b168aa5a946e27f9572b3d99f956f3a3ed2e7d3045")); + // SHA-256 hash for "FOOBAR" + Assert.assertTrue(client.getResponseBody().contains( + "24c422e681f1c1bd08286c7aaf5d23a5f088dcdb0b219806b3a9e579244f00c5")); String timeoutValue = client.getResponseBody().substring(client.getResponseBody().indexOf("Second-")); timeoutValue = timeoutValue.substring("Second-".length(), timeoutValue.indexOf('<')); Assert.assertTrue(Integer.valueOf(timeoutValue).intValue() <= 20); // Unlock /myfolder again - client.setRequest(new String[] { "UNLOCK /myfolder/ HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Lock-Token: " + lockToken + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "UNLOCK /myfolder/ HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Lock-Token: " + lockToken + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); // Delete /myfolder - client.setRequest(new String[] { "DELETE /myfolder HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "DELETE /myfolder HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); - client.setRequest(new String[] { "DELETE /myfolder2 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "DELETE /myfolder2 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); - client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); Assert.assertFalse(client.getResponseBody().contains("/myfolder")); validateXml(client.getResponseBody()); - } + + @Test + public void testCopyOutsideSubpath() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + // Create a temp webapp that can be safely written to + File tempWebapp = new File(getTemporaryDirectory(), "webdav-subpath"); + File subPath = new File(tempWebapp, "aaa"); + Assert.assertTrue(subPath.mkdirs()); + + Context ctxt = tomcat.addContext("", tempWebapp.getAbsolutePath()); + Wrapper webdavServlet = Tomcat.addServlet(ctxt, "webdav", new WebdavServlet()); + webdavServlet.addInitParameter("listings", "true"); + webdavServlet.addInitParameter("readonly", "false"); + webdavServlet.addInitParameter("serveSubpathOnly", "true"); + ctxt.addServletMappingDecoded("/aaa/*", "webdav"); + tomcat.start(); + + ctxt.getResources().setCacheMaxSize(10); + ctxt.getResources().setCacheObjectMaxSize(1); + + Client client = new Client(); + client.setPort(getPort()); + + // Create a file + // @formatter:off + client.setRequest(new String[] { + "PUT /aaa/file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); + + // Copy file1.txt to file2.txt + // @formatter:off + client.setRequest(new String[] { + "COPY /aaa/file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Destination: http://localhost:" + getPort() + "/aaa/file2.txt" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); + + // Move file2.txt to file3.txt + // @formatter:off + client.setRequest(new String[] { + "MOVE /aaa/file2.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Destination: http://localhost:" + getPort() + "/aaa/file3.txt" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); + + // Copy file1.txt outside sub-path + // @formatter:off + client.setRequest(new String[] { + "COPY /aaa/file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Destination: http://localhost:" + getPort() + "/file1.txt" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, client.getStatusCode()); + + // Move file1.txt outside sub-path + // @formatter:off + client.setRequest(new String[] { + "MOVE /aaa/file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Destination: http://localhost:" + getPort() + "/file1.txt" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(); + client.processRequest(true); + Assert.assertEquals(HttpServletResponse.SC_FORBIDDEN, client.getStatusCode()); +} + + @Test public void testSharedLocks() throws Exception { Tomcat tomcat = getTomcatInstance(); @@ -712,79 +1031,122 @@ client.setPort(getPort()); // Create a few folders and files - client.setRequest(new String[] { "MKCOL /myfolder HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "MKCOL /myfolder HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "MKCOL /myfolder/myfolder2/ HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + + client.setRequest(new String[] { + "MKCOL /myfolder/myfolder2/ HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "MKCOL /myfolder/myfolder3 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "MKCOL /myfolder/myfolder3 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "MKCOL /myfolder/myfolder2/myfolder4 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "MKCOL /myfolder/myfolder2/myfolder4 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "MKCOL /myfolder/myfolder2/myfolder4/myfolder5 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "MKCOL /myfolder/myfolder2/myfolder4/myfolder5 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "PUT /file1.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + + client.setRequest(new String[] { + "PUT /file1.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "PUT /myfolder/myfolder3/file2.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + + client.setRequest(new String[] { + "PUT /myfolder/myfolder3/file2.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "PUT /myfolder/myfolder2/myfolder4/file3.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/myfolder2/myfolder4/file3.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); // Lock /myfolder - client.setRequest(new String[] { "LOCK /myfolder HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_SHARED_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_SHARED_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_SHARED_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_SHARED_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -797,33 +1159,47 @@ } Assert.assertNotNull(lockToken); - client.setRequest(new String[] { "LOCK /myfolder/myfolder2/myfolder4/myfolder5 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "If: (" + lockToken + ")" + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder/myfolder2/myfolder4/myfolder5 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "If: (" + lockToken + ")" + CRLF + + "Content-Length: " + LOCK_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); // This should conflict, submitting a token does not help Assert.assertEquals(WebdavStatus.SC_LOCKED, client.getStatusCode()); // Lock refresh /myfolder - client.setRequest(new String[] { "LOCK /myfolder HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Timeout: Infinite" + SimpleHttpClient.CRLF + - "If: (" + lockToken + ")" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Timeout: Infinite" + CRLF + + "If: (" + lockToken + ")" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); - client.setRequest(new String[] { "LOCK /myfolder/myfolder2/myfolder4 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_SHARED_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_SHARED_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder/myfolder2/myfolder4 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_SHARED_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_SHARED_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -837,11 +1213,16 @@ Assert.assertNotNull(lockToken2); // Take a second different lock on the same collection - client.setRequest(new String[] { "LOCK /myfolder/myfolder2/myfolder4 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_SHARED_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_SHARED_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder/myfolder2/myfolder4 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_SHARED_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_SHARED_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -855,10 +1236,14 @@ Assert.assertNotNull(lockToken3); validateXml(client.getResponseBody()); - client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "PROPFIND / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); @@ -869,101 +1254,149 @@ Assert.assertTrue(Integer.valueOf(timeoutValue).intValue() > 100000); validateXml(client.getResponseBody()); - client.setRequest(new String[] { "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_LOCKED, client.getStatusCode()); - client.setRequest(new String[] { "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "If: (" + lockToken + ")" + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "If: (" + lockToken + ")" + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_PRECONDITION_FAILED, client.getStatusCode()); - client.setRequest(new String[] { "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "If: (" + lockToken + ")" + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "If: (" + lockToken + ")" + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CREATED, client.getStatusCode()); - client.setRequest(new String[] { "UNLOCK /myfolder/myfolder3 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Lock-Token: " + lockToken2 + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "UNLOCK /myfolder/myfolder3 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Lock-Token: " + lockToken2 + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_CONFLICT, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains("")); - client.setRequest(new String[] { "UNLOCK /myfolder/myfolder2/myfolder4/myfolder5 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Lock-Token: " + lockToken2 + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "UNLOCK /myfolder/myfolder2/myfolder4/myfolder5 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Lock-Token: " + lockToken2 + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); - client.setRequest(new String[] { "UNLOCK /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Lock-Token: " + lockToken3 + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "UNLOCK /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Lock-Token: " + lockToken3 + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); - client.setRequest(new String[] { "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + // @formatter:off + + client.setRequest(new String[] { + "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_LOCKED, client.getStatusCode()); - client.setRequest(new String[] { "UNLOCK /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Lock-Token: " + lockToken + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + + client.setRequest(new String[] { + "UNLOCK /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Lock-Token: " + lockToken + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, client.getStatusCode()); - client.setRequest(new String[] { "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: 12" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT + CONTENT}); + // @formatter:off + + client.setRequest(new String[] { + "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 12" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_NO_CONTENT, client.getStatusCode()); - client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "PROPFIND / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); // Verify all the shared locks are cleared Assert.assertFalse(client.getResponseBody().contains("urn:uuid:")); validateXml(client.getResponseBody()); - } @Test @@ -993,11 +1426,16 @@ Client client = new Client(); client.setPort(getPort()); - client.setRequest(new String[] { "LOCK /myfolder/myfolder3 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder/myfolder3 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -1010,11 +1448,16 @@ } Assert.assertNotNull(lockToken); - client.setRequest(new String[] { "LOCK /myfolder/myfolder7 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_SHARED_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_SHARED_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder/myfolder7 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_SHARED_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_SHARED_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -1027,11 +1470,16 @@ } Assert.assertNotNull(lockToken2); - client.setRequest(new String[] { "LOCK /myfolder/myfolder7/myfolder8 HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Content-Length: " + LOCK_SHARED_BODY.length() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + LOCK_SHARED_BODY }); + // @formatter:off + client.setRequest(new String[] { + "LOCK /myfolder/myfolder7/myfolder8 HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: " + LOCK_SHARED_BODY.length() + CRLF + + "Connection: Close" + CRLF + + CRLF + + LOCK_SHARED_BODY + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); @@ -1044,32 +1492,41 @@ } Assert.assertNotNull(lockToken3); - client.setRequest(new String[] { "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + "If: ()" + // Obvious wrong token " (" + lockToken + " " + lockToken2 + " " + lockToken3 + ")" + // lockToken is not there " ( [W/\"4-1729375899470\"])" + // Not locked - " (" + lockToken + ")" + SimpleHttpClient.CRLF + // lockToken is not there - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + " (" + lockToken + ")" + CRLF + // lockToken is not there + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_PRECONDITION_FAILED, client.getStatusCode()); - client.setRequest(new String[] { "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + + // @formatter:off + client.setRequest(new String[] { + "PUT /myfolder/myfolder2/myfolder4/myfolder5/file4.txt HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + "If: ()" + // Obvious wrong token " ( [W/\"4-1729375899470\"])" + // Not locked " (" + lockToken + ")" + // lockToken is not there - " (" + lockToken2 + " " + lockToken3 + ")" + SimpleHttpClient.CRLF + // Correct - "Content-Length: 6" + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + CONTENT }); + " (" + lockToken2 + " " + lockToken3 + ")" + CRLF + // Correct + "Content-Length: 6" + CRLF + + "Connection: Close" + CRLF + + CRLF + + CONTENT + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_CREATED, client.getStatusCode()); - } @Test @@ -1094,16 +1551,19 @@ Client client = new Client(); client.setPort(getPort()); - client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - "Connection: Close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }); + // @formatter:off + client.setRequest(new String[] { + "PROPFIND / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on client.connect(); client.processRequest(true); Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, client.getStatusCode()); Assert.assertTrue(client.getResponseBody().contains(">testvalue")); validateXml(client.getResponseBody()); - } public static class CustomPropertyStore implements PropertyStore { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavServletOptionCollection.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServletOptionCollection.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavServletOptionCollection.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServletOptionCollection.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,6 +26,8 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.apache.tomcat.util.http.Method; + /* * Split into multiple tests as a single test takes so long it impacts the time * of an entire test run. @@ -35,8 +37,8 @@ @Parameters public static Collection inputs() { - String[] methods = new String[] { "GET", "POST", "HEAD", "TRACE", "PUT", "DELETE", - "MKCOL", "LOCK", "UNLOCK", "COPY", "MOVE", "PROPFIND", "PROPPATCH" }; + String[] methods = new String[] { Method.GET, Method.POST, Method.HEAD, Method.TRACE, Method.PUT, Method.DELETE, + Method.MKCOL, Method.LOCK, Method.UNLOCK, Method.COPY, Method.MOVE, Method.PROPFIND, Method.PROPPATCH }; List result = new ArrayList<>(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavServletOptionsFile.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServletOptionsFile.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavServletOptionsFile.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServletOptionsFile.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,6 +26,8 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.apache.tomcat.util.http.Method; + /* * Split into multiple tests as a single test takes so long it impacts the time * of an entire test run. @@ -35,8 +37,8 @@ @Parameters public static Collection inputs() { - String[] methods = new String[] { "GET", "POST", "HEAD", "TRACE", "PUT", "DELETE", - "MKCOL", "LOCK", "UNLOCK", "COPY", "MOVE", "PROPFIND", "PROPPATCH" }; + String[] methods = new String[] { Method.GET, Method.POST, Method.HEAD, Method.TRACE, Method.PUT, Method.DELETE, + Method.MKCOL, Method.LOCK, Method.UNLOCK, Method.COPY, Method.MOVE, Method.PROPFIND, Method.PROPPATCH }; List result = new ArrayList<>(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavServletOptionsUnknown.java tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServletOptionsUnknown.java --- tomcat10-10.1.34/test/org/apache/catalina/servlets/TestWebdavServletOptionsUnknown.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/servlets/TestWebdavServletOptionsUnknown.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,6 +26,8 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; +import org.apache.tomcat.util.http.Method; + /* * Split into multiple tests as a single test takes so long it impacts the time * of an entire test run. @@ -35,8 +37,8 @@ @Parameters public static Collection inputs() { - String[] methods = new String[] { "GET", "POST", "HEAD", "TRACE", "PUT", "DELETE", - "MKCOL", "LOCK", "UNLOCK", "COPY", "MOVE", "PROPFIND", "PROPPATCH" }; + String[] methods = new String[] { Method.GET, Method.POST, Method.HEAD, Method.TRACE, Method.PUT, Method.DELETE, + Method.MKCOL, Method.LOCK, Method.UNLOCK, Method.COPY, Method.MOVE, Method.PROPFIND, Method.PROPPATCH }; List result = new ArrayList<>(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/session/Benchmarks.java tomcat10-10.1.52/test/org/apache/catalina/session/Benchmarks.java --- tomcat10-10.1.34/test/org/apache/catalina/session/Benchmarks.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/session/Benchmarks.java 2026-01-23 19:33:36.000000000 +0000 @@ -353,8 +353,8 @@ throw new IOException("Only read " + read + " bytes"); } } - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/session/FileStoreTest.java tomcat10-10.1.52/test/org/apache/catalina/session/FileStoreTest.java --- tomcat10-10.1.34/test/org/apache/catalina/session/FileStoreTest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/session/FileStoreTest.java 2026-01-23 19:33:36.000000000 +0000 @@ -59,13 +59,13 @@ @Before public void beforeEachTest() throws IOException { fileStore.setDirectory(SESS_TEMPPATH); - if (!dir.mkdir()) { - Assert.fail(); + if (!dir.exists() && !dir.mkdir()) { + Assert.fail(dir.getAbsolutePath()); } - if (!file1.createNewFile()) { + if (!file1.exists() && !file1.createNewFile()) { Assert.fail(); } - if (!file2.createNewFile()) { + if (!file2.exists() && !file2.createNewFile()) { Assert.fail(); } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/session/TestFileStoreConcurrency.java tomcat10-10.1.52/test/org/apache/catalina/session/TestFileStoreConcurrency.java --- tomcat10-10.1.34/test/org/apache/catalina/session/TestFileStoreConcurrency.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/session/TestFileStoreConcurrency.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,188 @@ +/* + * 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.File; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.Manager; +import org.apache.catalina.Session; +import org.apache.catalina.startup.ExpandWar; +import org.apache.tomcat.unittest.TesterContext; + +/* + * Test for https://bz.apache.org/bugzilla/show_bug.cgi?id=69781 + * + * The test currently fails almost instantly on markt's desktop with a 5s run time. + * + * This needs to be run manually. It will not run as part of the standard unit tests as it is named "Tester...". This + * could be changed once the bug has been fixed. + */ +public class TestFileStoreConcurrency { + + private static final int TEST_RUN_TIME_MS = 5000; + + private static final FileStore FILE_STORE = new FileStore(); + private static final String STORE_DIR = "STORE_TMP"; + private static final File STORE_FILE = new File(STORE_DIR); + + private static final int SESSION_COUNT = 10; + private static final Session[] SESSIONS = new Session[SESSION_COUNT]; + + + @BeforeClass + public static void setup() { + Context context = new TesterContext(); + Manager manager = new TesterManager(); + manager.setContext(context); + for (int i = 0; i < SESSION_COUNT; i++) { + SESSIONS[i] = new StandardSession(null); + SESSIONS[i].setManager(manager); + SESSIONS[i].setId(Integer.toString(i)); + } + + FILE_STORE.setDirectory(STORE_FILE.getAbsolutePath()); + FILE_STORE.setManager(manager); + } + + + @AfterClass + public static void cleanUp() { + ExpandWar.delete(STORE_FILE); + } + + + @Test + public void testConcurrency() throws Exception { + SaveTask saveTask = new SaveTask(); + Thread saveThread = new Thread(saveTask); + saveThread.start(); + + LoadTask loadTask = new LoadTask(); + Thread loadThread = new Thread(loadTask); + loadThread.start(); + + RemoveTask removeTask = new RemoveTask(); + Thread removeThread = new Thread(removeTask); + removeThread.start(); + + Thread.sleep(TEST_RUN_TIME_MS); + + saveTask.stop(); + loadTask.stop(); + removeTask.stop(); + + saveThread.join(); + loadThread.join(); + removeThread.join(); + + Assert.assertFalse("Exception during save", saveTask.getFailed()); + Assert.assertFalse("Exception during load", loadTask.getFailed()); + Assert.assertFalse("Exception during remove", removeTask.getFailed()); + + System.out.println("Looped over sessions [" + saveTask.getLoopCount() + "] times calling save()"); + System.out.println("Looped over sessions [" + loadTask.getLoopCount() + "] times calling load()"); + System.out.println("Looped over sessions [" + removeTask.getLoopCount() + "] times calling remove()"); + } + + + private static final class SaveTask extends TaskBase { + + @Override + protected void doTask(Session session) throws Exception { + FILE_STORE.save(session); + } + + @Override + protected String getTaskName() { + return "save"; + } + } + + + private static final class LoadTask extends TaskBase { + + @Override + protected void doTask(Session session) throws Exception { + FILE_STORE.load(session.getId()); + } + + @Override + protected String getTaskName() { + return "load"; + } + } + + + private static final class RemoveTask extends TaskBase { + + @Override + protected void doTask(Session session) throws Exception { + FILE_STORE.remove(session.getId()); + } + + @Override + protected String getTaskName() { + return "remove"; + } + } + + + private abstract static class TaskBase implements Runnable { + + private volatile boolean stop = false; + private volatile boolean failed = false; + private volatile int loopCount = 0; + + @Override + public void run() { + while (!stop) { + for (Session session : SESSIONS) { + try { + doTask(session); + } catch (Exception e) { + System.out.println("Failed to " + getTaskName() + " session [" + session.getId() + "]"); + e.printStackTrace(System.out); + stop = true; + failed = true; + } + } + loopCount++; + } + } + + public void stop() { + stop = true; + } + + public boolean getFailed() { + return failed; + } + + public int getLoopCount() { + return loopCount; + } + + protected abstract void doTask(Session session) throws Exception; + protected abstract String getTaskName(); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/session/TestPersistentManager.java tomcat10-10.1.52/test/org/apache/catalina/session/TestPersistentManager.java --- tomcat10-10.1.34/test/org/apache/catalina/session/TestPersistentManager.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/session/TestPersistentManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -108,6 +108,7 @@ context.setParent(host); Connector connector = EasyMock.createNiceMock(Connector.class); + EasyMock.replay(connector); Request req = new Request(connector) { @Override public Context getContext() { @@ -116,7 +117,6 @@ }; req.setRequestedSessionId("invalidSession"); HttpServletRequest request = new RequestFacade(req); - EasyMock.replay(connector); requestCachingSessionListener.request = request; manager.setContext(context); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/session/TestPersistentManagerDataSourceStore.java tomcat10-10.1.52/test/org/apache/catalina/session/TestPersistentManagerDataSourceStore.java --- tomcat10-10.1.34/test/org/apache/catalina/session/TestPersistentManagerDataSourceStore.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/session/TestPersistentManagerDataSourceStore.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,6 +29,7 @@ import jakarta.servlet.http.HttpSession; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; import org.apache.catalina.Context; @@ -36,6 +37,7 @@ import org.apache.catalina.Session; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.compat.JreCompat; public class TestPersistentManagerDataSourceStore extends TomcatBaseTest { @@ -79,6 +81,8 @@ @Test public void testDSStore() throws Exception { + Assume.assumeTrue(JreCompat.isJre16Available()); + // Setup Tomcat instance Tomcat tomcat = getTomcatInstance(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/session/TesterManager.java tomcat10-10.1.52/test/org/apache/catalina/session/TesterManager.java --- tomcat10-10.1.34/test/org/apache/catalina/session/TesterManager.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/session/TesterManager.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,205 @@ +/* + * 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 org.apache.catalina.Context; +import org.apache.catalina.Manager; +import org.apache.catalina.Session; +import org.apache.catalina.SessionIdGenerator; + +public class TesterManager implements Manager { + + private Context context; + + @Override + public Context getContext() { + return context; + } + + @Override + public void setContext(Context context) { + this.context = context; + } + + @Override + public SessionIdGenerator getSessionIdGenerator() { + return null; + } + + @Override + public void setSessionIdGenerator(SessionIdGenerator sessionIdGenerator) { + // NO-OP + } + + @Override + public long getSessionCounter() { + return 0; + } + + @Override + public int getMaxActive() { + return 0; + } + + @Override + public void setMaxActive(int maxActive) { + // NO-OP + } + + @Override + public int getActiveSessions() { + return 0; + } + + @Override + public long getExpiredSessions() { + return 0; + } + + @Override + public void setExpiredSessions(long expiredSessions) { + // NO-OP + } + + @Override + public int getRejectedSessions() { + return 0; + } + + @Override + public int getSessionMaxAliveTime() { + return 0; + } + + @Override + public void setSessionMaxAliveTime(int sessionMaxAliveTime) { + // NO-OP + } + + @Override + public int getSessionAverageAliveTime() { + return 0; + } + + @Override + public int getSessionCreateRate() { + return 0; + } + + @Override + public int getSessionExpireRate() { + return 0; + } + + @Override + public void add(Session session) { + // NO-OP + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + // NO-OP + } + + @Override + public void changeSessionId(Session session, String newId) { + // NO-OP + } + + @Override + public Session createEmptySession() { + return new StandardSession(this); + } + + @Override + public Session createSession(String sessionId) { + return null; + } + + @Override + public Session findSession(String id) throws IOException { + return null; + } + + @Override + public Session[] findSessions() { + return null; + } + + @Override + public void load() throws ClassNotFoundException, IOException { + // NO-OP + } + + @Override + public void remove(Session session) { + // NO-OP + } + + @Override + public void remove(Session session, boolean update) { + // NO-OP + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + // NO-OP + } + + @Override + public void unload() throws IOException { + // NO-OP + } + + @Override + public void backgroundProcess() { + // NO-OP + } + + @Override + public boolean willAttributeDistribute(String name, Object value) { + return false; + } + + @Override + public void setNotifyBindingListenerOnUnchangedValue(boolean notifyBindingListenerOnUnchangedValue) { + // NO-OP + } + + @Override + public void setNotifyAttributeListenerOnUnchangedValue(boolean notifyAttributeListenerOnUnchangedValue) { + // NO-OP + } + + @Override + public void setSessionActivityCheck(boolean sessionActivityCheck) { + // NO-OP + } + + @Override + public void setSessionLastAccessAtStart(boolean sessionLastAccessAtStart) { + // NO-OP + } + + @Override + public void setSessionCounter(long sessionCounter) { + // NO-OP + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/DuplicateMappingParamFilter.java tomcat10-10.1.52/test/org/apache/catalina/startup/DuplicateMappingParamFilter.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/DuplicateMappingParamFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/DuplicateMappingParamFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,9 +27,6 @@ /** * Test Mock with wrong Annotation! - * - * @author Peter Rossbach - * */ @WebFilter(value = "/param", filterName="paramDFilter", urlPatterns = { "/param1" , "/param2" }) diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/DuplicateMappingParamServlet.java tomcat10-10.1.52/test/org/apache/catalina/startup/DuplicateMappingParamServlet.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/DuplicateMappingParamServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/DuplicateMappingParamServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -28,8 +28,6 @@ /** * Test Mock with wrong Annotation! - * - * @author Peter Rossbach */ @WebServlet(value = "/annotation/overwrite", urlPatterns ="/param2", name = "param", initParams = { @WebInitParam(name = "foo", value = "Hello"), diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/HostConfigAutomaticDeploymentBaseTest.java tomcat10-10.1.52/test/org/apache/catalina/startup/HostConfigAutomaticDeploymentBaseTest.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/HostConfigAutomaticDeploymentBaseTest.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/HostConfigAutomaticDeploymentBaseTest.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,860 @@ +/* + * 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.startup; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; + +import org.junit.Assert; + +import org.apache.catalina.Context; +import org.apache.catalina.Host; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.core.StandardHost; +import org.apache.catalina.util.ContextName; + +public class HostConfigAutomaticDeploymentBaseTest extends TomcatBaseTest { + + protected static final ContextName APP_NAME = new ContextName("myapp", false); + protected static final File XML_SOURCE = new File("test/deployment/context.xml"); + protected static final File WAR_XML_SOURCE = new File("test/deployment/context.war"); + protected static final File WAR_XML_COPYXML_FALSE_SOURCE = new File("test/deployment/contextCopyXMLFalse.war"); + protected static final File WAR_XML_COPYXML_TRUE_SOURCE = new File("test/deployment/contextCopyXMLTrue.war"); + protected static final File WAR_XML_UNPACKWAR_FALSE_SOURCE = new File("test/deployment/contextUnpackWARFalse.war"); + protected static final File WAR_XML_UNPACKWAR_TRUE_SOURCE = new File("test/deployment/contextUnpackWARTrue.war"); + protected static final File WAR_SOURCE = new File("test/deployment/noContext.war"); + protected static final File WAR_BROKEN_SOURCE = new File("test/deployment/broken.war"); + protected static final File DIR_XML_SOURCE = new File("test/deployment/dirContext"); + protected static final File DIR_XML_SOURCE_META_INF = new File("test/deployment/dirContext/META-INF"); + protected static final File DIR_SOURCE = new File("test/deployment/dirNoContext"); + + protected static final int XML = 1; + protected static final int EXT = 2; + protected static final int WAR = 3; + protected static final int DIR = 4; + protected static final int DIR_XML = 5; + + protected static final int NONE = 1; + protected static final int RELOAD = 2; + protected static final int REDEPLOY = 3; + + protected static final String XML_COOKIE_NAME = "XML_CONTEXT"; + protected static final String WAR_COOKIE_NAME = "WAR_CONTEXT"; + protected static final String DIR_COOKIE_NAME = "DIR_CONTEXT"; + + protected File external; + + + @Override + public void setUp() throws Exception { + super.setUp(); + + Tomcat tomcat = getTomcatInstance(); + + external = new File(getTemporaryDirectory(), "external"); + if (!external.exists() && !external.mkdir()) { + Assert.fail("Unable to create external for test"); + } + + // Disable background thread + tomcat.getEngine().setBackgroundProcessorDelay(-1); + + // Enable deployer + tomcat.getHost().addLifecycleListener(new HostConfig()); + + // Disable deployment on start up + tomcat.getHost().setDeployOnStartup(false); + + // Clean-up after test + addDeleteOnTearDown(new File(tomcat.basedir, "/conf")); + addDeleteOnTearDown(external); + } + + + protected void doTestDelete(boolean startXml, boolean startExternalWar, + boolean startExternalDir, boolean startWar, boolean startDir, + int toDelete, boolean resultXml, boolean resultWar, + boolean resultDir, String resultCookieName) throws Exception { + + Tomcat tomcat = getTomcatInstance(); + StandardHost host = (StandardHost) tomcat.getHost(); + + // Init + File xml = null; + File ext = null; + File war = null; + File dir = null; + + if (startXml && !startExternalWar && !startExternalDir) { + xml = createXmlInConfigBaseForAppbase(); + } + if (startExternalWar) { + ext = createWar(WAR_XML_SOURCE, false); + xml = createXmlInConfigBaseForExternal(ext); + } + if (startExternalDir) { + ext = createDirInExternal(true); + xml = createXmlInConfigBaseForExternal(ext); + } + if (startWar) { + war = createWar(WAR_XML_SOURCE, true); + } + if (startDir) { + dir = createDirInAppbase(true); + } + + if ((startWar || startExternalWar) && !startDir) { + host.setUnpackWARs(false); + } + + // Deploy the files we copied + tomcat.start(); + host.backgroundProcess(); + + // Remove the specified file + switch (toDelete) { + case XML: + ExpandWar.delete(xml); + break; + case EXT: + ExpandWar.delete(ext); + break; + case WAR: + ExpandWar.delete(war); + break; + case DIR: + ExpandWar.delete(dir); + break; + default: + Assert.fail(); + } + + // Trigger an auto-deployment cycle + host.backgroundProcess(); + + Context ctxt = (Context) host.findChild(APP_NAME.getName()); + + // Check the results + // External WAR and DIR should only be deleted if the test is testing + // behaviour when the external resource is deleted + if (toDelete == EXT) { + if (ext == null) { + Assert.fail(); + } else { + Assert.assertFalse(ext.exists()); + } + } else { + if (startExternalWar) { + if (ext == null) { + Assert.fail(); + } else { + Assert.assertTrue(ext.isFile()); + } + } + if (startExternalDir) { + if (ext == null) { + Assert.fail(); + } else { + Assert.assertTrue(ext.isDirectory()); + } + } + } + + if (resultXml) { + if (xml == null) { + Assert.fail(); + } else { + Assert.assertTrue(xml.isFile()); + } + } + if (resultWar) { + if (war == null) { + Assert.fail(); + } else { + Assert.assertTrue(war.isFile()); + } + } + if (resultDir) { + if (dir == null) { + Assert.fail(); + } else { + Assert.assertTrue(dir.isDirectory()); + } + } + + if (!resultXml && (startExternalWar || startExternalDir)) { + Assert.assertNull(ctxt); + } + if (!resultWar && !resultDir) { + if (resultXml) { + Assert.assertNotNull(ctxt); + Assert.assertEquals(LifecycleState.FAILED, ctxt.getState()); + } else { + Assert.assertNull(ctxt); + } + } + + if (ctxt != null) { + Assert.assertEquals(resultCookieName, ctxt.getSessionCookieName()); + } + } + + + protected void doTestDeployment(boolean deployXML, boolean copyXML, + boolean unpackWARs, LifecycleState resultState, String cookieName, + boolean resultXml, boolean resultWar, boolean resultDir) + throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + // Start the instance + tomcat.start(); + + // Set the attributes + StandardHost host = (StandardHost) tomcat.getHost(); + host.setDeployXML(deployXML); + host.setCopyXML(copyXML); + host.setUnpackWARs(unpackWARs); + + // Trigger automatic deployment + host.backgroundProcess(); + + // Test the results + Context ctxt = (Context) tomcat.getHost().findChild(APP_NAME.getPath()); + if (resultState == null) { + Assert.assertNull(ctxt); + } else { + Assert.assertNotNull(ctxt); + Assert.assertEquals(resultState, ctxt.getState()); + Assert.assertEquals(cookieName, ctxt.getSessionCookieName()); + } + + File xml = new File( + host.getConfigBaseFile(), APP_NAME.getBaseName() + ".xml"); + Assert.assertEquals( + Boolean.valueOf(resultXml), Boolean.valueOf(xml.isFile())); + + File war = new File( + host.getAppBaseFile(), APP_NAME.getBaseName() + ".war"); + Assert.assertEquals( + Boolean.valueOf(resultWar), Boolean.valueOf(war.isFile())); + + File dir = new File(host.getAppBase(), APP_NAME.getBaseName()); + Assert.assertEquals( + Boolean.valueOf(resultDir), Boolean.valueOf(dir.isDirectory())); + } + + + protected void doTestModify(boolean startXml, boolean startExternalWar, + boolean startExternalDir, boolean startWar, boolean startDir, + int toModify, boolean resultXml, boolean resultWar, + boolean resultDir, String resultCookieName, int resultAction) + throws Exception { + doTestModify(startXml, startExternalWar, startExternalDir, startWar, + startDir, toModify, resultXml, resultWar, resultDir, + resultCookieName, resultAction, LifecycleState.STARTED); + } + + + protected void doTestModify(boolean startXml, boolean startExternalWar, + boolean startExternalDir, boolean startWar, boolean startDir, + int toModify, boolean resultXml, boolean resultWar, + boolean resultDir, String resultCookieName, int resultAction, + LifecycleState resultState) throws Exception { + + Tomcat tomcat = getTomcatInstance(); + StandardHost host = (StandardHost) tomcat.getHost(); + + // Init + File xml = null; + File ext = null; + File war = null; + File dir = null; + + long testStartTime = System.currentTimeMillis(); + + if (startXml && !startExternalWar && !startExternalDir) { + xml = createXmlInConfigBaseForAppbase(); + } + if (startExternalWar) { + ext = createWar(WAR_XML_SOURCE, false); + xml = createXmlInConfigBaseForExternal(ext); + } + if (startExternalDir) { + ext = createDirInAppbase(true); + xml = createXmlInConfigBaseForExternal(ext); + } + if (startWar) { + war = createWar(WAR_XML_SOURCE, true); + } + if (startDir) { + dir = createDirInAppbase(true); + } + + if ((startWar || startExternalWar) && !startDir) { + host.setUnpackWARs(false); + } + + // Deploy the files we copied + tomcat.start(); + host.backgroundProcess(); + + // Update the last modified time. Make sure that the OS reports a change + // in modification time that HostConfig can detect. Change is made + // relative to test start time to ensure new modification times are + // sufficiently different. + switch (toModify) { + case XML: + if (xml == null) { + Assert.fail(); + } else { + Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( + testStartTime - 10 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); + } + break; + case EXT: + if (ext == null) { + Assert.fail(); + } else { + Assert.assertTrue("Failed to set last modified for [" + ext + "]", ext.setLastModified( + testStartTime - 10 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); + } + break; + case WAR: + if (war == null) { + Assert.fail(); + } else { + Assert.assertTrue("Failed to set last modified for [" + war + "]", war.setLastModified( + testStartTime - 10 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); + } + break; + case DIR: + if (dir == null) { + Assert.fail(); + } else { + Assert.assertTrue("Failed to set last modified for [" + dir + "]", dir.setLastModified( + testStartTime - 10 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); + } + break; + default: + Assert.fail(); + } + + Context oldContext = (Context) host.findChild(APP_NAME.getName()); + StateTracker tracker = new StateTracker(); + oldContext.addLifecycleListener(tracker); + + // Trigger an auto-deployment cycle + host.backgroundProcess(); + + Context newContext = (Context) host.findChild(APP_NAME.getName()); + + // Check the results + if (resultXml) { + if (xml == null) { + Assert.fail(); + } else { + Assert.assertTrue(xml.isFile()); + } + } + if (resultWar) { + if (war == null) { + Assert.fail(); + } else { + Assert.assertTrue(war.isFile()); + } + } + if (resultDir) { + if (dir == null) { + Assert.fail(); + } else { + Assert.assertTrue(dir.isDirectory()); + } + } + + if (!resultXml && (startExternalWar || startExternalDir)) { + Assert.assertNull(newContext); + } + if (!resultWar && !resultDir) { + if (resultXml) { + Assert.assertNotNull(newContext); + if (!startExternalWar && !startExternalDir) { + Assert.assertEquals(LifecycleState.FAILED, + newContext.getState()); + } else { + Assert.assertEquals(LifecycleState.STARTED, + newContext.getState()); + } + } else { + Assert.assertNull(newContext); + } + } + + if (newContext != null) { + Assert.assertEquals(resultCookieName, + newContext.getSessionCookieName()); + Assert.assertEquals(resultState, newContext.getState()); + } + + if (resultAction == NONE) { + Assert.assertSame(oldContext, newContext); + Assert.assertEquals("", tracker.getHistory()); + } else if (resultAction == RELOAD) { + Assert.assertSame(oldContext, newContext); + Assert.assertEquals("stopstart", tracker.getHistory()); + } else if (resultAction == REDEPLOY) { + Assert.assertNotSame(oldContext, newContext); + // No init or start as that will be in a new context object + Assert.assertEquals("stopafter_destroy", tracker.getHistory()); + } else { + Assert.fail(); + } + } + + + protected void doTestAddition(boolean startXml, boolean startExternalWar, + boolean startExternalDir, boolean startWar, boolean startDir, + int toAdd, boolean resultXml, boolean resultWar, + boolean resultDir, String resultCookieName, int resultAction) + throws Exception { + + doTestAddition(startXml, startExternalWar, startExternalDir, startWar, + startDir, false, true, toAdd, resultXml, resultWar, resultDir, + resultCookieName, resultAction, LifecycleState.STARTED); + } + + + protected void doTestAddition(boolean startXml, boolean startExternalWar, + boolean startExternalDir, boolean startWar, boolean startDir, + boolean copyXML, boolean deployXML, int toAdd, boolean resultXml, + boolean resultWar, boolean resultDir, String resultCookieName, + int resultAction, LifecycleState state) + throws Exception { + + Tomcat tomcat = getTomcatInstance(); + StandardHost host = (StandardHost) tomcat.getHost(); + + // Init + File xml = null; + File ext = null; + File war = null; + File dir = null; + + if (startXml && !startExternalWar && !startExternalDir) { + xml = createXmlInConfigBaseForAppbase(); + } + if (startExternalWar) { + ext = createWar(WAR_XML_SOURCE, false); + xml = createXmlInConfigBaseForExternal(ext); + } + if (startExternalDir) { + ext = createDirInExternal(true); + xml = createXmlInConfigBaseForExternal(ext); + } + if (startWar) { + war = createWar(WAR_XML_SOURCE, true); + } + if (startDir) { + dir = createDirInAppbase(toAdd != DIR_XML); + } + + if ((startWar || startExternalWar) && !startDir) { + host.setUnpackWARs(false); + } + + host.setCopyXML(copyXML); + host.setDeployXML(deployXML); + + // Deploy the files we copied + tomcat.start(); + host.backgroundProcess(); + + // Change the specified file + switch (toAdd) { + case XML: + if (xml == null) { + xml = createXmlInConfigBaseForAppbase(); + } else { + Assert.fail(); + } + break; + case EXT: + if (ext == null && xml == null) { + ext = createWar(WAR_XML_SOURCE, false); + xml = createXmlInConfigBaseForExternal(ext); + } else { + Assert.fail(); + } + break; + case WAR: + if (war == null) { + war = createWar(WAR_XML_SOURCE, true); + } else { + Assert.fail(); + } + break; + case DIR: + if (dir == null) { + dir = createDirInAppbase(true); + } else { + Assert.fail(); + } + break; + case DIR_XML: + dir = createDirXmlInAppbase(); + xml = getXmlInConfigBaseForAppbase(); + break; + default: + Assert.fail(); + } + + Context oldContext = (Context) host.findChild(APP_NAME.getName()); + StateTracker tracker = new StateTracker(); + oldContext.addLifecycleListener(tracker); + + // Trigger an auto-deployment cycle + host.backgroundProcess(); + + Context newContext = (Context) host.findChild(APP_NAME.getName()); + + // Check the results + if (resultXml) { + if (xml == null) { + Assert.fail(); + } else { + Assert.assertTrue(xml.isFile()); + } + } + if (resultWar) { + if (war == null) { + Assert.fail(); + } else { + Assert.assertTrue(war.isFile()); + } + } + if (resultDir) { + if (dir == null) { + Assert.fail(); + } else { + Assert.assertTrue(dir.isDirectory()); + } + } + + if (!resultXml && (startExternalWar || startExternalDir)) { + Assert.assertNull(newContext); + } + if (!resultWar && !resultDir) { + if (resultXml) { + Assert.assertNotNull(newContext); + if (!startExternalWar && !startExternalDir) { + Assert.assertEquals(LifecycleState.FAILED, + newContext.getState()); + } else { + Assert.assertEquals(LifecycleState.STARTED, + newContext.getState()); + } + } else { + Assert.assertNull(newContext); + } + } + + if (newContext != null) { + Assert.assertEquals(resultCookieName, + newContext.getSessionCookieName()); + } + + if (resultAction == NONE) { + Assert.assertSame(oldContext, newContext); + Assert.assertEquals("", tracker.getHistory()); + } else if (resultAction == RELOAD) { + Assert.assertSame(oldContext, newContext); + Assert.assertEquals("stopstart", tracker.getHistory()); + } else if (resultAction == REDEPLOY) { + if (newContext == null) { + Assert.fail(); + } else { + Assert.assertEquals(state, newContext.getState()); + } + Assert.assertNotSame(oldContext, newContext); + // No init or start as that will be in a new context object + Assert.assertEquals("stopafter_destroy", tracker.getHistory()); + } else { + Assert.fail(); + } + } + + + protected void doTestBrokenAppWithAntiLocking(boolean unpackWARs) + throws Exception { + + Tomcat tomcat = getTomcatInstance(); + StandardHost host = (StandardHost) tomcat.getHost(); + + host.setUnpackWARs(unpackWARs); + + File war = createWar(WAR_BROKEN_SOURCE, false); + createXmlInConfigBaseForExternal(war, true); + + File dir = new File(host.getAppBaseFile(), APP_NAME.getBaseName()); + + tomcat.start(); + + // Simulate deploy on start-up + tomcat.getHost().backgroundProcess(); + + Assert.assertTrue(war.isFile()); + if (unpackWARs) { + Assert.assertTrue(dir.isDirectory()); + } + } + + + protected void doTestUnpackWAR(boolean unpackWARs, boolean unpackWAR, + boolean external, boolean resultDir) throws Exception { + + Tomcat tomcat = getTomcatInstance(); + StandardHost host = (StandardHost) tomcat.getHost(); + + host.setUnpackWARs(unpackWARs); + + tomcat.start(); + + File war; + if (unpackWAR) { + war = createWar(WAR_XML_UNPACKWAR_TRUE_SOURCE, !external); + } else { + war = createWar(WAR_XML_UNPACKWAR_FALSE_SOURCE, !external); + } + if (external) { + createXmlInConfigBaseForExternal(war); + } + + host.backgroundProcess(); + + File dir = new File(host.getAppBase(), APP_NAME.getBaseName()); + Assert.assertEquals( + Boolean.valueOf(resultDir), Boolean.valueOf(dir.isDirectory())); + } + + + protected void doTestCopyXML(boolean copyXmlHost, boolean copyXmlWar, + boolean external, boolean resultXml) throws Exception { + + Tomcat tomcat = getTomcatInstance(); + StandardHost host = (StandardHost) tomcat.getHost(); + + host.setCopyXML(copyXmlHost); + + tomcat.start(); + + File war; + if (copyXmlWar) { + war = createWar(WAR_XML_COPYXML_TRUE_SOURCE, !external); + } else { + war = createWar(WAR_XML_COPYXML_FALSE_SOURCE, !external); + } + if (external) { + createXmlInConfigBaseForExternal(war); + } + + host.backgroundProcess(); + + File xml = new File(host.getConfigBaseFile(), + APP_NAME.getBaseName() + ".xml"); + Assert.assertEquals( + Boolean.valueOf(resultXml), Boolean.valueOf(xml.isFile())); + + Context context = (Context) host.findChild(APP_NAME.getName()); + if (external) { + Assert.assertEquals(XML_COOKIE_NAME, + context.getSessionCookieName()); + } else { + Assert.assertEquals(WAR_COOKIE_NAME, + context.getSessionCookieName()); + } + } + + + protected void doTestUpdateWarOffline(File srcWar, boolean deployOnStartUp, boolean autoDeploy) + throws Exception { + Tomcat tomcat = getTomcatInstance(); + StandardHost host = (StandardHost) tomcat.getHost(); + host.setDeployOnStartup(deployOnStartUp); + + File war = createWar(srcWar, true); + // Make the WAR appear to have been created earlier + Assert.assertTrue("Failed to set last modified for [" + war + "]", war.setLastModified( + war.lastModified() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); + + tomcat.addWebapp(APP_NAME.getPath(), war.getAbsolutePath()); + tomcat.start(); + + // Get the last modified timestamp for the expanded dir + File dir = new File(host.getAppBase(), APP_NAME.getBaseName()); + // Make the DIR appear to have been created earlier + long lastModified = war.lastModified() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS; + Assert.assertTrue("Failed to set last modified for [" + dir + "]", + dir.setLastModified(lastModified)); + + host.stop(); + Assert.assertTrue("Failed to set last modified for [" + war + "]", + war.setLastModified(System.currentTimeMillis())); + host.start(); + if (autoDeploy) { + host.backgroundProcess(); + } + + long newLastModified = dir.lastModified(); + + Assert.assertNotEquals("Timestamp hasn't changed", lastModified, newLastModified); + } + + + protected File createDirInAppbase(boolean withXml) throws IOException { + File dir = new File(getTomcatInstance().getHost().getAppBaseFile(), + APP_NAME.getBaseName()); + if (withXml) { + recursiveCopy(DIR_XML_SOURCE.toPath(), dir.toPath()); + } else { + recursiveCopy(DIR_SOURCE.toPath(), dir.toPath()); + } + return dir; + } + + + protected File createDirXmlInAppbase() throws IOException { + File dir = new File(getTomcatInstance().getHost().getAppBaseFile(), + APP_NAME.getBaseName() + "/META-INF"); + recursiveCopy(DIR_XML_SOURCE_META_INF.toPath(), dir.toPath()); + return dir; + } + + + protected File createDirInExternal(boolean withXml) throws IOException { + File ext = new File(external, "external" + ".war"); + if (withXml) { + recursiveCopy(DIR_XML_SOURCE.toPath(), ext.toPath()); + } else { + recursiveCopy(DIR_SOURCE.toPath(), ext.toPath()); + } + return ext; + } + + + protected File createWar(File src, boolean useAppbase) throws IOException { + File dest; + if (useAppbase) { + dest = new File(getTomcatInstance().getHost().getAppBaseFile(), + APP_NAME.getBaseName() + ".war"); + } else { + dest = new File(external, "external" + ".war"); + } + Files.copy(src.toPath(), dest.toPath()); + // Make sure that HostConfig thinks the WAR has been modified. + Assert.assertTrue("Failed to set last modified for [" + dest + "]", dest.setLastModified( + System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); + return dest; + } + + + protected File createXmlInConfigBaseForAppbase() throws IOException { + File xml = getXmlInConfigBaseForAppbase(); + File parent = xml.getParentFile(); + if (!parent.isDirectory()) { + Assert.assertTrue(parent.mkdirs()); + } + Files.copy(XML_SOURCE.toPath(), xml.toPath()); + // Make sure that HostConfig thinks the xml has been modified. + Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( + System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); + return xml; + } + + + protected File getXmlInConfigBaseForAppbase() { + Host host = getTomcatInstance().getHost(); + return new File(host.getConfigBaseFile(), APP_NAME + ".xml"); + } + + + protected File createXmlInConfigBaseForExternal(File ext) throws IOException { + return createXmlInConfigBaseForExternal(ext, false); + } + + + protected File createXmlInConfigBaseForExternal(File ext, boolean antiLocking) + throws IOException { + File xml = new File(getTomcatInstance().getHost().getConfigBaseFile(), + APP_NAME + ".xml"); + File parent = xml.getParentFile(); + if (!parent.isDirectory()) { + Assert.assertTrue(parent.mkdirs()); + } + + try (FileOutputStream fos = new FileOutputStream(xml)) { + StringBuilder context = new StringBuilder(); + context.append(""); + fos.write(context.toString().getBytes(StandardCharsets.ISO_8859_1)); + } + // Make sure that HostConfig thinks the xml has been modified. + Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( + System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); + return xml; + } + + + private static class StateTracker implements LifecycleListener { + + private StringBuilder stateHistory = new StringBuilder(); + + @Override + public void lifecycleEvent(LifecycleEvent event) { + + String type = event.getType(); + + if (type.equals(Lifecycle.START_EVENT) || + type.equals(Lifecycle.STOP_EVENT) || + type.equals(Lifecycle.AFTER_DESTROY_EVENT)) { + stateHistory.append(type); + } + } + + + public String getHistory() { + return stateHistory.toString(); + } + } + + + public static class TesterContext extends StandardContext { + // No functional change + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/ParamFilter.java tomcat10-10.1.52/test/org/apache/catalina/startup/ParamFilter.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/ParamFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/ParamFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,7 @@ import jakarta.servlet.annotation.WebInitParam; /** - * Test Mock to check Filter Annotations - * @author Peter Rossbach + * Test Mock to check Filter Annotations. */ @WebFilter(value = "/param", filterName = "paramFilter", dispatcherTypes = { DispatcherType.ERROR, DispatcherType.ASYNC }, diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/SimpleHttpClient.java tomcat10-10.1.52/test/org/apache/catalina/startup/SimpleHttpClient.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/SimpleHttpClient.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/SimpleHttpClient.java 2026-01-23 19:33:36.000000000 +0000 @@ -205,18 +205,28 @@ return redirectUri; } - public void connect(int connectTimeout, int soTimeout) - throws UnknownHostException, IOException { + public void connect(Socket socket, int connectTimeout, int soTimeout, boolean connect) throws UnknownHostException, IOException { SocketAddress addr = new InetSocketAddress("localhost", port); - socket = new Socket(); + this.socket = socket; socket.setSoTimeout(soTimeout); - socket.connect(addr,connectTimeout); + if (connect) { + socket.connect(addr, connectTimeout); + } OutputStream os = createOutputStream(socket); writer = new OutputStreamWriter(os, requestBodyEncoding); InputStream is = socket.getInputStream(); Reader r = new InputStreamReader(is, responseBodyEncoding); reader = new BufferedReader(r); } + + public void connect(int connectTimeout, int soTimeout) throws UnknownHostException, IOException { + connect(new Socket(), connectTimeout, soTimeout, true); + } + + public void connect(Socket socket) throws UnknownHostException, IOException { + connect(socket, 10000, 10000, false); + } + public void connect() throws UnknownHostException, IOException { connect(10000, 10000); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestContextConfigAnnotation.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestContextConfigAnnotation.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestContextConfigAnnotation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestContextConfigAnnotation.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,8 +46,6 @@ /** * Check Servlet 3.0 Spec 8.2.3.3: Override annotation parameter from web.xml or * fragment. - * - * @author Peter Rossbach */ public class TestContextConfigAnnotation { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentA.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentA.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentA.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentA.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,482 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.catalina.startup; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -import org.junit.Assert; -import org.junit.Test; - -import org.apache.catalina.Context; -import org.apache.catalina.Host; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.core.StandardContext; -import org.apache.catalina.core.StandardHost; -import org.apache.catalina.util.ContextName; - -/** - * The purpose of this class is to test the automatic deployment features of the - * {@link HostConfig} implementation. - */ -public class TestHostConfigAutomaticDeploymentA extends TomcatBaseTest { - - private static final ContextName APP_NAME = new ContextName("myapp", false); - private static final File XML_SOURCE = - new File("test/deployment/context.xml"); - private static final File WAR_XML_SOURCE = - new File("test/deployment/context.war"); - private static final File DIR_XML_SOURCE = - new File("test/deployment/dirContext"); - private static final File DIR_SOURCE = - new File("test/deployment/dirNoContext"); - - private static final int XML = 1; - private static final int EXT = 2; - private static final int WAR = 3; - private static final int DIR = 4; - - private static final String XML_COOKIE_NAME = "XML_CONTEXT"; - private static final String WAR_COOKIE_NAME = "WAR_CONTEXT"; - private static final String DIR_COOKIE_NAME = "DIR_CONTEXT"; - // private static final String DEFAULT_COOKIE_NAME = "JSESSIONID"; - - private File external; - - @Override - public void setUp() throws Exception { - super.setUp(); - - Tomcat tomcat = getTomcatInstance(); - - external = new File(getTemporaryDirectory(), "external"); - if (!external.exists() && !external.mkdir()) { - Assert.fail("Unable to create external for test"); - } - - // Disable background thread - tomcat.getEngine().setBackgroundProcessorDelay(-1); - - // Enable deployer - tomcat.getHost().addLifecycleListener(new HostConfig()); - - // Disable deployment on start up - tomcat.getHost().setDeployOnStartup(false); - - // Clean-up after test - addDeleteOnTearDown(new File(tomcat.basedir, "/conf")); - addDeleteOnTearDown(external); - } - - - /* - * Expected behaviour for the deletion of files. - * - * Artifacts present Artifact Artifacts remaining - * XML WAR EXT DIR Removed XML WAR EXT DIR Notes - * N N N Y DIR - - - N - * N Y N N WAR - N - - - * N Y N Y DIR - Y - R 1 - * N Y N Y WAR - N - N - * Y N N N XML N - - - - * Y N N Y DIR N - - N - * Y N N Y XML R - - Y 2 - * Y N Y N EXT Y - N - - * Y N Y N XML N - Y - - * Y N Y Y DIR R - Y R 1,2 - * Y N Y Y EXT Y - N N - * Y N Y Y XML N - Y N - * Y Y N N WAR N N - - - * Y Y N N XML N N - - - * Y Y N Y DIR R Y - R 1,2 - * Y Y N Y WAR N N - - - * Y Y N Y XML R Y - Y - * - * Notes: 1. The DIR will be re-created since unpackWARs is true. - * 2. The XML will be extracted from the WAR/DIR if deployXML and - * copyXML are true. - */ - @Test - public void testDeleteDirRemoveDir() throws Exception { - doTestDelete(false, false, false, false, true, DIR, false, false, false, - null); - } - - @Test - public void testDeleteWarRemoveWar() throws Exception { - doTestDelete(false, false, false, true, false, WAR, false, false, false, - null); - } - - @Test - public void testDeleteWarDirRemoveDir() throws Exception { - doTestDelete(false, false, false, true, true, DIR, false, true, true, - WAR_COOKIE_NAME); - } - - @Test - public void testDeleteWarDirRemoveWar() throws Exception { - doTestDelete(false, false, false, true, true, WAR, false, false, false, - null); - } - - @Test - public void testDeleteXmlRemoveXml() throws Exception { - doTestDelete(true, false, false, false, false, XML, false, false, false, - null); - } - - @Test - public void testDeleteXmlDirRemoveDir() throws Exception { - doTestDelete(true, false, false, false, true, DIR, false, false, false, - null); - } - - @Test - public void testDeleteXmlDirRemoveXml() throws Exception { - doTestDelete(true, false, false, false, true, XML, false, false, true, - DIR_COOKIE_NAME); - } - - @Test - public void testDeleteXmlDirRemoveXmlCopyXml() throws Exception { - ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); - doTestDelete(true, false, false, false, true, XML, true, false, true, - DIR_COOKIE_NAME); - } - - @Test - public void testDeleteXmlExtwarRemoveExt() throws Exception { - doTestDelete(true, true, false, false, false, EXT, true, false, false, - XML_COOKIE_NAME); - } - - @Test - public void testDeleteXmlExtdirRemoveExt() throws Exception { - doTestDelete(true, false, true, false, false, EXT, true, false, false, - XML_COOKIE_NAME); - } - - @Test - public void testDeleteXmlExtwarRemoveXml() throws Exception { - doTestDelete(true, true, false, false, false, XML, false, false, false, - null); - } - - @Test - public void testDeleteXmlExtdirRemoveXml() throws Exception { - doTestDelete(true, false, true, false, false, XML, false, false, false, - null); - } - - @Test - public void testDeleteXmlExtwarDirRemoveDir() throws Exception { - doTestDelete(true, true, false, false, true, DIR, true, false, true, - XML_COOKIE_NAME); - } - - @Test - public void testDeleteXmlExtwarDirRemoveExt() throws Exception { - doTestDelete(true, true, false, false, true, EXT, true, false, false, - XML_COOKIE_NAME); - } - - @Test - public void testDeleteXmlExtwarDirRemoveXml() throws Exception { - doTestDelete(true, true, false, false, true, XML, false, false, false, - null); - } - - @Test - public void testDeleteXmlWarRemoveWar() throws Exception { - doTestDelete(true, false, false, true, false, WAR, false, false, false, - null); - } - - @Test - public void testDeleteXmlWarRemoveXml() throws Exception { - doTestDelete(true, false, false, true, false, XML, false, true, false, - WAR_COOKIE_NAME); - } - - @Test - public void testDeleteXmlWarRemoveXmlCopyXml() throws Exception { - ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); - doTestDelete(true, false, false, true, false, XML, true, true, false, - WAR_COOKIE_NAME); - } - - @Test - public void testDeleteXmlWarDirRemoveDir() throws Exception { - doTestDelete(true, false, false, true, true, DIR, false, true, true, - WAR_COOKIE_NAME); - } - - @Test - public void testDeleteXmlWarDirRemoveDirCopyXml() throws Exception { - ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); - doTestDelete(true, false, false, true, true, DIR, true, true, true, - WAR_COOKIE_NAME); - } - - @Test - public void testDeleteXmlWarDirRemoveWar() throws Exception { - doTestDelete(true, false, false, true, true, WAR, false, false, false, - null); - } - - @Test - public void testDeleteXmlWarDirRemoveWarCopyXml() throws Exception { - ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); - doTestDelete(true, false, false, true, true, WAR, false, false, false, - null); - } - - @Test - public void testDeleteXmlWarDirRemoveXml() throws Exception { - doTestDelete(true, false, false, true, true, XML, false, true, true, - DIR_COOKIE_NAME); - } - - @Test - public void testDeleteXmlWarDirRemoveXmlCopyXml() throws Exception { - ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); - doTestDelete(true, false, false, true, true, XML, true, true, true, - WAR_COOKIE_NAME); - } - - private void doTestDelete(boolean startXml, boolean startExternalWar, - boolean startExternalDir, boolean startWar, boolean startDir, - int toDelete, boolean resultXml, boolean resultWar, - boolean resultDir, String resultCookieName) throws Exception { - - Tomcat tomcat = getTomcatInstance(); - StandardHost host = (StandardHost) tomcat.getHost(); - - // Init - File xml = null; - File ext = null; - File war = null; - File dir = null; - - if (startXml && !startExternalWar && !startExternalDir) { - xml = createXmlInConfigBaseForAppbase(); - } - if (startExternalWar) { - ext = createWar(WAR_XML_SOURCE, false); - xml = createXmlInConfigBaseForExternal(ext); - } - if (startExternalDir) { - ext = createDirInExternal(true); - xml = createXmlInConfigBaseForExternal(ext); - } - if (startWar) { - war = createWar(WAR_XML_SOURCE, true); - } - if (startDir) { - dir = createDirInAppbase(true); - } - - if ((startWar || startExternalWar) && !startDir) { - host.setUnpackWARs(false); - } - - // Deploy the files we copied - tomcat.start(); - host.backgroundProcess(); - - // Remove the specified file - switch (toDelete) { - case XML: - ExpandWar.delete(xml); - break; - case EXT: - ExpandWar.delete(ext); - break; - case WAR: - ExpandWar.delete(war); - break; - case DIR: - ExpandWar.delete(dir); - break; - default: - Assert.fail(); - } - - // Trigger an auto-deployment cycle - host.backgroundProcess(); - - Context ctxt = (Context) host.findChild(APP_NAME.getName()); - - // Check the results - // External WAR and DIR should only be deleted if the test is testing - // behaviour when the external resource is deleted - if (toDelete == EXT) { - if (ext == null) { - Assert.fail(); - } else { - Assert.assertFalse(ext.exists()); - } - } else { - if (startExternalWar) { - if (ext == null) { - Assert.fail(); - } else { - Assert.assertTrue(ext.isFile()); - } - } - if (startExternalDir) { - if (ext == null) { - Assert.fail(); - } else { - Assert.assertTrue(ext.isDirectory()); - } - } - } - - if (resultXml) { - if (xml == null) { - Assert.fail(); - } else { - Assert.assertTrue(xml.isFile()); - } - } - if (resultWar) { - if (war == null) { - Assert.fail(); - } else { - Assert.assertTrue(war.isFile()); - } - } - if (resultDir) { - if (dir == null) { - Assert.fail(); - } else { - Assert.assertTrue(dir.isDirectory()); - } - } - - if (!resultXml && (startExternalWar || startExternalDir)) { - Assert.assertNull(ctxt); - } - if (!resultWar && !resultDir) { - if (resultXml) { - Assert.assertNotNull(ctxt); - Assert.assertEquals(LifecycleState.FAILED, ctxt.getState()); - } else { - Assert.assertNull(ctxt); - } - } - - if (ctxt != null) { - Assert.assertEquals(resultCookieName, ctxt.getSessionCookieName()); - } - } - - - private File createDirInAppbase(boolean withXml) throws IOException { - File dir = new File(getTomcatInstance().getHost().getAppBaseFile(), - APP_NAME.getBaseName()); - if (withXml) { - recursiveCopy(DIR_XML_SOURCE.toPath(), dir.toPath()); - } else { - recursiveCopy(DIR_SOURCE.toPath(), dir.toPath()); - } - return dir; - } - - private File createDirInExternal(boolean withXml) throws IOException { - File ext = new File(external, "external" + ".war"); - if (withXml) { - recursiveCopy(DIR_XML_SOURCE.toPath(), ext.toPath()); - } else { - recursiveCopy(DIR_SOURCE.toPath(), ext.toPath()); - } - return ext; - } - - private File createWar(File src, boolean useAppbase) throws IOException { - File dest; - if (useAppbase) { - dest = new File(getTomcatInstance().getHost().getAppBaseFile(), - APP_NAME.getBaseName() + ".war"); - } else { - dest = new File(external, "external" + ".war"); - } - Files.copy(src.toPath(), dest.toPath()); - // Make sure that HostConfig thinks the WAR has been modified. - Assert.assertTrue("Failed to set last modified for [" + dest + "]", dest.setLastModified( - System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - return dest; - } - - private File createXmlInConfigBaseForAppbase() throws IOException { - File xml = getXmlInConfigBaseForAppbase(); - File parent = xml.getParentFile(); - if (!parent.isDirectory()) { - Assert.assertTrue(parent.mkdirs()); - } - Files.copy(XML_SOURCE.toPath(), xml.toPath()); - // Make sure that HostConfig thinks the xml has been modified. - Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( - System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - return xml; - } - - private File getXmlInConfigBaseForAppbase() { - Host host = getTomcatInstance().getHost(); - return new File(host.getConfigBaseFile(), APP_NAME + ".xml"); - } - - private File createXmlInConfigBaseForExternal(File ext) throws IOException { - return createXmlInConfigBaseForExternal(ext, false); - } - - private File createXmlInConfigBaseForExternal(File ext, boolean antiLocking) - throws IOException { - File xml = new File(getTomcatInstance().getHost().getConfigBaseFile(), - APP_NAME + ".xml"); - File parent = xml.getParentFile(); - if (!parent.isDirectory()) { - Assert.assertTrue(parent.mkdirs()); - } - - try (FileOutputStream fos = new FileOutputStream(xml)) { - StringBuilder context = new StringBuilder(); - context.append(""); - fos.write(context.toString().getBytes(StandardCharsets.ISO_8859_1)); - } - // Make sure that HostConfig thinks the xml has been modified. - Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( - System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - return xml; - } - - public static class TesterContext extends StandardContext { - // No functional change - } -} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentAddition.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentAddition.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentAddition.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentAddition.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,182 @@ +/* + * 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.startup; + +import org.junit.Test; + +import org.apache.catalina.LifecycleState; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentAddition extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for the addition of files. + * + * Artifacts present copyXML deployXML Artifact Artifacts remaining + * XML WAR EXT DIR Added XML WAR EXT DIR Action + * N Y N N N Y DIR - Y - A None + * N N N Y N Y WAR - A - R Redeploy + * Y N N N N Y DIR Y - - A None + * N N N Y N Y XML A - - Y Redeploy + * Y N N N N Y WAR Y A - - Reload + * N Y N N N Y XML A Y - - Redeploy + * Y Y N N N Y DIR Y Y - A None + * Y N N Y N Y WAR Y A - N Reload + * N Y N Y N Y XML A Y - Y Redeploy + * Y N Y N N Y DIR Y - Y A None + * Y N Y N N Y WAR Y A Y - None + * N N N Y N Y EXT A - A R Redeploy + * N Y N N N Y EXT A Y A - Redeploy + * + * N N N Y Y/N N DIR+XML - - - Y Redeploy (failed) + * N N N Y Y Y DIR+XML A - - Y Redeploy + * N N N Y N Y DIR+XML - - - Y Redeploy + * + * Addition of a file is treated as if the added file has been modified + * with the following additional actions: + * - If a WAR is added, any DIR is removed and may be recreated depending on + * unpackWARs. + * - If an XML file is added that refers to an external docBase any WAR or + * DIR in the appBase will be removed. The DIR may be recreated if the + * external resource is a WAR and unpackWARs is true. + * - If a DIR is added when a WAR already exists and unpackWARs is false, + * the DIR will be ignored but a warning will be logged when the DIR is + * first detected. If the WAR is removed, the DIR will be left and may be + * deployed via automatic deployment. + * - If a WAR is added when an external WAR already exists for the same + * context, the WAR will be treated the same way as a DIR is treated in + * the previous bullet point. + */ + @Test + public void testAdditionWarAddDir() throws Exception { + doTestAddition(false, false, false, true, false, DIR, + false, true, true, WAR_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionDirAddWar() throws Exception { + doTestAddition(false, false, false, false, true, WAR, + false, true, true, WAR_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionXmlAddDir() throws Exception { + doTestAddition(true, false, false, false, false, DIR, + true, false, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionDirAddXml() throws Exception { + doTestAddition(false, false, false, false, true, XML, + true, false, true, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionXmlAddWar() throws Exception { + doTestAddition(true, false, false, false, false, WAR, + true, true, false, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testAdditionWarAddXml() throws Exception { + doTestAddition(false, false, false, true, false, XML, + true, true, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionXmlWarAddDir() throws Exception { + doTestAddition(true, false, false, true, false, DIR, + true, true, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionXmlDirAddWar() throws Exception { + doTestAddition(true, false, false, false, true, WAR, + true, true, false, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testAdditionWarDirAddXml() throws Exception { + doTestAddition(false, false, false, true, true, XML, + true, true, true, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionXmlExtwarAddDir() throws Exception { + doTestAddition(true, true, false, false, false, DIR, + true, false, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionXmlExtdirAddDir() throws Exception { + doTestAddition(true, false, true, false, false, DIR, + true, false, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionXmlExtwarAddWar() throws Exception { + doTestAddition(true, true, false, false, false, WAR, + true, true, false, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionXmlExtdirAddWar() throws Exception { + doTestAddition(true, false, true, false, false, WAR, + true, true, false, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionDirAddXmlExtwar() throws Exception { + doTestAddition(false, false, false, false, true, EXT, + true, false, true, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionWarAddXmlExtwar() throws Exception { + doTestAddition(false, false, false, true, false, EXT, + true, true, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionDirAddDirXmlTF() throws Exception { + doTestAddition(false, false, false, false, true, true, false, DIR_XML, + false, false, true, null, REDEPLOY, LifecycleState.FAILED); + } + + @Test + public void testAdditionDirAddDirXmlFF() throws Exception { + doTestAddition(false, false, false, false, true, false, false, DIR_XML, + false, false, true, null, REDEPLOY, LifecycleState.FAILED); + } + + @Test + public void testAdditionDirAddDirXmlTT() throws Exception { + doTestAddition(false, false, false, false, true, true, true, DIR_XML, + true, false, true, DIR_COOKIE_NAME, REDEPLOY, + LifecycleState.STARTED); + } + + @Test + public void testAdditionDirAddDirXmlFT() throws Exception { + doTestAddition(false, false, false, false, true, false, true, DIR_XML, + false, false, true, DIR_COOKIE_NAME, REDEPLOY, + LifecycleState.STARTED); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentB.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentB.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentB.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentB.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,687 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.catalina.startup; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -import org.junit.Assert; -import org.junit.Test; - -import org.apache.catalina.Context; -import org.apache.catalina.Host; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.core.StandardContext; -import org.apache.catalina.core.StandardHost; -import org.apache.catalina.util.ContextName; - -/** - * The purpose of this class is to test the automatic deployment features of the - * {@link HostConfig} implementation. - */ -public class TestHostConfigAutomaticDeploymentB extends TomcatBaseTest { - - private static final ContextName APP_NAME = new ContextName("myapp", false); - private static final File XML_SOURCE = - new File("test/deployment/context.xml"); - private static final File WAR_XML_SOURCE = - new File("test/deployment/context.war"); - private static final File WAR_SOURCE = - new File("test/deployment/noContext.war"); - private static final File DIR_XML_SOURCE = - new File("test/deployment/dirContext"); - private static final File DIR_SOURCE = - new File("test/deployment/dirNoContext"); - - private static final String XML_COOKIE_NAME = "XML_CONTEXT"; - private static final String WAR_COOKIE_NAME = "WAR_CONTEXT"; - private static final String DIR_COOKIE_NAME = "DIR_CONTEXT"; - // private static final String DEFAULT_COOKIE_NAME = "JSESSIONID"; - - private File external; - - @Override - public void setUp() throws Exception { - super.setUp(); - - Tomcat tomcat = getTomcatInstance(); - - external = new File(getTemporaryDirectory(), "external"); - if (!external.exists() && !external.mkdir()) { - Assert.fail("Unable to create external for test"); - } - - // Disable background thread - tomcat.getEngine().setBackgroundProcessorDelay(-1); - - // Enable deployer - tomcat.getHost().addLifecycleListener(new HostConfig()); - - // Disable deployment on start up - tomcat.getHost().setDeployOnStartup(false); - - // Clean-up after test - addDeleteOnTearDown(new File(tomcat.basedir, "/conf")); - addDeleteOnTearDown(external); - } - - - /* - * Expected behaviour for deployment of an XML file. - * deployXML copyXML unpackWARs XML WAR DIR - * Y/N Y/N Y/N Y N N - * - * Note: Context will fail to start because no valid docBase is present. - */ - @Test - public void testDeploymentXmlFFF() throws Exception { - createXmlInConfigBaseForAppbase(); - doTestDeployment(false, false, false, - LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlFFT() throws Exception { - createXmlInConfigBaseForAppbase(); - doTestDeployment(false, false, true, - LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlFTF() throws Exception { - createXmlInConfigBaseForAppbase(); - doTestDeployment(false, true, false, - LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlFTT() throws Exception { - createXmlInConfigBaseForAppbase(); - doTestDeployment(false, true, true, - LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlTFF() throws Exception { - createXmlInConfigBaseForAppbase(); - doTestDeployment(true, false, false, - LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlTFT() throws Exception { - createXmlInConfigBaseForAppbase(); - doTestDeployment(true, false, true, - LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlTTF() throws Exception { - createXmlInConfigBaseForAppbase(); - doTestDeployment(true, true, false, - LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlTTT() throws Exception { - createXmlInConfigBaseForAppbase(); - doTestDeployment(true, true, true, - LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); - } - - - /* - * Expected behaviour for deployment of an XML file that points to an - * external WAR. - * deployXML copyXML unpackWARs XML WAR DIR - * Y/N Y/N Y Y N Y - * Y/N Y/N N Y N N - * - * Notes: No WAR file is present in the appBase because it is an external - * WAR. - * Any context.xml file embedded in the external WAR file is ignored. - */ - @Test - public void testDeploymentXmlExternalWarXmlFFF() throws Exception { - File war = createWar(WAR_XML_SOURCE, false); - createXmlInConfigBaseForExternal(war); - doTestDeployment(false, false, false, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalWarXmlFFT() throws Exception { - File war = createWar(WAR_XML_SOURCE, false); - createXmlInConfigBaseForExternal(war); - doTestDeployment(false, false, true, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, true); - } - - @Test - public void testDeploymentXmlExternalWarXmlFTF() throws Exception { - File war = createWar(WAR_XML_SOURCE, false); - createXmlInConfigBaseForExternal(war); - doTestDeployment(false, true, false, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalWarXmlFTT() throws Exception { - File war = createWar(WAR_XML_SOURCE, false); - createXmlInConfigBaseForExternal(war); - doTestDeployment(false, true, true, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, true); - } - - @Test - public void testDeploymentXmlExternalWarXmlTFF() throws Exception { - File war = createWar(WAR_XML_SOURCE, false); - createXmlInConfigBaseForExternal(war); - doTestDeployment(true, false, false, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalWarXmlTFT() throws Exception { - File war = createWar(WAR_XML_SOURCE, false); - createXmlInConfigBaseForExternal(war); - doTestDeployment(true, false, true, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, true); - } - - @Test - public void testDeploymentXmlExternalWarXmlTTF() throws Exception { - File war = createWar(WAR_XML_SOURCE, false); - createXmlInConfigBaseForExternal(war); - doTestDeployment(true, true, false, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalWarXmlTTT() throws Exception { - File war = createWar(WAR_XML_SOURCE, false); - createXmlInConfigBaseForExternal(war); - doTestDeployment(true, true, true, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, true); - } - - - /* - * Expected behaviour for deployment of an XML file that points to an - * external DIR. - * deployXML copyXML unpackWARs XML WAR DIR - * Y/N Y/N Y/N Y N N - * - * Notes: Any context.xml file embedded in the external DIR file is ignored. - */ - @Test - public void testDeploymentXmlExternalDirXmlFFF() throws Exception { - File dir = createDirInExternal(true); - createXmlInConfigBaseForExternal(dir); - doTestDeployment(false, false, false, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalDirXmlFFT() throws Exception { - File dir = createDirInExternal(true); - createXmlInConfigBaseForExternal(dir); - doTestDeployment(false, false, true, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalDirXmlFTF() throws Exception { - File dir = createDirInExternal(true); - createXmlInConfigBaseForExternal(dir); - doTestDeployment(false, true, false, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalDirXmlFTT() throws Exception { - File dir = createDirInExternal(true); - createXmlInConfigBaseForExternal(dir); - doTestDeployment(false, true, true, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalDirXmlTFF() throws Exception { - File dir = createDirInExternal(true); - createXmlInConfigBaseForExternal(dir); - doTestDeployment(true, false, false, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalDirXmlTFT() throws Exception { - File dir = createDirInExternal(true); - createXmlInConfigBaseForExternal(dir); - doTestDeployment(true, false, true, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalDirXmlTTF() throws Exception { - File dir = createDirInExternal(true); - createXmlInConfigBaseForExternal(dir); - doTestDeployment(true, true, false, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - @Test - public void testDeploymentXmlExternalDirXmlTTT() throws Exception { - File dir = createDirInExternal(true); - createXmlInConfigBaseForExternal(dir); - doTestDeployment(true, true, true, - LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); - } - - - /* - * Expected behaviour for deployment of a WAR with an embedded XML file. - * deployXML copyXML unpackWARs XML WAR DIR - * N Y/N N N Y N - * N Y/N Y N Y Y - * Y N N N Y N - * Y N Y N Y Y - * Y Y N Y Y N - * Y Y Y Y Y Y - */ - @Test - public void testDeploymentWarXmlFFF() throws Exception { - createWar(WAR_XML_SOURCE, true); - doTestDeployment(false, false, false, - LifecycleState.FAILED, null, false, true, false); - } - - @Test - public void testDeploymentWarXmlFFT() throws Exception { - createWar(WAR_XML_SOURCE, true); - doTestDeployment(false, false, true, - LifecycleState.FAILED, null, false, true, true); - } - - @Test - public void testDeploymentWarXmlFTF() throws Exception { - createWar(WAR_XML_SOURCE, true); - doTestDeployment(false, true, false, - LifecycleState.FAILED, null, false, true, false); - } - - @Test - public void testDeploymentWarXmlFTT() throws Exception { - createWar(WAR_XML_SOURCE, true); - doTestDeployment(false, true, true, - LifecycleState.FAILED, null, false, true, true); - } - - @Test - public void testDeploymentWarXmlTFF() throws Exception { - createWar(WAR_XML_SOURCE, true); - doTestDeployment(true, false, false, - LifecycleState.STARTED, WAR_COOKIE_NAME, false, true, false); - } - - @Test - public void testDeploymentWarXmlTFT() throws Exception { - createWar(WAR_XML_SOURCE, true); - doTestDeployment(true, false, true, - LifecycleState.STARTED, WAR_COOKIE_NAME, false, true, true); - } - - @Test - public void testDeploymentWarXmlTTF() throws Exception { - createWar(WAR_XML_SOURCE, true); - doTestDeployment(true, true, false, - LifecycleState.STARTED, WAR_COOKIE_NAME, true, true, false); - } - - @Test - public void testDeploymentWarXmlTTT() throws Exception { - createWar(WAR_XML_SOURCE, true); - doTestDeployment(true, true, true, - LifecycleState.STARTED, WAR_COOKIE_NAME, true, true, true); - } - - - /* - * Expected behaviour for deployment of a WAR without an embedded XML file. - * deployXML copyXML unpackWARs XML WAR DIR - * Y/N Y/N N N Y N - * Y/N Y/N Y N Y Y - */ - @Test - public void testDeploymentWarFFF() throws Exception { - createWar(WAR_SOURCE, true); - doTestDeployment(false, false, false, - LifecycleState.STARTED, null, false, true, false); - } - - @Test - public void testDeploymentWarFFT() throws Exception { - createWar(WAR_SOURCE, true); - doTestDeployment(false, false, true, - LifecycleState.STARTED, null, false, true, true); - } - - @Test - public void testDeploymentWarFTF() throws Exception { - createWar(WAR_SOURCE, true); - doTestDeployment(false, true, false, - LifecycleState.STARTED, null, false, true, false); - } - - @Test - public void testDeploymentWarFTT() throws Exception { - createWar(WAR_SOURCE, true); - doTestDeployment(false, true, true, - LifecycleState.STARTED, null, false, true, true); - } - - @Test - public void testDeploymentWarTFF() throws Exception { - createWar(WAR_SOURCE, true); - doTestDeployment(true, false, false, - LifecycleState.STARTED, null, false, true, false); - } - - @Test - public void testDeploymentWarTFT() throws Exception { - createWar(WAR_SOURCE, true); - doTestDeployment(true, false, true, - LifecycleState.STARTED, null, false, true, true); - } - - @Test - public void testDeploymentWarTTF() throws Exception { - createWar(WAR_SOURCE, true); - doTestDeployment(true, true, false, - LifecycleState.STARTED, null, false, true, false); - } - - @Test - public void testDeploymentWarTTT() throws Exception { - createWar(WAR_SOURCE, true); - doTestDeployment(true, true, true, - LifecycleState.STARTED, null, false, true, true); - } - - - /* - * Expected behaviour for deployment of a DIR with an embedded XML file. - * deployXML copyXML unpackWARs XML WAR DIR - * N Y/N Y/N N N Y - * Y N Y/N N N Y - * Y Y Y/N Y N Y - */ - @Test - public void testDeploymentDirXmlFFF() throws Exception { - createDirInAppbase(true); - doTestDeployment(false, false, false, - LifecycleState.FAILED, null, false, false, true); - } - - @Test - public void testDeploymentDirXmlFFT() throws Exception { - createDirInAppbase(true); - doTestDeployment(false, false, true, - LifecycleState.FAILED, null, false, false, true); - } - - @Test - public void testDeploymentDirXmlFTF() throws Exception { - createDirInAppbase(true); - doTestDeployment(false, true, false, - LifecycleState.FAILED, null, false, false, true); - } - - @Test - public void testDeploymentDirXmlFTT() throws Exception { - createDirInAppbase(true); - doTestDeployment(false, true, true, - LifecycleState.FAILED, null, false, false, true); - } - - @Test - public void testDeploymentDirXmlTFF() throws Exception { - createDirInAppbase(true); - doTestDeployment(true, false, false, - LifecycleState.STARTED, DIR_COOKIE_NAME, false, false, true); - } - - @Test - public void testDeploymentDirXmlTFT() throws Exception { - createDirInAppbase(true); - doTestDeployment(true, false, true, - LifecycleState.STARTED, DIR_COOKIE_NAME, false, false, true); - } - - @Test - public void testDeploymentDirXmlTTF() throws Exception { - createDirInAppbase(true); - doTestDeployment(true, true, false, - LifecycleState.STARTED, DIR_COOKIE_NAME, true, false, true); - } - - @Test - public void testDeploymentDirXmlTTT() throws Exception { - createDirInAppbase(true); - doTestDeployment(true, true, true, - LifecycleState.STARTED, DIR_COOKIE_NAME, true, false, true); - } - - - /* - * Expected behaviour for deployment of a DIR without an embedded XML file. - * deployXML copyXML unpackWARs XML WAR DIR - * Y/N Y/N Y/N N N Y - */ - @Test - public void testDeploymentDirFFF() throws Exception { - createDirInAppbase(false); - doTestDeployment(false, false, false, - LifecycleState.STARTED, null, false, false, true); - } - - @Test - public void testDeploymentDirFFT() throws Exception { - createDirInAppbase(false); - doTestDeployment(false, false, true, - LifecycleState.STARTED, null, false, false, true); - } - - @Test - public void testDeploymentDirFTF() throws Exception { - createDirInAppbase(false); - doTestDeployment(false, true, false, - LifecycleState.STARTED, null, false, false, true); - } - - @Test - public void testDeploymentDirFTT() throws Exception { - createDirInAppbase(false); - doTestDeployment(false, true, true, - LifecycleState.STARTED, null, false, false, true); - } - - @Test - public void testDeploymentDirTFF() throws Exception { - createDirInAppbase(false); - doTestDeployment(true, false, false, - LifecycleState.STARTED, null, false, false, true); - } - - @Test - public void testDeploymentDirTFT() throws Exception { - createDirInAppbase(false); - doTestDeployment(true, false, true, - LifecycleState.STARTED, null, false, false, true); - } - - @Test - public void testDeploymentDirTTF() throws Exception { - createDirInAppbase(false); - doTestDeployment(true, true, false, - LifecycleState.STARTED, null, false, false, true); - } - - @Test - public void testDeploymentDirTTT() throws Exception { - createDirInAppbase(false); - doTestDeployment(true, true, true, - LifecycleState.STARTED, null, false, false, true); - } - - private void doTestDeployment(boolean deployXML, boolean copyXML, - boolean unpackWARs, LifecycleState resultState, String cookieName, - boolean resultXml, boolean resultWar, boolean resultDir) - throws Exception { - - Tomcat tomcat = getTomcatInstance(); - - // Start the instance - tomcat.start(); - - // Set the attributes - StandardHost host = (StandardHost) tomcat.getHost(); - host.setDeployXML(deployXML); - host.setCopyXML(copyXML); - host.setUnpackWARs(unpackWARs); - - // Trigger automatic deployment - host.backgroundProcess(); - - // Test the results - Context ctxt = (Context) tomcat.getHost().findChild(APP_NAME.getPath()); - if (resultState == null) { - Assert.assertNull(ctxt); - } else { - Assert.assertNotNull(ctxt); - Assert.assertEquals(resultState, ctxt.getState()); - Assert.assertEquals(cookieName, ctxt.getSessionCookieName()); - } - - File xml = new File( - host.getConfigBaseFile(), APP_NAME.getBaseName() + ".xml"); - Assert.assertEquals( - Boolean.valueOf(resultXml), Boolean.valueOf(xml.isFile())); - - File war = new File( - host.getAppBaseFile(), APP_NAME.getBaseName() + ".war"); - Assert.assertEquals( - Boolean.valueOf(resultWar), Boolean.valueOf(war.isFile())); - - File dir = new File(host.getAppBase(), APP_NAME.getBaseName()); - Assert.assertEquals( - Boolean.valueOf(resultDir), Boolean.valueOf(dir.isDirectory())); - } - - - private File createDirInAppbase(boolean withXml) throws IOException { - File dir = new File(getTomcatInstance().getHost().getAppBaseFile(), - APP_NAME.getBaseName()); - if (withXml) { - recursiveCopy(DIR_XML_SOURCE.toPath(), dir.toPath()); - } else { - recursiveCopy(DIR_SOURCE.toPath(), dir.toPath()); - } - return dir; - } - - private File createDirInExternal(boolean withXml) throws IOException { - File ext = new File(external, "external" + ".war"); - if (withXml) { - recursiveCopy(DIR_XML_SOURCE.toPath(), ext.toPath()); - } else { - recursiveCopy(DIR_SOURCE.toPath(), ext.toPath()); - } - return ext; - } - - private File createWar(File src, boolean useAppbase) throws IOException { - File dest; - if (useAppbase) { - dest = new File(getTomcatInstance().getHost().getAppBaseFile(), - APP_NAME.getBaseName() + ".war"); - } else { - dest = new File(external, "external" + ".war"); - } - Files.copy(src.toPath(), dest.toPath()); - // Make sure that HostConfig thinks the WAR has been modified. - Assert.assertTrue("Failed to set last modified for [" + dest + "]", dest.setLastModified( - System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - return dest; - } - - private File createXmlInConfigBaseForAppbase() throws IOException { - File xml = getXmlInConfigBaseForAppbase(); - File parent = xml.getParentFile(); - if (!parent.isDirectory()) { - Assert.assertTrue(parent.mkdirs()); - } - Files.copy(XML_SOURCE.toPath(), xml.toPath()); - // Make sure that HostConfig thinks the xml has been modified. - Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( - System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - return xml; - } - - private File getXmlInConfigBaseForAppbase() { - Host host = getTomcatInstance().getHost(); - return new File(host.getConfigBaseFile(), APP_NAME + ".xml"); - } - - private File createXmlInConfigBaseForExternal(File ext) throws IOException { - return createXmlInConfigBaseForExternal(ext, false); - } - - private File createXmlInConfigBaseForExternal(File ext, boolean antiLocking) - throws IOException { - File xml = new File(getTomcatInstance().getHost().getConfigBaseFile(), - APP_NAME + ".xml"); - File parent = xml.getParentFile(); - if (!parent.isDirectory()) { - Assert.assertTrue(parent.mkdirs()); - } - - try (FileOutputStream fos = new FileOutputStream(xml)) { - StringBuilder context = new StringBuilder(); - context.append(""); - fos.write(context.toString().getBytes(StandardCharsets.ISO_8859_1)); - } - // Make sure that HostConfig thinks the xml has been modified. - Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( - System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - return xml; - } - - public static class TesterContext extends StandardContext { - // No functional change - } -} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentBrokenApp.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentBrokenApp.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentBrokenApp.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentBrokenApp.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * 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.startup; + +import org.junit.Test; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentBrokenApp extends HostConfigAutomaticDeploymentBaseTest { + + @Test + public void testBrokenAppWithAntiLockingF() throws Exception { + doTestBrokenAppWithAntiLocking(false); + } + + @Test + public void testBrokenAppWithAntiLockingT() throws Exception { + doTestBrokenAppWithAntiLocking(true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,1162 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.catalina.startup; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsInstanceOf.instanceOf; - -import org.junit.Assert; -import org.junit.Test; - -import org.apache.catalina.Context; -import org.apache.catalina.Host; -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleEvent; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.core.StandardContext; -import org.apache.catalina.core.StandardHost; -import org.apache.catalina.util.ContextName; - -/** - * The purpose of this class is to test the automatic deployment features of the - * {@link HostConfig} implementation. - */ -public class TestHostConfigAutomaticDeploymentC extends TomcatBaseTest { - - private static final ContextName APP_NAME = new ContextName("myapp", false); - private static final File XML_SOURCE = - new File("test/deployment/context.xml"); - private static final File WAR_XML_SOURCE = - new File("test/deployment/context.war"); - private static final File WAR_XML_COPYXML_FALSE_SOURCE = - new File("test/deployment/contextCopyXMLFalse.war"); - private static final File WAR_XML_COPYXML_TRUE_SOURCE = - new File("test/deployment/contextCopyXMLTrue.war"); - private static final File WAR_XML_UNPACKWAR_FALSE_SOURCE = - new File("test/deployment/contextUnpackWARFalse.war"); - private static final File WAR_XML_UNPACKWAR_TRUE_SOURCE = - new File("test/deployment/contextUnpackWARTrue.war"); - private static final File WAR_SOURCE = - new File("test/deployment/noContext.war"); - private static final File WAR_BROKEN_SOURCE = - new File("test/deployment/broken.war"); - private static final File DIR_XML_SOURCE = - new File("test/deployment/dirContext"); - private static final File DIR_XML_SOURCE_META_INF = - new File("test/deployment/dirContext/META-INF"); - private static final File DIR_SOURCE = - new File("test/deployment/dirNoContext"); - - private static final int XML = 1; - private static final int EXT = 2; - private static final int WAR = 3; - private static final int DIR = 4; - private static final int DIR_XML = 5; - - private static final int NONE = 1; - private static final int RELOAD = 2; - private static final int REDEPLOY = 3; - - private static final String XML_COOKIE_NAME = "XML_CONTEXT"; - private static final String WAR_COOKIE_NAME = "WAR_CONTEXT"; - private static final String DIR_COOKIE_NAME = "DIR_CONTEXT"; - // private static final String DEFAULT_COOKIE_NAME = "JSESSIONID"; - - private File external; - - @Override - public void setUp() throws Exception { - super.setUp(); - - Tomcat tomcat = getTomcatInstance(); - - external = new File(getTemporaryDirectory(), "external"); - if (!external.exists() && !external.mkdir()) { - Assert.fail("Unable to create external for test"); - } - - // Disable background thread - tomcat.getEngine().setBackgroundProcessorDelay(-1); - - // Enable deployer - tomcat.getHost().addLifecycleListener(new HostConfig()); - - // Disable deployment on start up - tomcat.getHost().setDeployOnStartup(false); - - // Clean-up after test - addDeleteOnTearDown(new File(tomcat.basedir, "/conf")); - addDeleteOnTearDown(external); - } - - - /* - * Expected behaviour for modification of files. - * - * Artifacts present Artifact Artifacts remaining - * XML WAR EXT DIR Modified XML WAR EXT DIR Action - * N N N Y DIR - - - M None - * N Y N N WAR - M - - Redeploy - * N Y N Y DIR - Y - M None - * N Y N Y WAR - M - R Redeploy - * Y N N N XML M - - - Redeploy - * Y N N Y DIR Y - - M None - * Y N N Y XML M - - Y Redeploy - * Y N Y N EXT Y - M - Reload if WAR - * Y N Y N XML M - Y - Redeploy - * Y N Y Y DIR Y - Y M None - * Y N Y Y EXT Y - M R Reload - * Y N Y Y XML M - Y Y Redeploy - * Y Y N N WAR Y M - - Reload - * Y Y N N XML M Y - - Redeploy - * Y Y N Y DIR Y Y - M None - * Y Y N Y WAR Y M - - Reload - * Y Y N Y XML M Y - Y Redeploy - */ - @Test - public void testModifyDirUpdateDir() throws Exception { - doTestModify(false, false, false, false, true, DIR, - false, false, true, DIR_COOKIE_NAME, NONE); - } - - @Test - public void testModifyWarUpdateWar() throws Exception { - doTestModify(false, false, false, true, false, WAR, - false, true, false, WAR_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testModifyWarDirUpdateDir() throws Exception { - // DIR_COOKIE_NAME since Tomcat is going to assume DIR is expanded WAR - doTestModify(false, false, false, true, true, DIR, - false, true, true, DIR_COOKIE_NAME, NONE); - } - - @Test - public void testModifyWarDirUpdateWar() throws Exception { - doTestModify(false, false, false, true, true, WAR, - false, true, true, WAR_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testModifyXmlUpdateXml() throws Exception { - doTestModify(true, false, false, false, false, XML, - true, false, false, XML_COOKIE_NAME, REDEPLOY, - LifecycleState.FAILED); - } - - @Test - public void testModifyXmlDirUpdateDir() throws Exception { - doTestModify(true, false, false, false, true, DIR, - true, false, true, XML_COOKIE_NAME, NONE); - } - - @Test - public void testModifyXmlDirUpdateXml() throws Exception { - doTestModify(true, false, false, false, true, XML, - true, false, true, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testModifyXmlExtwarUpdateExtwar() throws Exception { - doTestModify(true, true, false, false, false, EXT, - true, false, false, XML_COOKIE_NAME, RELOAD); - } - - @Test - public void testModifyXmlExtdirUpdateExtdir() throws Exception { - doTestModify(true, false, true, false, false, EXT, - true, false, false, XML_COOKIE_NAME, NONE); - } - - @Test - public void testModifyXmlExtwarUpdateXml() throws Exception { - doTestModify(true, true, false, false, false, XML, - true, false, false, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testModifyXmlExtdirUpdateXml() throws Exception { - doTestModify(true, false, true, false, false, XML, - true, false, false, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testModifyXmlExtwarDirUpdateDir() throws Exception { - doTestModify(true, true, false, false, true, DIR, - true, false, false, XML_COOKIE_NAME, NONE); - } - - @Test - public void testModifyXmlExtwarDirUpdateExt() throws Exception { - doTestModify(true, true, false, false, true, EXT, - true, false, true, XML_COOKIE_NAME, RELOAD); - } - - @Test - public void testModifyXmlExtwarDirUpdateXml() throws Exception { - doTestModify(true, true, false, false, true, XML, - true, false, false, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testModifyXmlWarUpdateWar() throws Exception { - doTestModify(true, false, false, true, false, WAR, - true, true, false, XML_COOKIE_NAME, RELOAD); - } - - @Test - public void testModifyXmlWarUpdateXml() throws Exception { - doTestModify(true, false, false, true, false, XML, - true, true, false, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testModifyXmlWarDirUpdateDir() throws Exception { - doTestModify(true, false, false, true, true, DIR, - true, true, true, XML_COOKIE_NAME, NONE); - } - - @Test - public void testModifyXmlWarDirUpdateWar() throws Exception { - doTestModify(true, false, false, true, true, WAR, - true, true, true, XML_COOKIE_NAME, RELOAD); - } - - @Test - public void testModifyXmlWarDirUpdateXml() throws Exception { - doTestModify(true, false, false, true, true, XML, - true, true, true, XML_COOKIE_NAME, REDEPLOY); - } - - private void doTestModify(boolean startXml, boolean startExternalWar, - boolean startExternalDir, boolean startWar, boolean startDir, - int toModify, boolean resultXml, boolean resultWar, - boolean resultDir, String resultCookieName, int resultAction) - throws Exception { - doTestModify(startXml, startExternalWar, startExternalDir, startWar, - startDir, toModify, resultXml, resultWar, resultDir, - resultCookieName, resultAction, LifecycleState.STARTED); - } - - private void doTestModify(boolean startXml, boolean startExternalWar, - boolean startExternalDir, boolean startWar, boolean startDir, - int toModify, boolean resultXml, boolean resultWar, - boolean resultDir, String resultCookieName, int resultAction, - LifecycleState resultState) throws Exception { - - Tomcat tomcat = getTomcatInstance(); - StandardHost host = (StandardHost) tomcat.getHost(); - - // Init - File xml = null; - File ext = null; - File war = null; - File dir = null; - - long testStartTime = System.currentTimeMillis(); - - if (startXml && !startExternalWar && !startExternalDir) { - xml = createXmlInConfigBaseForAppbase(); - } - if (startExternalWar) { - ext = createWar(WAR_XML_SOURCE, false); - xml = createXmlInConfigBaseForExternal(ext); - } - if (startExternalDir) { - ext = createDirInAppbase(true); - xml = createXmlInConfigBaseForExternal(ext); - } - if (startWar) { - war = createWar(WAR_XML_SOURCE, true); - } - if (startDir) { - dir = createDirInAppbase(true); - } - - if ((startWar || startExternalWar) && !startDir) { - host.setUnpackWARs(false); - } - - // Deploy the files we copied - tomcat.start(); - host.backgroundProcess(); - - // Update the last modified time. Make sure that the OS reports a change - // in modification time that HostConfig can detect. Change is made - // relative to test start time to ensure new modification times are - // sufficiently different. - switch (toModify) { - case XML: - if (xml == null) { - Assert.fail(); - } else { - Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( - testStartTime - 10 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - } - break; - case EXT: - if (ext == null) { - Assert.fail(); - } else { - Assert.assertTrue("Failed to set last modified for [" + ext + "]", ext.setLastModified( - testStartTime - 10 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - } - break; - case WAR: - if (war == null) { - Assert.fail(); - } else { - Assert.assertTrue("Failed to set last modified for [" + war + "]", war.setLastModified( - testStartTime - 10 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - } - break; - case DIR: - if (dir == null) { - Assert.fail(); - } else { - Assert.assertTrue("Failed to set last modified for [" + dir + "]", dir.setLastModified( - testStartTime - 10 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - } - break; - default: - Assert.fail(); - } - - Context oldContext = (Context) host.findChild(APP_NAME.getName()); - StateTracker tracker = new StateTracker(); - oldContext.addLifecycleListener(tracker); - - // Trigger an auto-deployment cycle - host.backgroundProcess(); - - Context newContext = (Context) host.findChild(APP_NAME.getName()); - - // Check the results - if (resultXml) { - if (xml == null) { - Assert.fail(); - } else { - Assert.assertTrue(xml.isFile()); - } - } - if (resultWar) { - if (war == null) { - Assert.fail(); - } else { - Assert.assertTrue(war.isFile()); - } - } - if (resultDir) { - if (dir == null) { - Assert.fail(); - } else { - Assert.assertTrue(dir.isDirectory()); - } - } - - if (!resultXml && (startExternalWar || startExternalDir)) { - Assert.assertNull(newContext); - } - if (!resultWar && !resultDir) { - if (resultXml) { - Assert.assertNotNull(newContext); - if (!startExternalWar && !startExternalDir) { - Assert.assertEquals(LifecycleState.FAILED, - newContext.getState()); - } else { - Assert.assertEquals(LifecycleState.STARTED, - newContext.getState()); - } - } else { - Assert.assertNull(newContext); - } - } - - if (newContext != null) { - Assert.assertEquals(resultCookieName, - newContext.getSessionCookieName()); - Assert.assertEquals(resultState, newContext.getState()); - } - - if (resultAction == NONE) { - Assert.assertSame(oldContext, newContext); - Assert.assertEquals("", tracker.getHistory()); - } else if (resultAction == RELOAD) { - Assert.assertSame(oldContext, newContext); - Assert.assertEquals("stopstart", tracker.getHistory()); - } else if (resultAction == REDEPLOY) { - Assert.assertNotSame(oldContext, newContext); - // No init or start as that will be in a new context object - Assert.assertEquals("stopafter_destroy", tracker.getHistory()); - } else { - Assert.fail(); - } - } - - - /* - * Expected behaviour for the addition of files. - * - * Artifacts present copyXML deployXML Artifact Artifacts remaining - * XML WAR EXT DIR Added XML WAR EXT DIR Action - * N Y N N N Y DIR - Y - A None - * N N N Y N Y WAR - A - R Redeploy - * Y N N N N Y DIR Y - - A None - * N N N Y N Y XML A - - Y Redeploy - * Y N N N N Y WAR Y A - - Reload - * N Y N N N Y XML A Y - - Redeploy - * Y Y N N N Y DIR Y Y - A None - * Y N N Y N Y WAR Y A - N Reload - * N Y N Y N Y XML A Y - Y Redeploy - * Y N Y N N Y DIR Y - Y A None - * Y N Y N N Y WAR Y A Y - None - * N N N Y N Y EXT A - A R Redeploy - * N Y N N N Y EXT A Y A - Redeploy - * - * N N N Y Y/N N DIR+XML - - - Y Redeploy (failed) - * N N N Y Y Y DIR+XML A - - Y Redeploy - * N N N Y N Y DIR+XML - - - Y Redeploy - * - * Addition of a file is treated as if the added file has been modified - * with the following additional actions: - * - If a WAR is added, any DIR is removed and may be recreated depending on - * unpackWARs. - * - If an XML file is added that refers to an external docBase any WAR or - * DIR in the appBase will be removed. The DIR may be recreated if the - * external resource is a WAR and unpackWARs is true. - * - If a DIR is added when a WAR already exists and unpackWARs is false, - * the DIR will be ignored but a warning will be logged when the DIR is - * first detected. If the WAR is removed, the DIR will be left and may be - * deployed via automatic deployment. - * - If a WAR is added when an external WAR already exists for the same - * context, the WAR will be treated the same way as a DIR is treated in - * the previous bullet point. - */ - @Test - public void testAdditionWarAddDir() throws Exception { - doTestAddition(false, false, false, true, false, DIR, - false, true, true, WAR_COOKIE_NAME, NONE); - } - - @Test - public void testAdditionDirAddWar() throws Exception { - doTestAddition(false, false, false, false, true, WAR, - false, true, true, WAR_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testAdditionXmlAddDir() throws Exception { - doTestAddition(true, false, false, false, false, DIR, - true, false, true, XML_COOKIE_NAME, NONE); - } - - @Test - public void testAdditionDirAddXml() throws Exception { - doTestAddition(false, false, false, false, true, XML, - true, false, true, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testAdditionXmlAddWar() throws Exception { - doTestAddition(true, false, false, false, false, WAR, - true, true, false, XML_COOKIE_NAME, RELOAD); - } - - @Test - public void testAdditionWarAddXml() throws Exception { - doTestAddition(false, false, false, true, false, XML, - true, true, false, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testAdditionXmlWarAddDir() throws Exception { - doTestAddition(true, false, false, true, false, DIR, - true, true, true, XML_COOKIE_NAME, NONE); - } - - @Test - public void testAdditionXmlDirAddWar() throws Exception { - doTestAddition(true, false, false, false, true, WAR, - true, true, false, XML_COOKIE_NAME, RELOAD); - } - - @Test - public void testAdditionWarDirAddXml() throws Exception { - doTestAddition(false, false, false, true, true, XML, - true, true, true, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testAdditionXmlExtwarAddDir() throws Exception { - doTestAddition(true, true, false, false, false, DIR, - true, false, true, XML_COOKIE_NAME, NONE); - } - - @Test - public void testAdditionXmlExtdirAddDir() throws Exception { - doTestAddition(true, false, true, false, false, DIR, - true, false, true, XML_COOKIE_NAME, NONE); - } - - @Test - public void testAdditionXmlExtwarAddWar() throws Exception { - doTestAddition(true, true, false, false, false, WAR, - true, true, false, XML_COOKIE_NAME, NONE); - } - - @Test - public void testAdditionXmlExtdirAddWar() throws Exception { - doTestAddition(true, false, true, false, false, WAR, - true, true, false, XML_COOKIE_NAME, NONE); - } - - @Test - public void testAdditionDirAddXmlExtwar() throws Exception { - doTestAddition(false, false, false, false, true, EXT, - true, false, true, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testAdditionWarAddXmlExtwar() throws Exception { - doTestAddition(false, false, false, true, false, EXT, - true, true, false, XML_COOKIE_NAME, REDEPLOY); - } - - @Test - public void testAdditionDirAddDirXmlTF() throws Exception { - doTestAddition(false, false, false, false, true, true, false, DIR_XML, - false, false, true, null, REDEPLOY, LifecycleState.FAILED); - } - - @Test - public void testAdditionDirAddDirXmlFF() throws Exception { - doTestAddition(false, false, false, false, true, false, false, DIR_XML, - false, false, true, null, REDEPLOY, LifecycleState.FAILED); - } - - @Test - public void testAdditionDirAddDirXmlTT() throws Exception { - doTestAddition(false, false, false, false, true, true, true, DIR_XML, - true, false, true, DIR_COOKIE_NAME, REDEPLOY, - LifecycleState.STARTED); - } - - @Test - public void testAdditionDirAddDirXmlFT() throws Exception { - doTestAddition(false, false, false, false, true, false, true, DIR_XML, - false, false, true, DIR_COOKIE_NAME, REDEPLOY, - LifecycleState.STARTED); - } - - private void doTestAddition(boolean startXml, boolean startExternalWar, - boolean startExternalDir, boolean startWar, boolean startDir, - int toAdd, boolean resultXml, boolean resultWar, - boolean resultDir, String resultCookieName, int resultAction) - throws Exception { - - doTestAddition(startXml, startExternalWar, startExternalDir, startWar, - startDir, false, true, toAdd, resultXml, resultWar, resultDir, - resultCookieName, resultAction, LifecycleState.STARTED); - } - - private void doTestAddition(boolean startXml, boolean startExternalWar, - boolean startExternalDir, boolean startWar, boolean startDir, - boolean copyXML, boolean deployXML, int toAdd, boolean resultXml, - boolean resultWar, boolean resultDir, String resultCookieName, - int resultAction, LifecycleState state) - throws Exception { - - Tomcat tomcat = getTomcatInstance(); - StandardHost host = (StandardHost) tomcat.getHost(); - - // Init - File xml = null; - File ext = null; - File war = null; - File dir = null; - - if (startXml && !startExternalWar && !startExternalDir) { - xml = createXmlInConfigBaseForAppbase(); - } - if (startExternalWar) { - ext = createWar(WAR_XML_SOURCE, false); - xml = createXmlInConfigBaseForExternal(ext); - } - if (startExternalDir) { - ext = createDirInExternal(true); - xml = createXmlInConfigBaseForExternal(ext); - } - if (startWar) { - war = createWar(WAR_XML_SOURCE, true); - } - if (startDir) { - dir = createDirInAppbase(toAdd != DIR_XML); - } - - if ((startWar || startExternalWar) && !startDir) { - host.setUnpackWARs(false); - } - - host.setCopyXML(copyXML); - host.setDeployXML(deployXML); - - // Deploy the files we copied - tomcat.start(); - host.backgroundProcess(); - - // Change the specified file - switch (toAdd) { - case XML: - if (xml == null) { - xml = createXmlInConfigBaseForAppbase(); - } else { - Assert.fail(); - } - break; - case EXT: - if (ext == null && xml == null) { - ext = createWar(WAR_XML_SOURCE, false); - xml = createXmlInConfigBaseForExternal(ext); - } else { - Assert.fail(); - } - break; - case WAR: - if (war == null) { - war = createWar(WAR_XML_SOURCE, true); - } else { - Assert.fail(); - } - break; - case DIR: - if (dir == null) { - dir = createDirInAppbase(true); - } else { - Assert.fail(); - } - break; - case DIR_XML: - dir = createDirXmlInAppbase(); - xml = getXmlInConfigBaseForAppbase(); - break; - default: - Assert.fail(); - } - - Context oldContext = (Context) host.findChild(APP_NAME.getName()); - StateTracker tracker = new StateTracker(); - oldContext.addLifecycleListener(tracker); - - // Trigger an auto-deployment cycle - host.backgroundProcess(); - - Context newContext = (Context) host.findChild(APP_NAME.getName()); - - // Check the results - if (resultXml) { - if (xml == null) { - Assert.fail(); - } else { - Assert.assertTrue(xml.isFile()); - } - } - if (resultWar) { - if (war == null) { - Assert.fail(); - } else { - Assert.assertTrue(war.isFile()); - } - } - if (resultDir) { - if (dir == null) { - Assert.fail(); - } else { - Assert.assertTrue(dir.isDirectory()); - } - } - - if (!resultXml && (startExternalWar || startExternalDir)) { - Assert.assertNull(newContext); - } - if (!resultWar && !resultDir) { - if (resultXml) { - Assert.assertNotNull(newContext); - if (!startExternalWar && !startExternalDir) { - Assert.assertEquals(LifecycleState.FAILED, - newContext.getState()); - } else { - Assert.assertEquals(LifecycleState.STARTED, - newContext.getState()); - } - } else { - Assert.assertNull(newContext); - } - } - - if (newContext != null) { - Assert.assertEquals(resultCookieName, - newContext.getSessionCookieName()); - } - - if (resultAction == NONE) { - Assert.assertSame(oldContext, newContext); - Assert.assertEquals("", tracker.getHistory()); - } else if (resultAction == RELOAD) { - Assert.assertSame(oldContext, newContext); - Assert.assertEquals("stopstart", tracker.getHistory()); - } else if (resultAction == REDEPLOY) { - if (newContext == null) { - Assert.fail(); - } else { - Assert.assertEquals(state, newContext.getState()); - } - Assert.assertNotSame(oldContext, newContext); - // No init or start as that will be in a new context object - Assert.assertEquals("stopafter_destroy", tracker.getHistory()); - } else { - Assert.fail(); - } - } - - - /* - * Test context unpackWAR setting. - * If context.getUnpackWAR != Host.getUnpackWARs the Host wins. - */ - @Test - public void testUnpackWARFFF() throws Exception { - doTestUnpackWAR(false, false, false, false); - } - - @Test - public void testUnpackWARFFT() throws Exception { - doTestUnpackWAR(false, false, true, false); - } - - @Test - public void testUnpackWARFTF() throws Exception { - doTestUnpackWAR(false, true, false, false); - } - - @Test - public void testUnpackWARFTT() throws Exception { - doTestUnpackWAR(false, true, true, false); - } - - @Test - public void testUnpackWARTFF() throws Exception { - doTestUnpackWAR(true, false, false, false); - } - - @Test - public void testUnpackWARTFT() throws Exception { - // External WAR - therefore XML in WAR will be ignored - doTestUnpackWAR(true, false, true, true); - } - - @Test - public void testUnpackWARTTF() throws Exception { - doTestUnpackWAR(true, true, false, true); - } - - @Test - public void testUnpackWARTTT() throws Exception { - doTestUnpackWAR(true, true, true, true); - } - - private void doTestUnpackWAR(boolean unpackWARs, boolean unpackWAR, - boolean external, boolean resultDir) throws Exception { - - Tomcat tomcat = getTomcatInstance(); - StandardHost host = (StandardHost) tomcat.getHost(); - - host.setUnpackWARs(unpackWARs); - - tomcat.start(); - - File war; - if (unpackWAR) { - war = createWar(WAR_XML_UNPACKWAR_TRUE_SOURCE, !external); - } else { - war = createWar(WAR_XML_UNPACKWAR_FALSE_SOURCE, !external); - } - if (external) { - createXmlInConfigBaseForExternal(war); - } - - host.backgroundProcess(); - - File dir = new File(host.getAppBase(), APP_NAME.getBaseName()); - Assert.assertEquals( - Boolean.valueOf(resultDir), Boolean.valueOf(dir.isDirectory())); - } - - - @Test - public void testBrokenAppWithAntiLockingF() throws Exception { - testBrokenAppWithAntiLocking(false); - } - - @Test - public void testBrokenAppWithAntiLockingT() throws Exception { - testBrokenAppWithAntiLocking(true); - } - - private void testBrokenAppWithAntiLocking(boolean unpackWARs) - throws Exception { - - Tomcat tomcat = getTomcatInstance(); - StandardHost host = (StandardHost) tomcat.getHost(); - - host.setUnpackWARs(unpackWARs); - - File war = createWar(WAR_BROKEN_SOURCE, false); - createXmlInConfigBaseForExternal(war, true); - - File dir = new File(host.getAppBaseFile(), APP_NAME.getBaseName()); - - tomcat.start(); - - // Simulate deploy on start-up - tomcat.getHost().backgroundProcess(); - - Assert.assertTrue(war.isFile()); - if (unpackWARs) { - Assert.assertTrue(dir.isDirectory()); - } - } - - private File createDirInAppbase(boolean withXml) throws IOException { - File dir = new File(getTomcatInstance().getHost().getAppBaseFile(), - APP_NAME.getBaseName()); - if (withXml) { - recursiveCopy(DIR_XML_SOURCE.toPath(), dir.toPath()); - } else { - recursiveCopy(DIR_SOURCE.toPath(), dir.toPath()); - } - return dir; - } - - private File createDirXmlInAppbase() throws IOException { - File dir = new File(getTomcatInstance().getHost().getAppBaseFile(), - APP_NAME.getBaseName() + "/META-INF"); - recursiveCopy(DIR_XML_SOURCE_META_INF.toPath(), dir.toPath()); - return dir; - } - - private File createDirInExternal(boolean withXml) throws IOException { - File ext = new File(external, "external" + ".war"); - if (withXml) { - recursiveCopy(DIR_XML_SOURCE.toPath(), ext.toPath()); - } else { - recursiveCopy(DIR_SOURCE.toPath(), ext.toPath()); - } - return ext; - } - - private File createWar(File src, boolean useAppbase) throws IOException { - File dest; - if (useAppbase) { - dest = new File(getTomcatInstance().getHost().getAppBaseFile(), - APP_NAME.getBaseName() + ".war"); - } else { - dest = new File(external, "external" + ".war"); - } - Files.copy(src.toPath(), dest.toPath()); - // Make sure that HostConfig thinks the WAR has been modified. - Assert.assertTrue("Failed to set last modified for [" + dest + "]", dest.setLastModified( - System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - return dest; - } - - private File createXmlInConfigBaseForAppbase() throws IOException { - File xml = getXmlInConfigBaseForAppbase(); - File parent = xml.getParentFile(); - if (!parent.isDirectory()) { - Assert.assertTrue(parent.mkdirs()); - } - Files.copy(XML_SOURCE.toPath(), xml.toPath()); - // Make sure that HostConfig thinks the xml has been modified. - Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( - System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - return xml; - } - - private File getXmlInConfigBaseForAppbase() { - Host host = getTomcatInstance().getHost(); - return new File(host.getConfigBaseFile(), APP_NAME + ".xml"); - } - - private File createXmlInConfigBaseForExternal(File ext) throws IOException { - return createXmlInConfigBaseForExternal(ext, false); - } - - private File createXmlInConfigBaseForExternal(File ext, boolean antiLocking) - throws IOException { - File xml = new File(getTomcatInstance().getHost().getConfigBaseFile(), - APP_NAME + ".xml"); - File parent = xml.getParentFile(); - if (!parent.isDirectory()) { - Assert.assertTrue(parent.mkdirs()); - } - - try (FileOutputStream fos = new FileOutputStream(xml)) { - StringBuilder context = new StringBuilder(); - context.append(""); - fos.write(context.toString().getBytes(StandardCharsets.ISO_8859_1)); - } - // Make sure that HostConfig thinks the xml has been modified. - Assert.assertTrue("Failed to set last modified for [" + xml + "]", xml.setLastModified( - System.currentTimeMillis() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - return xml; - } - - private static class StateTracker implements LifecycleListener { - - private StringBuilder stateHistory = new StringBuilder(); - - @Override - public void lifecycleEvent(LifecycleEvent event) { - - String type = event.getType(); - - if (type.equals(Lifecycle.START_EVENT) || - type.equals(Lifecycle.STOP_EVENT) || - type.equals(Lifecycle.AFTER_DESTROY_EVENT)) { - stateHistory.append(type); - } - } - - - public String getHistory() { - return stateHistory.toString(); - } - } - - - /* - * Test context copyXML setting. - * If context.copyXML != Host.copyXML the Host wins. - * For external WARs, a context.xml must always already exist - */ - @Test - public void testCopyXMLFFF() throws Exception { - doTestCopyXML(false, false, false, false); - } - - @Test - public void testCopyXMLFFT() throws Exception { - doTestCopyXML(false, false, true, true); - } - - @Test - public void testCopyXMLFTF() throws Exception { - doTestCopyXML(false, true, false, true); - } - - @Test - public void testCopyXMLFTT() throws Exception { - doTestCopyXML(false, true, true, true); - } - - @Test - public void testCopyXMLTFF() throws Exception { - doTestCopyXML(true, false, false, true); - } - - @Test - public void testCopyXMLTFT() throws Exception { - doTestCopyXML(true, false, true, true); - } - - @Test - public void testCopyXMLTTF() throws Exception { - doTestCopyXML(true, true, false, true); - } - - @Test - public void testCopyXMLTTT() throws Exception { - doTestCopyXML(true, true, true, true); - } - - private void doTestCopyXML(boolean copyXmlHost, boolean copyXmlWar, - boolean external, boolean resultXml) throws Exception { - - Tomcat tomcat = getTomcatInstance(); - StandardHost host = (StandardHost) tomcat.getHost(); - - host.setCopyXML(copyXmlHost); - - tomcat.start(); - - File war; - if (copyXmlWar) { - war = createWar(WAR_XML_COPYXML_TRUE_SOURCE, !external); - } else { - war = createWar(WAR_XML_COPYXML_FALSE_SOURCE, !external); - } - if (external) { - createXmlInConfigBaseForExternal(war); - } - - host.backgroundProcess(); - - File xml = new File(host.getConfigBaseFile(), - APP_NAME.getBaseName() + ".xml"); - Assert.assertEquals( - Boolean.valueOf(resultXml), Boolean.valueOf(xml.isFile())); - - Context context = (Context) host.findChild(APP_NAME.getName()); - if (external) { - Assert.assertEquals(XML_COOKIE_NAME, - context.getSessionCookieName()); - } else { - Assert.assertEquals(WAR_COOKIE_NAME, - context.getSessionCookieName()); - } - } - - - @Test - public void testSetContextClassName() throws Exception { - - Tomcat tomcat = getTomcatInstance(); - - Host host = tomcat.getHost(); - if (host instanceof StandardHost) { - StandardHost standardHost = (StandardHost) host; - standardHost.setContextClass(TesterContext.class.getName()); - } - - // Copy the WAR file - File war = new File(host.getAppBaseFile(), - APP_NAME.getBaseName() + ".war"); - Files.copy(WAR_XML_SOURCE.toPath(), war.toPath()); - - // Deploy the copied war - tomcat.start(); - host.backgroundProcess(); - - // Check the Context class - Context ctxt = (Context) host.findChild(APP_NAME.getName()); - - assertThat(ctxt, instanceOf(TesterContext.class)); - } - - - public static class TesterContext extends StandardContext { - // No functional change - } - - - @Test - public void testUpdateWarOfflineNoContextFF() throws Exception { - doTestUpdateWarOffline(WAR_SOURCE, false, false); - } - - - @Test - public void testUpdateWarOfflineNoContextTF() throws Exception { - doTestUpdateWarOffline(WAR_SOURCE, true, false); - } - - - @Test - public void testUpdateWarOfflineNoContextFT() throws Exception { - doTestUpdateWarOffline(WAR_SOURCE, false, true); - } - - - @Test - public void testUpdateWarOfflineNoContextTT() throws Exception { - doTestUpdateWarOffline(WAR_SOURCE, true, true); - } - - - @Test - public void testUpdateWarOfflineContextFF() throws Exception { - doTestUpdateWarOffline(WAR_XML_SOURCE, false, false); - } - - - @Test - public void testUpdateWarOfflineContextTF() throws Exception { - doTestUpdateWarOffline(WAR_XML_SOURCE, true, false); - } - - - @Test - public void testUpdateWarOfflineContextFT() throws Exception { - doTestUpdateWarOffline(WAR_XML_SOURCE, false, true); - } - - - @Test - public void testUpdateWarOfflineContextTT() throws Exception { - doTestUpdateWarOffline(WAR_XML_SOURCE, true, true); - } - - - private void doTestUpdateWarOffline(File srcWar, boolean deployOnStartUp, boolean autoDeploy) - throws Exception { - Tomcat tomcat = getTomcatInstance(); - StandardHost host = (StandardHost) tomcat.getHost(); - host.setDeployOnStartup(deployOnStartUp); - - File war = createWar(srcWar, true); - // Make the WAR appear to have been created earlier - Assert.assertTrue("Failed to set last modified for [" + war + "]", war.setLastModified( - war.lastModified() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS)); - - tomcat.addWebapp(APP_NAME.getPath(), war.getAbsolutePath()); - tomcat.start(); - - // Get the last modified timestamp for the expanded dir - File dir = new File(host.getAppBase(), APP_NAME.getBaseName()); - // Make the DIR appear to have been created earlier - long lastModified = war.lastModified() - 2 * HostConfig.FILE_MODIFICATION_RESOLUTION_MS; - Assert.assertTrue("Failed to set last modified for [" + dir + "]", - dir.setLastModified(lastModified)); - - host.stop(); - Assert.assertTrue("Failed to set last modified for [" + war + "]", - war.setLastModified(System.currentTimeMillis())); - host.start(); - if (autoDeploy) { - host.backgroundProcess(); - } - - long newLastModified = dir.lastModified(); - - Assert.assertNotEquals("Timestamp hasn't changed", lastModified, newLastModified); - } -} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC8.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC8.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC8.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC8.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,504 @@ +/* + * 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.startup; + +import java.io.File; +import java.nio.file.Files; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsInstanceOf.instanceOf; + +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.Host; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.core.StandardHost; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentC8 extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for modification of files. + * + * Artifacts present Artifact Artifacts remaining + * XML WAR EXT DIR Modified XML WAR EXT DIR Action + * N N N Y DIR - - - M None + * N Y N N WAR - M - - Redeploy + * N Y N Y DIR - Y - M None + * N Y N Y WAR - M - R Redeploy + * Y N N N XML M - - - Redeploy + * Y N N Y DIR Y - - M None + * Y N N Y XML M - - Y Redeploy + * Y N Y N EXT Y - M - Reload if WAR + * Y N Y N XML M - Y - Redeploy + * Y N Y Y DIR Y - Y M None + * Y N Y Y EXT Y - M R Reload + * Y N Y Y XML M - Y Y Redeploy + * Y Y N N WAR Y M - - Reload + * Y Y N N XML M Y - - Redeploy + * Y Y N Y DIR Y Y - M None + * Y Y N Y WAR Y M - - Reload + * Y Y N Y XML M Y - Y Redeploy + */ + @Test + public void testModifyDirUpdateDir() throws Exception { + doTestModify(false, false, false, false, true, DIR, + false, false, true, DIR_COOKIE_NAME, NONE); + } + + @Test + public void testModifyWarUpdateWar() throws Exception { + doTestModify(false, false, false, true, false, WAR, + false, true, false, WAR_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyWarDirUpdateDir() throws Exception { + // DIR_COOKIE_NAME since Tomcat is going to assume DIR is expanded WAR + doTestModify(false, false, false, true, true, DIR, + false, true, true, DIR_COOKIE_NAME, NONE); + } + + @Test + public void testModifyWarDirUpdateWar() throws Exception { + doTestModify(false, false, false, true, true, WAR, + false, true, true, WAR_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlUpdateXml() throws Exception { + doTestModify(true, false, false, false, false, XML, + true, false, false, XML_COOKIE_NAME, REDEPLOY, + LifecycleState.FAILED); + } + + @Test + public void testModifyXmlDirUpdateDir() throws Exception { + doTestModify(true, false, false, false, true, DIR, + true, false, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testModifyXmlDirUpdateXml() throws Exception { + doTestModify(true, false, false, false, true, XML, + true, false, true, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlExtwarUpdateExtwar() throws Exception { + doTestModify(true, true, false, false, false, EXT, + true, false, false, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testModifyXmlExtdirUpdateExtdir() throws Exception { + doTestModify(true, false, true, false, false, EXT, + true, false, false, XML_COOKIE_NAME, NONE); + } + + @Test + public void testModifyXmlExtwarUpdateXml() throws Exception { + doTestModify(true, true, false, false, false, XML, + true, false, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlExtdirUpdateXml() throws Exception { + doTestModify(true, false, true, false, false, XML, + true, false, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlExtwarDirUpdateDir() throws Exception { + doTestModify(true, true, false, false, true, DIR, + true, false, false, XML_COOKIE_NAME, NONE); + } + + @Test + public void testModifyXmlExtwarDirUpdateExt() throws Exception { + doTestModify(true, true, false, false, true, EXT, + true, false, true, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testModifyXmlExtwarDirUpdateXml() throws Exception { + doTestModify(true, true, false, false, true, XML, + true, false, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlWarUpdateWar() throws Exception { + doTestModify(true, false, false, true, false, WAR, + true, true, false, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testModifyXmlWarUpdateXml() throws Exception { + doTestModify(true, false, false, true, false, XML, + true, true, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlWarDirUpdateDir() throws Exception { + doTestModify(true, false, false, true, true, DIR, + true, true, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testModifyXmlWarDirUpdateWar() throws Exception { + doTestModify(true, false, false, true, true, WAR, + true, true, true, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testModifyXmlWarDirUpdateXml() throws Exception { + doTestModify(true, false, false, true, true, XML, + true, true, true, XML_COOKIE_NAME, REDEPLOY); + } + + /* + * Expected behaviour for the addition of files. + * + * Artifacts present copyXML deployXML Artifact Artifacts remaining + * XML WAR EXT DIR Added XML WAR EXT DIR Action + * N Y N N N Y DIR - Y - A None + * N N N Y N Y WAR - A - R Redeploy + * Y N N N N Y DIR Y - - A None + * N N N Y N Y XML A - - Y Redeploy + * Y N N N N Y WAR Y A - - Reload + * N Y N N N Y XML A Y - - Redeploy + * Y Y N N N Y DIR Y Y - A None + * Y N N Y N Y WAR Y A - N Reload + * N Y N Y N Y XML A Y - Y Redeploy + * Y N Y N N Y DIR Y - Y A None + * Y N Y N N Y WAR Y A Y - None + * N N N Y N Y EXT A - A R Redeploy + * N Y N N N Y EXT A Y A - Redeploy + * + * N N N Y Y/N N DIR+XML - - - Y Redeploy (failed) + * N N N Y Y Y DIR+XML A - - Y Redeploy + * N N N Y N Y DIR+XML - - - Y Redeploy + * + * Addition of a file is treated as if the added file has been modified + * with the following additional actions: + * - If a WAR is added, any DIR is removed and may be recreated depending on + * unpackWARs. + * - If an XML file is added that refers to an external docBase any WAR or + * DIR in the appBase will be removed. The DIR may be recreated if the + * external resource is a WAR and unpackWARs is true. + * - If a DIR is added when a WAR already exists and unpackWARs is false, + * the DIR will be ignored but a warning will be logged when the DIR is + * first detected. If the WAR is removed, the DIR will be left and may be + * deployed via automatic deployment. + * - If a WAR is added when an external WAR already exists for the same + * context, the WAR will be treated the same way as a DIR is treated in + * the previous bullet point. + */ + @Test + public void testAdditionWarAddDir() throws Exception { + doTestAddition(false, false, false, true, false, DIR, + false, true, true, WAR_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionDirAddWar() throws Exception { + doTestAddition(false, false, false, false, true, WAR, + false, true, true, WAR_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionXmlAddDir() throws Exception { + doTestAddition(true, false, false, false, false, DIR, + true, false, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionDirAddXml() throws Exception { + doTestAddition(false, false, false, false, true, XML, + true, false, true, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionXmlAddWar() throws Exception { + doTestAddition(true, false, false, false, false, WAR, + true, true, false, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testAdditionWarAddXml() throws Exception { + doTestAddition(false, false, false, true, false, XML, + true, true, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionXmlWarAddDir() throws Exception { + doTestAddition(true, false, false, true, false, DIR, + true, true, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionXmlDirAddWar() throws Exception { + doTestAddition(true, false, false, false, true, WAR, + true, true, false, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testAdditionWarDirAddXml() throws Exception { + doTestAddition(false, false, false, true, true, XML, + true, true, true, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionXmlExtwarAddDir() throws Exception { + doTestAddition(true, true, false, false, false, DIR, + true, false, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionXmlExtdirAddDir() throws Exception { + doTestAddition(true, false, true, false, false, DIR, + true, false, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionXmlExtwarAddWar() throws Exception { + doTestAddition(true, true, false, false, false, WAR, + true, true, false, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionXmlExtdirAddWar() throws Exception { + doTestAddition(true, false, true, false, false, WAR, + true, true, false, XML_COOKIE_NAME, NONE); + } + + @Test + public void testAdditionDirAddXmlExtwar() throws Exception { + doTestAddition(false, false, false, false, true, EXT, + true, false, true, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionWarAddXmlExtwar() throws Exception { + doTestAddition(false, false, false, true, false, EXT, + true, true, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testAdditionDirAddDirXmlTF() throws Exception { + doTestAddition(false, false, false, false, true, true, false, DIR_XML, + false, false, true, null, REDEPLOY, LifecycleState.FAILED); + } + + @Test + public void testAdditionDirAddDirXmlFF() throws Exception { + doTestAddition(false, false, false, false, true, false, false, DIR_XML, + false, false, true, null, REDEPLOY, LifecycleState.FAILED); + } + + @Test + public void testAdditionDirAddDirXmlTT() throws Exception { + doTestAddition(false, false, false, false, true, true, true, DIR_XML, + true, false, true, DIR_COOKIE_NAME, REDEPLOY, + LifecycleState.STARTED); + } + + @Test + public void testAdditionDirAddDirXmlFT() throws Exception { + doTestAddition(false, false, false, false, true, false, true, DIR_XML, + false, false, true, DIR_COOKIE_NAME, REDEPLOY, + LifecycleState.STARTED); + } + + /* + * Test context unpackWAR setting. + * If context.getUnpackWAR != Host.getUnpackWARs the Host wins. + */ + @Test + public void testUnpackWARFFF() throws Exception { + doTestUnpackWAR(false, false, false, false); + } + + @Test + public void testUnpackWARFFT() throws Exception { + doTestUnpackWAR(false, false, true, false); + } + + @Test + public void testUnpackWARFTF() throws Exception { + doTestUnpackWAR(false, true, false, false); + } + + @Test + public void testUnpackWARFTT() throws Exception { + doTestUnpackWAR(false, true, true, false); + } + + @Test + public void testUnpackWARTFF() throws Exception { + doTestUnpackWAR(true, false, false, false); + } + + @Test + public void testUnpackWARTFT() throws Exception { + // External WAR - therefore XML in WAR will be ignored + doTestUnpackWAR(true, false, true, true); + } + + @Test + public void testUnpackWARTTF() throws Exception { + doTestUnpackWAR(true, true, false, true); + } + + @Test + public void testUnpackWARTTT() throws Exception { + doTestUnpackWAR(true, true, true, true); + } + + @Test + public void testBrokenAppWithAntiLockingF() throws Exception { + doTestBrokenAppWithAntiLocking(false); + } + + @Test + public void testBrokenAppWithAntiLockingT() throws Exception { + doTestBrokenAppWithAntiLocking(true); + } + + /* + * Test context copyXML setting. + * If context.copyXML != Host.copyXML the Host wins. + * For external WARs, a context.xml must always already exist + */ + @Test + public void testCopyXMLFFF() throws Exception { + doTestCopyXML(false, false, false, false); + } + + @Test + public void testCopyXMLFFT() throws Exception { + doTestCopyXML(false, false, true, true); + } + + @Test + public void testCopyXMLFTF() throws Exception { + doTestCopyXML(false, true, false, true); + } + + @Test + public void testCopyXMLFTT() throws Exception { + doTestCopyXML(false, true, true, true); + } + + @Test + public void testCopyXMLTFF() throws Exception { + doTestCopyXML(true, false, false, true); + } + + @Test + public void testCopyXMLTFT() throws Exception { + doTestCopyXML(true, false, true, true); + } + + @Test + public void testCopyXMLTTF() throws Exception { + doTestCopyXML(true, true, false, true); + } + + @Test + public void testCopyXMLTTT() throws Exception { + doTestCopyXML(true, true, true, true); + } + + @Test + public void testSetContextClassName() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + Host host = tomcat.getHost(); + if (host instanceof StandardHost) { + StandardHost standardHost = (StandardHost) host; + standardHost.setContextClass(TesterContext.class.getName()); + } + + // Copy the WAR file + File war = new File(host.getAppBaseFile(), + APP_NAME.getBaseName() + ".war"); + Files.copy(WAR_XML_SOURCE.toPath(), war.toPath()); + + // Deploy the copied war + tomcat.start(); + host.backgroundProcess(); + + // Check the Context class + Context ctxt = (Context) host.findChild(APP_NAME.getName()); + + assertThat(ctxt, instanceOf(TesterContext.class)); + } + + + @Test + public void testUpdateWarOfflineNoContextFF() throws Exception { + doTestUpdateWarOffline(WAR_SOURCE, false, false); + } + + + @Test + public void testUpdateWarOfflineNoContextTF() throws Exception { + doTestUpdateWarOffline(WAR_SOURCE, true, false); + } + + + @Test + public void testUpdateWarOfflineNoContextFT() throws Exception { + doTestUpdateWarOffline(WAR_SOURCE, false, true); + } + + + @Test + public void testUpdateWarOfflineNoContextTT() throws Exception { + doTestUpdateWarOffline(WAR_SOURCE, true, true); + } + + + @Test + public void testUpdateWarOfflineContextFF() throws Exception { + doTestUpdateWarOffline(WAR_XML_SOURCE, false, false); + } + + + @Test + public void testUpdateWarOfflineContextTF() throws Exception { + doTestUpdateWarOffline(WAR_XML_SOURCE, true, false); + } + + + @Test + public void testUpdateWarOfflineContextFT() throws Exception { + doTestUpdateWarOffline(WAR_XML_SOURCE, false, true); + } + + + @Test + public void testUpdateWarOfflineContextTT() throws Exception { + doTestUpdateWarOffline(WAR_XML_SOURCE, true, true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentContextClassName.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentContextClassName.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentContextClassName.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentContextClassName.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.startup; + +import java.io.File; +import java.nio.file.Files; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.IsInstanceOf.instanceOf; + +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.Host; +import org.apache.catalina.core.StandardHost; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentContextClassName extends HostConfigAutomaticDeploymentBaseTest { + + @Test + public void testSetContextClassName() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + + Host host = tomcat.getHost(); + if (host instanceof StandardHost) { + StandardHost standardHost = (StandardHost) host; + standardHost.setContextClass(TesterContext.class.getName()); + } + + // Copy the WAR file + File war = new File(host.getAppBaseFile(), + APP_NAME.getBaseName() + ".war"); + Files.copy(WAR_XML_SOURCE.toPath(), war.toPath()); + + // Deploy the copied war + tomcat.start(); + host.backgroundProcess(); + + // Check the Context class + Context ctxt = (Context) host.findChild(APP_NAME.getName()); + + assertThat(ctxt, instanceOf(TesterContext.class)); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentCopyXML.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentCopyXML.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentCopyXML.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentCopyXML.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * 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.startup; + +import org.junit.Test; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentCopyXML extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Test context copyXML setting. + * If context.copyXML != Host.copyXML the Host wins. + * For external WARs, a context.xml must always already exist + */ + @Test + public void testCopyXMLFFF() throws Exception { + doTestCopyXML(false, false, false, false); + } + + @Test + public void testCopyXMLFFT() throws Exception { + doTestCopyXML(false, false, true, true); + } + + @Test + public void testCopyXMLFTF() throws Exception { + doTestCopyXML(false, true, false, true); + } + + @Test + public void testCopyXMLFTT() throws Exception { + doTestCopyXML(false, true, true, true); + } + + @Test + public void testCopyXMLTFF() throws Exception { + doTestCopyXML(true, false, false, true); + } + + @Test + public void testCopyXMLTFT() throws Exception { + doTestCopyXML(true, false, true, true); + } + + @Test + public void testCopyXMLTTF() throws Exception { + doTestCopyXML(true, true, false, true); + } + + @Test + public void testCopyXMLTTT() throws Exception { + doTestCopyXML(true, true, true, true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteA.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteA.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteA.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteA.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.startup; + +import org.junit.Test; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentDeleteA extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for the deletion of files. + * + * Artifacts present Artifact Artifacts remaining + * XML WAR EXT DIR Removed XML WAR EXT DIR Notes + * N N N Y DIR - - - N + * N Y N N WAR - N - - + * N Y N Y DIR - Y - R 1 + * N Y N Y WAR - N - N + * + * Notes: 1. The DIR will be re-created since unpackWARs is true. + */ + @Test + public void testDeleteDirRemoveDir() throws Exception { + doTestDelete(false, false, false, false, true, DIR, false, false, false, + null); + } + + @Test + public void testDeleteWarRemoveWar() throws Exception { + doTestDelete(false, false, false, true, false, WAR, false, false, false, + null); + } + + @Test + public void testDeleteWarDirRemoveDir() throws Exception { + doTestDelete(false, false, false, true, true, DIR, false, true, true, + WAR_COOKIE_NAME); + } + + @Test + public void testDeleteWarDirRemoveWar() throws Exception { + doTestDelete(false, false, false, true, true, WAR, false, false, false, + null); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteB.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteB.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteB.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteB.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.startup; + +import org.junit.Test; + +import org.apache.catalina.core.StandardHost; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentDeleteB extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for the deletion of files. + * + * Artifacts present Artifact Artifacts remaining + * XML WAR EXT DIR Removed XML WAR EXT DIR Notes + * Y N N N XML N - - - + * Y N N Y DIR N - - N + * Y N N Y XML R - - Y 2 + * Y N Y N EXT Y - N - + * Y N Y N XML N - Y - + * Y N Y Y DIR R - Y R 1,2 + * Y N Y Y EXT Y - N N + * Y N Y Y XML N - Y N + * + * Notes: 1. The DIR will be re-created since unpackWARs is true. + * 2. The XML will be extracted from the WAR/DIR if deployXML and + * copyXML are true. + */ + @Test + public void testDeleteXmlRemoveXml() throws Exception { + doTestDelete(true, false, false, false, false, XML, false, false, false, + null); + } + + @Test + public void testDeleteXmlDirRemoveDir() throws Exception { + doTestDelete(true, false, false, false, true, DIR, false, false, false, + null); + } + + @Test + public void testDeleteXmlDirRemoveXml() throws Exception { + doTestDelete(true, false, false, false, true, XML, false, false, true, + DIR_COOKIE_NAME); + } + + @Test + public void testDeleteXmlDirRemoveXmlCopyXml() throws Exception { + ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); + doTestDelete(true, false, false, false, true, XML, true, false, true, + DIR_COOKIE_NAME); + } + + @Test + public void testDeleteXmlExtwarRemoveExt() throws Exception { + doTestDelete(true, true, false, false, false, EXT, true, false, false, + XML_COOKIE_NAME); + } + + @Test + public void testDeleteXmlExtdirRemoveExt() throws Exception { + doTestDelete(true, false, true, false, false, EXT, true, false, false, + XML_COOKIE_NAME); + } + + @Test + public void testDeleteXmlExtwarRemoveXml() throws Exception { + doTestDelete(true, true, false, false, false, XML, false, false, false, + null); + } + + @Test + public void testDeleteXmlExtdirRemoveXml() throws Exception { + doTestDelete(true, false, true, false, false, XML, false, false, false, + null); + } + + @Test + public void testDeleteXmlExtwarDirRemoveDir() throws Exception { + doTestDelete(true, true, false, false, true, DIR, true, false, true, + XML_COOKIE_NAME); + } + + @Test + public void testDeleteXmlExtwarDirRemoveExt() throws Exception { + doTestDelete(true, true, false, false, true, EXT, true, false, false, + XML_COOKIE_NAME); + } + + @Test + public void testDeleteXmlExtwarDirRemoveXml() throws Exception { + doTestDelete(true, true, false, false, true, XML, false, false, false, + null); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteC.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteC.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteC.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDeleteC.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,101 @@ +/* + * 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.startup; + +import org.junit.Test; + +import org.apache.catalina.core.StandardHost; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentDeleteC extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for the deletion of files. + * + * Artifacts present Artifact Artifacts remaining + * XML WAR EXT DIR Removed XML WAR EXT DIR Notes + * Y Y N N WAR N N - - + * Y Y N N XML N N - - + * Y Y N Y DIR R Y - R 1,2 + * Y Y N Y WAR N N - - + * Y Y N Y XML R Y - Y + * + * Notes: 1. The DIR will be re-created since unpackWARs is true. + * 2. The XML will be extracted from the WAR/DIR if deployXML and + * copyXML are true. + */ + @Test + public void testDeleteXmlWarRemoveWar() throws Exception { + doTestDelete(true, false, false, true, false, WAR, false, false, false, + null); + } + + @Test + public void testDeleteXmlWarRemoveXml() throws Exception { + doTestDelete(true, false, false, true, false, XML, false, true, false, + WAR_COOKIE_NAME); + } + + @Test + public void testDeleteXmlWarRemoveXmlCopyXml() throws Exception { + ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); + doTestDelete(true, false, false, true, false, XML, true, true, false, + WAR_COOKIE_NAME); + } + + @Test + public void testDeleteXmlWarDirRemoveDir() throws Exception { + doTestDelete(true, false, false, true, true, DIR, false, true, true, + WAR_COOKIE_NAME); + } + + @Test + public void testDeleteXmlWarDirRemoveDirCopyXml() throws Exception { + ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); + doTestDelete(true, false, false, true, true, DIR, true, true, true, + WAR_COOKIE_NAME); + } + + @Test + public void testDeleteXmlWarDirRemoveWar() throws Exception { + doTestDelete(true, false, false, true, true, WAR, false, false, false, + null); + } + + @Test + public void testDeleteXmlWarDirRemoveWarCopyXml() throws Exception { + ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); + doTestDelete(true, false, false, true, true, WAR, false, false, false, + null); + } + + @Test + public void testDeleteXmlWarDirRemoveXml() throws Exception { + doTestDelete(true, false, false, true, true, XML, false, true, true, + DIR_COOKIE_NAME); + } + + @Test + public void testDeleteXmlWarDirRemoveXmlCopyXml() throws Exception { + ((StandardHost) getTomcatInstance().getHost()).setCopyXML(true); + doTestDelete(true, false, false, true, true, XML, true, true, true, + WAR_COOKIE_NAME); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDir.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDir.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDir.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDir.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * 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.startup; + +import org.junit.Test; + +import org.apache.catalina.LifecycleState; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentDir extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for deployment of a DIR without an embedded XML file. + * deployXML copyXML unpackWARs XML WAR DIR + * Y/N Y/N Y/N N N Y + */ + @Test + public void testDeploymentDirFFF() throws Exception { + createDirInAppbase(false); + doTestDeployment(false, false, false, + LifecycleState.STARTED, null, false, false, true); + } + + @Test + public void testDeploymentDirFFT() throws Exception { + createDirInAppbase(false); + doTestDeployment(false, false, true, + LifecycleState.STARTED, null, false, false, true); + } + + @Test + public void testDeploymentDirFTF() throws Exception { + createDirInAppbase(false); + doTestDeployment(false, true, false, + LifecycleState.STARTED, null, false, false, true); + } + + @Test + public void testDeploymentDirFTT() throws Exception { + createDirInAppbase(false); + doTestDeployment(false, true, true, + LifecycleState.STARTED, null, false, false, true); + } + + @Test + public void testDeploymentDirTFF() throws Exception { + createDirInAppbase(false); + doTestDeployment(true, false, false, + LifecycleState.STARTED, null, false, false, true); + } + + @Test + public void testDeploymentDirTFT() throws Exception { + createDirInAppbase(false); + doTestDeployment(true, false, true, + LifecycleState.STARTED, null, false, false, true); + } + + @Test + public void testDeploymentDirTTF() throws Exception { + createDirInAppbase(false); + doTestDeployment(true, true, false, + LifecycleState.STARTED, null, false, false, true); + } + + @Test + public void testDeploymentDirTTT() throws Exception { + createDirInAppbase(false); + doTestDeployment(true, true, true, + LifecycleState.STARTED, null, false, false, true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDirXml.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDirXml.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDirXml.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentDirXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,91 @@ +/* + * 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.startup; + +import org.junit.Test; + +import org.apache.catalina.LifecycleState; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentDirXml extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for deployment of a DIR with an embedded XML file. + * deployXML copyXML unpackWARs XML WAR DIR + * N Y/N Y/N N N Y + * Y N Y/N N N Y + * Y Y Y/N Y N Y + */ + @Test + public void testDeploymentDirXmlFFF() throws Exception { + createDirInAppbase(true); + doTestDeployment(false, false, false, + LifecycleState.FAILED, null, false, false, true); + } + + @Test + public void testDeploymentDirXmlFFT() throws Exception { + createDirInAppbase(true); + doTestDeployment(false, false, true, + LifecycleState.FAILED, null, false, false, true); + } + + @Test + public void testDeploymentDirXmlFTF() throws Exception { + createDirInAppbase(true); + doTestDeployment(false, true, false, + LifecycleState.FAILED, null, false, false, true); + } + + @Test + public void testDeploymentDirXmlFTT() throws Exception { + createDirInAppbase(true); + doTestDeployment(false, true, true, + LifecycleState.FAILED, null, false, false, true); + } + + @Test + public void testDeploymentDirXmlTFF() throws Exception { + createDirInAppbase(true); + doTestDeployment(true, false, false, + LifecycleState.STARTED, DIR_COOKIE_NAME, false, false, true); + } + + @Test + public void testDeploymentDirXmlTFT() throws Exception { + createDirInAppbase(true); + doTestDeployment(true, false, true, + LifecycleState.STARTED, DIR_COOKIE_NAME, false, false, true); + } + + @Test + public void testDeploymentDirXmlTTF() throws Exception { + createDirInAppbase(true); + doTestDeployment(true, true, false, + LifecycleState.STARTED, DIR_COOKIE_NAME, true, false, true); + } + + @Test + public void testDeploymentDirXmlTTT() throws Exception { + createDirInAppbase(true); + doTestDeployment(true, true, true, + LifecycleState.STARTED, DIR_COOKIE_NAME, true, false, true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentModification.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentModification.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentModification.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentModification.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,167 @@ +/* + * 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.startup; + +import org.junit.Test; + +import org.apache.catalina.LifecycleState; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentModification extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for modification of files. + * + * Artifacts present Artifact Artifacts remaining + * XML WAR EXT DIR Modified XML WAR EXT DIR Action + * N N N Y DIR - - - M None + * N Y N N WAR - M - - Redeploy + * N Y N Y DIR - Y - M None + * N Y N Y WAR - M - R Redeploy + * Y N N N XML M - - - Redeploy + * Y N N Y DIR Y - - M None + * Y N N Y XML M - - Y Redeploy + * Y N Y N EXT Y - M - Reload if WAR + * Y N Y N XML M - Y - Redeploy + * Y N Y Y DIR Y - Y M None + * Y N Y Y EXT Y - M R Reload + * Y N Y Y XML M - Y Y Redeploy + * Y Y N N WAR Y M - - Reload + * Y Y N N XML M Y - - Redeploy + * Y Y N Y DIR Y Y - M None + * Y Y N Y WAR Y M - - Reload + * Y Y N Y XML M Y - Y Redeploy + */ + @Test + public void testModifyDirUpdateDir() throws Exception { + doTestModify(false, false, false, false, true, DIR, + false, false, true, DIR_COOKIE_NAME, NONE); + } + + @Test + public void testModifyWarUpdateWar() throws Exception { + doTestModify(false, false, false, true, false, WAR, + false, true, false, WAR_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyWarDirUpdateDir() throws Exception { + // DIR_COOKIE_NAME since Tomcat is going to assume DIR is expanded WAR + doTestModify(false, false, false, true, true, DIR, + false, true, true, DIR_COOKIE_NAME, NONE); + } + + @Test + public void testModifyWarDirUpdateWar() throws Exception { + doTestModify(false, false, false, true, true, WAR, + false, true, true, WAR_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlUpdateXml() throws Exception { + doTestModify(true, false, false, false, false, XML, + true, false, false, XML_COOKIE_NAME, REDEPLOY, + LifecycleState.FAILED); + } + + @Test + public void testModifyXmlDirUpdateDir() throws Exception { + doTestModify(true, false, false, false, true, DIR, + true, false, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testModifyXmlDirUpdateXml() throws Exception { + doTestModify(true, false, false, false, true, XML, + true, false, true, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlExtwarUpdateExtwar() throws Exception { + doTestModify(true, true, false, false, false, EXT, + true, false, false, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testModifyXmlExtdirUpdateExtdir() throws Exception { + doTestModify(true, false, true, false, false, EXT, + true, false, false, XML_COOKIE_NAME, NONE); + } + + @Test + public void testModifyXmlExtwarUpdateXml() throws Exception { + doTestModify(true, true, false, false, false, XML, + true, false, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlExtdirUpdateXml() throws Exception { + doTestModify(true, false, true, false, false, XML, + true, false, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlExtwarDirUpdateDir() throws Exception { + doTestModify(true, true, false, false, true, DIR, + true, false, false, XML_COOKIE_NAME, NONE); + } + + @Test + public void testModifyXmlExtwarDirUpdateExt() throws Exception { + doTestModify(true, true, false, false, true, EXT, + true, false, true, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testModifyXmlExtwarDirUpdateXml() throws Exception { + doTestModify(true, true, false, false, true, XML, + true, false, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlWarUpdateWar() throws Exception { + doTestModify(true, false, false, true, false, WAR, + true, true, false, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testModifyXmlWarUpdateXml() throws Exception { + doTestModify(true, false, false, true, false, XML, + true, true, false, XML_COOKIE_NAME, REDEPLOY); + } + + @Test + public void testModifyXmlWarDirUpdateDir() throws Exception { + doTestModify(true, false, false, true, true, DIR, + true, true, true, XML_COOKIE_NAME, NONE); + } + + @Test + public void testModifyXmlWarDirUpdateWar() throws Exception { + doTestModify(true, false, false, true, true, WAR, + true, true, true, XML_COOKIE_NAME, RELOAD); + } + + @Test + public void testModifyXmlWarDirUpdateXml() throws Exception { + doTestModify(true, false, false, true, true, XML, + true, true, true, XML_COOKIE_NAME, REDEPLOY); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUnpackWAR.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUnpackWAR.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUnpackWAR.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUnpackWAR.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * 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.startup; + +import org.junit.Test; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentUnpackWAR extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Test context unpackWAR setting. + * If context.getUnpackWAR != Host.getUnpackWARs the Host wins. + */ + @Test + public void testUnpackWARFFF() throws Exception { + doTestUnpackWAR(false, false, false, false); + } + + @Test + public void testUnpackWARFFT() throws Exception { + doTestUnpackWAR(false, false, true, false); + } + + @Test + public void testUnpackWARFTF() throws Exception { + doTestUnpackWAR(false, true, false, false); + } + + @Test + public void testUnpackWARFTT() throws Exception { + doTestUnpackWAR(false, true, true, false); + } + + @Test + public void testUnpackWARTFF() throws Exception { + doTestUnpackWAR(true, false, false, false); + } + + @Test + public void testUnpackWARTFT() throws Exception { + // External WAR - therefore XML in WAR will be ignored + doTestUnpackWAR(true, false, true, true); + } + + @Test + public void testUnpackWARTTF() throws Exception { + doTestUnpackWAR(true, true, false, true); + } + + @Test + public void testUnpackWARTTT() throws Exception { + doTestUnpackWAR(true, true, true, true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUpdateWarOffline.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUpdateWarOffline.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUpdateWarOffline.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentUpdateWarOffline.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,73 @@ +/* + * 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.startup; + +import org.junit.Test; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentUpdateWarOffline extends HostConfigAutomaticDeploymentBaseTest { + + @Test + public void testUpdateWarOfflineNoContextFF() throws Exception { + doTestUpdateWarOffline(WAR_SOURCE, false, false); + } + + + @Test + public void testUpdateWarOfflineNoContextTF() throws Exception { + doTestUpdateWarOffline(WAR_SOURCE, true, false); + } + + + @Test + public void testUpdateWarOfflineNoContextFT() throws Exception { + doTestUpdateWarOffline(WAR_SOURCE, false, true); + } + + + @Test + public void testUpdateWarOfflineNoContextTT() throws Exception { + doTestUpdateWarOffline(WAR_SOURCE, true, true); + } + + + @Test + public void testUpdateWarOfflineContextFF() throws Exception { + doTestUpdateWarOffline(WAR_XML_SOURCE, false, false); + } + + + @Test + public void testUpdateWarOfflineContextTF() throws Exception { + doTestUpdateWarOffline(WAR_XML_SOURCE, true, false); + } + + + @Test + public void testUpdateWarOfflineContextFT() throws Exception { + doTestUpdateWarOffline(WAR_XML_SOURCE, false, true); + } + + + @Test + public void testUpdateWarOfflineContextTT() throws Exception { + doTestUpdateWarOffline(WAR_XML_SOURCE, true, true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWar.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWar.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWar.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWar.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,90 @@ +/* + * 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.startup; + +import org.junit.Test; + +import org.apache.catalina.LifecycleState; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentWar extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for deployment of a WAR without an embedded XML file. + * deployXML copyXML unpackWARs XML WAR DIR + * Y/N Y/N N N Y N + * Y/N Y/N Y N Y Y + */ + @Test + public void testDeploymentWarFFF() throws Exception { + createWar(WAR_SOURCE, true); + doTestDeployment(false, false, false, + LifecycleState.STARTED, null, false, true, false); + } + + @Test + public void testDeploymentWarFFT() throws Exception { + createWar(WAR_SOURCE, true); + doTestDeployment(false, false, true, + LifecycleState.STARTED, null, false, true, true); + } + + @Test + public void testDeploymentWarFTF() throws Exception { + createWar(WAR_SOURCE, true); + doTestDeployment(false, true, false, + LifecycleState.STARTED, null, false, true, false); + } + + @Test + public void testDeploymentWarFTT() throws Exception { + createWar(WAR_SOURCE, true); + doTestDeployment(false, true, true, + LifecycleState.STARTED, null, false, true, true); + } + + @Test + public void testDeploymentWarTFF() throws Exception { + createWar(WAR_SOURCE, true); + doTestDeployment(true, false, false, + LifecycleState.STARTED, null, false, true, false); + } + + @Test + public void testDeploymentWarTFT() throws Exception { + createWar(WAR_SOURCE, true); + doTestDeployment(true, false, true, + LifecycleState.STARTED, null, false, true, true); + } + + @Test + public void testDeploymentWarTTF() throws Exception { + createWar(WAR_SOURCE, true); + doTestDeployment(true, true, false, + LifecycleState.STARTED, null, false, true, false); + } + + @Test + public void testDeploymentWarTTT() throws Exception { + createWar(WAR_SOURCE, true); + doTestDeployment(true, true, true, + LifecycleState.STARTED, null, false, true, true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWarXml.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWarXml.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWarXml.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentWarXml.java 2026-01-23 19:33:36.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.startup; + +import org.junit.Test; + +import org.apache.catalina.LifecycleState; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentWarXml extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for deployment of a WAR with an embedded XML file. + * deployXML copyXML unpackWARs XML WAR DIR + * N Y/N N N Y N + * N Y/N Y N Y Y + * Y N N N Y N + * Y N Y N Y Y + * Y Y N Y Y N + * Y Y Y Y Y Y + */ + @Test + public void testDeploymentWarXmlFFF() throws Exception { + createWar(WAR_XML_SOURCE, true); + doTestDeployment(false, false, false, + LifecycleState.FAILED, null, false, true, false); + } + + @Test + public void testDeploymentWarXmlFFT() throws Exception { + createWar(WAR_XML_SOURCE, true); + doTestDeployment(false, false, true, + LifecycleState.FAILED, null, false, true, true); + } + + @Test + public void testDeploymentWarXmlFTF() throws Exception { + createWar(WAR_XML_SOURCE, true); + doTestDeployment(false, true, false, + LifecycleState.FAILED, null, false, true, false); + } + + @Test + public void testDeploymentWarXmlFTT() throws Exception { + createWar(WAR_XML_SOURCE, true); + doTestDeployment(false, true, true, + LifecycleState.FAILED, null, false, true, true); + } + + @Test + public void testDeploymentWarXmlTFF() throws Exception { + createWar(WAR_XML_SOURCE, true); + doTestDeployment(true, false, false, + LifecycleState.STARTED, WAR_COOKIE_NAME, false, true, false); + } + + @Test + public void testDeploymentWarXmlTFT() throws Exception { + createWar(WAR_XML_SOURCE, true); + doTestDeployment(true, false, true, + LifecycleState.STARTED, WAR_COOKIE_NAME, false, true, true); + } + + @Test + public void testDeploymentWarXmlTTF() throws Exception { + createWar(WAR_XML_SOURCE, true); + doTestDeployment(true, true, false, + LifecycleState.STARTED, WAR_COOKIE_NAME, true, true, false); + } + + @Test + public void testDeploymentWarXmlTTT() throws Exception { + createWar(WAR_XML_SOURCE, true); + doTestDeployment(true, true, true, + LifecycleState.STARTED, WAR_COOKIE_NAME, true, true, true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXml.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXml.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXml.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,91 @@ +/* + * 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.startup; + +import org.junit.Test; + +import org.apache.catalina.LifecycleState; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentXml extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for deployment of an XML file. + * deployXML copyXML unpackWARs XML WAR DIR + * Y/N Y/N Y/N Y N N + * + * Note: Context will fail to start because no valid docBase is present. + */ + @Test + public void testDeploymentXmlFFF() throws Exception { + createXmlInConfigBaseForAppbase(); + doTestDeployment(false, false, false, + LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlFFT() throws Exception { + createXmlInConfigBaseForAppbase(); + doTestDeployment(false, false, true, + LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlFTF() throws Exception { + createXmlInConfigBaseForAppbase(); + doTestDeployment(false, true, false, + LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlFTT() throws Exception { + createXmlInConfigBaseForAppbase(); + doTestDeployment(false, true, true, + LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlTFF() throws Exception { + createXmlInConfigBaseForAppbase(); + doTestDeployment(true, false, false, + LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlTFT() throws Exception { + createXmlInConfigBaseForAppbase(); + doTestDeployment(true, false, true, + LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlTTF() throws Exception { + createXmlInConfigBaseForAppbase(); + doTestDeployment(true, true, false, + LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlTTT() throws Exception { + createXmlInConfigBaseForAppbase(); + doTestDeployment(true, true, true, + LifecycleState.FAILED, XML_COOKIE_NAME, true, false, false); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalDirXml.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalDirXml.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalDirXml.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalDirXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,102 @@ +/* + * 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.startup; + +import java.io.File; + +import org.junit.Test; + +import org.apache.catalina.LifecycleState; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentXmlExternalDirXml extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for deployment of an XML file that points to an + * external DIR. + * deployXML copyXML unpackWARs XML WAR DIR + * Y/N Y/N Y/N Y N N + * + * Notes: Any context.xml file embedded in the external DIR file is ignored. + */ + @Test + public void testDeploymentXmlExternalDirXmlFFF() throws Exception { + File dir = createDirInExternal(true); + createXmlInConfigBaseForExternal(dir); + doTestDeployment(false, false, false, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalDirXmlFFT() throws Exception { + File dir = createDirInExternal(true); + createXmlInConfigBaseForExternal(dir); + doTestDeployment(false, false, true, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalDirXmlFTF() throws Exception { + File dir = createDirInExternal(true); + createXmlInConfigBaseForExternal(dir); + doTestDeployment(false, true, false, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalDirXmlFTT() throws Exception { + File dir = createDirInExternal(true); + createXmlInConfigBaseForExternal(dir); + doTestDeployment(false, true, true, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalDirXmlTFF() throws Exception { + File dir = createDirInExternal(true); + createXmlInConfigBaseForExternal(dir); + doTestDeployment(true, false, false, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalDirXmlTFT() throws Exception { + File dir = createDirInExternal(true); + createXmlInConfigBaseForExternal(dir); + doTestDeployment(true, false, true, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalDirXmlTTF() throws Exception { + File dir = createDirInExternal(true); + createXmlInConfigBaseForExternal(dir); + doTestDeployment(true, true, false, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalDirXmlTTT() throws Exception { + File dir = createDirInExternal(true); + createXmlInConfigBaseForExternal(dir); + doTestDeployment(true, true, true, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalWarXml.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalWarXml.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalWarXml.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestHostConfigAutomaticDeploymentXmlExternalWarXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,105 @@ +/* + * 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.startup; + +import java.io.File; + +import org.junit.Test; + +import org.apache.catalina.LifecycleState; + +/** + * The purpose of this class is to test the automatic deployment features of the + * {@link HostConfig} implementation. + */ +public class TestHostConfigAutomaticDeploymentXmlExternalWarXml extends HostConfigAutomaticDeploymentBaseTest { + + /* + * Expected behaviour for deployment of an XML file that points to an + * external WAR. + * deployXML copyXML unpackWARs XML WAR DIR + * Y/N Y/N Y Y N Y + * Y/N Y/N N Y N N + * + * Notes: No WAR file is present in the appBase because it is an external + * WAR. + * Any context.xml file embedded in the external WAR file is ignored. + */ + @Test + public void testDeploymentXmlExternalWarXmlFFF() throws Exception { + File war = createWar(WAR_XML_SOURCE, false); + createXmlInConfigBaseForExternal(war); + doTestDeployment(false, false, false, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalWarXmlFFT() throws Exception { + File war = createWar(WAR_XML_SOURCE, false); + createXmlInConfigBaseForExternal(war); + doTestDeployment(false, false, true, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, true); + } + + @Test + public void testDeploymentXmlExternalWarXmlFTF() throws Exception { + File war = createWar(WAR_XML_SOURCE, false); + createXmlInConfigBaseForExternal(war); + doTestDeployment(false, true, false, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalWarXmlFTT() throws Exception { + File war = createWar(WAR_XML_SOURCE, false); + createXmlInConfigBaseForExternal(war); + doTestDeployment(false, true, true, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, true); + } + + @Test + public void testDeploymentXmlExternalWarXmlTFF() throws Exception { + File war = createWar(WAR_XML_SOURCE, false); + createXmlInConfigBaseForExternal(war); + doTestDeployment(true, false, false, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalWarXmlTFT() throws Exception { + File war = createWar(WAR_XML_SOURCE, false); + createXmlInConfigBaseForExternal(war); + doTestDeployment(true, false, true, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, true); + } + + @Test + public void testDeploymentXmlExternalWarXmlTTF() throws Exception { + File war = createWar(WAR_XML_SOURCE, false); + createXmlInConfigBaseForExternal(war); + doTestDeployment(true, true, false, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, false); + } + + @Test + public void testDeploymentXmlExternalWarXmlTTT() throws Exception { + File war = createWar(WAR_XML_SOURCE, false); + createXmlInConfigBaseForExternal(war); + doTestDeployment(true, true, true, + LifecycleState.STARTED, XML_COOKIE_NAME, true, false, true); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestStartupIPv6Connectors.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestStartupIPv6Connectors.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestStartupIPv6Connectors.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestStartupIPv6Connectors.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,149 @@ +/* + * 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.startup; + +import java.io.File; +import java.net.HttpURLConnection; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.URI; +import java.net.URL; +import java.util.Enumeration; + +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.catalina.Context; + +public class TestStartupIPv6Connectors extends TomcatBaseTest { + + private static String linklocalAddress; + private static String globalAddress; + + @Test + public void testIPv6Localhost() throws Exception { + assertHttpOkOnAddress("::1"); + } + + @Test + public void testIPv6LinkLocal() throws Exception { + Assume.assumeTrue("No IPv6 link-local present", linklocalAddress != null); + assertHttpOkOnAddress(linklocalAddress); + } + + @Test + public void testIPv6Global() throws Exception { + Assume.assumeTrue("No IPv6 global-unicast present", globalAddress != null); + assertHttpOkOnAddress(globalAddress); + } + + @Test + public void testIPv6MappedIPv4() throws Exception { + assertHttpOkOnAddress("::ffff:127.0.0.1"); + } + + @BeforeClass + public static void initializeTestIpv6Addresses() throws Exception { + Inet6Address possibleLinkLocalLoopback = null; + Inet6Address possibleLinkLocalOnGlobal = null; + Inet6Address possibleLinkLocalAny = null; + + Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface interf = interfaces.nextElement(); + if (!interf.isUp() || interf.isVirtual() || interf.isPointToPoint() || !interf.supportsMulticast()) { + continue; + } + Enumeration addresses = interf.getInetAddresses(); + boolean globalOnInterface = false; + while (addresses.hasMoreElements()) { + InetAddress address = addresses.nextElement(); + if (address instanceof Inet6Address) { + Inet6Address inet6Address = (Inet6Address) address; + if (!inet6Address.isAnyLocalAddress() && !inet6Address.isLoopbackAddress() && !inet6Address.isLinkLocalAddress() && !inet6Address.isMulticastAddress()) { + globalOnInterface = true; + if (!interf.isLoopback()) { + globalAddress = inet6Address.getHostAddress(); + break; + } + } + } + } + + // Second pass to get link-local results with specific order + addresses = interf.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress address = addresses.nextElement(); + if (address instanceof Inet6Address) { + Inet6Address inet6Address = (Inet6Address) address; + if (inet6Address.isLinkLocalAddress()) { + if (interf.isLoopback()) { + // Best option for mac + possibleLinkLocalLoopback = inet6Address; + } else if (globalOnInterface && possibleLinkLocalOnGlobal == null) { + // link-local on an interface that also has a global IPv6 (e.g. en0) + possibleLinkLocalOnGlobal = inet6Address; + } else if (possibleLinkLocalAny == null) { + possibleLinkLocalAny = inet6Address; + } + } + } + } + } + + if (possibleLinkLocalLoopback != null) { + linklocalAddress = possibleLinkLocalLoopback.getHostAddress(); + } else if (possibleLinkLocalOnGlobal != null) { + linklocalAddress = possibleLinkLocalOnGlobal.getHostAddress(); + } else if (possibleLinkLocalAny != null) { + linklocalAddress = possibleLinkLocalAny.getHostAddress(); + } + + } + + private void assertHttpOkOnAddress(String address) throws Exception { + Tomcat tomcat = getTomcatInstance(); + tomcat.getConnector().setProperty("address", address); + File baseDir = new File(getTemporaryDirectory(), "ipv6"); + addDeleteOnTearDown(baseDir); + Assert.assertTrue(baseDir.mkdirs() && baseDir.isDirectory()); + tomcat.setBaseDir(baseDir.getAbsolutePath()); + Context context = tomcat.addContext("", null); + Tomcat.addServlet(context, "simple", new HelloWorldServlet()); + context.addServletMappingDecoded("/", "simple"); + + try { + tomcat.start(); + } catch (Exception e) { + Assume.assumeNoException("Can't bind to " + address, e); + } + if (address.contains(":")) { + address = "[" + address + "]"; + } + URL url = new URI("http://" + address + ":" + getPort() + "/").toURL(); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.connect(); + Assert.assertEquals(HttpServletResponse.SC_OK, connection.getResponseCode()); + tomcat.stop(); + } +} \ No newline at end of file diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestStrictServletComplianceAttributes.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestStrictServletComplianceAttributes.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestStrictServletComplianceAttributes.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestStrictServletComplianceAttributes.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,77 @@ +/* + * 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.startup; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.Globals; +import org.apache.catalina.Manager; +import org.apache.catalina.session.ManagerBase; + +/** + * Tests STRICT_SERVLET_COMPLIANCE sets the attributes it is documented to set. + */ +public class TestStrictServletComplianceAttributes extends TomcatBaseTest { + private static final String STRICT_SERVLET_COMPLIANCE = "org.apache.catalina.STRICT_SERVLET_COMPLIANCE"; + private static String originalPropertyValue; + + @BeforeClass + public static void setup() { + originalPropertyValue = System.getProperty(STRICT_SERVLET_COMPLIANCE); + System.setProperty(STRICT_SERVLET_COMPLIANCE, "true"); + + // If Globals was already initialised in the same JVM (during the tests run through IDE), + // before the test sets the value to true, skip. + boolean globalsStrict = Globals.STRICT_SERVLET_COMPLIANCE; + Assume.assumeTrue("Globals was initialised before setting the property", globalsStrict); + } + + @AfterClass + public static void restoreStrictServletCompliance() { + if (originalPropertyValue == null) { + System.clearProperty(STRICT_SERVLET_COMPLIANCE); + } else { + System.setProperty(STRICT_SERVLET_COMPLIANCE, originalPropertyValue); + } + } + + @Test + public void contextFlagsSetWhenStrictComplianceIsEnabled() { + Context ctx = getProgrammaticRootContextWithManager(); + Assert.assertTrue("xmlValidation should be true under STRICT_SERVLET_COMPLIANCE.", ctx.getXmlValidation()); + Assert.assertTrue("xmlNamespaceAware should be true under STRICT_SERVLET_COMPLIANCE.", ctx.getXmlNamespaceAware()); + Assert.assertTrue("tldValidation should be true under STRICT_SERVLET_COMPLIANCE.", ctx.getTldValidation()); + Assert.assertFalse("useRelativeRedirects should be false under STRICT_SERVLET_COMPLIANCE.", ctx.getUseRelativeRedirects()); + Assert.assertTrue("alwaysAccessSession should be true under STRICT_SERVLET_COMPLIANCE.", ctx.getAlwaysAccessSession()); + Assert.assertTrue("contextGetResourceRequiresSlash should be true under STRICT_SERVLET_COMPLIANCE.", ctx.getContextGetResourceRequiresSlash()); + Assert.assertTrue("dispatcherWrapsSameObject should be true under STRICT_SERVLET_COMPLIANCE.", ctx.getDispatcherWrapsSameObject()); + Assert.assertFalse("All extension mapped servlets should be checked against welcome files under STRICT_SERVLET_COMPLIANCE.", ctx.isResourceOnlyServlet("jsp")); + + Manager manager = ctx.getManager(); + if (manager instanceof ManagerBase) { + ManagerBase managerBase = (ManagerBase) manager; + Assert.assertTrue("ManagerBase.sessionActivityCheck should be true under STRICT", managerBase.getSessionActivityCheck()); + Assert.assertTrue("ManagerBase.sessionLastAccessAtStart should be true under STRICT", managerBase.getSessionLastAccessAtStart()); + } + } + +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestTomcatNoServer.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestTomcatNoServer.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestTomcatNoServer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestTomcatNoServer.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,6 +19,7 @@ import java.io.File; import java.util.Arrays; import java.util.HashSet; +import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.jar.JarFile; @@ -66,27 +67,37 @@ Set embeddedExtensions = new HashSet<>(Arrays.asList(ctx.findMimeMappings())); - // Find entries present in conf/web.xml that are missing in embedded - Set missingInEmbedded = new HashSet<>(webXmlMimeMappings.keySet()); - missingInEmbedded.removeAll(embeddedExtensions); - if (missingInEmbedded.size() > 0) { - for (String missingExtension : missingInEmbedded) { - System.out.println("Missing in embedded: [" + missingExtension + - "]-[" + webXmlMimeMappings.get(missingExtension) + "]"); + boolean pass = true; + + /* + * Check that each entry for embedded is present in web.xml with the same media type. + * Also finds entries that are missing in conf/web.xml + */ + Iterator embeddedExtensionIterator = embeddedExtensions.iterator(); + while (embeddedExtensionIterator.hasNext()) { + String embeddedExtension = embeddedExtensionIterator.next(); + String embeddedMediaType = ctx.findMimeMapping(embeddedExtension); + + if (!embeddedMediaType.equals(webXmlMimeMappings.get(embeddedExtension))) { + pass = false; + System.out.println("Extension [" + embeddedExtension + "] is mapped to [" + embeddedMediaType + + "] in embedded but [" + webXmlMimeMappings.get(embeddedExtension) + "] in conf/web.xml"); } - Assert.fail("Embedded is missing [" + missingInEmbedded.size() + "] entries compared to conf/web.xml"); + // Remove from both whether they matched or not + embeddedExtensionIterator.remove(); + webXmlMimeMappings.remove(embeddedExtension); } - // Find entries present in embedded that are missing in conf/web.xml - Set missingInWebXml = new HashSet<>(embeddedExtensions); - missingInWebXml.removeAll(webXmlMimeMappings.keySet()); - if (missingInWebXml.size() > 0) { - for (String missingExtension : missingInWebXml) { - System.out.println("Missing in embedded: [" + missingExtension + - "]-[" + ctx.findMimeMapping(missingExtension) + "]"); + // Check for entries missing in embedded + if (webXmlMimeMappings.size() > 0) { + pass = false; + for (Map.Entry mapping : webXmlMimeMappings.entrySet()) { + System.out.println("Extension [" + mapping.getKey() + "] is mapped to [" + mapping.getValue() + + "] in conf/web.xml but [null] in embedded"); } - Assert.fail("Embedded is missing [" + missingInWebXml.size() + "] entries compared to conf/web.xml"); } + + Assert.assertTrue(pass); } @Test diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestTomcatStandalone.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestTomcatStandalone.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestTomcatStandalone.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestTomcatStandalone.java 2026-01-23 19:33:36.000000000 +0000 @@ -160,128 +160,136 @@ @Test public void testStandalone() throws Exception { + try (TomcatBaseTest.LogCapture logCapture = TomcatBaseTest.attachLogCapture(java.util.logging.Level.ALL, + org.apache.catalina.startup.VersionLoggerListener.class.getName())) { + // Test embedded with pseudo standalone + Tomcat tomcat = new Tomcat(); + tomcat.init(new ServerXml()); + + // No file system docBase required + Context ctx = tomcat.addContext("", null); + + Tomcat.addServlet(ctx, "myServlet", new HelloWorld()); + ctx.addServletMappingDecoded("/", "myServlet"); + + tomcat.start(); + // Emulate Tomcat main thread + new Thread() { + @Override + public void run() { + tomcat.getServer().await(); + try { + tomcat.stop(); + } catch (LifecycleException e) { + } + } + }.start(); - // Test embedded with pseudo standalone - - Tomcat tomcat = new Tomcat(); - tomcat.init(new ServerXml()); - - // No file system docBase required - Context ctx = tomcat.addContext("", null); - - Tomcat.addServlet(ctx, "myServlet", new HelloWorld()); - ctx.addServletMappingDecoded("/", "myServlet"); - - tomcat.start(); - // Emulate Tomcat main thread - new Thread() { - @Override - public void run() { - tomcat.getServer().await(); - try { - tomcat.stop(); - } catch (LifecycleException e) { - } - } - }.start(); - InetAddress localAddress = null; - Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); - while (networkInterfaces.hasMoreElements()) { - NetworkInterface ni = networkInterfaces.nextElement(); - if (!ni.isLoopback() && ni.isUp()) { - Enumeration addresses = ni.getInetAddresses(); - while (addresses.hasMoreElements()) { - InetAddress address = addresses.nextElement(); - if (address instanceof Inet4Address) { - localAddress = address; + assertVersionLoggerListenerOutput(logCapture); + InetAddress localAddress = null; + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface ni = networkInterfaces.nextElement(); + if (!ni.isLoopback() && ni.isUp()) { + Enumeration addresses = ni.getInetAddresses(); + while (addresses.hasMoreElements()) { + InetAddress address = addresses.nextElement(); + if (address instanceof Inet4Address) { + localAddress = address; + } } } } - } - ByteChunk res = TomcatBaseTest.getUrl("http://localhost:" + tomcat.getConnector().getLocalPort() + "/"); - Assert.assertEquals("Hello world", res.toString()); + ByteChunk res = TomcatBaseTest.getUrl("http://localhost:" + tomcat.getConnector().getLocalPort() + "/"); + Assert.assertEquals("Hello world", res.toString()); - // Use the shutdown command - if (localAddress != null) { - // Don't listen to non loopback - Exception ex = null; - try (Socket s = new Socket(localAddress, 8005)) { - s.getOutputStream().write("GOAWAY".getBytes(StandardCharsets.ISO_8859_1)); - } catch (Exception e) { - ex = e; + // Use the shutdown command + if (localAddress != null) { + // Don't listen to non loopback + Exception ex = null; + try (Socket s = new Socket(localAddress, 8005)) { + s.getOutputStream().write("GOAWAY".getBytes(StandardCharsets.ISO_8859_1)); + } catch (Exception e) { + ex = e; + } + Assert.assertNotNull(ex); } - Assert.assertNotNull(ex); - } - try (Socket s = new Socket(InetAddress.getLoopbackAddress(), 8005)) { - // Bad command - s.getOutputStream().write("GOAWAY".getBytes(StandardCharsets.ISO_8859_1)); - } - Thread.sleep(100); - Assert.assertEquals(LifecycleState.STARTED, tomcat.getService().getState()); - - try (Socket s = new Socket(InetAddress.getLoopbackAddress(), 8005)) { - s.getOutputStream().write("SHUTDOWN".getBytes(StandardCharsets.ISO_8859_1)); - } - Thread.sleep(100); - Assert.assertNotEquals(LifecycleState.STARTED, tomcat.getService().getState()); + try (Socket s = new Socket(InetAddress.getLoopbackAddress(), 8005)) { + // Bad command + s.getOutputStream().write("GOAWAY".getBytes(StandardCharsets.ISO_8859_1)); + } + Thread.sleep(100); + Assert.assertEquals(LifecycleState.STARTED, tomcat.getService().getState()); - // Second separate test, real standalone using Catalina - // This is done in one single test to avoid possible problems with the shutdown port + try (Socket s = new Socket(InetAddress.getLoopbackAddress(), 8005)) { + s.getOutputStream().write("SHUTDOWN".getBytes(StandardCharsets.ISO_8859_1)); + } + Thread.sleep(100); + Assert.assertNotEquals(LifecycleState.STARTED, tomcat.getService().getState()); - // Add descriptor to deploy - File descriptorsFolder = new File(getTemporaryDirectory(), "conf/Catalina/localhost"); - descriptorsFolder.mkdirs(); - try (FileOutputStream os = new FileOutputStream(new File(descriptorsFolder, "test.xml"))) { - os.write(TEST_WEBAPP_CONTEXT_XML.getBytes(StandardCharsets.UTF_8)); - } + // Second separate test, real standalone using Catalina + // This is done in one single test to avoid possible problems with the shutdown port + + // Add descriptor to deploy + File descriptorsFolder = new File(getTemporaryDirectory(), "conf/Catalina/localhost"); + descriptorsFolder.mkdirs(); + try (FileOutputStream os = new FileOutputStream(new File(descriptorsFolder, "test.xml"))) { + os.write(TEST_WEBAPP_CONTEXT_XML.getBytes(StandardCharsets.UTF_8)); + } + + Catalina catalina = new Catalina(); + catalina.setAwait(true); + // Embedded code generation uses Catalina, so it is the best spot to test it as well + File generatedCodeLocation = new File(getTemporaryDirectory(), "generated"); + new Thread() { + @Override + public void run() { + String[] args = {"start", "-generateCode", generatedCodeLocation.getAbsolutePath()}; + catalina.load(args); + catalina.start(); + } + }.start(); - Catalina catalina = new Catalina(); - catalina.setAwait(true); - // Embedded code generation uses Catalina, so it is the best spot to test it as well - File generatedCodeLocation = new File(getTemporaryDirectory(), "generated"); - new Thread() { - @Override - public void run() { - String[] args = { "start", "-generateCode", generatedCodeLocation.getAbsolutePath() }; - catalina.load(args); - catalina.start(); - } - }.start(); - - Service service = null; - int i = 0; - while (i < 500 && (service == null || service.getState() != LifecycleState.STARTED)) { - Server server = catalina.getServer(); - if (server != null && catalina.getServer().findServices().length > 0) { - service = catalina.getServer().findServices()[0]; + Service service = null; + int i = 0; + while (i < 500 && (service == null || service.getState() != LifecycleState.STARTED)) { + Server server = catalina.getServer(); + if (server != null && catalina.getServer().findServices().length > 0) { + service = catalina.getServer().findServices()[0]; + } + Thread.sleep(10); + i++; } - Thread.sleep(10); - i++; - } - Assert.assertNotNull(service); + Assert.assertNotNull(service); - Connector connector = service.findConnectors()[0]; - res = TomcatBaseTest.getUrl("http://localhost:" + connector.getLocalPort() + "/"); - Assert.assertTrue(res.toString().contains("404")); - - File codeFolder = new File(generatedCodeLocation, "catalinaembedded"); - File generatedLoader = new File(codeFolder, "DigesterGeneratedCodeLoader.java"); - File generatedServerXml = new File(codeFolder, "ServerXml.java"); - Assert.assertTrue(generatedLoader.exists()); - Assert.assertTrue(generatedServerXml.exists()); - - (new Catalina()).stopServer(); - i = 0; - while (true) { - Assert.assertTrue(i++ < 100); - if (service.getState() != LifecycleState.STARTED) { - break; + Connector connector = service.findConnectors()[0]; + res = TomcatBaseTest.getUrl("http://localhost:" + connector.getLocalPort() + "/"); + Assert.assertTrue(res.toString().contains("404")); + + File codeFolder = new File(generatedCodeLocation, "catalinaembedded"); + File generatedLoader = new File(codeFolder, "DigesterGeneratedCodeLoader.java"); + File generatedServerXml = new File(codeFolder, "ServerXml.java"); + Assert.assertTrue(generatedLoader.exists()); + Assert.assertTrue(generatedServerXml.exists()); + + (new Catalina()).stopServer(); + i = 0; + while (true) { + Assert.assertTrue(i++ < 100); + if (service.getState() != LifecycleState.STARTED) { + break; + } + Thread.sleep(10); } - Thread.sleep(10); - } + } + } + private void assertVersionLoggerListenerOutput(TomcatBaseTest.LogCapture logCapture) { + Assert.assertTrue("Missing server version line in VersionLoggerListener output.", logCapture.containsText(TomcatBaseTest.getKeyFromPropertiesFile("org.apache.catalina.startup", "versionLoggerListener.serverInfo.server.version"))); + Assert.assertTrue("Missing server number line in VersionLoggerListener output.", logCapture.containsText(TomcatBaseTest.getKeyFromPropertiesFile("org.apache.catalina.startup", "versionLoggerListener.serverInfo.server.number"))); + Assert.assertTrue("Missing server built line in VersionLoggerListener output.", logCapture.containsText(TomcatBaseTest.getKeyFromPropertiesFile("org.apache.catalina.startup", "versionLoggerListener.serverInfo.server.built"))); } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestWebappServiceLoader.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestWebappServiceLoader.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestWebappServiceLoader.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestWebappServiceLoader.java 2026-01-23 19:33:36.000000000 +0000 @@ -164,8 +164,8 @@ control.replay(); try { loader.loadServices(ServletContainerInitializer.class, names); - } catch (IOException e) { - assertThat(e.getCause(), instanceOf(ClassCastException.class)); + } catch (IOException ioe) { + assertThat(ioe.getCause(), instanceOf(ClassCastException.class)); } finally { control.verify(); } @@ -183,8 +183,8 @@ control.replay(); try { loader.loadServices(ServletContainerInitializer.class, names); - } catch (IOException e) { - assertThat(e.getCause(), instanceOf(ReflectiveOperationException.class)); + } catch (IOException ioe) { + assertThat(ioe.getCause(), instanceOf(ReflectiveOperationException.class)); } finally { control.verify(); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TestXmlValidationUsingContext.java tomcat10-10.1.52/test/org/apache/catalina/startup/TestXmlValidationUsingContext.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TestXmlValidationUsingContext.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TestXmlValidationUsingContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,83 @@ +/* + * 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.startup; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.Context; + +/** + * Tests XML validation works on the Context. + */ +public class TestXmlValidationUsingContext extends TomcatBaseTest { + @Test + public void contextValidationWithInvalidWebXml() throws Exception { + File appDir = getTemporaryDirectory(); + File webInf = new File(appDir, "WEB-INF"); + Assert.assertTrue(webInf.isDirectory() || webInf.mkdirs()); + writeInvalidXml(new File(webInf, "web.xml")); + Tomcat tomcat = getTomcatInstance(); + Context ctx = tomcat.addWebapp(null, "", appDir.getAbsolutePath()); + ctx.setXmlValidation(true); + ctx.setXmlNamespaceAware(true); + tomcat.start(); + Assert.assertFalse("Context should not be available when web.xml is invalid and validation is enabled", + ctx.getState().isAvailable()); + } + + @Test + public void contextValidationWithValidWebXml() throws Exception { + File appDir = getTemporaryDirectory(); + File webInf = new File(appDir, "WEB-INF"); + Assert.assertTrue(webInf.isDirectory() || webInf.mkdirs()); + writeValidXml(new File(webInf, "web.xml")); + Tomcat tomcat = getTomcatInstance(); + Context ctx = tomcat.addWebapp(null, "", appDir.getAbsolutePath()); + ctx.setXmlValidation(true); + ctx.setXmlNamespaceAware(true); + tomcat.start(); + Assert.assertTrue("Context should be available when web.xml is valid and validation is enabled", + ctx.getState().isAvailable()); + } + + private void writeValidXml(File webXml) throws IOException { + try (FileWriter fw = new FileWriter(webXml)) { + fw.write( + "\n" + + "\n" + + ""); + } + } + private void writeInvalidXml(File webXml) throws IOException { + try (FileWriter fw = new FileWriter(webXml)) { + fw.write( + "\n" + + "\n" + + ""); + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/startup/TomcatBaseTest.java tomcat10-10.1.52/test/org/apache/catalina/startup/TomcatBaseTest.java --- tomcat10-10.1.34/test/org/apache/catalina/startup/TomcatBaseTest.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/startup/TomcatBaseTest.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,10 +30,20 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; @@ -47,8 +57,13 @@ import org.junit.Before; import org.apache.catalina.Container; +import org.apache.catalina.ContainerEvent; +import org.apache.catalina.ContainerListener; import org.apache.catalina.Context; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleListener; import org.apache.catalina.LifecycleState; import org.apache.catalina.Manager; import org.apache.catalina.Server; @@ -64,6 +79,8 @@ import org.apache.coyote.http11.Http11NioProtocol; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap; +import org.apache.tomcat.util.http.Method; +import org.apache.tomcat.util.res.StringManager; import org.apache.tomcat.util.scan.StandardJarScanFilter; import org.apache.tomcat.util.scan.StandardJarScanner; @@ -136,8 +153,13 @@ ((StandardJarScanner) ctx.getJarScanner()).setScanClassPath(false); return ctx; } - - + public Context getProgrammaticRootContextWithManager() { + Context ctx = getProgrammaticRootContext(); + if (ctx.getManager() == null) { + ctx.setManager(new StandardManager()); + } + return ctx; + } /* * Sub-classes need to know port so they can connect */ @@ -543,7 +565,7 @@ value.append(';'); } } - out.println("PARAM/" + name + ": " + value); + out.println("PARAM:" + name + ": " + value); } out.println("SESSION-REQUESTED-ID: " + @@ -574,7 +596,7 @@ } int bodySize = 0; - if ("PUT".equalsIgnoreCase(request.getMethod())) { + if (Method.PUT.equals(request.getMethod())) { InputStream is = request.getInputStream(); int read = 0; byte[] buffer = new byte[8192]; @@ -632,12 +654,12 @@ public static int getUrl(String path, ByteChunk out, boolean followRedirects) throws IOException { - return methodUrl(path, out, DEFAULT_CLIENT_TIMEOUT_MS, null, null, "GET", followRedirects); + return methodUrl(path, out, DEFAULT_CLIENT_TIMEOUT_MS, null, null, Method.GET, followRedirects); } public static int headUrl(String path, ByteChunk out, Map> resHead) throws IOException { - return methodUrl(path, out, DEFAULT_CLIENT_TIMEOUT_MS, null, resHead, "HEAD"); + return methodUrl(path, out, DEFAULT_CLIENT_TIMEOUT_MS, null, resHead, Method.HEAD); } public static int getUrl(String path, ByteChunk out, Map> reqHead, @@ -648,7 +670,7 @@ public static int getUrl(String path, ByteChunk out, int readTimeout, Map> reqHead, Map> resHead) throws IOException { - return methodUrl(path, out, readTimeout, reqHead, resHead, "GET"); + return methodUrl(path, out, readTimeout, reqHead, resHead, Method.GET); } public static int methodUrl(String path, ByteChunk out, int readTimeout, @@ -924,4 +946,214 @@ session.setMaxInactiveInterval(newIntervalSecs); } } + + /** + * Captures logs for the given logger names in the current ClassLoader. + */ + public static class LogCapture implements AutoCloseable { + protected final Level level; + protected final String[] loggerNames; + protected final List logRecords = Collections.synchronizedList(new ArrayList<>()); + protected final Map previousLevelsOfLoggersMap = new IdentityHashMap<>(); + private volatile boolean installed = false; + protected final Handler handler = new Handler() { + @Override + public void publish(LogRecord record) { + logRecords.add(record); + } + + @Override + public void flush() { + } + + @Override + public void close() throws SecurityException { + logRecords.clear(); + } + }; + public LogCapture(Level level, String... loggerNames) { + this.level = level; + this.loggerNames = loggerNames; + } + + public void attach() { + if (!installed) { + for (String name : loggerNames) { + Logger logger = Logger.getLogger(name); + logger.addHandler(handler); + if (level != null) { + previousLevelsOfLoggersMap.put(logger, logger.getLevel()); + logger.setLevel(level); + } + } + installed = true; + } + } + + public boolean containsText(CharSequence s) { + for (LogRecord record : logRecords) { + if (record.getMessage().contains(s)) { + return true; + } + } + return false; + } + public boolean hasException(Class type) { + for (LogRecord record : logRecords) { + Throwable t = record.getThrown(); + while (t != null) { + if (type.isInstance(t)) {return true;} + t = t.getCause(); + } + } + return false; + } + + @Override + public void close() throws Exception { + for (Logger l : previousLevelsOfLoggersMap.keySet()) { + try { + l.removeHandler(handler); + } catch (Throwable ignore) { + } + try { + l.setLevel(previousLevelsOfLoggersMap.get(l)); + } catch (Throwable ignore) { + } + } + previousLevelsOfLoggersMap.clear(); + } + } + + public static LogCapture attachLogCapture(Level level, String... loggerNames) { + LogCapture logCapture = new LogCapture(level, loggerNames); + logCapture.attach(); + return logCapture; + } + + /** + * Captures webapp-scoped logs (e.g. ContextConfig/Digester) during the + * CONFIGURE_START phase of a {@link Context}. + */ + public static class WebappLogCapture extends LogCapture implements LifecycleListener { + private String lifecycleEvent = Lifecycle.CONFIGURE_START_EVENT; + public WebappLogCapture(String lifecycleEvent, Level level, String... loggerNames) { + this(level, loggerNames); + this.lifecycleEvent = lifecycleEvent; + } + public WebappLogCapture(Level level, String... loggerNames) { + super(level, loggerNames); + } + + @Override + public void lifecycleEvent(LifecycleEvent event) { + if (this.lifecycleEvent.equals(event.getType())) { + this.attach(); + } + } + } + + /** + * Installs a {@link WebappLogCapture} on the given {@link Context} so it runs + * before {@link ContextConfig} during CONFIGURE_START. + * @param ctx the webapp context + * @param level level for loggers (e.g. {@code Level.ALL}) + * @param loggerNames fully-qualified logger names + * @return the active capture + */ + public static WebappLogCapture attachWebappLogCapture(Context ctx, Level level, String... loggerNames) { + List lifecycleListenersToReAdd = new ArrayList<>(); + for (LifecycleListener l : ctx.findLifecycleListeners()) { + if (l instanceof ContextConfig) { + lifecycleListenersToReAdd.add(l); + } + } + for (LifecycleListener l : lifecycleListenersToReAdd) { + ctx.removeLifecycleListener(l); + } + WebappLogCapture webappLogCapture = new WebappLogCapture(level, loggerNames); + ctx.addLifecycleListener(webappLogCapture); + for (LifecycleListener l : lifecycleListenersToReAdd) { + ctx.addLifecycleListener(l); + } + return webappLogCapture; + } + + /** + * Returns the localized key in a LocalStrings.properties file. + * + * @param packagePath The package that contains LocalStrings.properties, e.g. 'org.apache.catalina.startup' + * @param key The key to find, e.g. 'versionLoggerListener.serverInfo.server.built' + * @param locale The locale to use, e.g. Locale.ENGLISH + * @return The prefix before the first argument placeholder and if no placeholder, returns the whole formatted string. + */ + public static String getKeyFromPropertiesFile(String packagePath, String key, Locale locale) { + StringManager sm; + if (locale != null) { + sm = StringManager.getManager(packagePath, locale); + } else { + sm = StringManager.getManager(packagePath); + } + + String formatted = sm.getString(key, "XXX"); + int insertIndex = formatted.indexOf("XXX"); + return (insertIndex == -1) ? formatted : formatted.substring(0, insertIndex); + } + public static String getKeyFromPropertiesFile(String packagePath, String key) { + return getKeyFromPropertiesFile(packagePath, key, Locale.getDefault()); + } + public static String getKeyFromPropertiesFile(StringManager sm, String key) { + String formatted = sm.getString(key, "XXX"); + int insertIndex = formatted.indexOf("XXX"); + return (insertIndex == -1) ? formatted : formatted.substring(0, insertIndex); + } + + /** + * Injects a {@link LifecycleListener} to a {@link Context} of a {@link Container} that sends {@code ADD_CHILD_EVENT}. + * Useful when deploying with the Manager / HostConfig. + */ + public static class ContainerInjector implements ContainerListener, AutoCloseable { + + private final Container container; + private final Predicate filter; + private final Consumer action; + private volatile boolean installed = false; + private String containerEvent = Container.ADD_CHILD_EVENT; + + private ContainerInjector(Container container, Predicate filter, Consumer action, String containerEvent) { + this.container = container; + this.filter = filter; + this.action = action; + if (containerEvent != null) { + this.containerEvent = containerEvent; + } + container.addContainerListener(this); + } + + public static ContainerInjector inject(Container container, Predicate filter, Consumer action) { + return new ContainerInjector(container, filter, action, null); + } + public static ContainerInjector inject(Container container, Predicate filter, Consumer action, String containerEvent) { + return new ContainerInjector(container, filter, action, containerEvent); + } + + @Override + public void containerEvent(ContainerEvent event) { + if (this.containerEvent.equals(event.getType()) && !installed) { + Object data = event.getData(); + if (data instanceof Context) { + Context ctx = (Context) data; + if (filter != null && filter.test(ctx)) { + action.accept(ctx); + installed = true; + } + } + } + } + + @Override + public void close() { + container.removeContainerListener(this); + } + } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/util/TestFilterUtil.java tomcat10-10.1.52/test/org/apache/catalina/util/TestFilterUtil.java --- tomcat10-10.1.34/test/org/apache/catalina/util/TestFilterUtil.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/util/TestFilterUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.util; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.GenericFilter; +import jakarta.servlet.GenericServlet; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.Wrapper; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; + +public class TestFilterUtil extends TomcatBaseTest { + + @Test + public void testContextRootMappedFilter() throws Exception { + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = getProgrammaticRootContext(); + + CountingFilter countingFilter = new CountingFilter(); + + FilterDef fd = new FilterDef(); + fd.setFilter(countingFilter); + fd.setFilterName("CountingFilter"); + + FilterMap fm = new FilterMap(); + fm.setFilterName(fd.getFilterName()); + fm.addURLPattern(""); + + ctx.addFilterDef(fd); + ctx.addFilterMap(fm); + + Wrapper w = tomcat.addServlet("", "Default", new DefaultServlet()); + w.addMapping("/"); + + tomcat.start(); + + ByteChunk bc = new ByteChunk(); + int rc; + + Assert.assertEquals(0, countingFilter.getCount()); + + rc = getUrl("http://localhost:" + getPort(), bc, false); + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + Assert.assertEquals(1, countingFilter.getCount()); + + rc = getUrl("http://localhost:" + getPort() + "/", bc, false); + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + Assert.assertEquals(2, countingFilter.getCount()); + + rc = getUrl("http://localhost:" + getPort() + "/not-a-context-root", bc, false); + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + Assert.assertEquals(2, countingFilter.getCount()); + } + + + public static class CountingFilter extends GenericFilter { + + private static final long serialVersionUID = 1L; + + private AtomicInteger count = new AtomicInteger(0); + + + public int getCount() { + return count.get(); + } + + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + count.incrementAndGet(); + chain.doFilter(request, response); + } + } + + + public static class DefaultServlet extends GenericServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { + res.setContentType("text/plain"); + res.setCharacterEncoding("UTF-8"); + res.getWriter().print("OK"); + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/util/TestParameterMapPerformance.java tomcat10-10.1.52/test/org/apache/catalina/util/TestParameterMapPerformance.java --- tomcat10-10.1.34/test/org/apache/catalina/util/TestParameterMapPerformance.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/util/TestParameterMapPerformance.java 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.catalina.util; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Assert; -import org.junit.Test; - -public class TestParameterMapPerformance { - - private static final int NUM_TESTS = 10; - private static final int NUM_SKIP = 4; - private static final int NUM_TEST_ITERATIONS = 1000000; - - private ParameterMap baseParams; - private ParameterMap testParams; - - @Test - public void testCompareStandardAndOptimizedMapConstructor() { - Map values = new HashMap<>(); - for (int i = 0; i < 50; i++) { - Integer integer = Integer.valueOf(i); - values.put(integer, integer); - } - - baseParams = new ParameterMap<>(values); - baseParams.setLocked(true); - - // warmup - for (int i = 0; i < NUM_TEST_ITERATIONS; i++) { - useStandardConstructor(); - useOptimizedMapConstructor(); - } - - List standardDurations = new ArrayList<>(); - for (int i = 0; i < NUM_TESTS; i++) { - System.gc(); - long startStandard = System.currentTimeMillis(); - for (int j = 0; j < NUM_TEST_ITERATIONS; j++) { - useStandardConstructor(); - } - long duration = System.currentTimeMillis() - startStandard; - standardDurations.add(Long.valueOf(duration)); - System.out.println("Done with standard in " + duration + "ms"); - } - /* - * The CI systems tend to produce outliers that lead to false failures so skip the longest two runs. - */ - long standardTotalDuration = - standardDurations.stream().sorted().limit(NUM_TESTS - 2).reduce(Long::sum).get().longValue(); - - List optimizedDurations = new ArrayList<>(); - for (int i = 0; i < NUM_TESTS; i++) { - System.gc(); - long startOptimized = System.currentTimeMillis(); - for (int j = 0; j < NUM_TEST_ITERATIONS; j++) { - useOptimizedMapConstructor(); - } - long duration = System.currentTimeMillis() - startOptimized; - optimizedDurations.add(Long.valueOf(duration)); - System.out.println("Done with optimized in " + duration + "ms"); - } - /* - * The CI systems tend to produce outliers that lead to false failures so skip the longest two runs. - */ - long optimizedTotalDuration = - optimizedDurations.stream().sorted().limit(NUM_TESTS - NUM_SKIP).reduce(Long::sum).get().longValue(); - - Assert.assertTrue("Standard: " + standardTotalDuration + "ms, Optimized: " + optimizedTotalDuration + "ms", - optimizedTotalDuration < standardTotalDuration); - } - - - private void useOptimizedMapConstructor() { - testParams = new ParameterMap<>(baseParams); - testParams.entrySet(); - } - - - private void useStandardConstructor() { - testParams = new ParameterMap<>(); - testParams.putAll(baseParams); - testParams.entrySet(); - } -} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/util/TestURLEncoder.java tomcat10-10.1.52/test/org/apache/catalina/util/TestURLEncoder.java --- tomcat10-10.1.34/test/org/apache/catalina/util/TestURLEncoder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/util/TestURLEncoder.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,11 +16,14 @@ */ package org.apache.catalina.util; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import org.junit.Assert; import org.junit.Test; +import org.apache.tomcat.util.buf.UDecoder; + public class TestURLEncoder { private static final String SPACE = " "; @@ -53,4 +56,29 @@ xml.removeSafeCharacter('&'); Assert.assertEquals(AMPERSAND_ENCODED, xml.encode(AMPERSAND, StandardCharsets.UTF_8)); } + + + @Test(expected = IllegalArgumentException.class) + public void testOssFuzz01() { + /* + * Round-trip URL encoding with ASCII only works for valid ASCII characters. + */ + testRoundTrip("\uFFFD", StandardCharsets.US_ASCII); + } + + + @Test + public void testOssFuzz02() { + testRoundTrip("\uFFFD", StandardCharsets.UTF_8); + } + + + private void testRoundTrip(String input, Charset charset) { + URLEncoder encoder = new URLEncoder(); + + String encoded = encoder.encode(input, charset); + String decoded = UDecoder.URLDecode(encoded, charset); + + Assert.assertEquals(input, decoded); + } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestAccessLogValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestAccessLogValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestAccessLogValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestAccessLogValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -45,6 +45,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.json.JSONParser; import org.apache.tomcat.util.json.ParseException; @@ -109,7 +110,7 @@ parameterSets.add(new Object[] {"pct-I", JSON_TYPE, "/", "%I", "\\{\"threadName\":\"http-nio2?-" + LOCAL_IP_PATTERN + "-auto-\\d+-exec-\\d+\"\\}"}); parameterSets.add(new Object[] {"pct-l", TEXT_TYPE, "/", "%l", "-"}); parameterSets.add(new Object[] {"pct-l", JSON_TYPE, "/", "%l", "\\{\"logicalUserName\":\"-\"\\}"}); - parameterSets.add(new Object[] {"pct-m", TEXT_TYPE, "/", "%m", "GET"}); + parameterSets.add(new Object[] {"pct-m", TEXT_TYPE, "/", "%m", Method.GET}); parameterSets.add(new Object[] {"pct-m", JSON_TYPE, "/", "%m", "\\{\"method\":\"GET\"\\}"}); parameterSets.add(new Object[] {"pct-p", TEXT_TYPE, "/", "%p", "\\d+"}); parameterSets.add(new Object[] {"pct-p", JSON_TYPE, "/", "%p", "\\{\"port\":\"\\d+\"\\}"}); @@ -208,18 +209,12 @@ this.writer = writer; } - /** - * Log the specified message to the log file, switching files if - * the date has changed since the previous log call. - * - * @param message Message to be logged - */ @Override public void log(CharArrayWriter message) { try { message.writeTo(writer); - } catch (IOException ex) { - log.error("Could not write to writer", ex); + } catch (IOException ioe) { + log.error("Could not write to writer", ioe); } } } @@ -235,18 +230,12 @@ this.writer = writer; } - /** - * Log the specified message to the log file, switching files if - * the date has changed since the previous log call. - * - * @param message Message to be logged - */ @Override public void log(CharArrayWriter message) { try { message.writeTo(writer); - } catch (IOException ex) { - log.error("Could not write to writer", ex); + } catch (IOException ioe) { + log.error("Could not write to writer", ioe); } } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestCrawlerSessionManagerValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestCrawlerSessionManagerValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestCrawlerSessionManagerValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestCrawlerSessionManagerValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -165,7 +165,7 @@ private HttpSession createSessionExpectations(CrawlerSessionManagerValve valve, boolean isBot) { HttpSession session = EasyMock.createMock(HttpSession.class); if (isBot) { - EasyMock.expect(session.getId()).andReturn("id").times(2); + EasyMock.expect(session.getId()).andReturn("id").times(1); session.setAttribute(EasyMock.eq(valve.getClass().getName()), EasyMock.anyObject(HttpSessionBindingListener.class)); EasyMock.expectLastCall(); @@ -184,6 +184,8 @@ String contextPath, String userAgent) { Request request = EasyMock.createMock(Request.class); EasyMock.expect(request.getRemoteAddr()).andReturn(ip); + EasyMock.expect(request.getRemoteAddr()).andReturn(ip); + EasyMock.expect(request.getHost()).andReturn(simpleHostWithName(hostname)); EasyMock.expect(request.getHost()).andReturn(simpleHostWithName(hostname)); EasyMock.expect(request.getContext()).andReturn(simpleContextWithName(contextPath)); IExpectationSetters setter = EasyMock.expect(request.getSession(false)).andReturn(null); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestExtendedAccessLogValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestExtendedAccessLogValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestExtendedAccessLogValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestExtendedAccessLogValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,48 +16,220 @@ */ package org.apache.catalina.valves; +import java.io.CharArrayWriter; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +import org.apache.catalina.Context; +import org.apache.catalina.Host; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.http.Method; + +@RunWith(Parameterized.class) +public class TestExtendedAccessLogValve extends TomcatBaseTest { + + // Requests can return in the client before log() has been called + private static final long SLEEP = 2; + private static final long SLEEP_MAX = 21000; + + @Parameterized.Parameters(name = "{index}: pattern=[{0}]") + public static Collection data() { + List patterns = new ArrayList<>(); + patterns.add(new Object[]{"basic", "time cs-method cs-uri-stem cs-uri-query"}); + patterns.add(new Object[]{"ip", "time cs-method sc-status c-ip s-ip s-dns c-dns"}); + patterns.add(new Object[]{"headers", "time cs-method cs(Referer) cs(Cookie) sc(Content-Type)"}); + patterns.add(new Object[]{"bytes", "date time cs-method cs-uri bytes time-taken cached"}); + patterns.add(new Object[]{"time", "date time time-taken-ns time-taken-us time-taken-ms time-taken-fracsec time-taken-s"}); + patterns.add(new Object[]{"tomcat1", "x-threadname x-A(testSCAttr) x-C(COOKIE-1_3) x-O(Custom)"}); + patterns.add(new Object[]{"tomcat2", "x-R(testRAttr) x-S(sessionAttr) x-P(testParam)"}); + patterns.add(new Object[]{"tomcat3", "x-H(authType) x-H(characterEncoding) x-H(connectionId) x-H(contentLength)"}); + patterns.add(new Object[]{"tomcat4", "x-H(locale) x-H(protocol) x-H(remoteUser) x-H(requestedSessionId)"}); + patterns.add(new Object[]{"tomcat5", "x-H(requestedSessionIdFromCookie) x-H(requestedSessionIdValid) x-H(scheme) x-H(secure)"}); + return patterns; + } + + @Parameter(0) + public String name; + + @Parameter(1) + public String logPattern; + + + /** + * Extend AbstractAccessLogValve to retrieve log output. + */ + public final class TesterExtendedAccessLogValve extends ExtendedAccessLogValve { + + private CharArrayWriter writer; + + public TesterExtendedAccessLogValve(CharArrayWriter writer) { + this.writer = writer; + } + + @Override + public void log(CharArrayWriter message) { + try { + message.writeTo(writer); + } catch (IOException ioe) { + log.error("Could not write to writer", ioe); + } + } + } + + + @Test + public void testLogFormat() throws Exception { + + Tomcat tomcat = getTomcatInstance(); + Host host = tomcat.getHost(); + + // Create temporary directory for logs + File logDir = getTemporaryDirectory(); + + CharArrayWriter writer = new CharArrayWriter(); + TesterExtendedAccessLogValve valve = new TesterExtendedAccessLogValve(writer); + valve.setBuffered(false); + valve.setPattern(logPattern); + valve.setDirectory(logDir.getAbsolutePath()); + valve.setPrefix("access_log_" + name); + + host.getPipeline().addValve(valve); + + // Add test servlet + Context ctx = getProgrammaticRootContext(); + Tomcat.addServlet(ctx, "testServlet", new HttpServlet() { + private static final long serialVersionUID = 1L; + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + req.getServletContext().setAttribute("testSCAttr", "testSCAttrValue"); + req.getSession(true).setAttribute("sessionAttr", "sessionAttrValue"); + req.setAttribute("testRAttr", "testRValue"); + resp.addHeader("Custom", "value1"); + resp.addHeader("Custom", "value2"); + resp.getWriter().write("Test response"); + } + }); + ctx.addServletMappingDecoded("/test", "testServlet"); + + tomcat.start(); + + String url = "http://localhost:" + getPort() + "/test?testParam=testValue"; + ByteChunk out = new ByteChunk(); + Map> reqHead = new HashMap<>(); + List cookieHeaders = new ArrayList<>(); + cookieHeaders.add("COOKIE-1_1=1_1;COOKIE-1_2=1_2;COOKIE-1_3=1_3"); + reqHead.put("Cookie", cookieHeaders); + List refererHeader = new ArrayList<>(); + refererHeader.add("/some/path"); + reqHead.put("Referer", refererHeader); + List contentTypeHeader = new ArrayList<>(); + contentTypeHeader.add("text/plain"); + reqHead.put("Content-Type", contentTypeHeader); + Map> resHead = new HashMap<>(); + int status = getUrl(url, out, reqHead, resHead); + Assert.assertEquals(HttpServletResponse.SC_OK, status); + + long startWait = System.currentTimeMillis(); + String content = writer.toString(); + while (countLogLines(content) == 0 && System.currentTimeMillis() - startWait < SLEEP_MAX) { + try { + Thread.sleep(SLEEP); + } catch (InterruptedException ex) { + log.error("Exception during sleep", ex); + } + content = writer.toString(); + } -public class TestExtendedAccessLogValve { - - @Test - public void alpha() { - Assert.assertEquals("\"foo\"", ExtendedAccessLogValve.wrap("foo")); - } - - @Test - public void testNull() { - Assert.assertEquals("-", ExtendedAccessLogValve.wrap(null)); - } - - @Test - public void empty() { - Assert.assertEquals("\"\"", ExtendedAccessLogValve.wrap("")); - } - - @Test - public void singleQuoteMiddle() { - Assert.assertEquals("\"foo'bar\"", ExtendedAccessLogValve.wrap("foo'bar")); - } + processLogContent(content); +} - @Test - public void doubleQuoteMiddle() { - Assert.assertEquals("\"foo\"\"bar\"", ExtendedAccessLogValve.wrap("foo\"bar")); - } - @Test - public void doubleQuoteStart() { - Assert.assertEquals("\"\"\"foobar\"", ExtendedAccessLogValve.wrap("\"foobar")); + private int countLogLines(String content) { + int result = 0; + String[] lines = content.split("\\r?\\n"); + for (String line : lines) { + if (!line.startsWith("#") && !line.trim().isEmpty()) { + result++; + } + } + return result; + } + + + private void processLogContent(String content) { + String[] lines = content.split("\\r?\\n"); + + List dataLines = new ArrayList<>(); + for (String line : lines) { + if (!line.startsWith("#")) { // Skip directives + dataLines.add(line.trim()); + } else { + if (line.startsWith("#Fields: ")) { + Assert.assertEquals(line.substring("#Fields: ".length()), logPattern); + } + } + } + + Assert.assertTrue("No data entries found", !dataLines.isEmpty()); + + String entryLine = dataLines.get(0); + System.out.println(name + ": " + entryLine); + + String[] parts = entryLine.split("\\s+"); + + String[] expectedFields = logPattern.split("\\s+"); + + Assert.assertEquals(expectedFields.length, parts.length); + + for (int i=0; i < expectedFields.length; i++) { + checkField(expectedFields[i], parts[i]); + } + } + + + private void checkField(String fieldId, String value) { + if ("time".equals(fieldId)) { + Assert.assertTrue("Invalid time format", isTimeFormat(value)); + } else if ("cs-method".equals(fieldId)) { + Assert.assertEquals(Method.GET, value); + } else if (fieldId.startsWith("c-ip")) { + // IPv4 with optional port + Assert.assertTrue(value.matches("^\\d{1,3}(\\.\\d{1,3}){3}(:\\d+)?$")); + } else if ("cs-uri-stem".equals(fieldId)) { + Assert.assertEquals("/test", value); + } else if (fieldId.equals("sc-status")) { + Assert.assertEquals("200", value); + } else if (fieldId.contains("Referer")) { + Assert.assertTrue(value.equals("\"/some/path\"")); + } else if ("bytes".equals(fieldId)) { + // Non-negative integer or '-' + Assert.assertTrue(value.equals("-") || value.matches("\\d+")); + } else if ("time-taken".equals(fieldId)) { + // Fixed format (e.g., 0.015) + Assert.assertTrue(value.matches("^\\d+(\\.\\d+)?$")); + } } - @Test - public void doubleQuoteEnd() { - Assert.assertEquals("\"foobar\"\"\"", ExtendedAccessLogValve.wrap("foobar\"")); - } - @Test - public void doubleQuote() { - Assert.assertEquals("\"\"\"\"", ExtendedAccessLogValve.wrap("\"")); + private boolean isTimeFormat(String s) { + return Pattern.matches("^\\d{2}:\\d{2}(:\\d{2}(\\.\\d+)?)?$", s); } -} +} \ No newline at end of file diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestExtendedAccessLogValveWrap.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestExtendedAccessLogValveWrap.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestExtendedAccessLogValveWrap.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestExtendedAccessLogValveWrap.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.valves; + +import java.io.CharArrayWriter; + +import org.junit.Assert; +import org.junit.Test; + +public class TestExtendedAccessLogValveWrap { + + @Test + public void alpha() { + CharArrayWriter buf = new CharArrayWriter(); + ExtendedAccessLogValve.wrap("foo", buf); + Assert.assertEquals("\"foo\"", buf.toString()); + } + + @Test + public void testNull() { + CharArrayWriter buf = new CharArrayWriter(); + ExtendedAccessLogValve.wrap(null, buf); + Assert.assertEquals("-", buf.toString()); + } + + @Test + public void empty() { + CharArrayWriter buf = new CharArrayWriter(); + ExtendedAccessLogValve.wrap("", buf); + Assert.assertEquals("\"\"", buf.toString()); + } + + @Test + public void singleQuoteMiddle() { + CharArrayWriter buf = new CharArrayWriter(); + ExtendedAccessLogValve.wrap("foo'bar", buf); + Assert.assertEquals("\"foo'bar\"", buf.toString()); + } + + @Test + public void doubleQuoteMiddle() { + CharArrayWriter buf = new CharArrayWriter(); + ExtendedAccessLogValve.wrap("foo\"bar", buf); + Assert.assertEquals("\"foo\"\"bar\"", buf.toString()); + } + + @Test + public void doubleQuoteStart() { + CharArrayWriter buf = new CharArrayWriter(); + ExtendedAccessLogValve.wrap("\"foobar", buf); + Assert.assertEquals("\"\"\"foobar\"", buf.toString()); + } + + @Test + public void doubleQuoteEnd() { + CharArrayWriter buf = new CharArrayWriter(); + ExtendedAccessLogValve.wrap("foobar\"", buf); + Assert.assertEquals("\"foobar\"\"\"", buf.toString()); + } + + @Test + public void doubleQuote() { + CharArrayWriter buf = new CharArrayWriter(); + ExtendedAccessLogValve.wrap("\"", buf); + Assert.assertEquals("\"\"\"\"", buf.toString()); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestJDBCAccessLogValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestJDBCAccessLogValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestJDBCAccessLogValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestJDBCAccessLogValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,6 +27,7 @@ import jakarta.servlet.http.HttpServletResponse; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -37,6 +38,7 @@ import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.compat.JreCompat; @SuppressWarnings("deprecation") @RunWith(Parameterized.class) @@ -73,6 +75,8 @@ @Test public void testValve() throws Exception { + Assume.assumeTrue(JreCompat.isJre16Available()); + Tomcat tomcat = getTomcatInstance(); // No file system docBase required getTomcatInstanceTestWebapp(false, false); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestParameterLimitValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestParameterLimitValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestParameterLimitValve.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestParameterLimitValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,694 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.valves; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.servlet.MultipartConfigElement; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; + +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; +import org.apache.catalina.Context; +import org.apache.catalina.Wrapper; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.filters.FailedRequestFilter; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; +import org.apache.tomcat.util.scan.StandardJarScanner; + + +public class TestParameterLimitValve extends TomcatBaseTest { + + @Test + public void testSpecificUrlPatternLimit() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + parameterLimitValve.setUrlPatternLimits("/special/.*=2"); + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/special/endpoint", "snoop"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + int rc = getUrl("http://localhost:" + getPort() + "/special/endpoint?param1=value1¶m2=value2¶m3=value3", + new ByteChunk(), null); + Assert.assertEquals(400, rc); + + rc = getUrl("http://localhost:" + getPort() + "/special/endpoint?param1=value1¶m2=value2", new ByteChunk(), + null); + Assert.assertEquals(200, rc); + + // @formatter:off + byte[] body = ( + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: close" + CRLF + + "Transfer-Encoding: chunked" + CRLF + + "Content-Type: application/x-www-form-urlencoded" + CRLF + + CRLF + + "param1=value1¶m2=value2¶m3=value3" + CRLF).getBytes(StandardCharsets.UTF_8); + // @formatter:on + + rc = postUrl(body, "http://localhost:" + getPort() + "/special/endpoint", new ByteChunk(), null); + + Assert.assertEquals(400, rc); + + // @formatter:off + body = ( + "POST / HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Connection: close" + CRLF + + "Transfer-Encoding: chunked" + CRLF + + "Content-Type: application/x-www-form-urlencoded" + CRLF + + CRLF + + "param1=value1¶m2=value2" + CRLF).getBytes(StandardCharsets.UTF_8); + // @formatter:on + + rc = postUrl(body, "http://localhost:" + getPort() + "/special/endpoint", new ByteChunk(), null); + + Assert.assertEquals(200, rc); + + body = ("POST / HTTP/1.1" + CRLF + "Host: localhost:" + getPort() + CRLF + "Connection: close" + CRLF + + "Transfer-Encoding: chunked" + CRLF + "Content-Type: application/x-www-form-urlencoded" + CRLF + CRLF + + "param1=value1¶m2=value2" + CRLF).getBytes(StandardCharsets.UTF_8); + + rc = postUrl(body, "http://localhost:" + getPort() + "/special/endpoint?param3=value3", new ByteChunk(), null); + + Assert.assertEquals(400, rc); + + body = ("POST / HTTP/1.1" + CRLF + "Host: localhost:" + getPort() + CRLF + "Connection: close" + CRLF + + "Transfer-Encoding: chunked" + CRLF + "Content-Type: application/x-www-form-urlencoded" + CRLF + CRLF + + "param1=value1" + CRLF).getBytes(StandardCharsets.UTF_8); + + rc = postUrl(body, "http://localhost:" + getPort() + "/special/endpoint?param2=value2", new ByteChunk(), null); + + Assert.assertEquals(200, rc); + + rc = getUrl("http://localhost:" + getPort() + "/special/endpoint", new ByteChunk(), null); + + Assert.assertEquals(200, rc); + } + + @Test + public void testMultipleEqualsPatternLimit() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + parameterLimitValve.setUrlPatternLimits("/special====2"); + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/special===", "snoop"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + int rc = getUrl("http://localhost:" + getPort() + "/special===?param1=value1¶m2=value2", new ByteChunk(), + null); + + Assert.assertEquals(200, rc); + } + + @Test + public void testEncodedUrlPatternLimit() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + parameterLimitValve.setUrlPatternLimits("/special%20endpoint=2"); + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/special endpoint", "snoop"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + int rc = getUrl( + "http://localhost:" + getPort() + "/special%20endpoint?param1=value1¶m2=value2¶m3=value3", + new ByteChunk(), null); + + Assert.assertEquals(400, rc); + } + + @Test + public void testMultipleSpecificUrlPatternsLimit() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + tomcat.getConnector().setMaxParameterCount(2); + + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + parameterLimitValve + .setUrlPatternLimits("/special/.*=2" + CRLF + "/special2/.*=3" + CRLF + "/my/special/url1=1"); + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/special/endpoint", "snoop"); + ctx.addServletMappingDecoded("/special2/endpoint", "snoop"); + ctx.addServletMappingDecoded("/my/special/url1", "snoop"); + ctx.addServletMappingDecoded("/my/special/url2", "snoop"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + int rc = getUrl("http://localhost:" + getPort() + "/special/endpoint?param1=value1¶m2=value2¶m3=value3", + new ByteChunk(), null); + Assert.assertEquals(400, rc); + + rc = getUrl( + "http://localhost:" + getPort() + + "/special2/endpoint?param1=value1¶m2=value2¶m3=value3¶m4=value4", + new ByteChunk(), null); + Assert.assertEquals(400, rc); + + rc = getUrl("http://localhost:" + getPort() + "/my/special/url1?param1=value1¶m2=value2", new ByteChunk(), + null); + Assert.assertEquals(400, rc); + + rc = getUrl("http://localhost:" + getPort() + "/my/special/url2?param1=value1¶m2=value2¶m3=value3", + new ByteChunk(), null); + Assert.assertEquals(400, rc); + + rc = getUrl("http://localhost:" + getPort() + "/special/endpoint?param1=value1¶m2=value2", new ByteChunk(), + null); + Assert.assertEquals(200, rc); + + rc = getUrl("http://localhost:" + getPort() + "/special2/endpoint?param1=value1¶m2=value2¶m3=value3", + new ByteChunk(), null); + Assert.assertEquals(200, rc); + + rc = getUrl("http://localhost:" + getPort() + "/my/special/url1?param1=value1", new ByteChunk(), null); + Assert.assertEquals(200, rc); + + rc = getUrl("http://localhost:" + getPort() + "/my/special/url2?param1=value1¶m2=value2", new ByteChunk(), + null); + Assert.assertEquals(200, rc); + } + + @Test + public void testNoMatchingPatternWithConnectorLimit() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + tomcat.getConnector().setMaxParameterCount(1); + + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + parameterLimitValve.setUrlPatternLimits("/special/.*=2"); + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/other/endpoint", "snoop"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + int rc = getUrl("http://localhost:" + getPort() + "/other/endpoint?param1=value1¶m2=value2", + new ByteChunk(), null); + + Assert.assertEquals(400, rc); + } + + @Test + public void testUrlPatternLimitsFromFile() throws Exception { + File configFile = File.createTempFile("parameter_limit", ".config"); + try (PrintWriter writer = new PrintWriter(new FileWriter(configFile))) { + writer.println("# Commented line - empty line follows"); + writer.println(""); + writer.println("/api/.*=2"); + writer.println("# Commented line"); + } + + Tomcat tomcat = getTomcatInstance(); + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + + try (BufferedReader reader = new BufferedReader(new FileReader(configFile))) { + parameterLimitValve.setUrlPatternLimits(reader); + } + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/api/test", "snoop"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + int rc = getUrl("http://localhost:" + getPort() + "/api/test?param1=value1¶m2=value2", new ByteChunk(), + null); + Assert.assertEquals(200, rc); + + rc = getUrl("http://localhost:" + getPort() + "/api/test?param1=value1¶m2=value2¶m3=value3", + new ByteChunk(), null); + Assert.assertEquals(400, rc); + } + + @Test + public void testUrlPatternLimitsWithEmptyFile() throws Exception { + File configFile = File.createTempFile("parameter_limit", ".config"); + + Tomcat tomcat = getTomcatInstance(); + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + + try (BufferedReader reader = new BufferedReader(new FileReader(configFile))) { + parameterLimitValve.setUrlPatternLimits(reader); + } + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/api/test", "snoop"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + int rc = getUrl("http://localhost:" + getPort() + "/api/test?param1=value1¶m2=value2", new ByteChunk(), + null); + Assert.assertEquals(200, rc); + } + + @Test + public void testUrlPatternLimitsFromFileAndProperty() throws Exception { + File configFile = File.createTempFile("parameter_limit", ".config"); + try (PrintWriter writer = new PrintWriter(new FileWriter(configFile))) { + writer.println("# Commented line"); + writer.println("/api/.*=2"); + writer.println("# Commented line"); + } + + Tomcat tomcat = getTomcatInstance(); + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + + parameterLimitValve.setUrlPatternLimits("/admin/.*=2"); + + try (BufferedReader reader = new BufferedReader(new FileReader(configFile))) { + parameterLimitValve.setUrlPatternLimits(reader); + } + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/api/test", "snoop"); + ctx.addServletMappingDecoded("/admin/test", "snoop"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + int rc = getUrl("http://localhost:" + getPort() + "/api/test?param1=value1¶m2=value2", new ByteChunk(), + null); + Assert.assertEquals(200, rc); + + rc = getUrl("http://localhost:" + getPort() + "/admin/test?param1=value1¶m2=value2", new ByteChunk(), null); + Assert.assertEquals(200, rc); + + rc = getUrl("http://localhost:" + getPort() + "/api/test?param1=value1¶m2=value2¶m3=value3", + new ByteChunk(), null); + Assert.assertEquals(400, rc); + + rc = getUrl("http://localhost:" + getPort() + "/admin/test?param1=value1¶m2=value2¶m3=value3", + new ByteChunk(), null); + Assert.assertEquals(400, rc); + } + + @Test + public void testServerUrlPatternLimit() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getParent().getPipeline().addValve(parameterLimitValve); + parameterLimitValve.setUrlPatternLimits("/.*=2"); + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/special/endpoint", "snoop"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + int rc = getUrl("http://localhost:" + getPort() + "/special/endpoint?param1=value1¶m2=value2¶m3=value3", + new ByteChunk(), null); + + Assert.assertEquals(400, rc); + + rc = getUrl("http://localhost:" + getPort() + "/special/endpoint?param1=value1¶m2=value2", new ByteChunk(), + null); + + Assert.assertEquals(200, rc); + } + + @Test + public void testServerAndContextUrlPatternLimit() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + Context ctx1 = tomcat.addContext("context1", null); + ((StandardJarScanner) ctx1.getJarScanner()).setScanClassPath(false); + + Context ctx2 = tomcat.addContext("context2", null); + ((StandardJarScanner) ctx2.getJarScanner()).setScanClassPath(false); + + Context ctx3 = tomcat.addContext("context3", null); + ((StandardJarScanner) ctx2.getJarScanner()).setScanClassPath(false); + + ParameterLimitValve serverParameterLimitValve = new ParameterLimitValve(); + ParameterLimitValve contextParameterLimitValve = new ParameterLimitValve(); + ParameterLimitValve context3ParameterLimitValve = new ParameterLimitValve(); + + ctx1.getParent().getPipeline().addValve(serverParameterLimitValve); + + ctx1.getPipeline().addValve(contextParameterLimitValve); + ctx3.getPipeline().addValve(context3ParameterLimitValve); + + serverParameterLimitValve.setUrlPatternLimits("/.*=2"); + contextParameterLimitValve.setUrlPatternLimits("/special/.*=3"); + context3ParameterLimitValve.setUrlPatternLimits("/special/.*=1"); + + Tomcat.addServlet(ctx1, "snoop", new SnoopServlet()); + ctx1.addServletMappingDecoded("/special/endpoint", "snoop"); + + Tomcat.addServlet(ctx2, "snoop", new SnoopServlet()); + ctx2.addServletMappingDecoded("/special/endpoint", "snoop"); + + Tomcat.addServlet(ctx3, "snoop", new SnoopServlet()); + ctx3.addServletMappingDecoded("/special/endpoint", "snoop"); + + addFailedRequestFilter(ctx1); + addFailedRequestFilter(ctx2); + addFailedRequestFilter(ctx3); + + tomcat.start(); + + int rc = getUrl("http://localhost:" + getPort() + + "/context1/special/endpoint?param1=value1¶m2=value2¶m3=value3", new ByteChunk(), null); + + Assert.assertEquals(200, rc); + + rc = getUrl("http://localhost:" + getPort() + + "/context2/special/endpoint?param1=value1¶m2=value2¶m3=value3", new ByteChunk(), null); + + Assert.assertEquals(400, rc); + + rc = getUrl("http://localhost:" + getPort() + "/context3/special/endpoint?param1=value1¶m2=value2", + new ByteChunk(), null); + + Assert.assertEquals(400, rc); + } + + + @Test + public void testMultipart() throws Exception { + doTestMultipart(50, 10, 512, true); + } + + + @Test + public void testMultipartParameterLimitExceeded01() throws Exception { + doTestMultipart(1, 10, 512, false); + } + + + @Test + public void testMultipartParameterLimitExceeded02() throws Exception { + doTestMultipart(5, 10, 512, false); + } + + + @Test + public void testMultipartPartLimitExceeded() throws Exception { + doTestMultipart(50, 1, 512, false); + } + + + @Test + public void testMultipartPartHeaderSizeLimitExceeded() throws Exception { + doTestMultipart(50, 10, 1, false); + } + + + private void doTestMultipart(int maxParameterCount, int maxPartCount, int maxPartHeaderSize, boolean okExpected) throws Exception { + + Tomcat tomcat = getTomcatInstance(); + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + parameterLimitValve.setUrlPatternLimits("/upload/.*=" + Integer.toString(maxParameterCount) + "," + + Integer.toString(maxPartCount) + "," + Integer.toString(maxPartHeaderSize)); + + Wrapper w = Tomcat.addServlet(ctx, "multipart", new MultipartServlet()); + // Use defaults for Multipart + w.setMultipartConfigElement(new MultipartConfigElement("")); + ctx.addServletMappingDecoded("/upload/*", "multipart"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + // Construct a simple multipart body with two parts + String boundary = "--simpleBoundary"; + + // @formatter:off + String content = "--" + boundary + CRLF + + "Content-Disposition: form-data; name=\"part1\"" + CRLF + + CRLF + + "part value 1" + CRLF + + "--" + boundary + CRLF + + "Content-Disposition: form-data; name=\"part2\"" + CRLF + + CRLF + + "part value 2" + CRLF + + "--" + boundary + "--" + CRLF; + // @formatter:on + + Map> reqHeaders = new HashMap<>(); + reqHeaders.put("Content-Type", List.of("multipart/form-data; boundary=" + boundary)); + reqHeaders.put("Content-Length", List.of(Integer.toString(content.length()))); + + int rc = postUrl(content.getBytes(), "http://localhost:" + getPort() + "/upload/endpoint?" + + "param1=value1¶m2=value2¶m3=value3¶m4=value4", + new ByteChunk(), reqHeaders, null); + + if (okExpected) { + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + } else { + Assert.assertTrue(Integer.toString(rc), + rc == HttpServletResponse.SC_BAD_REQUEST || rc == HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE || + rc == HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + + @Test + public void testMaxParameterCountLimitExceeded01_02_00_00() throws Exception { + doTestMaxParameterCountLimitExceeded(1, 2, 0, 0, false); + } + + + @Test + public void testMaxParameterCountLimitExceeded01_00_02_00() throws Exception { + doTestMaxParameterCountLimitExceeded(1, 0, 2, 0, false); + } + + + @Test + public void testMaxParameterCountLimitExceeded01_00_00_02() throws Exception { + doTestMaxParameterCountLimitExceeded(1, 0, 0, 2, false); + } + + + @Test + public void testMaxParameterCountLimitExceeded01_01_00_00() throws Exception { + doTestMaxParameterCountLimitExceeded(1, 1, 0, 0, true); + } + + + @Test + public void testMaxParameterCountLimitExceeded01_00_01_00() throws Exception { + doTestMaxParameterCountLimitExceeded(1, 0, 1, 0, true); + } + + + @Test + public void testMaxParameterCountLimitExceeded01_00_00_01() throws Exception { + doTestMaxParameterCountLimitExceeded(1, 0, 0, 1, true); + } + + + @Test + public void testMaxParameterCountLimitExceeded02_01_01_00() throws Exception { + doTestMaxParameterCountLimitExceeded(2, 1, 1, 0, true); + } + + + @Test + public void testMaxParameterCountLimitExceeded02_01_0_01() throws Exception { + doTestMaxParameterCountLimitExceeded(2, 1, 0, 1, true); + } + + + @Test + public void testMaxParameterCountLimitExceeded02_00_01_01() throws Exception { + doTestMaxParameterCountLimitExceeded(2, 0, 1, 1, true); + } + + + @Test + public void testMaxParameterCountLimitExceeded03_01_01_01() throws Exception { + doTestMaxParameterCountLimitExceeded(3, 1, 1, 1, true); + } + + + private void doTestMaxParameterCountLimitExceeded(int maxParameterCount, int textPartCount, int filePartCount, + int queryStringCount, boolean okExpected) throws Exception { + + Tomcat tomcat = getTomcatInstance(); + StandardContext ctx = (StandardContext) getProgrammaticRootContext(); + + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + ctx.getPipeline().addValve(parameterLimitValve); + // Only looking to test maxParameterCount + parameterLimitValve.setUrlPatternLimits("/upload/.*=" + Integer.toString(maxParameterCount) + ",-1,-1"); + + Wrapper w = Tomcat.addServlet(ctx, "multipart", new MultipartServlet()); + // Use defaults for Multipart + w.setMultipartConfigElement(new MultipartConfigElement("")); + ctx.addServletMappingDecoded("/upload/*", "multipart"); + + addFailedRequestFilter(ctx); + + tomcat.start(); + + // Construct a simple multi-part body + String boundary = "--simpleBoundary"; + + StringBuilder content = new StringBuilder(); + int part = 1; + + for (int i = 0; i < textPartCount; i++) { + content.append("--").append(boundary).append(CRLF); + content.append("Content-Disposition: form-data; name=\"part").append(part).append("\"").append(CRLF); + content.append(CRLF); + content.append("part value ").append(part).append(CRLF); + part++; + } + + for (int i = 0; i < filePartCount; i++) { + content.append("--").append(boundary).append(CRLF); + content.append("Content-Disposition: form-data; name=\"part").append(part).append("\"; filename=\"part") + .append(part).append("\"").append(CRLF); + content.append("Content-Type: text/plain").append(CRLF); + content.append(CRLF); + content.append("part value ").append(part).append(CRLF); + part++; + } + + content.append("--").append(boundary).append("--").append(CRLF); + + StringBuilder queryString = new StringBuilder(); + for (int i = 0; i < queryStringCount; i++) { + if (i > 0) { + queryString.append("&"); + } + queryString.append("param"); + queryString.append(part); + queryString.append("=value"); + queryString.append(part); + part++; + } + + + Map> reqHeaders = new HashMap<>(); + reqHeaders.put("Content-Type", List.of("multipart/form-data; boundary=" + boundary)); + reqHeaders.put("Content-Length", List.of(Integer.toString(content.length()))); + + int rc = postUrl(content.toString().getBytes(), "http://localhost:" + getPort() + "/upload/endpoint?" + + queryString.toString(), new ByteChunk(), reqHeaders, null); + + if (okExpected) { + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + } else { + Assert.assertTrue(Integer.toString(rc), + rc == HttpServletResponse.SC_BAD_REQUEST || rc == HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE || + rc == HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } + } + + private static class MultipartServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("UTF-8"); + PrintWriter pw = resp.getWriter(); + pw.println("Parts: " + req.getParts().size()); + pw.println("Parameters: " + req.getParameterMap().size()); + } + } + + + private static void addFailedRequestFilter(Context context) { + FilterDef failedRequestFilter = new FilterDef(); + failedRequestFilter.setFilterName("failedRequestFilter"); + failedRequestFilter.setFilterClass(FailedRequestFilter.class.getName()); + FilterMap failedRequestFilterMap = new FilterMap(); + failedRequestFilterMap.setFilterName("failedRequestFilter"); + failedRequestFilterMap.addURLPatternDecoded("/*"); + context.addFilterDef(failedRequestFilter); + context.addFilterMap(failedRequestFilterMap); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestParameterLimitValveConfig.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestParameterLimitValveConfig.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestParameterLimitValveConfig.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestParameterLimitValveConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.catalina.valves; + +import org.junit.Test; + +public class TestParameterLimitValveConfig { + + @Test(expected = IllegalArgumentException.class) + public void testNoEquals() { + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + parameterLimitValve.setUrlPatternLimits("/abc"); + } + + + @Test(expected = IllegalArgumentException.class) + public void testInvalidLimitCount02() { + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + parameterLimitValve.setUrlPatternLimits("/abc=1,2"); + + } + + + @Test(expected = IllegalArgumentException.class) + public void testInvalidLimitCount04() { + ParameterLimitValve parameterLimitValve = new ParameterLimitValve(); + parameterLimitValve.setUrlPatternLimits("/abc=1,2,3,4"); + + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestRemoteIpValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestRemoteIpValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestRemoteIpValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestRemoteIpValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -1217,6 +1217,26 @@ doTestPattern(internalProxiesPattern, "100.127.255.255", true); doTestPattern(internalProxiesPattern, "100.128.0.0", false); doTestPattern(internalProxiesPattern, "100.130.0.0", false); + // Bug 69600 - IPv6 RFC 4193 Unique Local IPv6 Unicast Addresses + doTestPattern(internalProxiesPattern, "fe79:ffff:ffff:ffff:ffff:ffff:ffff:ffff", false); + doTestPattern(internalProxiesPattern, "fe80:0000:0000:0000:0000:0000:0000:0000", true); + doTestPattern(internalProxiesPattern, "fe80::", true); + doTestPattern(internalProxiesPattern, "fe80:0000:0000:0000:0000:0000:0000:0001", true); + doTestPattern(internalProxiesPattern, "fe80::1", true); + doTestPattern(internalProxiesPattern, "fe80:1234:5678:9abc:def0:1234:5678:9abc", true); + doTestPattern(internalProxiesPattern, "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true); + doTestPattern(internalProxiesPattern, "fec0:0000:0000:0000:0000:0000:0000:0000", false); + doTestPattern(internalProxiesPattern, "fec0::", false); + // Bug 69600 - IPv6 RFC 4291 Link Local IPv6 Unicast Addresses + doTestPattern(internalProxiesPattern, "fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", false); + doTestPattern(internalProxiesPattern, "fc00:0000:0000:0000:0000:0000:0000:0000", true); + doTestPattern(internalProxiesPattern, "fc00::", true); + doTestPattern(internalProxiesPattern, "fc00:0000:0000:0000:0000:0000:0000:0001", true); + doTestPattern(internalProxiesPattern, "fc00::1", true); + doTestPattern(internalProxiesPattern, "fc00:1234:5678:9abc:def0:1234:5678:9abc", true); + doTestPattern(internalProxiesPattern, "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true); + doTestPattern(internalProxiesPattern, "fe00:0000:0000:0000:0000:0000:0000:0000", false); + doTestPattern(internalProxiesPattern, "fe00::", false); } private void doTestPattern(Pattern pattern, String input, boolean expectedMatch) { diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestRequestFilterValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestRequestFilterValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestRequestFilterValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestRequestFilterValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -100,6 +100,7 @@ } } + @SuppressWarnings("deprecation") private void oneTest(String allow, String deny, boolean denyStatus, boolean addConnectorPort, boolean usePeerAddress, boolean auth, String property, String type, boolean allowed) { // PREPARE @@ -125,7 +126,7 @@ if (usePeerAddress) { request.setRemoteAddr(ADDR_OTHER); request.getCoyoteRequest().peerAddr().setString(property); - ((RemoteAddrValve) valve).setUsePeerAddress(true); + valve.setUsePeerAddress(true); msg.append(" peer='" + property + "'"); } else { request.setRemoteAddr(property); @@ -141,7 +142,7 @@ if (usePeerAddress) { request.setRemoteAddr(ADDR_OTHER); request.getCoyoteRequest().peerAddr().setString(property); - ((RemoteCIDRValve) valve).setUsePeerAddress(true); + valve.setUsePeerAddress(true); msg.append(" peer='" + property + "'"); } else { request.setRemoteAddr(property); @@ -170,11 +171,11 @@ } if (addConnectorPort) { if (valve instanceof RemoteAddrValve) { - ((RemoteAddrValve) valve).setAddConnectorPort(true); + valve.setAddConnectorPort(true); } else if (valve instanceof RemoteHostValve) { - ((RemoteHostValve) valve).setAddConnectorPort(true); + valve.setAddConnectorPort(true); } else if (valve instanceof RemoteCIDRValve) { - ((RemoteCIDRValve) valve).setAddConnectorPort(true); + valve.setAddConnectorPort(true); } else { Assert.fail("Can only set 'addConnectorPort' for RemoteAddrValve, RemoteHostValve and RemoteCIDRValve"); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestSSLValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestSSLValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestSSLValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestSSLValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,7 +21,6 @@ import java.util.logging.Level; import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.apache.catalina.Globals; @@ -36,11 +35,23 @@ public static class MockRequest extends Request { - public MockRequest() { - super(EasyMock.createMock(Connector.class)); + private static MockRequest single_instance = null; + + public MockRequest(Connector connector) { + super(connector); setCoyoteRequest(new org.apache.coyote.Request()); } + public static MockRequest getInstance() { + if (single_instance == null) { + Connector connector = EasyMock.createNiceMock(Connector.class); + EasyMock.replay(connector); + single_instance = new MockRequest(connector); + } + + return single_instance; + } + @Override public void setAttribute(String name, Object value) { getCoyoteRequest().getAttributes().put(name, value); @@ -91,22 +102,30 @@ "yoTBqEpJloWksrypqp3iL4PAL5+KkB2zp66+MVAg8LcEDFJggBBJCtv4SCWV7ZOB", "WLu8gep+XCwSn0Wb6D3eFs4DoIiMvQ6g2rS/pk7o5eWj", "-----END CERTIFICATE-----" }; - private SSLValve valve = new SSLValve(); - - private MockRequest mockRequest = new MockRequest(); - private Valve mockNext = EasyMock.createMock(Valve.class); + private final SSLValve valve = new SSLValve(); + private MockRequest mockRequest; + private final Valve mockNext = EasyMock.createMock(Valve.class); - @Before public void setUp() throws Exception { + setUp(null); + } + + public void setUp(Connector connector) throws Exception { valve.setNext(mockNext); + if (connector == null) { + mockRequest = MockRequest.getInstance(); + } else { + EasyMock.replay(connector); + mockRequest = new MockRequest(connector); + } mockNext.invoke(mockRequest, null); EasyMock.replay(mockNext); } - @Test - public void testSslHeader() { + public void testSslHeader() throws Exception { + setUp(); final String headerName = "myheader"; final String headerValue = "BASE64_HEADER_VALUE"; mockRequest.setHeader(headerName, headerValue); @@ -116,7 +135,8 @@ @Test - public void testSslHeaderNull() { + public void testSslHeaderNull() throws Exception { + setUp(); final String headerName = "myheader"; mockRequest.setHeader(headerName, null); @@ -125,7 +145,8 @@ @Test - public void testSslHeaderNullModHeader() { + public void testSslHeaderNullModHeader() throws Exception { + setUp(); final String headerName = "myheader"; final String nullModHeaderValue = "(null)"; mockRequest.setHeader(headerName, nullModHeaderValue); @@ -136,12 +157,14 @@ @Test public void testSslHeaderNullName() throws Exception { + setUp(); Assert.assertNull(valve.mygetHeader(mockRequest, null)); } @Test public void testSslHeaderMultiples() throws Exception { + setUp(); final String headerName = "myheader"; final String headerValue = "BASE64_HEADER_VALUE"; mockRequest.addHeader(headerName, headerValue); @@ -153,6 +176,7 @@ @Test public void testSslClientCertHeaderSingleSpace() throws Exception { + setUp(); String singleSpaced = certificateSingleLine(" "); mockRequest.setHeader(valve.getSslClientCertHeader(), singleSpaced); @@ -164,6 +188,7 @@ @Test public void testSslClientCertHeaderMultiSpace() throws Exception { + setUp(); String singleSpaced = certificateSingleLine(" "); mockRequest.setHeader(valve.getSslClientCertHeader(), singleSpaced); @@ -175,6 +200,7 @@ @Test public void testSslClientCertHeaderTab() throws Exception { + setUp(); String singleSpaced = certificateSingleLine("\t"); mockRequest.setHeader(valve.getSslClientCertHeader(), singleSpaced); @@ -186,6 +212,7 @@ @Test public void testSslClientCertHeaderEscaped() throws Exception { + setUp(); String cert = certificateEscaped(); mockRequest.setHeader(valve.getSslClientEscapedCertHeader(), cert); @@ -197,6 +224,7 @@ @Test public void testSslClientCertNull() throws Exception { + setUp(); TesterLogValidationFilter f = TesterLogValidationFilter.add(null, "", null, "org.apache.catalina.valves.SSLValve"); @@ -210,6 +238,7 @@ @Test public void testSslClientCertShorter() throws Exception { + setUp(); mockRequest.setHeader(valve.getSslClientCertHeader(), "shorter than hell"); TesterLogValidationFilter f = TesterLogValidationFilter.add(null, "", null, @@ -225,6 +254,7 @@ @Test public void testSslClientCertIgnoredBegin() throws Exception { + setUp(); String[] linesBegin = Arrays.copyOf(CERTIFICATE_LINES, CERTIFICATE_LINES.length); linesBegin[0] = "3fisjcme3kdsakasdfsadkafsd3"; String begin = certificateSingleLine(linesBegin, " "); @@ -238,6 +268,7 @@ @Test public void testSslClientCertBadFormat() throws Exception { + setUp(); String[] linesDeleted = Arrays.copyOf(CERTIFICATE_LINES, CERTIFICATE_LINES.length / 2); String deleted = certificateSingleLine(linesDeleted, " "); mockRequest.setHeader(valve.getSslClientCertHeader(), deleted); @@ -255,8 +286,10 @@ @Test public void testClientCertProviderNotFound() throws Exception { - EasyMock.expect(mockRequest.getConnector().getProperty("clientCertProvider")).andStubReturn("wontBeFound"); - EasyMock.replay(mockRequest.getConnector()); + Connector connector = EasyMock.createNiceMock(Connector.class); + EasyMock.expect(connector.getProperty("clientCertProvider")).andStubReturn("wontBeFound"); + setUp(connector); + mockRequest.setHeader(valve.getSslClientCertHeader(), certificateSingleLine(" ")); TesterLogValidationFilter f = TesterLogValidationFilter.add(Level.SEVERE, null, @@ -271,6 +304,7 @@ @Test public void testSslCipherHeaderPresent() throws Exception { + setUp(); String cipher = "ciphered-with"; mockRequest.setHeader(valve.getSslCipherHeader(), cipher); @@ -282,6 +316,7 @@ @Test public void testSslSessionIdHeaderPresent() throws Exception { + setUp(); String session = "ssl-session"; mockRequest.setHeader(valve.getSslSessionIdHeader(), session); @@ -293,6 +328,7 @@ @Test public void testSslCipherUserKeySizeHeaderPresent() throws Exception { + setUp(); Integer keySize = Integer.valueOf(452); mockRequest.setHeader(valve.getSslCipherUserKeySizeHeader(), String.valueOf(keySize)); @@ -304,12 +340,14 @@ @Test(expected = NumberFormatException.class) public void testSslCipherUserKeySizeHeaderBadFormat() throws Exception { + setUp(); mockRequest.setHeader(valve.getSslCipherUserKeySizeHeader(), "not-an-integer"); try { valve.invoke(mockRequest, null); } catch (NumberFormatException e) { Assert.assertNull(mockRequest.getAttribute(Globals.KEY_SIZE_ATTR)); + mockRequest.setHeader(valve.getSslCipherUserKeySizeHeader(), null); throw e; } } @@ -353,4 +391,4 @@ Assert.assertNotNull(certificates[0]); Assert.assertEquals(0, f.getMessageCount()); } -} \ No newline at end of file +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/TestStuckThreadDetectionValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/TestStuckThreadDetectionValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/TestStuckThreadDetectionValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/TestStuckThreadDetectionValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -68,8 +68,8 @@ public void run() { try { getUrl("http://localhost:" + getPort() + "/myservlet", result, null); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } } @@ -112,8 +112,8 @@ public void run() { try { getUrl("http://localhost:" + getPort() + "/myservlet", result, null); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/rewrite/TestResolverSSL.java tomcat10-10.1.52/test/org/apache/catalina/valves/rewrite/TestResolverSSL.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/rewrite/TestResolverSSL.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/rewrite/TestResolverSSL.java 2026-01-23 19:33:36.000000000 +0000 @@ -160,7 +160,7 @@ @Override public void invoke(Request request, Response response) throws IOException, ServletException { PrintWriter writer = response.getWriter(); - Resolver resolver = new ResolverImpl(request); + Resolver resolver = new ResolverImpl(request, request.getContext().getLogger()); for (String key : keys) { resolve(key, resolver, writer); } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java tomcat10-10.1.52/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java --- tomcat10-10.1.34/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/valves/rewrite/TestRewriteValve.java 2026-01-23 19:33:36.000000000 +0000 @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.net.HttpURLConnection; +import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashMap; @@ -44,6 +45,7 @@ import org.apache.catalina.startup.TomcatBaseTest; import org.apache.catalina.valves.ValveBase; import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.http.Method; /* * Implementation note: @@ -63,7 +65,7 @@ @Test public void testBackslashPercentSign() throws Exception { - doTestRewrite("RewriteRule ^(.*) /a/\\%5A", "/", "/a/%255A"); + doTestRewrite("RewriteRule ^(.*) /a/\\%5A", "/", "/a/%5A"); } @Test @@ -142,7 +144,7 @@ @Test public void testRewriteMap10() throws Exception { - doTestRewrite("RewriteMap lc int:escape\n" + "RewriteRule ^(.*) ${lc:$1}", "/c/a%20aa", "/c/a%2520aa"); + doTestRewrite("RewriteMap lc int:escape\n" + "RewriteRule ^(.*) ${lc:$1}", "/c/a%20aa", "/c/a%20aa"); } @Test @@ -300,17 +302,112 @@ } @Test - public void testQueryString() throws Exception { + public void testQueryStringTargetOnly() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2", "/b/id=1", "/c/id=1", "je=2"); + } + + @Test + public void testQueryStringTargetOnlyQSA() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2 [QSA]", "/b/id=1", "/c/id=1", "je=2"); + } + + @Test + public void testQueryStringTargetOnlyQSD() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2 [QSD]", "/b/id=1", "/c/id=1", "je=2"); + } + + @Test + public void testQueryStringTargetOnlyQSAQSD() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2 [QSA,QSD]", "/b/id=1", "/c/id=1", "je=2"); + } + + @Test + public void testQueryStringTargetOnlyQS() throws Exception { doTestRewrite("RewriteRule ^/b/(.*) /c?$1", "/b/id=1", "/c", "id=1"); } @Test + public void testQueryStringTargetOnlyQSAQS() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [QSA]", "/b/id=1", "/c", "id=1"); + } + + @Test + public void testQueryStringTargetOnlyQSDQS() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [QSD]", "/b/id=1", "/c", "id=1"); + } + + @Test + public void testQueryStringTargetOnlyQSAQSDQS() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [QSA,QSD]", "/b/id=1", "/c", "id=1"); + } + + @Test + public void testQueryStringSourceOnly() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1", "/b/d?id=1", "/c/d", "id=1"); + } + + @Test + public void testQueryStringSourceOnlyQSA() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSA]", "/b/d?id=1", "/c/d", "id=1"); + } + + @Test + public void testQueryStringSourceOnlyQSD() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSD]", "/b/d?id=1", "/c/d", null); + } + + @Test + public void testQueryStringSourceOnlyQSAQSD() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSA,QSD]", "/b/d?id=1", "/c/d", null); + } + + @Test + public void testQueryStringSourceAndTarget() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1", "/b/d?je=2", "/c/d", "id=1"); + } + + @Test + public void testQueryStringSourceAndTargetQSA() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1 [QSA]", "/b/d?je=2", "/c/d", "id=1&je=2"); + } + + @Test + public void testQueryStringSourceAndTargetQSD() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1 [QSD]", "/b/d?je=2", "/c/d", "id=1"); + } + + @Test + public void testQueryStringSourceAndTargetQSAQSD() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1 [QSA,QSD]", "/b/d?je=2", "/c/d", "id=1"); + } + + @Test + public void testQueryStringEncoded01() throws Exception { + doTestRewrite("RewriteCond %{QUERY_STRING} a=(.*)\nRewriteRule ^/b.*$ /%1 [QSD]", "/b?a=c", "/c", null); + } + + @Test + public void testQueryStringEncoded02() throws Exception { + doTestRewrite("RewriteCond %{QUERY_STRING} a=(.*)\nRewriteRule ^/b.*$ /z/%1 [QSD]", "/b?a=%2e%2e%2fc%2faAbB", "/z/%2e%2e%2fc%2faAbB", null); + } + + @Test public void testQueryStringRemove() throws Exception { - doTestRewrite("RewriteRule ^/b/(.*) /c/$1?", "/b/d?=1", "/c/d", null); + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?", "/b/d?id=1", "/c/d", null); } @Test public void testQueryStringRemove02() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSD]", "/b/d?id=1", "/c/d", null); + } + + @Test + public void testQueryStringRemoveInvalid() throws Exception { + doTestRewrite("RewriteRule ^/b/(.*) /c/$1?", "/b/d?=1", "/c/d", null); + } + + @Test + public void testQueryStringRemoveInvalid02() throws Exception { doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSD]", "/b/d?=1", "/c/d", null); } @@ -346,7 +443,7 @@ public void testNonAsciiQueryStringWithB() throws Exception { doTestRewrite("RewriteRule ^/b/(.*)/id=(.*) /c?filename=$1&id=$2 [B]", "/b/file01/id=%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95", "/c", - "filename=file01&id=%25E5%259C%25A8%25E7%25BA%25BF%25E6%25B5%258B%25E8%25AF%2595"); + "filename=file01&id=%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95"); } @@ -354,8 +451,8 @@ public void testNonAsciiQueryStringAndPathAndRedirectWithB() throws Exception { // Note the double encoding of the result (httpd produces the same result) doTestRewrite("RewriteRule ^/b/(.*)/(.*)/id=(.*) /c/$1?filename=$2&id=$3 [B,R]", - "/b/%E5%9C%A8%E7%BA%BF/file01/id=%E6%B5%8B%E8%AF%95", "/c/%25E5%259C%25A8%25E7%25BA%25BF", - "filename=file01&id=%25E6%25B5%258B%25E8%25AF%2595"); + "/b/%E5%9C%A8%E7%BA%BF/file01/id=%E6%B5%8B%E8%AF%95", "/c/%E5%9C%A8%E7%BA%BF", + "filename=file01&id=%E6%B5%8B%E8%AF%95"); } @@ -371,7 +468,7 @@ public void testUtf8WithBothQsFlagsB() throws Exception { // Note %C2%A1 == \u00A1 doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B]", "/b/%C2%A1/id=%C2%A1?di=%C2%AE", - "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1"); + "/c/%C2%A1%C2%A1", "id=%C2%A1"); } @@ -387,7 +484,7 @@ public void testUtf8WithBothQsFlagsRB() throws Exception { // Note %C2%A1 == \u00A1 doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B]", "/b/%C2%A1/id=%C2%A1?di=%C2%AE", - "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1"); + "/c/%C2%A1%C2%A1", "id=%C2%A1"); } @@ -413,7 +510,7 @@ public void testUtf8WithBothQsFlagsBQSA() throws Exception { // Note %C2%A1 == \u00A1 doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B,QSA]", "/b/%C2%A1/id=%C2%A1?di=%C2%AE", - "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1&di=%C2%AE"); + "/c/%C2%A1%C2%A1", "id=%C2%A1&di=%C2%AE"); } @@ -429,7 +526,7 @@ public void testUtf8WithBothQsFlagsRBQSA() throws Exception { // Note %C2%A1 == \u00A1 doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B,QSA]", "/b/%C2%A1/id=%C2%A1?di=%C2%AE", - "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1&di=%C2%AE"); + "/c/%C2%A1%C2%A1", "id=%C2%A1&di=%C2%AE"); } @@ -461,7 +558,7 @@ @Test public void testUtf8WithOriginalQsFlagsB() throws Exception { // Note %C2%A1 == \u00A1 - doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]", "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%25C2%25A1", + doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]", "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%C2%A1", "id=%C2%A1"); } @@ -476,7 +573,7 @@ @Test public void testUtf8WithOriginalQsFlagsRB() throws Exception { // Note %C2%A1 == \u00A1 - doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]", "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%25C2%25A1", + doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]", "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%C2%A1", "id=%C2%A1"); } @@ -510,8 +607,8 @@ @Test public void testUtf8WithRewriteQsFlagsB() throws Exception { // Note %C2%A1 == \u00A1 - doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B]", "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%25C2%25A1", - "id=%25C2%25A1"); + doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B]", "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%C2%A1", + "id=%C2%A1"); } @@ -534,8 +631,8 @@ @Test public void testUtf8WithRewriteQsFlagsRB() throws Exception { // Note %C2%A1 == \u00A1 - doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B]", "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%25C2%25A1", - "id=%25C2%25A1"); + doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B]", "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%C2%A1", + "id=%C2%A1"); } @@ -575,7 +672,7 @@ @Test public void testUtf8FlagsB() throws Exception { // Note %C2%A1 == \u00A1 - doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]", "/b/%C2%A1", "/c/%C2%A1%25C2%25A1"); + doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]", "/b/%C2%A1", "/c/%C2%A1%C2%A1"); } @@ -589,7 +686,7 @@ @Test public void testUtf8FlagsRB() throws Exception { // Note %C2%A1 == \u00A1 - doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]", "/b/%C2%A1", "/c/%C2%A1%25C2%25A1"); + doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]", "/b/%C2%A1", "/c/%C2%A1%C2%A1"); } @@ -615,7 +712,7 @@ public void testFlagsNC() throws Exception { // https://bz.apache.org/bugzilla/show_bug.cgi?id=60116 doTestRewrite("RewriteCond %{QUERY_STRING} a=([a-z]*) [NC]\n" + "RewriteRule .* - [E=X-Test:%1]", "/c?a=aAa", - "/c", null, "aAa"); + "/c", "a=aAa", "aAa"); } @Test @@ -784,6 +881,7 @@ rewriteValve.setConfiguration(config); Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/a/Z", "snoop"); ctx.addServletMappingDecoded("/a/%5A", "snoop"); ctx.addServletMappingDecoded("/c/*", "snoop"); ctx.addServletMappingDecoded("/W/*", "snoop"); @@ -794,9 +892,9 @@ ByteChunk res = new ByteChunk(); int rc = methodUrl("http://localhost:" + getPort() + request, res, DEFAULT_CLIENT_TIMEOUT_MS, - reqHead, - resHead, - "GET", true); + reqHead, + resHead, + Method.GET, true); res.setCharset(StandardCharsets.UTF_8); if (expectedURI == null) { @@ -804,12 +902,16 @@ // were written into the request target Assert.assertEquals(400, rc); } else { + // If there is an expected URI, the request should be successful + Assert.assertEquals(200, rc); String body = res.toString(); RequestDescriptor requestDesc = SnoopResult.parse(body); String requestURI = requestDesc.getRequestInfo("REQUEST-URI"); Assert.assertEquals(expectedURI, requestURI); - if (expectedQueryString != null) { + if (expectedQueryString == null) { + Assert.assertTrue(requestDesc.getParams().isEmpty()); + } else { String queryString = requestDesc.getRequestInfo("REQUEST-QUERY-STRING"); Assert.assertEquals(expectedQueryString, queryString); } @@ -843,7 +945,7 @@ ByteChunk res = new ByteChunk(); Map> resHead = new HashMap<>(); int rc = methodUrl("http://localhost:" + getPort() + request, res, DEFAULT_CLIENT_TIMEOUT_MS, null, resHead, - "GET", false); + Method.GET, false); res.setCharset(StandardCharsets.UTF_8); if (expectedURI == null) { @@ -885,7 +987,7 @@ reqHead.put("cookie", Arrays.asList("test=data")); ByteChunk res = new ByteChunk(); int rc = methodUrl("http://localhost:" + getPort() + "/source/cookieTest", res, DEFAULT_CLIENT_TIMEOUT_MS, - reqHead, null, "GET", false); + reqHead, null, Method.GET, false); Assert.assertEquals(HttpServletResponse.SC_OK, rc); @@ -929,4 +1031,87 @@ } } } + + + @Test + public void testEncodedUriSimple() throws Exception { + doTestRewriteWithEncoding("aaa"); + } + + + @Test + public void testEncodedUriEncodedQuestionMark01() throws Exception { + doTestRewriteWithEncoding("a%3fa"); + } + + + @Test + public void testEncodedUriEncodedQuestionMark02() throws Exception { + doTestRewriteWithEncoding("%3faa"); + } + + + @Test + public void testEncodedUriEncodedQuestionMark03() throws Exception { + doTestRewriteWithEncoding("aa%3f"); + } + + + @Test + public void testEncodedUriEncodedQuestionMarkAndQueryString() throws Exception { + doTestRewriteWithEncoding("a%3fa?b=c", "a%3fa", "b=c"); + } + + + @Test + public void testEncodedUriEncodedSemicolon01() throws Exception { + doTestRewriteWithEncoding("a%3ba"); + } + + + @Test + public void testEncodedUriEncodedSemicolon02() throws Exception { + doTestRewriteWithEncoding("%3baa"); + } + + + @Test + public void testEncodedUriEncodedSemicolon03() throws Exception { + doTestRewriteWithEncoding("aa%3b"); + } + + + private void doTestRewriteWithEncoding(String segment) throws Exception { + doTestRewriteWithEncoding(segment, segment, null); + } + + private void doTestRewriteWithEncoding(String segment, String expectedSegment, String expectedQueryString) + throws Exception { + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = tomcat.addContext("", null); + + RewriteValve rewriteValve = new RewriteValve(); + tomcat.getHost().getPipeline().addValve(rewriteValve); + + rewriteValve.setConfiguration("RewriteRule ^/source/(.*)$ /target/$1"); + + Tomcat.addServlet(ctx, "snoop", new SnoopServlet()); + ctx.addServletMappingDecoded("/target/*", "snoop"); + + tomcat.start(); + + ByteChunk res = new ByteChunk(); + int rc = getUrl("http://localhost:" + getPort() + "/source/" + segment, res, false); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + + res.setCharset(StandardCharsets.UTF_8); + String body = res.toString(); + Assert.assertTrue(body, body.contains("REQUEST-URI: /target/" + expectedSegment)); + Assert.assertTrue(body, body.contains("PATH-INFO: /" + + URLDecoder.decode(expectedSegment, StandardCharsets.UTF_8))); + Assert.assertTrue(body, body.contains("REQUEST-QUERY-STRING: " + expectedQueryString)); + } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/webresources/AbstractTestResourceSet.java tomcat10-10.1.52/test/org/apache/catalina/webresources/AbstractTestResourceSet.java --- tomcat10-10.1.34/test/org/apache/catalina/webresources/AbstractTestResourceSet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/webresources/AbstractTestResourceSet.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,6 +44,10 @@ return ""; } + public String getMountPath() { + return ""; + } + public abstract File getBaseDir(); @Before @@ -77,55 +81,72 @@ private void doTestGetResourceRoot(boolean slash) { - String mount = getMount(); - if (!slash && mount.length() == 0) { + String mountPath = getMountPath(); + if (!slash && mountPath.length() == 0) { return; } - mount = mount + (slash ? "/" : ""); + String path = mountPath + (slash ? "/" : ""); - WebResource webResource = resourceRoot.getResource(mount); + WebResource webResource = resourceRoot.getResource(path); Assert.assertTrue(webResource.isDirectory()); - String expected; - if (getMount().length() > 0) { - expected = getMount().substring(1); + String expectedName; + if (getMountPath().length() > 0) { + expectedName = getMountPath().substring(1); } else { - expected = ""; + expectedName = ""; + } + String expectedPath = path; + if (!path.endsWith("/")) { + expectedPath += "/"; } - Assert.assertEquals(expected, webResource.getName()); - Assert.assertEquals(mount + (!slash ? "/" : ""), webResource.getWebappPath()); + Assert.assertEquals(expectedName, webResource.getName()); + Assert.assertEquals(expectedPath, webResource.getWebappPath()); } @Test - public final void testGetResourceDirA() { + public final void testGetResourceDirWithoutTrailingFileSeperator() { WebResource webResource = resourceRoot.getResource(getMount() + "/d1"); Assert.assertTrue(webResource.isDirectory()); Assert.assertEquals("d1", webResource.getName()); - Assert.assertEquals(getMount() + "/d1/", webResource.getWebappPath()); + Assert.assertEquals(getMountPath() + "/d1/", webResource.getWebappPath()); Assert.assertEquals(-1, webResource.getContentLength()); Assert.assertNull(webResource.getContent()); Assert.assertNull(webResource.getInputStream()); } @Test - public final void testGetResourceDirB() { + public final void testGetResourceDirWithTrailingFileSeperator() { WebResource webResource = resourceRoot.getResource(getMount() + "/d1/"); Assert.assertTrue(webResource.isDirectory()); Assert.assertEquals("d1", webResource.getName()); - Assert.assertEquals(getMount() + "/d1/", webResource.getWebappPath()); + Assert.assertEquals(getMountPath() + "/d1/", webResource.getWebappPath()); Assert.assertEquals(-1, webResource.getContentLength()); Assert.assertNull(webResource.getContent()); Assert.assertNull(webResource.getInputStream()); } @Test + public final void testGetResourceDirWithoutLeadingFileSeperator() { + // Use mount path for this test as the file separator needs to be missing + String mountPath = getMountPath(); + if (mountPath.isEmpty()) { + // Test is only meaningful when resource is mounted below web application root. + return; + } + WebResource webResource = resourceRoot.getResource(mountPath + "d1"); + Assert.assertFalse(webResource.exists()); + Assert.assertEquals(getMountPath() + "d1", webResource.getWebappPath()); + } + + @Test public final void testGetResourceFile() { WebResource webResource = resourceRoot.getResource(getMount() + "/d1/d1-f1.txt"); Assert.assertTrue(webResource.isFile()); Assert.assertEquals("d1-f1.txt", webResource.getName()); Assert.assertEquals( - getMount() + "/d1/d1-f1.txt", webResource.getWebappPath()); + getMountPath() + "/d1/d1-f1.txt", webResource.getWebappPath()); Assert.assertEquals(0, webResource.getContentLength()); Assert.assertEquals(0, webResource.getContent().length); Assert.assertNotNull(webResource.getInputStream()); @@ -281,18 +302,18 @@ Set results = resourceRoot.listWebAppPaths(mount + (slash ? "/" : "")); Set expected = new HashSet<>(); - expected.add(getMount() + "/d1/"); - expected.add(getMount() + "/d2/"); - expected.add(getMount() + "/f1.txt"); - expected.add(getMount() + "/f2.txt"); + expected.add(getMountPath() + "/d1/"); + expected.add(getMountPath() + "/d2/"); + expected.add(getMountPath() + "/f1.txt"); + expected.add(getMountPath() + "/f2.txt"); // Directories created by Subversion 1.6 and earlier clients Set optional = new HashSet<>(); - optional.add(getMount() + "/.svn/"); + optional.add(getMountPath() + "/.svn/"); // Files visible in some tests only - optional.add(getMount() + "/.ignore-me.txt"); + optional.add(getMountPath() + "/.ignore-me.txt"); // Files visible in some configurations only - optional.add(getMount() + "/META-INF/"); + optional.add(getMountPath() + "/META-INF/"); for (String result : results) { Assert.assertTrue(result, @@ -306,7 +327,7 @@ Set results = resourceRoot.listWebAppPaths(getMount() + "/d1"); Set expected = new HashSet<>(); - expected.add(getMount() + "/d1/d1-f1.txt"); + expected.add(getMountPath() + "/d1/d1-f1.txt"); // Directories created by Subversion 1.6 and earlier clients Set optional = new HashSet<>(); @@ -326,7 +347,7 @@ Set results = resourceRoot.listWebAppPaths(getMount() + "/d1/"); Set expected = new HashSet<>(); - expected.add(getMount() + "/d1/d1-f1.txt"); + expected.add(getMountPath() + "/d1/d1-f1.txt"); // Directories created by Subversion 1.6 and earlier clients Set optional = new HashSet<>(); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/webresources/AbstractTestResourceSetMount.java tomcat10-10.1.52/test/org/apache/catalina/webresources/AbstractTestResourceSetMount.java --- tomcat10-10.1.34/test/org/apache/catalina/webresources/AbstractTestResourceSetMount.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/webresources/AbstractTestResourceSetMount.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,6 +33,11 @@ return "/mount"; } + @Override + public final String getMountPath() { + return "/mount"; + } + @Test public final void testGetResourceAbove() { WebResource webResource = resourceRoot.getResource("/"); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/webresources/AbstractTestResourceSetMountTrailing.java tomcat10-10.1.52/test/org/apache/catalina/webresources/AbstractTestResourceSetMountTrailing.java --- tomcat10-10.1.34/test/org/apache/catalina/webresources/AbstractTestResourceSetMountTrailing.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/webresources/AbstractTestResourceSetMountTrailing.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,80 @@ +/* + * 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.webresources; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.WebResource; + +public abstract class AbstractTestResourceSetMountTrailing + extends AbstractTestResourceSet { + + @Override + public final String getMount() { + return "/mount/"; + } + + @Override + public final String getMountPath() { + return "/mount"; + } + + @Test + public final void testGetResourceAbove() { + WebResource webResource = resourceRoot.getResource("/"); + Assert.assertFalse(webResource.exists()); + } + + @Test + public final void testListAbove() { + String[] results = resourceRoot.list("/"); + + Assert.assertNotNull(results); + Assert.assertEquals(1, results.length); + Assert.assertEquals(getMountPath().substring(1), results[0]); + } + + @Test + public final void testListWebAppPathsAbove() { + Set results = resourceRoot.listWebAppPaths("/"); + + Assert.assertNotNull(results); + Assert.assertEquals(1, results.size()); + Assert.assertTrue(results.contains(getMountPath() + "/")); + } + + @Test + public void testMkdirAbove() { + Assert.assertFalse(resourceRoot.mkdir("/")); + } + + @Test + public void testWriteAbove() { + InputStream is = new ByteArrayInputStream("test".getBytes()); + Assert.assertFalse(resourceRoot.write("/", is, false)); + } + + @Override + public void testNoArgConstructor() { + // NO-OP + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/webresources/TestCachedResource.java tomcat10-10.1.52/test/org/apache/catalina/webresources/TestCachedResource.java --- tomcat10-10.1.34/test/org/apache/catalina/webresources/TestCachedResource.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/webresources/TestCachedResource.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,7 @@ */ package org.apache.catalina.webresources; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; import java.net.JarURLConnection; @@ -131,4 +132,38 @@ Assert.assertNotNull(is); } } + + + @Test + public void testGetContentWebInfClasses() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = new File("test/webapp"); + Context ctx = tomcat.addWebapp("/test", docBase.getAbsolutePath()); + tomcat.start(); + + URL url = ctx.getLoader().getClassLoader().getResource("bug69623-a.mdd"); + Object o = url.getContent(); + /* + * Could test the actual content but a non-null return without an exception is enough to demonstrate the bug has + * not occurred. + */ + Assert.assertTrue(o instanceof ByteArrayInputStream); + } + + + @Test + public void testGetContentWebInfLib() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = new File("test/webapp"); + Context ctx = tomcat.addWebapp("/test", docBase.getAbsolutePath()); + tomcat.start(); + + URL url = ctx.getLoader().getClassLoader().getResource("bug69623-b.mdd"); + Object o = url.getContent(); + /* + * Could test the actual content but a non-null return without an exception is enough to demonstrate the bug has + * not occurred. + */ + Assert.assertTrue(o instanceof ByteArrayInputStream); + } } diff -Nru tomcat10-10.1.34/test/org/apache/catalina/webresources/TestDirResourceSetMountTrailing.java tomcat10-10.1.52/test/org/apache/catalina/webresources/TestDirResourceSetMountTrailing.java --- tomcat10-10.1.34/test/org/apache/catalina/webresources/TestDirResourceSetMountTrailing.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/webresources/TestDirResourceSetMountTrailing.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,85 @@ +/* + * 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.webresources; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileAttribute; + +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import org.apache.catalina.WebResourceRoot; +import org.apache.catalina.WebResourceSet; +import org.apache.catalina.startup.ExpandWar; +import org.apache.catalina.startup.TomcatBaseTest; + +public class TestDirResourceSetMountTrailing extends AbstractTestResourceSetMountTrailing { + + private static Path tempDir; + private static File dir1; + + @BeforeClass + public static void before() throws IOException { + tempDir = Files.createTempDirectory("test", new FileAttribute[0]); + dir1 = new File(tempDir.toFile(), "dir1"); + TomcatBaseTest.recursiveCopy(new File("test/webresources/dir1").toPath(), dir1.toPath()); + } + + @AfterClass + public static void after() { + ExpandWar.delete(tempDir.toFile()); + } + + + @Override + public WebResourceRoot getWebResourceRoot() { + TesterWebResourceRoot root = new TesterWebResourceRoot(); + WebResourceSet webResourceSet = + new DirResourceSet(new TesterWebResourceRoot(), getMount(), + getBaseDir().getAbsolutePath(), "/"); + root.setMainResources(webResourceSet); + return root; + } + + @Override + protected boolean isWritable() { + return true; + } + + @Override + public File getBaseDir() { + return dir1; + } + + @Override + protected String getNewDirName() { + return "test-dir-03"; + } + + @Override + protected String getNewFileNameNull() { + return "test-null-03"; + } + + @Override + protected String getNewFileName() { + return "test-file-03"; + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/webresources/TestJarContents.java tomcat10-10.1.52/test/org/apache/catalina/webresources/TestJarContents.java --- tomcat10-10.1.34/test/org/apache/catalina/webresources/TestJarContents.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/webresources/TestJarContents.java 2026-01-23 19:33:36.000000000 +0000 @@ -25,9 +25,6 @@ import org.apache.catalina.WebResourceSet; -/** - * @author Kamnani, Jatin - */ public class TestJarContents { private static File empty; @@ -70,6 +67,9 @@ Assert.assertTrue(testJarContentsObject.mightContainResource( "d1/d1-f1.txt", jar.getAbsolutePath())); + Assert.assertTrue(testJarContentsObject.mightContainResource( + "f9.txt", jar.getAbsolutePath())); + Assert.assertFalse(testJarContentsObject.mightContainResource( "/d7/d1-f1.txt", jar.getAbsolutePath())); diff -Nru tomcat10-10.1.34/test/org/apache/catalina/webresources/TestJarResourceSetMountTrailing.java tomcat10-10.1.52/test/org/apache/catalina/webresources/TestJarResourceSetMountTrailing.java --- tomcat10-10.1.34/test/org/apache/catalina/webresources/TestJarResourceSetMountTrailing.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/webresources/TestJarResourceSetMountTrailing.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,60 @@ +/* + * 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.webresources; + +import java.io.File; + +import org.apache.catalina.WebResourceRoot; +import org.apache.catalina.WebResourceSet; + +public class TestJarResourceSetMountTrailing extends AbstractTestResourceSetMount { + + @Override + public WebResourceRoot getWebResourceRoot() { + File f = new File("test/webresources/dir1.jar"); + TesterWebResourceRoot root = new TesterWebResourceRoot(); + WebResourceSet webResourceSet = + new JarResourceSet(root, getMount(), f.getAbsolutePath(), "/"); + root.setMainResources(webResourceSet); + return root; + } + + @Override + protected boolean isWritable() { + return false; + } + + @Override + public File getBaseDir() { + return new File("test/webresources"); + } + + @Override + protected String getNewDirName() { + return "test-dir-10"; + } + + @Override + protected String getNewFileNameNull() { + return "test-null-10"; + } + + @Override + protected String getNewFileName() { + return "test-file-10"; + } +} diff -Nru tomcat10-10.1.34/test/org/apache/catalina/webresources/TestWebResourceContentType.java tomcat10-10.1.52/test/org/apache/catalina/webresources/TestWebResourceContentType.java --- tomcat10-10.1.34/test/org/apache/catalina/webresources/TestWebResourceContentType.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/catalina/webresources/TestWebResourceContentType.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,88 @@ +/* + * 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.webresources; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.catalina.util.IOTools; +import org.apache.tomcat.util.buf.ByteChunk; + +public class TestWebResourceContentType extends TomcatBaseTest { + + @Test + public void testContentType() throws Exception { + Tomcat tomcat = getTomcatInstance(); + File docBase = new File("test/webapp"); + Context ctx = tomcat.addContext("/test", docBase.getAbsolutePath()); + + // Configure Context + ctx.addMimeMapping("html", "text/html"); + ctx.addMimeMapping("txt", "text/plain"); + + + // Add custom default servlet + Tomcat.addServlet(ctx, "default", new DefaultServlet()); + ctx.addServletMappingDecoded("/*", "default"); + + tomcat.start(); + + ByteChunk body = new ByteChunk(); + Map> resHead = new HashMap<>(); + int rc = getUrl("http://localhost:" + getPort() + "/test/anything", body, resHead); + + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + Assert.assertEquals("text/html", resHead.get("Content-Type").get(0)); + } + + + private static class DefaultServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + URL url = req.getServletContext().getResource("/index.html"); + URLConnection uConn = url.openConnection(); + resp.setContentType(uConn.getContentType()); + resp.setContentLengthLong(uConn.getContentLengthLong()); + + try (InputStream is = uConn.getInputStream(); + OutputStream os = resp.getOutputStream()) { + IOTools.flow(is, os); + } + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/coyote/TestCompressionConfig.java tomcat10-10.1.52/test/org/apache/coyote/TestCompressionConfig.java --- tomcat10-10.1.34/test/org/apache/coyote/TestCompressionConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/TestCompressionConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,20 +29,33 @@ @RunWith(Parameterized.class) public class TestCompressionConfig { - @Parameterized.Parameters(name = "{index}: accept-encoding[{0}], ETag [{1}], compress[{2}]") + @Parameterized.Parameters(name = "{index}: accept-encoding[{0}], ETag [{1}], compress[{2}], useTE[{3}]") public static Collection parameters() { List parameterSets = new ArrayList<>(); - parameterSets.add(new Object[] { new String[] { }, null, Boolean.FALSE }); - parameterSets.add(new Object[] { new String[] { "gzip" }, null, Boolean.TRUE }); - parameterSets.add(new Object[] { new String[] { "xgzip" }, null, Boolean.FALSE }); - parameterSets.add(new Object[] { new String[] { "<>gzip" }, null, Boolean.FALSE }); - parameterSets.add(new Object[] { new String[] { "foo", "gzip" }, null, Boolean.TRUE }); - parameterSets.add(new Object[] { new String[] { "<>", "gzip" }, null, Boolean.TRUE }); - - parameterSets.add(new Object[] { new String[] { "gzip" }, null, Boolean.TRUE }); - parameterSets.add(new Object[] { new String[] { "gzip" }, "W/", Boolean.TRUE }); - parameterSets.add(new Object[] { new String[] { "gzip" }, "XX", Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { }, null, Boolean.FALSE, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "gzip" }, null, Boolean.TRUE, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "xgzip" }, null, Boolean.FALSE, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "<>gzip" }, null, Boolean.FALSE, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "foo", "gzip" }, null, Boolean.TRUE, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "<>", "gzip" }, null, Boolean.TRUE, Boolean.FALSE }); + + parameterSets.add(new Object[] { new String[] { "gzip" }, null, Boolean.TRUE, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "gzip" }, "W/", Boolean.TRUE, Boolean.FALSE }); + parameterSets.add(new Object[] { new String[] { "gzip" }, "XX", Boolean.FALSE, Boolean.FALSE }); + + parameterSets.add(new Object[] { new String[] { }, null, Boolean.FALSE, Boolean.TRUE }); + parameterSets.add(new Object[] { new String[] { "gzip" }, null, Boolean.TRUE, Boolean.TRUE }); + parameterSets.add(new Object[] { new String[] { "xgzip" }, null, Boolean.FALSE, Boolean.TRUE }); + parameterSets.add(new Object[] { new String[] { "<>gzip" }, null, Boolean.FALSE, Boolean.TRUE }); + parameterSets.add(new Object[] { new String[] { "foo", "gzip" }, null, Boolean.TRUE, Boolean.TRUE }); + parameterSets.add(new Object[] { new String[] { "<>", "gzip" }, null, Boolean.TRUE, Boolean.TRUE }); + + parameterSets.add(new Object[] { new String[] { "gzip" }, null, Boolean.TRUE, Boolean.TRUE }); + parameterSets.add(new Object[] { new String[] { "gzip" }, "W/", Boolean.TRUE, Boolean.TRUE }); + parameterSets.add(new Object[] { new String[] { "gzip" }, "XX", Boolean.TRUE, Boolean.TRUE }); + + parameterSets.add(new Object[] { new String[] { "foobar;foo=bar, gzip;bla=\"quoted\"" }, "XX", Boolean.TRUE, Boolean.TRUE }); return parameterSets; } @@ -53,6 +66,8 @@ public String eTag; @Parameter(2) public Boolean compress; + @Parameter(3) + public Boolean useTE; @Test public void testUseCompression() throws Exception { @@ -65,14 +80,34 @@ Response response = new Response(); for (String header : headers) { - request.getMimeHeaders().addValue("accept-encoding").setString(header); + if (useTE.booleanValue()) { + request.getMimeHeaders().addValue("TE").setString(header); + } else { + request.getMimeHeaders().addValue("accept-encoding").setString(header); + } } if (eTag != null) { response.getMimeHeaders().addValue("ETag").setString(eTag); } + boolean useCompression = compressionConfig.useCompression(request, response); + Assert.assertEquals(compress, Boolean.valueOf(useCompression)); - Assert.assertEquals(compress, Boolean.valueOf(compressionConfig.useCompression(request, response))); + if (useTE.booleanValue()) { + Assert.assertNull(response.getMimeHeaders().getHeader("Content-Encoding")); + if (useCompression) { + Assert.assertEquals("gzip", response.getMimeHeaders().getHeader("Transfer-Encoding")); + } else { + Assert.assertNull(response.getMimeHeaders().getHeader("Transfer-Encoding")); + } + } else { + Assert.assertNull(response.getMimeHeaders().getHeader("Transfer-Encoding")); + if (useCompression) { + Assert.assertEquals("gzip", response.getMimeHeaders().getHeader("Content-Encoding")); + } else { + Assert.assertNull(response.getMimeHeaders().getHeader("Content-Encoding")); + } + } } } diff -Nru tomcat10-10.1.34/test/org/apache/coyote/TestIoTimeouts.java tomcat10-10.1.52/test/org/apache/coyote/TestIoTimeouts.java --- tomcat10-10.1.34/test/org/apache/coyote/TestIoTimeouts.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/TestIoTimeouts.java 2026-01-23 19:33:36.000000000 +0000 @@ -108,12 +108,13 @@ } String[] request = new String[packetCount]; + // @formatter:off request[0] = - "POST /test HTTP/1.1" + CRLF + - "Host: localhost:8080" + CRLF + - "Transfer-Encoding: chunked" + CRLF + - "Connection: close" + CRLF + - CRLF; + "POST /test HTTP/1.1" + CRLF + + "Host: localhost:8080" + CRLF + + "Transfer-Encoding: chunked" + CRLF + + "Connection: close" + CRLF + + CRLF; request[1] = "b8" + CRLF + "{" + CRLF + @@ -124,6 +125,7 @@ " \"assetStoreId\": \"5051\", " + CRLF + " \"zipCode\": \"98109\"" + CRLF + "}" + CRLF; + // @formatter:on if (sendEndChunk) { request[2] = "0" + CRLF + diff -Nru tomcat10-10.1.34/test/org/apache/coyote/ajp/SimpleAjpClient.java tomcat10-10.1.52/test/org/apache/coyote/ajp/SimpleAjpClient.java --- tomcat10-10.1.34/test/org/apache/coyote/ajp/SimpleAjpClient.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/ajp/SimpleAjpClient.java 2026-01-23 19:33:36.000000000 +0000 @@ -23,6 +23,8 @@ import javax.net.SocketFactory; +import org.apache.tomcat.util.http.Method; + /** * AJP client that is not (yet) a full AJP client implementation as it just provides the functionality required for the * unit tests. The client uses blocking IO throughout. @@ -74,46 +76,46 @@ public void setMethod(String method) { method = method.toUpperCase(Locale.ENGLISH); switch (method) { - case "OPTIONS": + case Method.OPTIONS: this.method = 1; break; - case "GET": + case Method.GET: this.method = 2; break; - case "HEAD": + case Method.HEAD: this.method = 3; break; - case "POST": + case Method.POST: this.method = 4; break; - case "PUT": + case Method.PUT: this.method = 5; break; - case "DELETE": + case Method.DELETE: this.method = 6; break; - case "TRACE": + case Method.TRACE: this.method = 7; break; - case "PROPFIND": + case Method.PROPFIND: this.method = 8; break; - case "PROPPATCH": + case Method.PROPPATCH: this.method = 9; break; - case "MKCOL": + case Method.MKCOL: this.method = 10; break; - case "COPY": + case Method.COPY: this.method = 11; break; - case "MOVE": + case Method.MOVE: this.method = 12; break; - case "LOCK": + case Method.LOCK: this.method = 13; break; - case "UNLOCK": + case Method.UNLOCK: this.method = 14; break; case "ACL": @@ -163,33 +165,33 @@ public String getMethod() { switch (method) { case 1: - return "OPTIONS"; + return Method.OPTIONS; case 2: - return "GET"; + return Method.GET; case 3: - return "HEAD"; + return Method.HEAD; case 4: - return "POST"; + return Method.POST; case 5: - return "PUT"; + return Method.PUT; case 6: - return "DELETE"; + return Method.DELETE; case 7: - return "TRACE"; + return Method.TRACE; case 8: - return "PROPFIND"; + return Method.PROPFIND; case 9: - return "PROPPATCH"; + return Method.PROPPATCH; case 10: - return "MKCOL"; + return Method.MKCOL; case 11: - return "COPY"; + return Method.COPY; case 12: - return "MOVE"; + return Method.MOVE; case 13: - return "LOCK"; + return Method.LOCK; case 14: - return "UNLOCK"; + return Method.UNLOCK; case 15: return "ACL"; case 16: diff -Nru tomcat10-10.1.34/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java tomcat10-10.1.52/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java --- tomcat10-10.1.34/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/ajp/TestAbstractAjpProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,6 +41,7 @@ import org.apache.catalina.connector.Connector; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.res.StringManager; public class TestAbstractAjpProcessor extends TomcatBaseTest { @@ -390,7 +391,7 @@ @Test public void testMethod() throws Exception { RequestDescriptor desc = new RequestDescriptor(); - desc.putRequestInfo("REQUEST-METHOD", "LOCK"); + desc.putRequestInfo("REQUEST-METHOD", Method.LOCK); desc.putRequestInfo("REQUEST-URI", "/testMethod"); doSnoopTest(desc); } @@ -467,7 +468,7 @@ desc.putRequestInfo("REQUEST-REMOTE-HOST", "MYCLIENT"); desc.putRequestInfo("REQUEST-REMOTE-ADDR", "10.1.2.3"); desc.putRequestInfo("REQUEST-REMOTE-PORT", "34567"); - desc.putRequestInfo("REQUEST-METHOD", "LOCK"); + desc.putRequestInfo("REQUEST-METHOD", Method.LOCK); desc.putRequestInfo("REQUEST-URI", "/a/b/c"); desc.putRequestInfo("REQUEST-PROTOCOL", "HTTP/1.x"); desc.putRequestInfo("REQUEST-IS-SECURE", "true"); @@ -484,7 +485,7 @@ @Test public void testSmallBody() throws Exception { RequestDescriptor desc = new RequestDescriptor(); - desc.putRequestInfo("REQUEST-METHOD", "PUT"); + desc.putRequestInfo("REQUEST-METHOD", Method.PUT); desc.putRequestInfo("REQUEST-CONTENT-LENGTH", "100"); desc.putRequestInfo("REQUEST-BODY-SIZE", "100"); desc.putRequestInfo("REQUEST-URI", "/testSmallBody"); @@ -494,7 +495,7 @@ @Test public void testLargeBody() throws Exception { RequestDescriptor desc = new RequestDescriptor(); - desc.putRequestInfo("REQUEST-METHOD", "PUT"); + desc.putRequestInfo("REQUEST-METHOD", Method.PUT); desc.putRequestInfo("REQUEST-CONTENT-LENGTH", "10000"); desc.putRequestInfo("REQUEST-BODY-SIZE", "10000"); desc.putRequestInfo("REQUEST-URI", "/testLargeBody"); @@ -633,7 +634,7 @@ validateCpong(ajpClient.cping()); ajpClient.setUri("/test/echo-params.jsp"); - ajpClient.setMethod("POST"); + ajpClient.setMethod(Method.POST); TesterAjpMessage forwardMessage = ajpClient.createForwardMessage(); forwardMessage.addHeader(0xA008, "9"); if (multipleCL) { @@ -710,22 +711,22 @@ @Test public void testZeroLengthRequestBodyGetA() throws Exception { - doTestZeroLengthRequestBody("GET", true); + doTestZeroLengthRequestBody(Method.GET, true); } @Test public void testZeroLengthRequestBodyGetB() throws Exception { - doTestZeroLengthRequestBody("GET", false); + doTestZeroLengthRequestBody(Method.GET, false); } @Test public void testZeroLengthRequestBodyPostA() throws Exception { - doTestZeroLengthRequestBody("POST", true); + doTestZeroLengthRequestBody(Method.POST, true); } @Test public void testZeroLengthRequestBodyPostB() throws Exception { - doTestZeroLengthRequestBody("POST", false); + doTestZeroLengthRequestBody(Method.POST, false); } private void doTestZeroLengthRequestBody(String method, boolean callAvailable) throws Exception { @@ -1107,7 +1108,7 @@ response.setCharacterEncoding("UTF-8"); try (PrintWriter w = response.getWriter()) { - w.println("Method: " + (isPost ? "POST" : "GET") + ". Reading request body..."); + w.println("Method: " + (isPost ? Method.POST : Method.GET) + ". Reading request body..."); w.println("Request Body length in bytes: " + readCount); } } diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http11/TestHttp11InputBuffer.java tomcat10-10.1.52/test/org/apache/coyote/http11/TestHttp11InputBuffer.java --- tomcat10-10.1.34/test/org/apache/coyote/http11/TestHttp11InputBuffer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http11/TestHttp11InputBuffer.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,6 +34,7 @@ import org.apache.catalina.startup.TesterServlet; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.http.Method; public class TestHttp11InputBuffer extends TomcatBaseTest { @@ -76,8 +77,15 @@ connect(); String[] request = new String[1]; - request[0] = "GET http://localhost:8080/test HTTP/1.1" + CRLF + "Host: localhost:8080" + CRLF + - "X-Bug48839: abcd" + CRLF + "\tefgh" + CRLF + "Connection: close" + CRLF + CRLF; + // @formatter:off + request[0] = + "GET http://localhost:8080/test HTTP/1.1" + CRLF + + "Host: localhost:8080" + CRLF + + "X-Bug48839: abcd" + CRLF + + "\tefgh" + CRLF + + "Connection: close" + CRLF + + CRLF; + // @formatter:on setRequest(request); processRequest(); // blocks until response has been read @@ -322,8 +330,15 @@ connect(); String[] request = new String[1]; - request[0] = "GET http://localhost:8080/test HTTP/1.1" + CRLF + "Host: localhost:8080" + CRLF + - headerLine + CRLF + "X-Bug51557: abcd" + CRLF + "Connection: close" + CRLF + CRLF; + // @formatter:off + request[0] = + "GET http://localhost:8080/test HTTP/1.1" + CRLF + + "Host: localhost:8080" + CRLF + + headerLine + CRLF + + "X-Bug51557: abcd" + CRLF + + "Connection: close" + CRLF + + CRLF; + // @formatter:on setRequest(request); processRequest(); // blocks until response has been read @@ -447,8 +462,16 @@ connect(); String[] request = new String[1]; - request[0] = newLines + "GET http://localhost:8080/test HTTP/1.1" + CRLF + "Host: localhost:8080" + - CRLF + "X-Bug48839: abcd" + CRLF + "\tefgh" + CRLF + "Connection: close" + CRLF + CRLF; + // @formatter:off + request[0] = + newLines + + "GET http://localhost:8080/test HTTP/1.1" + CRLF + + "Host: localhost:8080" + CRLF + + "X-Bug48839: abcd" + CRLF + + "\tefgh" + CRLF + + "Connection: close" + CRLF + + CRLF; + // @formatter:on setRequest(request); processRequest(); // blocks until response has been read @@ -609,7 +632,7 @@ public void testInvalidMethod() { String[] request = new String[1]; - request[0] = "GET" + (char) 0 + " /test HTTP/1.1" + CRLF + "Host: localhost:8080" + CRLF + "Connection: close" + + request[0] = Method.GET + (char) 0 + " /test HTTP/1.1" + CRLF + "Host: localhost:8080" + CRLF + "Connection: close" + CRLF + CRLF; InvalidClient client = new InvalidClient(request); @@ -635,6 +658,21 @@ @Test + public void testInvalidHttp09Method() { + + String[] request = new String[1]; + request[0] = "POST /test" + CRLF; + + InvalidClient client = new InvalidClient(request); + + client.doRequest(); + // The response in that case is HTTP/0.9 so only the body + Assert.assertTrue(client.getResponseLine(), client.getResponseLine().contains("400")); + Assert.assertTrue(client.isResponseBodyOK()); + } + + + @Test public void testInvalidEndOfRequestLine01() { String[] request = new String[1]; @@ -764,8 +802,14 @@ tomcat.start(); - String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: localhost" + SimpleHttpClient.CRLF + ":b" + - SimpleHttpClient.CRLF + "X-Dummy:b" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + + "Host: localhost" + SimpleHttpClient.CRLF + + ":b" + SimpleHttpClient.CRLF + + "X-Dummy:b" + SimpleHttpClient.CRLF + + SimpleHttpClient.CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http11/TestHttp11Processor.java tomcat10-10.1.52/test/org/apache/coyote/http11/TestHttp11Processor.java --- tomcat10-10.1.34/test/org/apache/coyote/http11/TestHttp11Processor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http11/TestHttp11Processor.java 2026-01-23 19:33:36.000000000 +0000 @@ -50,6 +50,7 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Wrapper; import org.apache.catalina.connector.Connector; @@ -83,8 +84,12 @@ tomcat.start(); - String request = "GET /anything HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /anything HTTP/1.1" + CRLF + + "Host: any" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -106,7 +111,7 @@ // There should not be an end chunk Assert.assertFalse(client.getResponseBody().endsWith("0")); // The last portion of text should be there - Assert.assertTrue(client.getResponseBody().endsWith("line03" + SimpleHttpClient.CRLF)); + Assert.assertTrue(client.getResponseBody().endsWith("line03" + CRLF)); } private static class ResponseWithErrorServlet extends HttpServlet { @@ -148,8 +153,13 @@ public void testWithUnknownExpectation() throws Exception { getTomcatInstanceTestWebapp(false, true); - String request = "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + - SimpleHttpClient.CRLF + "Expect: unknown" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Expect: unknown" + CRLF + + CRLF; + // @formatter:on Client client = new Client(getPort()); client.setRequest(new String[] { request }); @@ -164,10 +174,16 @@ public void testWithTEVoid() throws Exception { getTomcatInstanceTestWebapp(false, true); - String request = "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + - SimpleHttpClient.CRLF + "Transfer-encoding: void" + SimpleHttpClient.CRLF + "Content-Length: 9" + - SimpleHttpClient.CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - SimpleHttpClient.CRLF + "test=data"; + // @formatter:off + String request = + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: void" + CRLF + + "Content-Length: 9" + CRLF + + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + + CRLF + + "test=data"; + // @formatter:on Client client = new Client(getPort()); client.setRequest(new String[] { request }); @@ -182,10 +198,16 @@ public void testWithTEBuffered() throws Exception { getTomcatInstanceTestWebapp(false, true); - String request = "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + - SimpleHttpClient.CRLF + "Transfer-encoding: buffered" + SimpleHttpClient.CRLF + "Content-Length: 9" + - SimpleHttpClient.CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - SimpleHttpClient.CRLF + "test=data"; + // @formatter:off + String request = + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: buffered" + CRLF + + "Content-Length: 9" + CRLF + + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + + CRLF + + "test=data"; + // @formatter:on Client client = new Client(getPort()); client.setRequest(new String[] { request }); @@ -213,12 +235,20 @@ getTomcatInstanceTestWebapp(false, true); - String request = "POST /test/echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + - SimpleHttpClient.CRLF + (withCL ? "Content-length: 1" + SimpleHttpClient.CRLF : "") + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + - SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + "Connection: close" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF + "9" + SimpleHttpClient.CRLF + "test=data" + - SimpleHttpClient.CRLF + "0" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "POST /test/echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + (withCL ? "Content-length: 1" + CRLF : "") + + "Transfer-encoding: chunked" + CRLF + + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + + "Connection: close" + CRLF + + CRLF + + "9" + CRLF + + "test=data" + CRLF + + "0" + CRLF + + CRLF; + // @formatter:on Client client = new Client(getPort()); client.setRequest(new String[] { request }); @@ -234,10 +264,16 @@ public void testWithTESavedRequest() throws Exception { getTomcatInstanceTestWebapp(false, true); - String request = "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + - SimpleHttpClient.CRLF + "Transfer-encoding: savedrequest" + SimpleHttpClient.CRLF + - "Content-Length: 9" + SimpleHttpClient.CRLF + - SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + SimpleHttpClient.CRLF + "test=data"; + // @formatter:off + String request = + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: savedrequest" + CRLF + + "Content-Length: 9" + CRLF + + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + + CRLF + + "test=data"; + // @formatter:on Client client = new Client(getPort()); client.setRequest(new String[] { request }); @@ -252,10 +288,16 @@ public void testWithTEUnsupported() throws Exception { getTomcatInstanceTestWebapp(false, true); - String request = "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + - SimpleHttpClient.CRLF + "Transfer-encoding: unsupported" + SimpleHttpClient.CRLF + "Content-Length: 9" + - SimpleHttpClient.CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - SimpleHttpClient.CRLF + "test=data"; + // @formatter:off + String request = + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: unsupported" + CRLF + + "Content-Length: 9" + CRLF + + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + + CRLF + + "test=data"; + // @formatter:on Client client = new Client(getPort()); client.setRequest(new String[] { request }); @@ -279,8 +321,8 @@ tomcat.start(); - String requestPart1 = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF; - String requestPart2 = "Host: any" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + String requestPart1 = "GET /foo HTTP/1.1" + CRLF; + String requestPart2 = "Host: any" + CRLF + CRLF; final Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { requestPart1, requestPart2 }); @@ -335,9 +377,15 @@ tomcat.start(); - String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: any" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /foo HTTP/1.1" + CRLF + + "Host: any" + CRLF + + CRLF + + "GET /foo HTTP/1.1" + CRLF + + "Host: any" + CRLF + + CRLF; + // @formatter:on final Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -534,12 +582,15 @@ tomcat.start(); - String request = "POST /echo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: localhost:" + getPort() + - SimpleHttpClient.CRLF + "Content-Length: 10" + SimpleHttpClient.CRLF; - if (useExpectation) { - request += "Expect: 100-continue" + SimpleHttpClient.CRLF; - } - request += SimpleHttpClient.CRLF + "HelloWorld"; + // @formatter:off + String request = + "POST /echo HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 10" + CRLF + + (useExpectation ? "Expect: 100-continue" + CRLF : "") + + CRLF + + "HelloWorld"; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -749,7 +800,7 @@ resp.setCharacterEncoding("UTF-8"); try { resp.getWriter().print("OK"); - } catch (IOException e) { + } catch (IOException ignore) { // Should never happen. Test will fail if it does. } ac.complete(); @@ -985,8 +1036,13 @@ tomcat.start(); - String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: a" + SimpleHttpClient.CRLF + "Host: b" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /foo HTTP/1.1" + CRLF + + "Host: a" + CRLF + + "Host: b" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1018,8 +1074,13 @@ tomcat.start(); - String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: a" + SimpleHttpClient.CRLF + "Host: a" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /foo HTTP/1.1" + CRLF + + "Host: a" + CRLF + + "Host: a" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1048,7 +1109,7 @@ tomcat.start(); - String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + String request = "GET /foo HTTP/1.1" + CRLF + CRLF; Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1077,8 +1138,12 @@ tomcat.start(); - String request = "GET http://a/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: b" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://a/foo HTTP/1.1" + CRLF + + "Host: b" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1107,8 +1172,12 @@ tomcat.start(); - String request = "GET http://a:8080/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: b:8080" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://a:8080/foo HTTP/1.1" + CRLF + + "Host: b:8080" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1137,8 +1206,12 @@ tomcat.start(); - String request = "GET http://user:pwd@a/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: b" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://user:pwd@a/foo HTTP/1.1" + CRLF + + "Host: b" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1170,8 +1243,12 @@ tomcat.start(); - String request = "GET http://a/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: " + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://a/foo HTTP/1.1" + CRLF + + "Host: " + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1203,8 +1280,12 @@ tomcat.start(); - String request = "GET http://a:8080/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: " + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://a:8080/foo HTTP/1.1" + CRLF + + "Host: " + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1236,8 +1317,12 @@ tomcat.start(); - String request = "GET http://user:pwd@a/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: " + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://user:pwd@a/foo HTTP/1.1" + CRLF + + "Host: " + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1270,8 +1355,12 @@ tomcat.start(); - String request = "GET http://a/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: a" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://a/foo HTTP/1.1" + CRLF + + "Host: a" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1305,8 +1394,12 @@ tomcat.start(); - String request = "GET http://a:8080/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: a:8080" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://a:8080/foo HTTP/1.1" + CRLF + + "Host: a:8080" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1341,8 +1434,12 @@ tomcat.start(); - String request = "GET http://user:pwd@a/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: a" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://user:pwd@a/foo HTTP/1.1" + CRLF + + "Host: a" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1376,8 +1473,12 @@ tomcat.start(); - String request = "GET http://a/foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: A" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET http://a/foo HTTP/1.1" + CRLF + + "Host: A" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1412,8 +1513,12 @@ tomcat.start(); - String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: " + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /foo HTTP/1.1" + CRLF + + "Host: " + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1448,8 +1553,12 @@ tomcat.start(); - String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: " + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /foo HTTP/1.1" + CRLF + + "Host: " + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1540,14 +1649,13 @@ tomcat.start(); - String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: localhost:" + getPort() + - SimpleHttpClient.CRLF; - - if (sendKeepAlive) { - request += "Connection: keep-alive" + SimpleHttpClient.CRLF; - } - - request += SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /foo HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + (sendKeepAlive ? "Connection: keep-alive" + CRLF : "") + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1657,8 +1765,13 @@ tomcat.start(); - String request = "POST /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: localhost:" + getPort() + - SimpleHttpClient.CRLF + "Content-Length: 10" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "POST /foo HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Content-Length: 10" + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request, "XXXXXXXXXX" }); @@ -1812,9 +1925,13 @@ tomcat.start(); - String request = "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: localhost:" + getPort() + - SimpleHttpClient.CRLF + "Transfer-Encoding: " + headerValue + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /foo HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Transfer-Encoding: " + headerValue + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1835,11 +1952,19 @@ getTomcatInstanceTestWebapp(false, true); - String request = "POST /test/echo-params.jsp HTTP/1.0" + SimpleHttpClient.CRLF + "Host: any" + - SimpleHttpClient.CRLF + "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + - SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + "Connection: close" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF + "9" + SimpleHttpClient.CRLF + "test=data" + - SimpleHttpClient.CRLF + "0" + SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "POST /test/echo-params.jsp HTTP/1.0" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: chunked" + CRLF + + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + + "Connection: close" + CRLF + + CRLF + + "9" + CRLF + + "test=data" + CRLF + + "0" + CRLF + + CRLF; + // @formatter:on Client client = new Client(getPort()); client.setRequest(new String[] { request }); @@ -1867,9 +1992,15 @@ tomcat.start(); - String request = "POST /foo HTTP/1.1" + SimpleHttpClient.CRLF + "Host: localhost:" + getPort() + - SimpleHttpClient.CRLF + "Expect: 100-continue" + SimpleHttpClient.CRLF + "Content-Length: 10" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF + "0123456789"; + // @formatter:off + String request = + "POST /foo HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + "Expect: 100-continue" + CRLF + + "Content-Length: 10" + CRLF + + CRLF + + "0123456789"; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1886,7 +2017,6 @@ client.processRequest(false); Assert.assertTrue(client.isResponse200()); - } @@ -1894,8 +2024,12 @@ public void testConnect() throws Exception { getTomcatInstanceTestWebapp(false, true); - String request = "CONNECT example.local HTTP/1.1" + SimpleHttpClient.CRLF + "Host: example.local" + - SimpleHttpClient.CRLF + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "CONNECT example.local HTTP/1.1" + CRLF + + "Host: example.local" + CRLF + + CRLF; + // @formatter:on Client client = new Client(getPort()); client.setRequest(new String[] { request }); @@ -1930,9 +2064,12 @@ tomcat.start(); - String request = "GET /ehs HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /ehs HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1959,9 +2096,12 @@ tomcat.start(); - String request = "GET /ehs HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /ehs HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); @@ -1989,9 +2129,12 @@ tomcat.start(); - String request = "GET /ehs HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost:" + getPort() + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + // @formatter:off + String request = + "GET /ehs HTTP/1.1" + CRLF + + "Host: localhost:" + getPort() + CRLF + + CRLF; + // @formatter:on Client client = new Client(tomcat.getConnector().getLocalPort()); client.setRequest(new String[] { request }); diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java tomcat10-10.1.52/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java --- tomcat10-10.1.34/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http11/filters/TestChunkedInputFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,10 @@ import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.ReadListener; import jakarta.servlet.ServletException; +import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -32,7 +35,9 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; +import org.apache.catalina.Wrapper; import org.apache.catalina.startup.SimpleHttpClient; import org.apache.catalina.startup.TesterServlet; import org.apache.catalina.startup.Tomcat; @@ -112,23 +117,25 @@ tomcat.start(); - String[] request = new String[]{ - "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: any" + SimpleHttpClient.CRLF + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + // @formatter:off + String[] request = new String[] { + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: chunked" + CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - "Connection: close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "3" + (chunkHeaderUsesCRLF ? SimpleHttpClient.CRLF : SimpleHttpClient.LF) + - "a=0" + (chunkUsesCRLF ? SimpleHttpClient.CRLF : SimpleHttpClient.LF) + - "4" + SimpleHttpClient.CRLF + - "&b=1" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + + "Connection: close" + CRLF + + CRLF + + "3" + (chunkHeaderUsesCRLF ? CRLF : SimpleHttpClient.LF) + + "a=0" + (chunkUsesCRLF ? CRLF : SimpleHttpClient.LF) + + "4" + CRLF + + "&b=1" + CRLF + + "0" + CRLF + "x-trailer1: Test", "Value1" + - (firstheaderUsesCRLF ? SimpleHttpClient.CRLF : SimpleHttpClient.LF) + + (firstheaderUsesCRLF ? CRLF : SimpleHttpClient.LF) + "x-trailer2: TestValue2" + - (secondheaderUsesCRLF ? SimpleHttpClient.CRLF : SimpleHttpClient.LF) + - (endUsesCRLF ? SimpleHttpClient.CRLF : SimpleHttpClient.LF) }; + (secondheaderUsesCRLF ? CRLF : SimpleHttpClient.LF) + + (endUsesCRLF ? CRLF : SimpleHttpClient.LF) }; + // @formatter:on TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); @@ -185,22 +192,24 @@ */ @Test public void testTrailingHeadersSizeLimitPipelining() throws Exception { + // @formatter:off doTestTrailingHeadersSizeLimit(19, - "x-trailer: Test" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: any" + SimpleHttpClient.CRLF + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + "x-trailer: Test" + CRLF + + CRLF + + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: chunked" + CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - "Connection: close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "3" + SimpleHttpClient.CRLF + - "a=0" + SimpleHttpClient.CRLF + - "4" + SimpleHttpClient.CRLF + - "&b=1" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + + "Connection: close" + CRLF + + CRLF + + "3" + CRLF + + "a=0" + CRLF + + "4" + CRLF + + "&b=1" + CRLF + + "0" + CRLF + "x-trailer: Test", true); + // @formatter:on } @@ -221,20 +230,23 @@ Assert.assertTrue(tomcat.getConnector().setProperty("maxTrailerSize", Integer.toString(trailerSizeLimit))); tomcat.start(); - String[] request = new String[]{ - "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: any" + SimpleHttpClient.CRLF + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + // @formatter:off + String[] request = new String[] { + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: chunked" + CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - "Connection: close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "3" + SimpleHttpClient.CRLF + - "a=0" + SimpleHttpClient.CRLF + - "4" + SimpleHttpClient.CRLF + - "&b=1" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - trailerHeader + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }; + "Connection: close" + CRLF + + CRLF + + "3" + CRLF + + "a=0" + CRLF + + "4" + CRLF + + "&b=1" + CRLF + + "0" + CRLF + + trailerHeader + CRLF + + CRLF + }; + // @formatter:on TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); @@ -289,19 +301,22 @@ extValue.append('x'); } - String[] request = new String[]{ - "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: any" + SimpleHttpClient.CRLF + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + // @formatter:off + String[] request = new String[] { + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: chunked" + CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - "Connection: close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "3" + extName + extValue.toString() + SimpleHttpClient.CRLF + - "a=0" + SimpleHttpClient.CRLF + - "4" + SimpleHttpClient.CRLF + - "&b=1" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }; + "Connection: close" + CRLF + + CRLF + + "3" + extName + extValue.toString() + CRLF + + "a=0" + CRLF + + "4" + CRLF + + "&b=1" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); @@ -330,19 +345,21 @@ tomcat.start(); + // @formatter:off String request = - "POST /echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: any" + SimpleHttpClient.CRLF + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: chunked" + CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - "Connection: close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "3" + SimpleHttpClient.CRLF + - "a=0" + SimpleHttpClient.CRLF + - "4" + SimpleHttpClient.CRLF + - "&b=1" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF; + "Connection: close" + CRLF + + CRLF + + "3" + CRLF + + "a=0" + CRLF + + "4" + CRLF + + "&b=1" + CRLF + + "0" + CRLF + + CRLF; + // @formatter:on TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); @@ -360,43 +377,43 @@ @Test public void testChunkSizeAbsent() throws Exception { - doTestChunkSize(false, false, SimpleHttpClient.CRLF, 10, 0); + doTestChunkSize(false, false, CRLF, 10, 0); } @Test public void testChunkSizeTwentyFive() throws Exception { - doTestChunkSize(true, true, "19" + SimpleHttpClient.CRLF - + "Hello World!Hello World!!" + SimpleHttpClient.CRLF, 40, 25); + doTestChunkSize(true, true, "19" + CRLF + + "Hello World!Hello World!!" + CRLF, 40, 25); } @Test public void testChunkSizeEightDigit() throws Exception { - doTestChunkSize(true, true, "0000000C" + SimpleHttpClient.CRLF - + "Hello World!" + SimpleHttpClient.CRLF, 20, 12); + doTestChunkSize(true, true, "0000000C" + CRLF + + "Hello World!" + CRLF, 20, 12); } @Test public void testChunkSizeNineDigit() throws Exception { - doTestChunkSize(false, false, "00000000C" + SimpleHttpClient.CRLF - + "Hello World!" + SimpleHttpClient.CRLF, 20, 12); + doTestChunkSize(false, false, "00000000C" + CRLF + + "Hello World!" + CRLF, 20, 12); } @Test public void testChunkSizeLong() throws Exception { - doTestChunkSize(true, false, "7fFFffFF" + SimpleHttpClient.CRLF - + "Hello World!" + SimpleHttpClient.CRLF, 10, 10); + doTestChunkSize(true, false, "7fFFffFF" + CRLF + + "Hello World!" + CRLF, 10, 10); } @Test public void testChunkSizeIntegerMinValue() throws Exception { - doTestChunkSize(false, false, "80000000" + SimpleHttpClient.CRLF - + "Hello World!" + SimpleHttpClient.CRLF, 10, 10); + doTestChunkSize(false, false, "80000000" + CRLF + + "Hello World!" + CRLF, 10, 10); } @Test public void testChunkSizeMinusOne() throws Exception { - doTestChunkSize(false, false, "ffffffff" + SimpleHttpClient.CRLF - + "Hello World!" + SimpleHttpClient.CRLF, 10, 10); + doTestChunkSize(false, false, "ffffffff" + CRLF + + "Hello World!" + CRLF, 10, 10); } /** @@ -429,15 +446,18 @@ tomcat.start(); - String request = "POST /echo-params.jsp HTTP/1.1" - + SimpleHttpClient.CRLF + "Host: any" + SimpleHttpClient.CRLF - + "Transfer-encoding: chunked" + SimpleHttpClient.CRLF - + "Content-Type: text/plain" + SimpleHttpClient.CRLF; - if (expectPass) { - request += "Connection: close" + SimpleHttpClient.CRLF; - } - request += SimpleHttpClient.CRLF + chunks + "0" + SimpleHttpClient.CRLF - + SimpleHttpClient.CRLF; + // @formatter:off + String request = + "POST /echo-params.jsp HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: chunked" + CRLF + + "Content-Type: text/plain" + CRLF + + (expectPass ? "Connection: close" + CRLF : "") + + CRLF + + chunks + + "0" + CRLF + + CRLF; + // @formatter:on TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); // Need to use the content length here as variations in Connector and @@ -500,20 +520,23 @@ tomcat.start(); - String[] request = new String[]{ - "POST / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost" + SimpleHttpClient.CRLF + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + // @formatter:off + String[] request = new String[] { + "POST / HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Transfer-encoding: chunked" + CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - "Connection: close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "3" + SimpleHttpClient.CRLF + - "a=0" + SimpleHttpClient.CRLF + - "4" + SimpleHttpClient.CRLF + - "&b=1" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - "x@trailer: Test" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }; + "Connection: close" + CRLF + + CRLF + + "3" + CRLF + + "a=0" + CRLF + + "4" + CRLF + + "&b=1" + CRLF + + "0" + CRLF + + "x@trailer: Test" + CRLF + + CRLF + }; + // @formatter:on TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); client.setRequest(request); @@ -685,12 +708,15 @@ tomcat.start(); - String[] request = new String[]{ - "POST / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost" + SimpleHttpClient.CRLF + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "3" + SimpleHttpClient.CRLF }; + // @formatter:off + String[] request = new String[] { + "POST / HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Transfer-encoding: chunked" + CRLF + + CRLF + + "3" + CRLF + }; + // @formatter:on TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); client.setUseContentLength(true); @@ -741,15 +767,18 @@ tomcat.start(); - String[] request = new String[]{ - "GET / HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: localhost" + SimpleHttpClient.CRLF + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "20" + SimpleHttpClient.CRLF + - "01234567890123456789012345678901" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF }; + // @formatter:off + String[] request = new String[] { + "GET / HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Transfer-encoding: chunked" + CRLF + + CRLF + + "20" + CRLF + + "01234567890123456789012345678901" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on TrailerClient client = new TrailerClient(tomcat.getConnector().getLocalPort()); client.setUseContentLength(true); @@ -816,6 +845,64 @@ } + private static class NonBlockingReadLineServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + int lineCount = 0; + int pauseCount = 0; + long lastRead = 0; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + + final AsyncContext ctx = req.startAsync(); + ServletInputStream is = req.getInputStream(); + is.setReadListener(new ReadListener() { + @Override + public void onDataAvailable() throws IOException { + byte[] buf = new byte[1024]; + do { + int n = is.read(buf); + if (n < 0) { + break; + } else if (n > 0) { + String line = new String(buf, 0, n, StandardCharsets.UTF_8); + Assert.assertTrue(line.length() > 0); + long thisRead = System.nanoTime(); + if (lineCount > 0) { + /* + * After the first line, look for a pause of at least 800ms between reads. + */ + if ((thisRead - lastRead) > TimeUnit.MILLISECONDS.toNanos(800)) { + pauseCount++; + } + } + lastRead = thisRead; + lineCount++; + } + } while (is.isReady()); + } + + @Override + public void onAllDataRead() throws IOException { + resp.setContentType("text/plain"); + PrintWriter pw = resp.getWriter(); + pw.write(Integer.toString(lineCount) + "," + Integer.toString(pauseCount)); + ctx.complete(); + } + + @Override + public void onError(Throwable throwable) { + throwable.printStackTrace(); + } + + }); + + } + } + + private static class ReadLineClient extends SimpleHttpClient { ReadLineClient(int port) { @@ -844,27 +931,84 @@ tomcat.getConnector().setProperty("connectionTimeout", "300000"); tomcat.start(); - String[] request = new String[]{ - "POST /test HTTP/1.1" + SimpleHttpClient.CRLF + - "Host: any" + SimpleHttpClient.CRLF + - "Transfer-encoding: chunked" + SimpleHttpClient.CRLF + + // @formatter:off + String[] request = new String[] { + "POST /test HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: chunked" + CRLF + + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + + "Connection: close" + CRLF + + CRLF + + "7" + CRLF + + "DATA01\n", CRLF + + "7", CRLF + + "DATA02\n" + CRLF, + "7" + CRLF + + // Split the CRLF between writes + "DATA03\n" + SimpleHttpClient.CR, + SimpleHttpClient.LF + + "7" + CRLF + + "DATA04\n", CRLF + + "13" + CRLF, + "DATA05DATA05DATA05\n" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on + + ReadLineClient client = new ReadLineClient(tomcat.getConnector().getLocalPort()); + client.setRequest(request); + + client.connect(300000,300000); + client.processRequest(); + Assert.assertTrue(client.isResponse200()); + /* + * Output is "," so there should be 5 lines read with a pause between each. + */ + Assert.assertEquals("5,4", client.getResponseBody()); + } + + + @Test + public void testChunkedSplitWithNonBlocking() throws Exception { + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = getProgrammaticRootContext(); + + NonBlockingReadLineServlet servlet = new NonBlockingReadLineServlet(); + Wrapper wrapper = Tomcat.addServlet(ctx, "servlet", servlet); + wrapper.setAsyncSupported(true); + ctx.addServletMappingDecoded("/test", "servlet"); + + tomcat.getConnector().setProperty("connectionTimeout", "300000"); + tomcat.start(); + + // @formatter:off + String[] request = new String[] { + "POST /test HTTP/1.1" + CRLF + + "Host: any" + CRLF + + "Transfer-encoding: chunked" + CRLF + SimpleHttpClient.HTTP_HEADER_CONTENT_TYPE_FORM_URL_ENCODING + - "Connection: close" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF + - "7" + SimpleHttpClient.CRLF + - "DATA01\n" + SimpleHttpClient.CRLF, - "7" + SimpleHttpClient.CRLF + - "DATA02\n" + SimpleHttpClient.CRLF, - "7" + SimpleHttpClient.CRLF + + "Connection: close" + CRLF + + CRLF + + "7" + CRLF + + "DATA01\n", CRLF + + "7", CRLF + + "DATA02\n" + CRLF, + "7" + CRLF + // Split the CRLF between writes "DATA03\n" + SimpleHttpClient.CR, SimpleHttpClient.LF + - "7" + SimpleHttpClient.CRLF + - "DATA04\n" + SimpleHttpClient.CRLF, - "7" + SimpleHttpClient.CRLF + - "DATA05\n" + SimpleHttpClient.CRLF + - "0" + SimpleHttpClient.CRLF + - SimpleHttpClient.CRLF}; + "7" + CRLF + + "DATA04\n", CRLF + + "13" + CRLF, + "DATA05DATA05DATA05\n" + CRLF + + "0" + CRLF + + CRLF + }; + // @formatter:on ReadLineClient client = new ReadLineClient(tomcat.getConnector().getLocalPort()); client.setRequest(request); diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/Http2TestBase.java tomcat10-10.1.52/test/org/apache/coyote/http2/Http2TestBase.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/Http2TestBase.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/Http2TestBase.java 2026-01-23 19:33:36.000000000 +0000 @@ -63,6 +63,7 @@ import org.apache.coyote.http2.Http2Parser.Output; import org.apache.tomcat.util.compat.JrePlatform; import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.http.parser.Priority; import org.apache.tomcat.util.net.TesterSupport; @@ -224,7 +225,7 @@ protected void buildGetRequest(byte[] frameHeader, ByteBuffer headersPayload, byte[] padding, int streamId, String url) { List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", url)); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -262,7 +263,7 @@ protected void buildSimpleGetRequestPart1(byte[] frameHeader, ByteBuffer headersPayload, int streamId) { List
    headers = new ArrayList<>(3); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/simple")); @@ -375,7 +376,7 @@ byte[] padding, boolean withTrailers, int streamId) { MimeHeaders headers = new MimeHeaders(); - headers.addValue(":method").setString("POST"); + headers.addValue(":method").setString(Method.POST); headers.addValue(":scheme").setString("http"); headers.addValue(":path").setString(path); headers.addValue(":authority").setString("localhost:" + getPort()); @@ -451,7 +452,7 @@ protected void buildHeadRequest(byte[] headersFrameHeader, ByteBuffer headersPayload, int streamId, String path) { MimeHeaders headers = new MimeHeaders(); - headers.addValue(":method").setString("HEAD"); + headers.addValue(":method").setString(Method.HEAD); headers.addValue(":scheme").setString("http"); headers.addValue(":path").setString(path); headers.addValue(":authority").setString("localhost:" + getPort()); @@ -671,6 +672,11 @@ } protected void openClientConnection(boolean tls) throws IOException { + openClientConnection(tls, true); + } + + protected void openClientConnection(boolean tls, boolean autoAckSettings) throws IOException { + SocketFactory socketFactory = tls ? TesterSupport.configureClientSsl() : SocketFactory.getDefault(); // Open a connection s = socketFactory.createSocket("localhost", getPort()); @@ -680,7 +686,7 @@ InputStream is = s.getInputStream(); input = new TestInput(is); - output = new TestOutput(); + output = new TestOutput(autoAckSettings); parser = new TesterHttp2Parser("-1", input, output); hpackEncoder = new HpackEncoder(); } @@ -802,7 +808,7 @@ } - void sendClientPreface() throws IOException { + protected void sendClientPreface() throws IOException { os.write(Http2Parser.CLIENT_PREFACE_START); os.write(EMPTY_SETTINGS_FRAME); os.flush(); @@ -1065,6 +1071,8 @@ public class TestOutput implements Output, HeaderEmitter { + private final boolean autoAckSettings; + private StringBuffer trace = new StringBuffer(); private String lastStreamId = "0"; private ConnectionSettingsRemote remoteSettings = new ConnectionSettingsRemote("-1"); @@ -1073,6 +1081,10 @@ private long bytesRead; private volatile HpackDecoder hpackDecoder = null; + public TestOutput(boolean autoAckSettings) { + this.autoAckSettings = autoAckSettings; + } + public void setTraceBody(boolean traceBody) { this.traceBody = traceBody; } @@ -1092,14 +1104,14 @@ @Override - public ByteBuffer startRequestBodyFrame(int streamId, int payloadSize, boolean endOfStream) { + public ByteBuffer startRequestBodyFrame(int streamId, int dataLength, boolean endOfStream) { lastStreamId = Integer.toString(streamId); - bytesRead += payloadSize; + bytesRead += dataLength; if (traceBody) { - bodyBuffer = ByteBuffer.allocate(payloadSize); + bodyBuffer = ByteBuffer.allocate(dataLength); return bodyBuffer; } else { - trace.append(lastStreamId + "-Body-" + payloadSize + "\n"); + trace.append(lastStreamId + "-Body-" + dataLength + "\n"); return null; } } @@ -1203,7 +1215,9 @@ trace.append("0-Settings-Ack\n"); } else { trace.append("0-Settings-End\n"); - sendSettings(0, true); + if (autoAckSettings) { + sendSettings(0, true); + } } } diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestAsync.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestAsync.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestAsync.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestAsync.java 2026-01-23 19:33:36.000000000 +0000 @@ -239,8 +239,8 @@ public void run() { try { write(); - } catch (IOException e) { - throw new IllegalStateException(e); + } catch (IOException ioe) { + throw new IllegalStateException(ioe); } } }, 200, TimeUnit.MILLISECONDS); diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestAsyncError.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestAsyncError.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestAsyncError.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestAsyncError.java 2026-01-23 19:33:36.000000000 +0000 @@ -49,10 +49,12 @@ Tomcat tomcat = getTomcatInstance(); + AsyncErrorServlet asyncErrorServlet = new AsyncErrorServlet(); + Context ctxt = getProgrammaticRootContext(); Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); ctxt.addServletMappingDecoded("/simple", "simple"); - Wrapper w = Tomcat.addServlet(ctxt, "async", new AsyncErrorServlet()); + Wrapper w = Tomcat.addServlet(ctxt, "async", asyncErrorServlet); w.setAsyncSupported(true); ctxt.addServletMappingDecoded("/async", "async"); tomcat.start(); @@ -81,12 +83,12 @@ sendRst(3, Http2Error.CANCEL.getCode()); int count = 0; - while (count < 50 && TestListener.getErrorCount() == 0) { + while (count < 50 && asyncErrorServlet.getErrorCount() == 0) { count++; Thread.sleep(100); } - Assert.assertEquals(1, TestListener.getErrorCount()); + Assert.assertEquals(1, asyncErrorServlet.getErrorCount()); } @@ -94,11 +96,16 @@ private static final long serialVersionUID = 1L; + private TestListener testListener = new TestListener(); + + int getErrorCount() { + return testListener.getErrorCount(); + } + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { final AsyncContext asyncContext = req.startAsync(); - TestListener testListener = new TestListener(); asyncContext.addListener(testListener); MessageGenerator msgGenerator = new MessageGenerator(resp); @@ -139,9 +146,9 @@ private static final class TestListener implements AsyncListener { - private static final AtomicInteger errorCount = new AtomicInteger(0); + private final AtomicInteger errorCount = new AtomicInteger(0); - public static int getErrorCount() { + public int getErrorCount() { return errorCount.get(); } diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestFlowControl.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestFlowControl.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestFlowControl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestFlowControl.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,6 +24,7 @@ import org.junit.Assert; import org.junit.Test; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.res.StringManager; @@ -49,7 +50,7 @@ ByteBuffer headersPayload = ByteBuffer.allocate(128); MimeHeaders headers = new MimeHeaders(); - headers.addValue(":method").setString("POST"); + headers.addValue(":method").setString(Method.POST); headers.addValue(":scheme").setString("http"); headers.addValue(":path").setString("/path-does-not-exist"); headers.addValue(":authority").setString("localhost:" + getPort()); diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestHpack.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestHpack.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestHpack.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestHpack.java 2026-01-23 19:33:36.000000000 +0000 @@ -147,4 +147,68 @@ decoder.decode(output); Assert.assertEquals(headerValue, headers2.getHeader(headerName)); } + + + @Test + public void testDecodeIntegerMaxValue() throws HpackException { + ByteBuffer bb = ByteBuffer.allocate(9); + bb.put((byte) 255); + bb.put((byte) 254); + bb.put((byte) 255); + bb.put((byte) 255); + bb.put((byte) 255); + bb.put((byte) 7); + bb.position(0); + + Assert.assertEquals(Integer.MAX_VALUE,Hpack.decodeInteger(bb, 1)); + } + + + @Test(expected = HpackException.class) + public void testDecodeIntegerMaxValuePlus1() throws HpackException { + ByteBuffer bb = ByteBuffer.allocate(9); + bb.put((byte) 255); + bb.put((byte) 255); + bb.put((byte) 255); + bb.put((byte) 255); + bb.put((byte) 255); + bb.put((byte) 7); + bb.position(0); + + Hpack.decodeInteger(bb, 1); + } + + @Test(expected = HpackException.class) + public void testDecodeIntegerOverflow() throws HpackException { + ByteBuffer bb = ByteBuffer.allocate(9); + bb.put((byte) 255); + bb.put((byte) 254); + bb.put((byte) 255); + bb.put((byte) 255); + bb.put((byte) 255); + bb.put((byte) 15); + bb.position(0); + + Hpack.decodeInteger(bb, 1); + } + + @Test(expected = HpackException.class) + public void testDecodeIntegerZeroValues() throws HpackException { + ByteBuffer bb = ByteBuffer.allocate(12); + bb.put((byte) 255); + bb.put((byte) 128); + bb.put((byte) 128); + bb.put((byte) 128); + bb.put((byte) 128); + bb.put((byte) 128); + bb.put((byte) 128); + bb.put((byte) 128); + bb.put((byte) 128); + bb.put((byte) 128); + bb.put((byte) 128); + bb.put((byte) 128); + bb.position(0); + + Hpack.decodeInteger(bb, 1); + } } diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2AccessLogs.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2AccessLogs.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2AccessLogs.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2AccessLogs.java 2026-01-23 19:33:36.000000000 +0000 @@ -147,8 +147,8 @@ public void log(CharArrayWriter message) { try { message.writeTo(writer); - } catch (IOException ex) { - log.error("Could not write to writer", ex); + } catch (IOException ioe) { + log.error("Could not write to writer", ioe); } } } diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2Limits.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Limits.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2Limits.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Limits.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,6 +32,7 @@ import org.apache.catalina.connector.Connector; import org.apache.coyote.http11.AbstractHttp11Protocol; import org.apache.coyote.http2.HpackEncoder.State; +import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.res.StringManager; @@ -312,7 +313,7 @@ parser.readFrame(); MatcherAssert.assertThat(output.getTrace(), RegexMatcher.matchesRegex("0-Goaway-\\[1\\]-\\[11\\]-\\[" + limitMessage + "\\]")); - } catch (IOException se) { + } catch (IOException ignore) { // Expected on some platforms } break; @@ -324,7 +325,7 @@ private void populateHeadersPayload(ByteBuffer headersPayload, List customHeaders, String path) throws Exception { MimeHeaders headers = new MimeHeaders(); - headers.addValue(":method").setString("GET"); + headers.addValue(":method").setString(Method.GET); headers.addValue(":scheme").setString("http"); headers.addValue(":path").setString(path); headers.addValue(":authority").setString("localhost:" + getPort()); diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2RequestParameters.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2RequestParameters.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2RequestParameters.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2RequestParameters.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.coyote.http2; + +import org.junit.Assert; +import org.junit.Test; + +public class TestHttp2RequestParameters extends Http2TestBase { + /** + * Test case for https://bz.apache.org/bugzilla/show_bug.cgi?id=69918 POST parameters are not returned from a call + * to any of the {@link org.apache.catalina.connector.Request} getParameterXXX() methods if the request is HTTP/2 + * and the content-length header is not set. + * + * @throws Exception If the test encounters an unexpected error + */ + @Test + public void testBug69918() throws Exception { + http2Connect(); + + sendParameterPostRequest(3, null, "a=1&b=2", -1, false); + output.setTraceBody(true); + + boolean foundBody = false; + while (parser.readFrame()) { + String trace = output.getTrace(); + if (trace.contains("3-Body-2")) { + foundBody = true; + } else if (trace.contains("3-Body-0")) { + Assert.fail("Parameter count was 0. Trace: " + trace); + } + if (trace.contains("3-EndOfStream")) { + break; + } + } + Assert.assertTrue("Parameter count should be 2, trace: " + output.getTrace(), foundBody); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2Section_3_5.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Section_3_5.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2Section_3_5.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Section_3_5.java 2026-01-23 19:33:36.000000000 +0000 @@ -47,7 +47,7 @@ // body will fail. try { sendPing(); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2Section_6_1.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Section_6_1.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2Section_6_1.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Section_6_1.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,7 @@ */ package org.apache.coyote.http2; +import java.nio.ByteBuffer; import java.util.logging.Level; import java.util.logging.Logger; @@ -63,6 +64,58 @@ readSimplePostResponse(true); // The window updates for padding could occur anywhere since they + // happen on a different thread to the response. + // The connection window update is always present if there is + // padding. + String trace = output.getTrace(); + String paddingWindowUpdate = "0-WindowSize-[9]\n"; + Assert.assertTrue(trace, trace.contains(paddingWindowUpdate)); + trace = trace.replace(paddingWindowUpdate, ""); + + // The stream window update may or may not be present depending on + // timing. Remove it if present. + if (trace.contains("3-WindowSize-[9]\n")) { + trace = trace.replace("3-WindowSize-[9]\n", ""); + } + + Assert.assertEquals("0-WindowSize-[119]\n" + "3-WindowSize-[119]\n" + "3-HeadersStart\n" + + "3-Header-[:status]-[200]\n" + "3-Header-[content-length]-[119]\n" + + "3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n" + "3-HeadersEnd\n" + "3-Body-119\n" + + "3-EndOfStream\n", trace); + } finally { + Logger.getLogger("org.apache.coyote").setLevel(Level.INFO); + Logger.getLogger("org.apache.tomcat.util.net").setLevel(Level.INFO); + } + } + + + @Test + public void testDataFrameWithPaddingAndContentLength() throws Exception { + Logger.getLogger("org.apache.coyote").setLevel(Level.ALL); + Logger.getLogger("org.apache.tomcat.util.net").setLevel(Level.ALL); + try { + http2Connect(); + + // Disable overhead protection for window update as it breaks the + // test + http2Protocol.setOverheadWindowUpdateThreshold(0); + + byte[] padding = new byte[8]; + + byte[] headersFrameHeader = new byte[9]; + ByteBuffer headersPayload = ByteBuffer.allocate(128); + byte[] dataFrameHeader = new byte[9]; + ByteBuffer dataPayload = ByteBuffer.allocate(128); + + // 128 byte payload, less 8 bytes padding, less 1 padding byte gives 119 bytes + buildPostRequest(headersFrameHeader, headersPayload, false, null, 119, "/simple", dataFrameHeader, + dataPayload, padding, false, 3); + writeFrame(headersFrameHeader, headersPayload); + writeFrame(dataFrameHeader, dataPayload); + + readSimplePostResponse(true); + + // The window updates for padding could occur anywhere since they // happen on a different thread to the response. // The connection window update is always present if there is // padding. diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2Section_8_1.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Section_8_1.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2Section_8_1.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2Section_8_1.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,6 +27,7 @@ import org.apache.catalina.startup.Tomcat; import org.apache.coyote.ContinueResponseTiming; import org.apache.coyote.http11.AbstractHttp11Protocol; +import org.apache.tomcat.util.http.Method; /** * Unit tests for Section 8.1 of RFC 7540.
    @@ -195,7 +196,7 @@ http2Connect(); List
    headers = new ArrayList<>(5); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/simple")); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -210,7 +211,7 @@ http2Connect(); List
    headers = new ArrayList<>(5); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/simple")); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -228,7 +229,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/simple")); headers.add(new Header("x-test", "test")); @@ -260,7 +261,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/simple")); headers.add(new Header("host", "localhost:" + getPort())); @@ -284,7 +285,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/simple")); headers.add(new Header("host", "localhost:" + getPort())); @@ -309,7 +310,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":authority", "localhost:" + getPort())); headers.add(new Header(":path", "/simple")); @@ -334,7 +335,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":authority", "localhost")); headers.add(new Header(":path", "/simple")); @@ -396,7 +397,7 @@ private void doTestHostHeaderInconsistent(String authority, String host) throws Exception { List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":authority", authority)); headers.add(new Header(":path", "/simple")); @@ -445,7 +446,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "abcd")); headers.add(new Header(":authority", "localhost:" + getPort())); headers.add(new Header(":path", "/simple")); @@ -470,7 +471,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "ab!cd")); headers.add(new Header(":authority", "localhost:" + getPort())); headers.add(new Header(":path", "/simple")); @@ -485,7 +486,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "")); headers.add(new Header(":authority", "localhost:" + getPort())); headers.add(new Header(":path", "/simple")); @@ -500,7 +501,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":authority", "localhost:" + getPort())); headers.add(new Header(":path", "/simple")); headers.add(new Header("host", "localhost:" + getPort())); diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2UpgradeHandler.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2UpgradeHandler.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestHttp2UpgradeHandler.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestHttp2UpgradeHandler.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,6 +24,7 @@ import org.apache.catalina.Context; import org.apache.catalina.startup.Tomcat; +import org.apache.tomcat.util.http.Method; public class TestHttp2UpgradeHandler extends Http2TestBase { @@ -130,7 +131,7 @@ openClientConnection(); - byte[] upgradeRequest = ((usePost ? "POST" : "GET") + " /" + (useReader ? "?useReader=true " : " ") + + byte[] upgradeRequest = ((usePost ? Method.POST : Method.GET) + " /" + (useReader ? "?useReader=true " : " ") + "HTTP/1.1\r\n" + "Host: localhost:" + getPort() + "\r\n" + "Content-Length: 18\r\n" + "Connection: Upgrade,HTTP2-Settings\r\n" + "Upgrade: h2c\r\n" + EMPTY_HTTP2_SETTINGS_HEADER + "\r\n" + "Small request body").getBytes(StandardCharsets.ISO_8859_1); diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestRfc9218.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestRfc9218.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestRfc9218.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestRfc9218.java 2026-01-23 19:33:36.000000000 +0000 @@ -17,6 +17,7 @@ package org.apache.coyote.http2; import java.io.IOException; +import java.nio.charset.StandardCharsets; import org.junit.Assert; import org.junit.Test; @@ -146,12 +147,15 @@ // 19 - 7021 body left // 21 - 6143 body left + // BZ 69614 - invalid priority update frames should be ignored + sendInvalidPriorityUpdate(17); + // Re-order the priorities sendPriorityUpdate(17, 2, true); /* * Add 8k to the connection window. Should clear the connection window over allocation and fully allocate 17 - * with the remainder split equally between 17 and 21. + * with the remainder split proportionally between 19 and 21. */ sendWindowUpdate(0, 1024 * 8); // Use try/catch as third read has been failing on some tests runs @@ -177,6 +181,39 @@ trace = trace.replace("21-Body-1365\n", ""); Assert.assertEquals(0, trace.length()); - // Test doesn't read the read of the body for streams 19 and 21. + // 19 - 5641 body left + // 21 - 4778 body left + + // Add 16k to the connection window. Should fully allocate 19 and 21. + sendWindowUpdate(0, 1024 * 16); + + try { + parser.readFrame(); + parser.readFrame(); + } catch (IOException ioe) { + // Dump for debugging purposes + ioe.printStackTrace(); + } + } + + + private void sendInvalidPriorityUpdate(int streamId) throws IOException { + byte[] payload = "u=1:i".getBytes(StandardCharsets.US_ASCII); + + byte[] priorityUpdateFrame = new byte[13 + payload.length]; + + // length + ByteUtil.setThreeBytes(priorityUpdateFrame, 0, 4 + payload.length); + // type + priorityUpdateFrame[3] = FrameType.PRIORITY_UPDATE.getIdByte(); + // Stream ID + ByteUtil.set31Bits(priorityUpdateFrame, 5, 0); + + // Payload + ByteUtil.set31Bits(priorityUpdateFrame, 9, streamId); + System.arraycopy(payload, 0, priorityUpdateFrame, 13, payload.length); + + os.write(priorityUpdateFrame); + os.flush(); } } diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestStandardSessionIntegrationHttp2.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestStandardSessionIntegrationHttp2.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestStandardSessionIntegrationHttp2.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestStandardSessionIntegrationHttp2.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,174 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.coyote.http2; + +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.ByteBuffer; +import java.util.concurrent.CountDownLatch; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.apache.tomcat.util.http.Method; +import org.apache.tomcat.util.http.MimeHeaders; + +public class TestStandardSessionIntegrationHttp2 extends Http2TestBase { + + @Test + public void testSessionIsNew() throws Exception { + + enableHttp2(); + + Tomcat tomcat = getTomcatInstance(); + + Context ctxt = getProgrammaticRootContext(); + // Need simple servlet for the HTTP upgrade + Tomcat.addServlet(ctxt, "simple", new SimpleServlet()); + ctxt.addServletMappingDecoded("/simple", "simple"); + // Servlet for this test + Tomcat.addServlet(ctxt, "session", new SessionServlet()); + ctxt.addServletMappingDecoded("/session", "session"); + tomcat.start(); + + openClientConnection(); + doHttpUpgrade(); + sendClientPreface(); + validateHttp2InitialResponse(); + + output.setTraceBody(true); + + // Make first request + // Generate headers + byte[] headersFrameHeader = new byte[9]; + ByteBuffer headersPayload = ByteBuffer.allocate(128); + + MimeHeaders headers = new MimeHeaders(); + headers.addValue(":method").setString(Method.GET); + headers.addValue(":scheme").setString("http"); + headers.addValue(":path").setString("/session"); + headers.addValue(":authority").setString("localhost:" + getPort()); + + hpackEncoder.encode(headers, headersPayload); + headersPayload.flip(); + + ByteUtil.setThreeBytes(headersFrameHeader, 0, headersPayload.limit()); + headersFrameHeader[3] = FrameType.HEADERS.getIdByte(); + // Flags. end of headers (0x04). end of stream (0x01) + headersFrameHeader[4] = 0x05; + // Stream id + ByteUtil.set31Bits(headersFrameHeader, 5, 3); + + writeFrame(headersFrameHeader, headersPayload); + + // Read headers from first request + parser.readFrame(); + // extract the session ID + String trace = output.getTrace(); + int index = trace.indexOf("JSESSIONID="); + String sessionID = trace.substring(index + 11, index + 11 + 32); + output.clearTrace(); + + // Make second request - can just 'update' first request + headersPayload.clear(); + headers.addValue("cookie").setString("JSESSIONID=" + sessionID); + hpackEncoder.encode(headers, headersPayload); + headersPayload.flip(); + + ByteUtil.setThreeBytes(headersFrameHeader, 0, headersPayload.limit()); + // Stream id + ByteUtil.set31Bits(headersFrameHeader, 5, 5); + + writeFrame(headersFrameHeader, headersPayload); + + // Request 2 headers + parser.readFrame(); + // body (request 1 or 2) + parser.readFrame(); + // body (request 1 or 2) + parser.readFrame(); + + trace = output.getTrace(); + Assert.assertTrue(trace, trace.contains("3-Body-OK")); + Assert.assertTrue(trace, trace.contains("5-Body-OK")); + } + + + private static class SessionServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + + resp.setContentType("text/plain"); + resp.setCharacterEncoding("UTF-8"); + PrintWriter pw = resp.getWriter(); + + boolean pass = true; + + HttpSession s = req.getSession(false); + CountDownLatch latch; + if (s == null) { + s = req.getSession(true); + if (!s.isNew()) { + // This is first request so session must be new. + pass = false; + } + latch = new CountDownLatch(1); + s.setAttribute("latch", latch); + // Return the session ID to the client + resp.flushBuffer(); + // Wait for the second request to this session + while (latch.getCount() > 0) { + try { + latch.await(); + } catch (InterruptedException e) { + // Ignore. Only proceed one the latch has counted down. + } + } + // Second request has accessed session. Client has joined the session. + if (s.isNew()) { + // Session should not be new once client has joined it. + pass = false; + } + } else { + if (s.isNew()) { + // This is second (or later) request. Client has joined the session. Sessions should not be new. + pass = false; + } + // Release the first request if it is still waiting. + latch = (CountDownLatch) s.getAttribute("latch"); + latch.countDown(); + } + + if (pass) { + pw.print("OK"); + } else { + pw.print("FAIL"); + } + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/coyote/http2/TestStreamProcessor.java tomcat10-10.1.52/test/org/apache/coyote/http2/TestStreamProcessor.java --- tomcat10-10.1.34/test/org/apache/coyote/http2/TestStreamProcessor.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/coyote/http2/TestStreamProcessor.java 2026-01-23 19:33:36.000000000 +0000 @@ -40,6 +40,7 @@ import org.apache.catalina.startup.Tomcat; import org.apache.tomcat.util.compat.JrePlatform; import org.apache.tomcat.util.http.FastHttpDateFormat; +import org.apache.tomcat.util.http.Method; public class TestStreamProcessor extends Http2TestBase { @@ -137,7 +138,7 @@ ByteBuffer headersPayload = ByteBuffer.allocate(128); List
    headers = new ArrayList<>(3); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/index.html")); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -192,7 +193,7 @@ ByteBuffer headersPayload = ByteBuffer.allocate(128); List
    headers = new ArrayList<>(3); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/noContent")); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -281,7 +282,7 @@ ByteBuffer headersPayload = ByteBuffer.allocate(128); List
    headers = new ArrayList<>(5); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/index.html")); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -326,7 +327,7 @@ ByteBuffer headersPayload = ByteBuffer.allocate(128); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/index^html")); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -370,7 +371,7 @@ ByteBuffer headersPayload = ByteBuffer.allocate(128); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/index.html?foo=[]")); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -416,7 +417,7 @@ ByteBuffer headersPayload = ByteBuffer.allocate(128); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/index.html?foo=[]")); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -523,7 +524,7 @@ ByteBuffer headersPayload = ByteBuffer.allocate(128); List
    headers = new ArrayList<>(3); - headers.add(new Header(":method", "GET")); + headers.add(new Header(":method", Method.GET)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":path", "/compression")); headers.add(new Header(":authority", "localhost:" + getPort())); @@ -571,7 +572,7 @@ http2Connect(); List
    headers = new ArrayList<>(4); - headers.add(new Header(":method", "CONNECT")); + headers.add(new Header(":method", Method.CONNECT)); headers.add(new Header(":scheme", "http")); headers.add(new Header(":authority", "example.local")); diff -Nru tomcat10-10.1.34/test/org/apache/el/TestELInJsp.java tomcat10-10.1.52/test/org/apache/el/TestELInJsp.java --- tomcat10-10.1.34/test/org/apache/el/TestELInJsp.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/el/TestELInJsp.java 2026-01-23 19:33:36.000000000 +0000 @@ -508,6 +508,16 @@ } + @Test + public void testBug69635() throws Exception { + getTomcatInstanceTestWebapp(true, true); + + ByteChunk res = getUrl("http://localhost:" + getPort() + "/test/bug6nnnn/bug69635.jsp"); + String result = res.toString(); + assertEcho(result, "01-3"); + } + + // Assertion for text contained with

    , e.g. printed by tags:echo private static void assertEcho(String result, String expected) { Assert.assertTrue(result, result.indexOf("

    " + expected + "

    ") > 0); diff -Nru tomcat10-10.1.34/test/org/apache/el/parser/TestAstIdentifier.java tomcat10-10.1.52/test/org/apache/el/parser/TestAstIdentifier.java --- tomcat10-10.1.34/test/org/apache/el/parser/TestAstIdentifier.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/el/parser/TestAstIdentifier.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,11 +16,18 @@ */ package org.apache.el.parser; +import jakarta.el.ELContext; +import jakarta.el.ELException; import jakarta.el.ELProcessor; +import jakarta.el.ExpressionFactory; +import jakarta.el.ValueExpression; import org.junit.Assert; +import org.junit.Assume; import org.junit.Test; +import org.apache.jasper.el.ELContextImpl; + public class TestAstIdentifier { @Test @@ -43,4 +50,75 @@ Integer.class); Assert.assertEquals(Integer.valueOf(Integer.MAX_VALUE), result); } + + + @Test + public void testIdentifierStart() { + /* + * This test only works on Java 11. + * + * Java 11 is the minimum Java version for Tomcat 10. + * + * In Java 12, the definition of Java Letter and/or Java Digit has changed. + */ + Assume.assumeTrue(Integer.valueOf(11).equals(Runtime.version().version().get(0))); + for (int i = 0; i < 0xFFFF; i++) { + if (Character.isJavaIdentifierStart(i)) { + testIdentifier((char) i, 'b'); + } else { + try { + testIdentifier((char) i, 'b'); + } catch (ELException e) { + continue; + } + Assert.fail("Expected EL exception for [" + i + "], [" + (char) i + "]"); + } + } + } + + + @Test + public void testIdentifierPart() { + /* + * This test only works on Java 11. + * + * Java 11 is the minimum Java version for Tomcat 10. + * + * In Java 12, the definition of Java Letter and/or Java Digit has changed. + */ + Assume.assumeTrue(Integer.valueOf(11).equals(Runtime.version().version().get(0))); + for (int i = 0; i < 0xFFFF; i++) { + if (Character.isJavaIdentifierPart(i)) { + testIdentifier('b', (char) i); + } else { + try { + testIdentifier((char) i, 'b'); + } catch (ELException e) { + continue; + } + Assert.fail("Expected EL exception for [" + i + "], [" + (char) i + "]"); + } + } + } + + + private void testIdentifier(char one, char two) { + ExpressionFactory factory = ExpressionFactory.newInstance(); + ELContext context = new ELContextImpl(factory); + + String s = "OK"; + ValueExpression var = factory.createValueExpression(s, String.class); + + String identifier = new String(new char[] { one , two }); + context.getVariableMapper().setVariable(identifier, var); + + ValueExpression ve = null; + try { + ve = factory.createValueExpression(context, "${" + identifier + "}", String.class); + } catch (Exception e) { + throw e; + } + + Assert.assertEquals(s, ve.getValue(context)); + } } diff -Nru tomcat10-10.1.34/test/org/apache/el/parser/TesterGenerateIdentifierRanges.java tomcat10-10.1.52/test/org/apache/el/parser/TesterGenerateIdentifierRanges.java --- tomcat10-10.1.34/test/org/apache/el/parser/TesterGenerateIdentifierRanges.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/el/parser/TesterGenerateIdentifierRanges.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,97 @@ +/* + * 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.el.parser; + +import org.junit.Test; + +/* + * The purpose of this class is to generate the ranges used in the JavaCC grammar for EL parsing. + * + * The ranges for Tomcat 10 were generated with Java 11. + * + * The generated ranges change in Java 12. + */ +public class TesterGenerateIdentifierRanges { + + /* + * Java Letter is all characters where Character.isJavaIdentifierStart() returns true. + */ + @Test + public void testGenerateJavaLetterRanges() { + int start = 0; + int end = 0; + boolean inRange = false; + + for (int i = 0 ; i < 0xFFFF; i++) { + if (Character.isJavaIdentifierStart(i)) { + if (!inRange) { + inRange = true; + start = i; + } + } else { + if (inRange) { + end = i - 1; + inRange = false; + System.out.print(" \"" + asUnicodeEscape(start) + "\""); + if (start == end) { + System.out.println(","); + } else { + System.out.println("-\"" + asUnicodeEscape(end) + "\","); + } + } + } + } + } + + + /* + * Java Digit is all characters where Character.isJavaIdentifierPart() returns true that aren't included in Java + * Letter. + */ + @Test + public void testGenerateJavaDigitRanges() { + int start = 0; + int end = 0; + boolean inRange = false; + + for (int i = 0 ; i < 0xFFFF; i++) { + if (Character.isJavaIdentifierPart(i) && !Character.isJavaIdentifierStart(i)) { + if (!inRange) { + inRange = true; + start = i; + } + } else { + if (inRange) { + end = i - 1; + inRange = false; + System.out.print(" \"" + asUnicodeEscape(start) + "\""); + if (start == end) { + System.out.println(","); + } else { + System.out.println("-\"" + asUnicodeEscape(end) + "\","); + } + } + } + } + } + + + + private static String asUnicodeEscape(int input) { + return String.format("\\u%04x", Integer.valueOf(input)); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/jasper/compiler/TestGenerator.java tomcat10-10.1.52/test/org/apache/jasper/compiler/TestGenerator.java --- tomcat10-10.1.34/test/org/apache/jasper/compiler/TestGenerator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/jasper/compiler/TestGenerator.java 2026-01-23 19:33:36.000000000 +0000 @@ -53,6 +53,8 @@ public class TestGenerator extends TomcatBaseTest { + private static final String NEW_LINE = System.lineSeparator(); + @Test public void testBug45015a() throws Exception { getTomcatInstanceTestWebapp(false, true); @@ -245,8 +247,8 @@ public int doEndTag() throws JspException { try { pageContext.getOut().print("attribute1: '" + attribute1 + "', " + "attribute2: '" + attribute2 + "'"); - } catch (IOException e) { - throw new JspException(e); + } catch (IOException ioe) { + throw new JspException(ioe); } return EVAL_PAGE; } @@ -261,11 +263,11 @@ try { getUrl("http://localhost:" + getPort() + "/test/bug5nnnn/bug56581.jsp", res, null); Assert.fail("An IOException was expected."); - } catch (IOException expected) { - // ErrorReportValve in Tomcat 8.0.9+ flushes and aborts the - // connection when an unexpected error is encountered and response - // has already been committed. It results in an exception here: - // java.io.IOException: Premature EOF + } catch (IOException ignore) { + /* + * ErrorReportValve flushes and aborts the connection when an unexpected error is encountered and response + * has already been committed. It results in an exception here: java.io.IOException: Premature EOF + */ } String result = res.toString(); @@ -323,7 +325,7 @@ } } if (!removeBlankLines && blankLineCount == 0) { - Assert.fail("TrimSpaceOptions.EXTENDED not configured but balnk lines have been removed"); + Assert.fail("TrimSpaceOptions.EXTENDED not configured but blank lines have been removed"); } else if (removeBlankLines && blankLineCount > 0) { Assert.fail("TrimSpaceOptions.EXTENDED does not allow the line to be just a new line character"); } @@ -394,6 +396,49 @@ doTestJspId(true); } + @Test + public void testNonstandardSets() throws Exception { + getTomcatInstanceTestWebapp(true, true); + + // This should break all subsequent requests + ByteChunk body = new ByteChunk(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/set-01.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=testValue" + NEW_LINE + + "request value=null" + NEW_LINE + + "session value=null" + NEW_LINE + + "application value=null", body.toString()); + body.recycle(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/set-02.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=testValue" + NEW_LINE + + "request value=null" + NEW_LINE + + "session value=null" + NEW_LINE + + "application value=null", body.toString()); + body.recycle(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/set-03.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=null" + NEW_LINE + + "request value=testValue" + NEW_LINE + + "session value=null" + NEW_LINE + + "application value=null", body.toString()); + body.recycle(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/set-04.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=null" + NEW_LINE + + "request value=null" + NEW_LINE + + "session value=testValue" + NEW_LINE + + "application value=null", body.toString()); + body.recycle(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/set-05.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=null" + NEW_LINE + + "request value=null" + NEW_LINE + + "session value=null" + NEW_LINE + + "application value=testValue", body.toString()); + body.recycle(); + } + private void doTestJspId(boolean document) throws Exception { getTomcatInstanceTestWebapp(false, true); @@ -689,6 +734,10 @@ } } + @Test + public void testLambdaScriptlets() throws Exception { + doTestJsp("lambda.jsp"); + } @Test public void testInfoConflictNone() throws Exception { @@ -987,6 +1036,37 @@ } @Test + public void testBug69508() throws Exception { + getTomcatInstanceTestWebapp(false, true); + + ByteChunk body = new ByteChunk(); + int rc = getUrl("http://localhost:" + getPort() + "/test/bug6nnnn/bug69508.jsp?init=InitCommand", body, null); + + String text = body.toString(); + Assert.assertEquals(text, HttpServletResponse.SC_OK, rc); + // include page URL with param cmd + Assert.assertTrue(text, text.contains("

    cmd - someCommand

    ")); + Assert.assertTrue(text, text.contains("

    param1 - value1

    ")); + Assert.assertTrue(text, text.contains("

    cmd - someCommandAbs

    ")); + Assert.assertTrue(text, text.contains("

    param1 - value1Abs

    ")); + // include page URL without param + Assert.assertTrue(text, text.contains("

    param2 - value2

    ")); + Assert.assertTrue(text, text.contains("

    param2 - value2Abs

    ")); + + Assert.assertTrue(text, text.contains("

    param3 - InitCommand

    ")); + Assert.assertTrue(text, text.contains("

    param3 - InitCommandAbs

    ")); + + Assert.assertTrue(text, text.contains("

    param4 - value4

    ")); + Assert.assertTrue(text, text.contains("

    param4 - value4Abs

    ")); + + Assert.assertTrue(text, text.contains("

    param5 - InitCommand

    ")); + Assert.assertTrue(text, text.contains("

    param5 - InitCommandAbs

    ")); + + Assert.assertTrue(text, text.contains("

    param6 - value6

    ")); + Assert.assertTrue(text, text.contains("

    param6 - value6Abs

    ")); + } + + @Test public void testTagReleaseWithPooling() throws Exception { doTestTagRelease(true); } @@ -1030,4 +1110,47 @@ Assert.assertEquals(body.toString(), expectedResponseCode, rc); } + + @Test + public void testNonstandardRemoves() throws Exception { + getTomcatInstanceTestWebapp(true, true); + + // This should break all subsequent requests + ByteChunk body = new ByteChunk(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/remove-01.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=null" + NEW_LINE + + "request value=testValue" + NEW_LINE + + "session value=testValue" + NEW_LINE + + "application value=testValue", body.toString()); + body.recycle(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/remove-02.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=testValue" + NEW_LINE + + "request value=null" + NEW_LINE + + "session value=testValue" + NEW_LINE + + "application value=testValue", body.toString()); + body.recycle(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/remove-03.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=testValue" + NEW_LINE + + "request value=testValue" + NEW_LINE + + "session value=null" + NEW_LINE + + "application value=testValue", body.toString()); + body.recycle(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/remove-04.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=testValue" + NEW_LINE + + "request value=testValue" + NEW_LINE + + "session value=testValue" + NEW_LINE + + "application value=null", body.toString()); + body.recycle(); + getUrl("http://localhost:" + getPort() + "/test/jsp/generator/nonstandard/remove-05.jsp", body, null); + Assert.assertEquals(NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + NEW_LINE + + "pageContext value=null" + NEW_LINE + + "request value=null" + NEW_LINE + + "session value=null" + NEW_LINE + + "application value=null", body.toString()); + body.recycle(); + } } diff -Nru tomcat10-10.1.34/test/org/apache/jasper/compiler/TestNonstandardTagPerformance.java tomcat10-10.1.52/test/org/apache/jasper/compiler/TestNonstandardTagPerformance.java --- tomcat10-10.1.34/test/org/apache/jasper/compiler/TestNonstandardTagPerformance.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/jasper/compiler/TestNonstandardTagPerformance.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,148 @@ +/* + * 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.jasper.compiler; + +import jakarta.el.ELContext; +import jakarta.el.ELManager; +import jakarta.el.ExpressionFactory; +import jakarta.servlet.jsp.PageContext; +import jakarta.servlet.jsp.TesterPageContext; + +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import org.apache.jasper.runtime.JspRuntimeLibrary; + +public class TestNonstandardTagPerformance { + static final long NUM_ITERATIONS = 100000000L; + + static final int NUM_TESTS = 5; + + private ELContext elContext; + + private ExpressionFactory factory; + + private ELManager manager; + public PageContext pageContext; + + public jakarta.el.ExpressionFactory _jsp_getExpressionFactory() { + return factory; + } + + private void newCode(jakarta.servlet.jsp.PageContext _jspx_page_context) throws java.lang.Throwable { + JspRuntimeLibrary.nonstandardSetTag(_jspx_page_context, null, _jspx_page_context, + jakarta.servlet.jsp.PageContext.PAGE_SCOPE); + _jspx_page_context.setAttribute("groupName", new Object(), jakarta.servlet.jsp.PageContext.PAGE_SCOPE); + } + + /** + * This code is stolen from a generated JSP using standard c:set logic, except that the value is replaced by "new + * Object()". This eliminates the EL cost from the benchmark so we can accurately focus on the c:set performance. + * + * @param _jspx_th_c_005fwhen_005f11 parent tag + * @param _jspx_page_context page context + * @return whether to continue execution + * @throws java.lang.Throwable unknown error + */ + private boolean oldCode(jakarta.servlet.jsp.tagext.JspTag _jspx_th_c_005fwhen_005f11, + jakarta.servlet.jsp.PageContext _jspx_page_context) throws java.lang.Throwable { +// jakarta.servlet.jsp.PageContext pageContext = _jspx_page_context; +// jakarta.servlet.jsp.JspWriter out = _jspx_page_context.getOut(); +// // c:set +// org.apache.taglibs.standard.tag.rt.core.SetTag _jspx_th_c_005fset_005f39 = new org.apache.taglibs.standard.tag.rt.core.SetTag(); +// _jsp_getInstanceManager().newInstance(_jspx_th_c_005fset_005f39); +// try { +// _jspx_th_c_005fset_005f39.setPageContext(_jspx_page_context); +// _jspx_th_c_005fset_005f39.setParent((jakarta.servlet.jsp.tagext.Tag) _jspx_th_c_005fwhen_005f11); +// // /WEB-INF/views/jsp/features/buybox/offerDisplayGroupLayout.jsp(230,12) name = var type = java.lang.String +// // reqTime = false required = false fragment = false deferredValue = false expectedTypeName = null +// // deferredMethod = false methodSignature = null +// _jspx_th_c_005fset_005f39.setVar("groupName"); +// // /WEB-INF/views/jsp/features/buybox/offerDisplayGroupLayout.jsp(230,12) name = value type = +// // javax.el.ValueExpression reqTime = true required = false fragment = false deferredValue = true +// // expectedTypeName = java.lang.Object deferredMethod = false methodSignature = null +// _jspx_th_c_005fset_005f39.setValue(new Object()); +// int _jspx_eval_c_005fset_005f39 = _jspx_th_c_005fset_005f39.doStartTag(); +// if (_jspx_th_c_005fset_005f39.doEndTag() == jakarta.servlet.jsp.tagext.Tag.SKIP_PAGE) { +// return true; +// } +// } finally { +// org.apache.jasper.runtime.JspRuntimeLibrary.releaseTag(_jspx_th_c_005fset_005f39, +// _jsp_getInstanceManager()); +// } + return false; + } + + /** + * This method depends on the availability of org.apache.taglibs.standard.tag.rt.core.SetTag, which is not typically + * available for Tomcat's unit tests. To execute the benchmark correctly, please: + *
      + *
    1. add a taglibs jar to the classpath
    2. + *
    3. uncomment the body of {@code oldCode()}
    4. + *
    5. run the test manually (IDE or command-line)
    6. + *
    7. use jvm args similar to + * + *
      +     * -Xmx1g -Xms1g -verbose:gc -XX:+UseParallelGC
      +     * 
      + * + *
    8. + *
    + * @throws Throwable generic error + */ + @Ignore + @Test + public void runBenchmark() throws Throwable { + long[] durations = new long[NUM_TESTS]; + for (int i = 0; i < NUM_ITERATIONS / 10; i++) { + oldCode(null, pageContext); + newCode(pageContext); + } + + for (int i = 0; i < NUM_TESTS; i++) { + long start = System.currentTimeMillis(); + for (long j = 0; j < NUM_ITERATIONS; j++) { + oldCode(null, pageContext); + } + durations[i] = System.currentTimeMillis() - start; + } + + for (int d = 0; d < durations.length; d++) { + System.out.println("Old: " + d + ". " + durations[d] + "ms"); + } + for (int i = 0; i < NUM_TESTS; i++) { + long start = System.currentTimeMillis(); + for (long j = 0; j < NUM_ITERATIONS; j++) { + newCode(pageContext); + } + durations[i] = System.currentTimeMillis() - start; + } + + for (int d = 0; d < durations.length; d++) { + System.out.println("New: " + d + ". " + durations[d] + "ms"); + } + } + + @Before + public void setupTestVars() { + manager = new ELManager(); + elContext = manager.getELContext(); + factory = ELManager.getExpressionFactory(); + pageContext = new TesterPageContext(elContext); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/jasper/compiler/TestValidator.java tomcat10-10.1.52/test/org/apache/jasper/compiler/TestValidator.java --- tomcat10-10.1.34/test/org/apache/jasper/compiler/TestValidator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/jasper/compiler/TestValidator.java 2026-01-23 19:33:36.000000000 +0000 @@ -269,8 +269,8 @@ public int doStartTag() throws JspException { try { pageContext.getOut().print("

    " + echo + "

    "); - } catch (IOException e) { - pageContext.getServletContext().log("Tag (Echo21) failure", e); + } catch (IOException ioe) { + pageContext.getServletContext().log("Tag (Echo21) failure", ioe); } return super.doStartTag(); } diff -Nru tomcat10-10.1.34/test/org/apache/jasper/runtime/TestJspRuntimeLibrary.java tomcat10-10.1.52/test/org/apache/jasper/runtime/TestJspRuntimeLibrary.java --- tomcat10-10.1.34/test/org/apache/jasper/runtime/TestJspRuntimeLibrary.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/jasper/runtime/TestJspRuntimeLibrary.java 2026-01-23 19:33:36.000000000 +0000 @@ -16,7 +16,12 @@ */ package org.apache.jasper.runtime; +import jakarta.el.ELManager; +import jakarta.servlet.jsp.PageContext; +import jakarta.servlet.jsp.TesterPageContextWithAttributes; + import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.apache.catalina.startup.TomcatBaseTest; @@ -24,6 +29,8 @@ public class TestJspRuntimeLibrary extends TomcatBaseTest { + private PageContext pageContext; + /* * Tests successful conversions */ @@ -140,4 +147,34 @@ private static void assertEcho(String result, String expected) { Assert.assertTrue(result, result.indexOf("

    " + expected + "

    ") > 0); } + + @Before + public void setupTestVars() { + pageContext = new TesterPageContextWithAttributes((new ELManager()).getELContext()); + } + + @Test + public void testNonstandardSetWithUndefinedScope() { + JspRuntimeLibrary.nonstandardSetTag(pageContext, "var", "value", PageContext.PAGE_SCOPE); + Assert.assertEquals("value", pageContext.getAttribute("var")); + Assert.assertEquals("value", pageContext.getAttribute("var", PageContext.PAGE_SCOPE)); + Assert.assertEquals(null, pageContext.getAttribute("var", PageContext.REQUEST_SCOPE)); + + JspRuntimeLibrary.nonstandardSetTag(pageContext, "var", null, PageContext.PAGE_SCOPE); + Assert.assertEquals(null, pageContext.getAttribute("var")); + + } + + @Test + public void testNonstandardSetWithDefinedScope() { + final int[] scopes = { PageContext.PAGE_SCOPE, PageContext.REQUEST_SCOPE, PageContext.SESSION_SCOPE, + PageContext.APPLICATION_SCOPE }; + for (int scope : scopes) { + JspRuntimeLibrary.nonstandardSetTag(pageContext, "var", "value", scope); + Assert.assertEquals("value", pageContext.getAttribute("var", scope)); + + JspRuntimeLibrary.nonstandardSetTag(pageContext, "var", null, scope); + Assert.assertEquals(null, pageContext.getAttribute("var", scope)); + } + } } diff -Nru tomcat10-10.1.34/test/org/apache/jasper/servlet/TestJspServlet.java tomcat10-10.1.52/test/org/apache/jasper/servlet/TestJspServlet.java --- tomcat10-10.1.34/test/org/apache/jasper/servlet/TestJspServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/jasper/servlet/TestJspServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,6 +32,7 @@ import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.descriptor.web.ErrorPage; +import org.apache.tomcat.util.http.Method; public class TestJspServlet extends TomcatBaseTest { @@ -60,7 +61,7 @@ // When using JaCoCo, the CI system seems to need a longer timeout int rc = methodUrl("http://localhost:" + getPort() + "/test/bug56568", - new ByteChunk(), 30000, null, null, "PUT"); + new ByteChunk(), 30000, null, null, Method.PUT); // Make sure we get the original 500 response and not a 405 response // which would indicate that error.jsp is complaining about being called @@ -74,7 +75,7 @@ getTomcatInstanceTestWebapp(false, true); int rc = methodUrl("http://localhost:" + getPort() + "/test/jsp/error.jsp", - new ByteChunk(), 500000, null, null, "PUT"); + new ByteChunk(), 500000, null, null, Method.PUT); // Make sure we get a 200 response and not a 405 response // which would indicate that error.jsp is complaining about being called @@ -88,7 +89,7 @@ getTomcatInstanceTestWebapp(false, true); int rc = methodUrl("http://localhost:" + getPort() + "/test/jsp/test.jsp", - new ByteChunk(), 500000, null, null, "PUT"); + new ByteChunk(), 500000, null, null, Method.PUT); // Make sure we get a 405 response which indicates that test.jsp is // complaining about being called with the PUT method. diff -Nru tomcat10-10.1.34/test/org/apache/juli/TestJsonFormatter.java tomcat10-10.1.52/test/org/apache/juli/TestJsonFormatter.java --- tomcat10-10.1.34/test/org/apache/juli/TestJsonFormatter.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/juli/TestJsonFormatter.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juli; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.logging.Formatter; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.tomcat.util.json.JSONParser; + +public class TestJsonFormatter { + + @Test + public void testFormat() throws Exception { + + Formatter formatter = new JsonFormatter(); + LogRecord logRecord = new LogRecord(Level.FINE, "Test log message"); + logRecord.setSourceClassName("org.apache.juli.TestJsonFormatter"); + logRecord.setSourceMethodName("testFormat"); + try { + throw new IllegalStateException("Bad state"); + } catch (IllegalStateException e) { + logRecord.setThrown(e); + } + + String result = formatter.format(logRecord); + + // Verify JSON content + Assert.assertTrue(result.startsWith("{")); + JSONParser parser = new JSONParser(new StringReader(result)); + LinkedHashMap json = parser.object(); + Assert.assertEquals(json.get("method"), "testFormat"); + @SuppressWarnings("unchecked") + ArrayList trace = (ArrayList) json.get("throwable"); + Assert.assertEquals(trace.get(0), "java.lang.IllegalStateException: Bad state"); + + } + +} diff -Nru tomcat10-10.1.34/test/org/apache/juli/TestLogUtil.java tomcat10-10.1.52/test/org/apache/juli/TestLogUtil.java --- tomcat10-10.1.34/test/org/apache/juli/TestLogUtil.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/juli/TestLogUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * 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.juli; + +import org.junit.Assert; +import org.junit.Test; + +public class TestLogUtil { + + @Test + public void testEscapeForLoggingEmptyString() { + doTestEscapeForLogging(""); + } + + + @Test + public void testEscapeForLoggingNone() { + doTestEscapeForLogging("No escaping"); + } + + + @Test + public void testEscapeForLoggingControlStart() { + doTestEscapeForLogging("\u0006Text", "\\u0006Text"); + } + + + @Test + public void testEscapeForLoggingControlMiddle() { + doTestEscapeForLogging("Text\u0006Text", "Text\\u0006Text"); + } + + + @Test + public void testEscapeForLoggingControlEnd() { + doTestEscapeForLogging("Text\u0006", "Text\\u0006"); + } + + + @Test + public void testEscapeForLoggingControlOnly() { + doTestEscapeForLogging("\u0006", "\\u0006"); + } + + + @Test + public void testEscapeForLoggingControlsStart() { + doTestEscapeForLogging("\u0006\u0007Text", "\\u0006\\u0007Text"); + } + + + @Test + public void testEscapeForLoggingControlsMiddle() { + doTestEscapeForLogging("Text\u0006\u0007Text", "Text\\u0006\\u0007Text"); + } + + + @Test + public void testEscapeForLoggingControlsEnd() { + doTestEscapeForLogging("Text\u0006\u0007", "Text\\u0006\\u0007"); + } + + + @Test + public void testEscapeForLoggingControlsOnly() { + doTestEscapeForLogging("\u0006\u0007", "\\u0006\\u0007"); + } + + + private void doTestEscapeForLogging(String input) { + doTestEscapeForLogging(input, input); + } + + + private void doTestEscapeForLogging(String input, String expected) { + String result = LogUtil.escape(input); + Assert.assertEquals(expected, result); + } +} \ No newline at end of file diff -Nru tomcat10-10.1.34/test/org/apache/juli/TestPerWebappJuliIntegration.java tomcat10-10.1.52/test/org/apache/juli/TestPerWebappJuliIntegration.java --- tomcat10-10.1.34/test/org/apache/juli/TestPerWebappJuliIntegration.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/juli/TestPerWebappJuliIntegration.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,164 @@ +/* + * 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.juli; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; + +public class TestPerWebappJuliIntegration extends TomcatBaseTest { + @Before + public void assumeJuliIsUsed() { + Assume.assumeTrue(LogManager.getLogManager().getClass().getName().equals(ClassLoaderLogManager.class.getName())); + } + private static final String APP_ID_A = "A"; + private static final String APP_ID_B = "B"; + private static final String HANDLER_ISOLATION_LOGGER = "handlerIsolationLogger"; + @Test + public void testPerWebappRootLogLevelIsolation() throws Exception { + Tomcat tomcat = getTomcatInstance(); + constructAppForLogLevelIsolationTest(tomcat, APP_ID_A, Level.FINE); + constructAppForLogLevelIsolationTest(tomcat, APP_ID_B, Level.INFO); + tomcat.start(); + ByteChunk res = getUrl("http://localhost:" + getPort() + "/juli" + APP_ID_A + "/log_level"); + ByteChunk res2 = getUrl("http://localhost:" + getPort() + "/juli" + APP_ID_B + "/log_level"); + Assert.assertEquals(Level.FINE.toString(), res.toString().trim()); + Assert.assertEquals(Level.INFO.toString(), res2.toString().trim()); + tomcat.stop(); + } + @Test + public void testPerWebappHandlersIsolation() throws Exception { + Tomcat tomcat = getTomcatInstance(); + ConstructAppResult resultA = constructAppForHandlerIsolationTest(tomcat, APP_ID_A, Level.INFO); + ConstructAppResult resultB = constructAppForHandlerIsolationTest(tomcat, APP_ID_B, Level.WARNING); + try(LogCapture logCaptureA = TomcatBaseTest.attachWebappLogCapture(resultA.context, null, HANDLER_ISOLATION_LOGGER); + LogCapture logCaptureB = TomcatBaseTest.attachWebappLogCapture(resultB.context, null, HANDLER_ISOLATION_LOGGER)) { + tomcat.start(); + Assert.assertEquals(200, getUrl("http://localhost:" + getPort() + "/juli" + APP_ID_A + "/test", new ByteChunk(), null)); + Assert.assertEquals(200, getUrl("http://localhost:" + getPort() + "/juli" + APP_ID_B + "/test", new ByteChunk(), null)); + Assert.assertTrue(logCaptureA.containsText("JULI-" + APP_ID_A + "-INFO")); + Assert.assertTrue(logCaptureB.containsText("JULI-" + APP_ID_B + "-INFO")); + + File logFileA = findLogFile(resultA.logsDir, "juli" + APP_ID_A + "."); + File logFileB = findLogFile(resultB.logsDir, "juli" + APP_ID_B + "."); + + Assert.assertNotNull(logFileA); + Assert.assertTrue("App " + APP_ID_A + " log file should contain the INFO message", Files.readString(logFileA.toPath()).contains("JULI-" + APP_ID_A + "-INFO")); + Assert.assertNull("App " + APP_ID_B + " log file should not exist", logFileB); + + tomcat.stop(); + } + } + + private void constructAppForLogLevelIsolationTest(Tomcat tomcat, String appId, Level logLevel) throws FileNotFoundException { + File appDir = new File(getTemporaryDirectory(), "juli" + appId); + addDeleteOnTearDown(appDir); + Assert.assertTrue(appDir.mkdirs() && appDir.isDirectory()); + File webInfClassesDir = new File(appDir, "WEB-INF/classes"); + Assert.assertTrue(webInfClassesDir.mkdirs() && webInfClassesDir.isDirectory()); + + File loggingPropertiesFile = new File(webInfClassesDir, "logging.properties"); + try (PrintWriter writer = new PrintWriter(loggingPropertiesFile)) { + writer.write(".level = " + logLevel); + } + + Context context = tomcat.addContext("/juli" + appId, appDir.getAbsolutePath()); + Tomcat.addServlet(context, "log_level", new HttpServlet() { + private static final long serialVersionUID = 1L; + + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + resp.getWriter().print(Logger.getLogger("").getLevel()); + } + }); + context.addServletMappingDecoded("/log_level", "log_level"); + } + private ConstructAppResult constructAppForHandlerIsolationTest(Tomcat tomcat, String appId, Level logLevel) throws FileNotFoundException { + File appDir = new File(getTemporaryDirectory(), "juliHandler" + appId); + addDeleteOnTearDown(appDir); + Assert.assertTrue(appDir.mkdirs() && appDir.isDirectory()); + File webInfClassesDir = new File(appDir, "WEB-INF/classes"); + Assert.assertTrue(webInfClassesDir.mkdirs() && webInfClassesDir.isDirectory()); + File logsDir = new File(appDir, "logs"); + Assert.assertTrue(logsDir.mkdirs() && logsDir.isDirectory()); + + File loggingProperties = new File(webInfClassesDir, "logging.properties"); + try (PrintWriter writer = new PrintWriter(loggingProperties)) { + writer.write("handlers = org.apache.juli.FileHandler\r\n" + + "org.apache.juli.FileHandler.level = " + logLevel + "\r\n" + + "org.apache.juli.FileHandler.directory = " + logsDir.getAbsolutePath().replace("\\", "\\\\") + "\r\n" + + "org.apache.juli.FileHandler.prefix = juli" + appId + ".\r\n" + ); + } + + Context context = tomcat.addContext("/juli" + appId, appDir.getAbsolutePath()); + Tomcat.addServlet(context, "test", new HttpServlet() { + private static final long serialVersionUID = 1L; + + @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) { + Logger root = Logger.getLogger(HANDLER_ISOLATION_LOGGER); + root.info("JULI-" + appId + "-INFO"); + } + }); + context.addServletMappingDecoded("/test", "test"); + return new ConstructAppResult(logsDir, context); + } + private static class ConstructAppResult { + private final File logsDir; + private final Context context; + ConstructAppResult(File logsDir, Context context) { + this.logsDir = logsDir; + this.context = context; + } + } + private static File findLogFile(File dir, String prefix) throws Exception { + List files; + int deadlineCounter = 0; + do { + try (Stream paths = Files.walk(dir.toPath())) { + files = paths.map(Path::toFile).filter(f -> f.isFile() && f.getName().startsWith(prefix)).collect(Collectors.toList()); + } + if (deadlineCounter > 0) { + Thread.sleep(100); + } + } while (++deadlineCounter < 3); + return files.isEmpty() ? null : files.get(0); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/naming/TestNamingContext.java tomcat10-10.1.52/test/org/apache/naming/TestNamingContext.java --- tomcat10-10.1.34/test/org/apache/naming/TestNamingContext.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/naming/TestNamingContext.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,6 +22,8 @@ import org.junit.Assert; import org.junit.Test; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.core.NamingContextListener; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.naming.factory.ResourceLinkFactory; @@ -31,6 +33,7 @@ public class TestNamingContext extends TomcatBaseTest { private static final String COMP_ENV = "comp/env"; + private static final String MODULE_ENV = "module/env"; private static final String GLOBAL_NAME = "global"; private static final String LOCAL_NAME = "local"; private static final String DATA = "Cabbage"; @@ -92,6 +95,59 @@ } + @Test + public void testModuleEquivalentToComp() throws Exception { + Tomcat tomcat = getTomcatInstance(); + tomcat.enableNaming(); + + org.apache.catalina.Context ctx = getProgrammaticRootContext(); + + tomcat.start(); + + // Equivalent to: Context initContext = new InitialContext(); + Context webappInitial = ContextBindings.getContext(ctx); + + // Make it writable (it is normally read-only) + String namingContextName = null; + LifecycleListener[] listeners = ctx.findLifecycleListeners(); + for (LifecycleListener listener : listeners) { + if (listener instanceof NamingContextListener) { + NamingContextListener namingListener = (NamingContextListener) listener; + namingContextName = namingListener.getName(); + break; + } + } + ContextAccessController.setWritable(namingContextName, ctx.getNamingToken()); + + // Nothing created so should be null + Object obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertNull(obj); + obj = doLookup(webappInitial, MODULE_ENV + "/" + LOCAL_NAME); + Assert.assertNull(obj); + + // Create in java:comp/env + webappInitial.bind(COMP_ENV + "/" + LOCAL_NAME, DATA); + + // Check it was created in java:comp/env and java:module/env + obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertEquals(DATA, obj); + obj = doLookup(webappInitial, MODULE_ENV + "/" + LOCAL_NAME); + Assert.assertEquals(DATA, obj); + + // Remove it + webappInitial.unbind(COMP_ENV + "/" + LOCAL_NAME); + + // Create in java:module/env + webappInitial.bind(MODULE_ENV + "/" + LOCAL_NAME, DATA); + + // Check it was created in java:comp/env and java:module/env + obj = doLookup(webappInitial, COMP_ENV + "/" + LOCAL_NAME); + Assert.assertEquals(DATA, obj); + obj = doLookup(webappInitial, MODULE_ENV + "/" + LOCAL_NAME); + Assert.assertEquals(DATA, obj); + } + + private Object doLookup(Context context, String name) { Object result = null; try { diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/buildutil/translate/TestUtils.java tomcat10-10.1.52/test/org/apache/tomcat/buildutil/translate/TestUtils.java --- tomcat10-10.1.34/test/org/apache/tomcat/buildutil/translate/TestUtils.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/buildutil/translate/TestUtils.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,57 +22,37 @@ public class TestUtils { @Test - public void testQuoteReplacement01() { - Assert.assertEquals("[{0}] a''a", Utils.formatValueImport("[{0}] a'a")); - } - - @Test - public void testQuoteReplacement02() { - Assert.assertEquals("[{0}] a''", Utils.formatValueImport("[{0}] a'")); - } - - - @Test - public void testQuoteReplacement03() { - Assert.assertEquals("''a [{0}]", Utils.formatValueImport("'a [{0}]")); - } - - @Test - public void testQuoteReplacement05() { - Assert.assertEquals("[{0}] ''a'' bbb", Utils.formatValueImport("[{0}] 'a' bbb")); - } - - @Test - public void testQuoteReplacement06() { - Assert.assertEquals("[{0}] ''aa'' bbb", Utils.formatValueImport("[{0}] 'aa' bbb")); - } - - @Test public void testFormatValue01() { - // Import from Tomcat - Assert.assertEquals("\\n\\\n\\n", Utils.formatValueImport("\\n\\\n\\n")); + // Import from POE + Assert.assertEquals("line1\\n\\\nline2\\n\\\nline3", Utils.formatValueImport("line1\nline2\nline3")); } @Test public void testFormatValue02() { - // Import from POEditor - Assert.assertEquals("\\n\\\n\\n", Utils.formatValueImport("\\n\\n")); + // Import from POE + Assert.assertEquals("\\n\\\nline2\\n\\\nline3", Utils.formatValueImport(Utils.PADDING + "\nline2\nline3")); } @Test public void testFormatValue03() { + // Import from POE + Assert.assertEquals("line1\\n\\\n\\tline2\\n\\\n\\tline3", Utils.formatValueImport("line1\n\tline2\n\tline3")); + } + + @Test + public void testFormatValue04() { // Export from Tomcat Assert.assertEquals("line1\\n\\\nline2\\n\\\nline3", Utils.formatValueExport("line1\nline2\nline3")); } @Test - public void testFormatValue04() { + public void testFormatValue05() { // Export from Tomcat Assert.assertEquals(Utils.PADDING + "\\n\\\nline2\\n\\\nline3", Utils.formatValueExport("\nline2\nline3")); } @Test - public void testFormatValue05() { + public void testFormatValue06() { // Export from Tomcat Assert.assertEquals("line1\\n\\\n\\tline2\\n\\\n\\tline3", Utils.formatValueExport("line1\n\tline2\n\tline3")); } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/security/TestSecurity2017Ocsp.java tomcat10-10.1.52/test/org/apache/tomcat/security/TestSecurity2017Ocsp.java --- tomcat10-10.1.34/test/org/apache/tomcat/security/TestSecurity2017Ocsp.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/security/TestSecurity2017Ocsp.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,114 @@ +/* + * 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.security; + +import java.io.IOException; +import java.net.SocketException; + +import javax.net.ssl.SSLHandshakeException; + +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.net.SSLHostConfig; +import org.apache.tomcat.util.net.TesterSupport; +import org.apache.tomcat.util.net.TesterSupport.SimpleServlet; +import org.apache.tomcat.util.net.ocsp.OcspBaseTest; +import org.apache.tomcat.util.net.ocsp.TesterOcspResponder; +import org.apache.tomcat.util.net.openssl.OpenSSLStatus; + +@RunWith(Parameterized.class) +public class TestSecurity2017Ocsp extends OcspBaseTest { + + private static TesterOcspResponder ocspResponder; + + @BeforeClass + public static void startOcspResponder() { + ocspResponder = new TesterOcspResponder(); + try { + ocspResponder.start(); + } catch (IOException ioe) { + ocspResponder = null; + } + } + + + @AfterClass + public static void stopOcspResponder() { + if (ocspResponder != null) { + ocspResponder.stop(); + ocspResponder = null; + } + } + + + /* + * In addition to testing Tomcat Native (where the CVE occurred), this also tests JSSE and OpenSSl via FFM. + */ + @Test(expected=SSLHandshakeException.class) + public void testCVE_2017_15698() throws Exception { + if ("OpenSSL-FFM".equals(connectorName)) { + Assume.assumeFalse(OpenSSLStatus.isBoringSSL() || OpenSSLStatus.isLibreSSLPre35()); + } + Assume.assumeNotNull(ocspResponder); + + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = tomcat.addContext("", null); + + Tomcat.addServlet(ctx, "simple", new SimpleServlet()); + ctx.addServletMappingDecoded("/simple", "simple"); + + // Use the default (valid, non-revoked) server certificate + TesterSupport.initSsl(tomcat, useOpenSSLTrust); + + // Require client certificates and enable verification + SSLHostConfig sslHostConfig = tomcat.getConnector().findSslHostConfigs()[0]; + sslHostConfig.setOcspEnabled(true); + sslHostConfig.setCertificateVerification("required"); + + // Configure a revoked client certificate with a long AIA + // Don't verify the server certificate + TesterSupport.configureClientSsl(false, TesterSupport.CLIENT_CRL_LONG_JKS); + + // Disable soft-fail + sslHostConfig.setOcspSoftFail(false); + + tomcat.start(); + + int rc; + try { + rc = getUrl("https://localhost:" + getPort() + "/simple", new ByteChunk(), false); + } catch (SocketException se) { + throw new SSLHandshakeException(se.getMessage()); + } + + // If the TLS handshake fails, the test won't get this far. + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/security/TestSecurity2023.java tomcat10-10.1.52/test/org/apache/tomcat/security/TestSecurity2023.java --- tomcat10-10.1.34/test/org/apache/tomcat/security/TestSecurity2023.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/security/TestSecurity2023.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,251 @@ +/* + * 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.security; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jakarta.servlet.MultipartConfigElement; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; + +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; +import org.apache.catalina.Context; +import org.apache.catalina.Wrapper; +import org.apache.catalina.authenticator.FormAuthenticator; +import org.apache.catalina.filters.FailedRequestFilter; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; +import org.apache.tomcat.util.descriptor.web.LoginConfig; +import org.apache.tomcat.util.descriptor.web.SecurityCollection; +import org.apache.tomcat.util.descriptor.web.SecurityConstraint; +import org.apache.tomcat.util.http.Method; + +public class TestSecurity2023 extends TomcatBaseTest { + /* + * https://www.cve.org/CVERecord?id=CVE-2023-24998 + * + * Fixed in + * 11.0.0-M3 https://github.com/apache/tomcat/commit/063e2e81ede50c287f737cc8e2915ce7217e886e + * 10.1.5 https://github.com/apache/tomcat/commit/8a2285f13affa961cc65595aad999db5efae45ce + * 9.0.71 https://github.com/apache/tomcat/commit/cf77cc545de0488fb89e24294151504a7432df74 + * + * https://www.cve.org/CVERecord?id=CVE-2023-28709 + * + * Fixed in + * 11.0.0-M5 https://github.com/apache/tomcat/commit/d53d8e7f77042cc32a3b98f589496a1ef5088e38 + * 10.1.8 https://github.com/apache/tomcat/commit/ba848da71c523d94950d3c53c19ea155189df9dc + * 9.0.74 https://github.com/apache/tomcat/commit/fbd81421629afe8b8a3922d59020cde81caea861 + */ + @Test + public void testCVE_2023_24998_CVE_2023_28709() throws Exception { + Tomcat tomcat = getTomcatInstance(); + tomcat.getConnector().setMaxParameterCount(2); + Context context = tomcat.addContext("", null); + Wrapper wrapper = Tomcat.addServlet(context, "test", new TestServlet()); + wrapper.setMultipartConfigElement(new MultipartConfigElement("")); + context.addServletMappingDecoded("/", "test"); + + FilterDef filterDef = new FilterDef(); + filterDef.setFilterName("FailedRequestFilter"); + filterDef.setFilterClass(FailedRequestFilter.class.getName()); + context.addFilterDef(filterDef); + FilterMap filterMap = new FilterMap(); + filterMap.setFilterName(filterDef.getFilterName()); + filterMap.addURLPatternDecoded("*"); + context.addFilterMap(filterMap); + + tomcat.start(); + + final String path = "http://localhost:" + getPort() + "/"; + int status = postMultipart(path, null, 1); + Assert.assertEquals(HttpServletResponse.SC_OK, status); + + status = postMultipart(path, null, 3); + Assert.assertEquals(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, status); + + status = postMultipart(path,"x=1", 1); + Assert.assertEquals(HttpServletResponse.SC_OK, status); + + status = postMultipart(path, "x=1&y=2", 1); + Assert.assertEquals(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, status); + } + + /* + * https://www.cve.org/CVERecord?id=CVE-2023-41080 + * + * Fixed in + * 11.0.0-M11 https://github.com/apache/tomcat/commit/e3703c9abb8fe0d5602f6ba8a8f11d4b6940815a + * 10.1.13 https://github.com/apache/tomcat/commit/bb4624a9f3e69d495182ebfa68d7983076407a27 + * 9.0.80 https://github.com/apache/tomcat/commit/77c0ce2d169efa248b64b992e547aad549ec906b + */ + @Test + public void testCVE_2023_41080() throws Exception { + Tomcat tomcat = getTomcatInstance(); + Context context = tomcat.addContext("", null); + + LoginConfig loginConfig = new LoginConfig(); + loginConfig.setAuthMethod("FORM"); + loginConfig.setLoginPage("/login"); + context.setLoginConfig(loginConfig); + context.getPipeline().addValve(new FormAuthenticator()); + + SecurityConstraint securityConstraint = new SecurityConstraint(); + securityConstraint.addAuthRole("admin"); + SecurityCollection securityCollection = new SecurityCollection(); + securityCollection.addPattern("/secret.html"); + securityCollection.addPattern("/example.com"); + securityConstraint.addCollection(securityCollection); + context.addConstraint(securityConstraint); + + tomcat.addUser("admin", "admin"); + tomcat.addRole("admin", "admin"); + + Tomcat.addServlet(context, "login", new TestServlet()); + context.addServletMappingDecoded("/login", "login"); + Tomcat.addServlet(context, "secret", new TestServlet()); + context.addServletMappingDecoded("/secret.html", "secret"); + Tomcat.addServlet(context, "example", new TestServlet()); + context.addServletMappingDecoded("/example.com", "example"); + + tomcat.start(); + + String location = doFormLoginAndGetRedirectLocation("http://localhost:" + getPort(), "/secret.html;@example.com"); + Assert.assertNotNull(location); + Assert.assertFalse(location.startsWith("//")); + URI locationUri = new URI(location); + Assert.assertNull(locationUri.getHost()); + Assert.assertTrue(locationUri.getPath().contains("secret.html")); + + location = doFormLoginAndGetRedirectLocation("http://localhost:" + getPort(), "//example.com"); + Assert.assertNotNull(location); + Assert.assertFalse(location.startsWith("//")); + } + + private static String doFormLoginAndGetRedirectLocation(String basePath, String targetPath) throws Exception { + String url = basePath + targetPath; + Map> resHead = new HashMap<>(); + int rc = getUrl(url, new ByteChunk(), resHead); + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + + String sessionCookie = null; + List cookies = resHead.get("Set-Cookie"); + if (cookies != null) { + for (String cookie : cookies) { + if (cookie.startsWith("JSESSIONID=")) { + sessionCookie = cookie.split(";")[0]; + break; + } + } + } + Assert.assertNotNull("JSESSIONID not found in response", sessionCookie); + String loginUrl = basePath + "/j_security_check"; + HttpURLConnection conn = (HttpURLConnection) new URI(loginUrl).toURL().openConnection(); + conn.setRequestMethod(Method.POST); + conn.setDoOutput(true); + conn.setInstanceFollowRedirects(false); // want to check the redirect location manually + conn.setRequestProperty("Cookie", sessionCookie); + conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + String params = "j_username=admin&j_password=admin"; + try (OutputStream os = conn.getOutputStream()) { + os.write(params.getBytes(StandardCharsets.UTF_8)); + } + int loginRc = conn.getResponseCode(); + Assert.assertEquals(HttpServletResponse.SC_SEE_OTHER, loginRc); + return conn.getHeaderField("Location"); + } + + private static int postMultipart(String path, String queryStringParams, int parts) throws IOException, URISyntaxException { + String urlStr = path + (queryStringParams == null || queryStringParams.isEmpty() ? "" : "?" + queryStringParams); + String boundary = "--simpleboundary"; + + byte[] body = buildMultipartBody(boundary, parts); + HttpURLConnection conn = (HttpURLConnection) new URI(urlStr).toURL().openConnection(); + conn.setDoOutput(true); + conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); + conn.setRequestProperty("Content-Length", String.valueOf(body.length)); + conn.setReadTimeout(1000000); + + try (OutputStream os = conn.getOutputStream()) { + os.write(body); + } + + int rc = conn.getResponseCode(); + InputStream inputStream = null; + if (rc == HttpServletResponse.SC_OK) { + inputStream = conn.getInputStream(); + } else if (rc == HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE) { + inputStream = conn.getErrorStream(); + } + if (inputStream != null) { + //noinspection StatementWithEmptyBody + while (inputStream.read() != -1) {} + inputStream.close(); + } + + return rc; + } + + private static byte[] buildMultipartBody(String boundary, int parts) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < parts; i++) { + sb.append("--"); + sb.append(boundary); + sb.append(CRLF); + sb.append("Content-Disposition: form-data; name=\"part\""); + sb.append(CRLF); + sb.append(CRLF); + sb.append("bodyOfPart"); + sb.append(CRLF); + } + sb.append("--"); + sb.append(boundary); + sb.append("--"); + sb.append(CRLF); + return sb.toString().getBytes(); + } + + public static class TestServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { + req.getParameterMap(); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/security/TestSecurity2025Http2.java tomcat10-10.1.52/test/org/apache/tomcat/security/TestSecurity2025Http2.java --- tomcat10-10.1.34/test/org/apache/tomcat/security/TestSecurity2025Http2.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/security/TestSecurity2025Http2.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.security; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.coyote.http2.Http2TestBase; + +public class TestSecurity2025Http2 extends Http2TestBase { + + /* + * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2025-53506 + * + * Fixed in + * 11.0.9 https://github.com/apache/tomcat/commit/be8f330f83ceddaf3baeed57522e571572b6b99b + * 10.1.43 https://github.com/apache/tomcat/commit/2aa6261276ebe50b99276953591e3a2be7898bdb + * 9.0.107 https://github.com/apache/tomcat/commit/434772930f362145516dd60681134e7f0cf8115b + */ + @Test + public void testCVE_2025_53506() throws Exception { + enableHttp2(100); + configureAndStartWebApplication(); + openClientConnection(false, false); + doHttpUpgrade(); + sendClientPreface(); + validateHttp2InitialResponse(100); + + int streamId = 3; + Throwable t = null; + try { + /* + * Note: The client will create streams and send requests faster than Tomcat can process them so the + * concurrent stream count will be well above 100 by the time the client sees the exception. However, + * Tomcat will only have processed the first 100. + */ + while (true) { + sendSimpleGetRequest(streamId); + streamId += 2; + } + } catch (IOException ioe) { + t = ioe; + } + Assert.assertNotNull(t); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/buf/TestCharsetUtil.java tomcat10-10.1.52/test/org/apache/tomcat/util/buf/TestCharsetUtil.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/buf/TestCharsetUtil.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/buf/TestCharsetUtil.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,7 +51,7 @@ * Only need to run this when we detect a new Charset. */ //@Test - public void testIsAcsiiSupersetAll() { + public void testIsAsciiSupersetAll() { for (Charset charset : Charset.availableCharsets().values()) { System.out.println("Testing: " + charset.name()); diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/buf/TestUDecoder.java tomcat10-10.1.52/test/org/apache/tomcat/util/buf/TestUDecoder.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/buf/TestUDecoder.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/buf/TestUDecoder.java 2026-01-23 19:33:36.000000000 +0000 @@ -276,7 +276,194 @@ bc.setCharset(StandardCharsets.UTF_8); UDecoder udecoder = new UDecoder(); - udecoder.convert(bc, solidusHandling); + udecoder.convert(bc, solidusHandling, EncodedSolidusHandling.DECODE); + + return bc.toString(); + } + + + @Test + public void testURLDecodeStringReverseSolidus01() throws IOException { + doTestReverseSolidus("xxxxxx", "xxxxxx"); + } + + + @Test + public void testURLDecodeStringReverseSolidus02() throws IOException { + doTestReverseSolidus("%20xxxx", " xxxx"); + } + + + @Test + public void testURLDecodeStringReverseSolidus03() throws IOException { + doTestReverseSolidus("xx%20xx", "xx xx"); + } + + + @Test + public void testURLDecodeStringReverseSolidus04() throws IOException { + doTestReverseSolidus("xxxx%20", "xxxx "); + } + + + @Test(expected = CharConversionException.class) + public void testURLDecodeStringReverseSolidus05a() throws IOException { + doTestReverseSolidus("%5cxxxx", EncodedSolidusHandling.REJECT); + } + + + @Test + public void testURLDecodeStringReverseSolidus05b() throws IOException { + String result = doTestReverseSolidus("%5cxxxx", EncodedSolidusHandling.PASS_THROUGH); + Assert.assertEquals("%5cxxxx", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus05c() throws IOException { + String result = doTestReverseSolidus("%5cxxxx", EncodedSolidusHandling.DECODE); + Assert.assertEquals("\\xxxx", result); + } + + + @Test(expected = CharConversionException.class) + public void testURLDecodeStringReverseSolidus06a() throws IOException { + doTestReverseSolidus("%5cxx%20xx", EncodedSolidusHandling.REJECT); + } + + + @Test + public void testURLDecodeStringReverseSolidus06b() throws IOException { + String result = doTestReverseSolidus("%5cxx%20xx", EncodedSolidusHandling.PASS_THROUGH); + Assert.assertEquals("%5cxx xx", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus06c() throws IOException { + String result = doTestReverseSolidus("%5cxx%20xx", EncodedSolidusHandling.DECODE); + Assert.assertEquals("\\xx xx", result); + } + + + @Test(expected = CharConversionException.class) + public void testURLDecodeStringReverseSolidus07a() throws IOException { + doTestReverseSolidus("xx%5c%20xx", EncodedSolidusHandling.REJECT); + } + + + @Test + public void testURLDecodeStringReverseSolidus07b() throws IOException { + String result = doTestReverseSolidus("xx%5c%20xx", EncodedSolidusHandling.PASS_THROUGH); + Assert.assertEquals("xx%5c xx", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus07c() throws IOException { + String result = doTestReverseSolidus("xx%5c%20xx", EncodedSolidusHandling.DECODE); + Assert.assertEquals("xx\\ xx", result); + } + + + @Test(expected = CharConversionException.class) + public void testURLDecodeStringReverseSolidus08a() throws IOException { + doTestReverseSolidus("xx%20%5cxx", EncodedSolidusHandling.REJECT); + } + + + @Test + public void testURLDecodeStringReverseSolidus08b() throws IOException { + String result = doTestReverseSolidus("xx%20%5cxx", EncodedSolidusHandling.PASS_THROUGH); + Assert.assertEquals("xx %5cxx", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus08c() throws IOException { + String result = doTestReverseSolidus("xx%20%5cxx", EncodedSolidusHandling.DECODE); + Assert.assertEquals("xx \\xx", result); + } + + + @Test(expected = CharConversionException.class) + public void testURLDecodeStringReverseSolidus09a() throws IOException { + doTestReverseSolidus("xx%20xx%5c", EncodedSolidusHandling.REJECT); + } + + + @Test + public void testURLDecodeStringReverseSolidus09b() throws IOException { + String result = doTestReverseSolidus("xx%20xx%5c", EncodedSolidusHandling.PASS_THROUGH); + Assert.assertEquals("xx xx%5c", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus09c() throws IOException { + String result = doTestReverseSolidus("xx%20xx%5c", EncodedSolidusHandling.DECODE); + Assert.assertEquals("xx xx\\", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus10a() throws IOException { + String result = doTestReverseSolidus("xx%25xx", EncodedSolidusHandling.REJECT); + Assert.assertEquals("xx%xx", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus10b() throws IOException { + String result = doTestReverseSolidus("xx%25xx", EncodedSolidusHandling.PASS_THROUGH); + Assert.assertEquals("xx%25xx", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus10c() throws IOException { + String result = doTestReverseSolidus("xx%25xx", EncodedSolidusHandling.DECODE); + Assert.assertEquals("xx%xx", result); + } + + + @Test(expected = CharConversionException.class) + public void testURLDecodeStringReverseSolidus11a() throws IOException { + String result = doTestReverseSolidus("xx%5c%25xx", EncodedSolidusHandling.REJECT); + Assert.assertEquals("xx%xx", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus11b() throws IOException { + String result = doTestReverseSolidus("xx%5c%25xx", EncodedSolidusHandling.PASS_THROUGH); + Assert.assertEquals("xx%5c%25xx", result); + } + + + @Test + public void testURLDecodeStringReverseSolidus11c() throws IOException { + String result = doTestReverseSolidus("xx%5c%25xx", EncodedSolidusHandling.DECODE); + Assert.assertEquals("xx\\%xx", result); + } + + + private void doTestReverseSolidus(String input, String expected) throws IOException { + for (EncodedSolidusHandling solidusHandling : EncodedSolidusHandling.values()) { + String result = doTestReverseSolidus(input, solidusHandling); + Assert.assertEquals(expected, result); + } + } + + + private String doTestReverseSolidus(String input, EncodedSolidusHandling reverseSolidusHandling) throws IOException { + byte[] b = input.getBytes(StandardCharsets.UTF_8); + ByteChunk bc = new ByteChunk(16); + bc.setBytes(b, 0, b.length); + bc.setCharset(StandardCharsets.UTF_8); + + UDecoder udecoder = new UDecoder(); + udecoder.convert(bc, EncodedSolidusHandling.REJECT, reverseSolidusHandling); return bc.toString(); } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/concurrent/TestKeyedReentrantReadWriteLock.java tomcat10-10.1.52/test/org/apache/tomcat/util/concurrent/TestKeyedReentrantReadWriteLock.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/concurrent/TestKeyedReentrantReadWriteLock.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/concurrent/TestKeyedReentrantReadWriteLock.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,31 @@ +/* + * 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.concurrent; + +import java.util.concurrent.locks.ReadWriteLock; + +import org.junit.Test; + +public class TestKeyedReentrantReadWriteLock { + + @Test(expected = IllegalStateException.class) + public void testUnlockWithoutLock() { + KeyedReentrantReadWriteLock locks = new KeyedReentrantReadWriteLock(); + ReadWriteLock lock = locks.getLock("any"); + lock.readLock().unlock(); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/descriptor/web/TestSecurityConstraint.java tomcat10-10.1.52/test/org/apache/tomcat/util/descriptor/web/TestSecurityConstraint.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/descriptor/web/TestSecurityConstraint.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/descriptor/web/TestSecurityConstraint.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,6 +31,7 @@ import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.http.Method; public class TestSecurityConstraint { @@ -50,7 +51,7 @@ GET_ONLY = new SecurityConstraint(); GET_ONLY.addAuthRole(ROLE1); SecurityCollection scGetOnly = new SecurityCollection(); - scGetOnly.addMethod("GET"); + scGetOnly.addMethod(Method.GET); scGetOnly.addPatternDecoded(URL_PATTERN); scGetOnly.setName("GET-ONLY"); GET_ONLY.addCollection(scGetOnly); @@ -58,7 +59,7 @@ POST_ONLY = new SecurityConstraint(); POST_ONLY.addAuthRole(ROLE1); SecurityCollection scPostOnly = new SecurityCollection(); - scPostOnly.addMethod("POST"); + scPostOnly.addMethod(Method.POST); scPostOnly.addPatternDecoded(URL_PATTERN); scPostOnly.setName("POST_ONLY"); POST_ONLY.addCollection(scPostOnly); @@ -66,7 +67,7 @@ GET_OMIT = new SecurityConstraint(); GET_OMIT.addAuthRole(ROLE1); SecurityCollection scGetOmit = new SecurityCollection(); - scGetOmit.addOmittedMethod("GET"); + scGetOmit.addOmittedMethod(Method.GET); scGetOmit.addPatternDecoded(URL_PATTERN); scGetOmit.setName("GET_OMIT"); GET_OMIT.addCollection(scGetOmit); @@ -74,7 +75,7 @@ POST_OMIT = new SecurityConstraint(); POST_OMIT.addAuthRole(ROLE1); SecurityCollection scPostOmit = new SecurityCollection(); - scPostOmit.addOmittedMethod("POST"); + scPostOmit.addOmittedMethod(Method.POST); scPostOmit.addPatternDecoded(URL_PATTERN); scPostOmit.setName("POST_OMIT"); POST_OMIT.addCollection(scPostOmit); @@ -143,15 +144,15 @@ // Example 13-5 // @ServletSecurity((httpMethodConstraints = { - // @HttpMethodConstraint(value = "GET", rolesAllowed = "R1"), - // @HttpMethodConstraint(value = "POST", rolesAllowed = "R1", + // @HttpMethodConstraint(value = Method.GET, rolesAllowed = "R1"), + // @HttpMethodConstraint(value = Method.POST, rolesAllowed = "R1", // transportGuarantee = TransportGuarantee.CONFIDENTIAL) // }) hmces.clear(); - hmces.add(new HttpMethodConstraintElement("GET", + hmces.add(new HttpMethodConstraintElement(Method.GET, new HttpConstraintElement( ServletSecurity.TransportGuarantee.NONE, ROLE1))); - hmces.add(new HttpMethodConstraintElement("POST", + hmces.add(new HttpMethodConstraintElement(Method.POST, new HttpConstraintElement( ServletSecurity.TransportGuarantee.CONFIDENTIAL, ROLE1))); @@ -166,10 +167,10 @@ Assert.assertTrue(result[i].findCollections()[0].findPattern(URL_PATTERN)); Assert.assertEquals(1, result[i].findCollections()[0].findMethods().length); String method = result[i].findCollections()[0].findMethods()[0]; - if ("GET".equals(method)) { + if (Method.GET.equals(method)) { Assert.assertEquals(ServletSecurity.TransportGuarantee.NONE.name(), result[i].getUserConstraint()); - } else if ("POST".equals(method)) { + } else if (Method.POST.equals(method)) { Assert.assertEquals(ServletSecurity.TransportGuarantee.CONFIDENTIAL.name(), result[i].getUserConstraint()); } else { @@ -179,9 +180,9 @@ // Example 13-6 // @ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"), - // httpMethodConstraints = @HttpMethodConstraint("GET")) + // httpMethodConstraints = @HttpMethodConstraint(Method.GET)) hmces.clear(); - hmces.add(new HttpMethodConstraintElement("GET")); + hmces.add(new HttpMethodConstraintElement(Method.GET)); element = new ServletSecurityElement( new HttpConstraintElement( ServletSecurity.TransportGuarantee.NONE, @@ -193,11 +194,11 @@ for (int i = 0; i < 2; i++) { Assert.assertTrue(result[i].findCollections()[0].findPattern(URL_PATTERN)); if (result[i].findCollections()[0].findMethods().length == 1) { - Assert.assertEquals("GET", + Assert.assertEquals(Method.GET, result[i].findCollections()[0].findMethods()[0]); Assert.assertFalse(result[i].getAuthConstraint()); } else if (result[i].findCollections()[0].findOmittedMethods().length == 1) { - Assert.assertEquals("GET", + Assert.assertEquals(Method.GET, result[i].findCollections()[0].findOmittedMethods()[0]); Assert.assertTrue(result[i].getAuthConstraint()); Assert.assertEquals(1, result[i].findAuthRoles().length); @@ -211,10 +212,10 @@ // Example 13-7 // @ServletSecurity(value = @HttpConstraint(rolesAllowed = "R1"), - // httpMethodConstraints = @HttpMethodConstraint(value="TRACE", + // httpMethodConstraints = @HttpMethodConstraint(value=Method.TRACE, // emptyRoleSemantic = EmptyRoleSemantic.DENY)) hmces.clear(); - hmces.add(new HttpMethodConstraintElement("TRACE", + hmces.add(new HttpMethodConstraintElement(Method.TRACE, new HttpConstraintElement(EmptyRoleSemantic.DENY))); element = new ServletSecurityElement( new HttpConstraintElement( @@ -227,12 +228,12 @@ for (int i = 0; i < 2; i++) { Assert.assertTrue(result[i].findCollections()[0].findPattern(URL_PATTERN)); if (result[i].findCollections()[0].findMethods().length == 1) { - Assert.assertEquals("TRACE", + Assert.assertEquals(Method.TRACE, result[i].findCollections()[0].findMethods()[0]); Assert.assertTrue(result[i].getAuthConstraint()); Assert.assertEquals(0, result[i].findAuthRoles().length); } else if (result[i].findCollections()[0].findOmittedMethods().length == 1) { - Assert.assertEquals("TRACE", + Assert.assertEquals(Method.TRACE, result[i].findCollections()[0].findOmittedMethods()[0]); Assert.assertTrue(result[i].getAuthConstraint()); Assert.assertEquals(1, result[i].findAuthRoles().length); @@ -303,7 +304,7 @@ // Should list GET as an omitted method Assert.assertEquals(0, sc.findMethods().length); Assert.assertEquals(1, sc.findOmittedMethods().length); - Assert.assertEquals("GET", sc.findOmittedMethods()[0]); + Assert.assertEquals(Method.GET, sc.findOmittedMethods()[0]); } @@ -321,7 +322,7 @@ // Should list POST as an omitted method Assert.assertEquals(0, sc.findMethods().length); Assert.assertEquals(1, sc.findOmittedMethods().length); - Assert.assertEquals("POST", sc.findOmittedMethods()[0]); + Assert.assertEquals(Method.POST, sc.findOmittedMethods()[0]); } @@ -339,7 +340,7 @@ // Should list GET as an method Assert.assertEquals(0, sc.findOmittedMethods().length); Assert.assertEquals(1, sc.findMethods().length); - Assert.assertEquals("GET", sc.findMethods()[0]); + Assert.assertEquals(Method.GET, sc.findMethods()[0]); } @@ -357,7 +358,7 @@ // Should list POST as an method Assert.assertEquals(0, sc.findOmittedMethods().length); Assert.assertEquals(1, sc.findMethods().length); - Assert.assertEquals("POST", sc.findMethods()[0]); + Assert.assertEquals(Method.POST, sc.findMethods()[0]); } @@ -398,8 +399,8 @@ Assert.assertEquals(2, sc.findOmittedMethods().length); HashSet omittedMethods = new HashSet<>(); omittedMethods.addAll(Arrays.asList(sc.findOmittedMethods())); - Assert.assertTrue(omittedMethods.remove("GET")); - Assert.assertTrue(omittedMethods.remove("POST")); + Assert.assertTrue(omittedMethods.remove(Method.GET)); + Assert.assertTrue(omittedMethods.remove(Method.POST)); } @@ -428,7 +429,7 @@ // Should list POST as a method Assert.assertEquals(1, sc.findMethods().length); Assert.assertEquals(0, sc.findOmittedMethods().length); - Assert.assertEquals("POST", sc.findMethods()[0]); + Assert.assertEquals(Method.POST, sc.findMethods()[0]); } @@ -447,6 +448,6 @@ // Should list GET as a method Assert.assertEquals(1, sc.findMethods().length); Assert.assertEquals(0, sc.findOmittedMethods().length); - Assert.assertEquals("GET", sc.findMethods()[0]); + Assert.assertEquals(Method.GET, sc.findMethods()[0]); } } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/descriptor/web/TestWebXml.java tomcat10-10.1.52/test/org/apache/tomcat/util/descriptor/web/TestWebXml.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/descriptor/web/TestWebXml.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/descriptor/web/TestWebXml.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,6 +32,7 @@ import org.apache.tomcat.util.descriptor.XmlErrorHandler; import org.apache.tomcat.util.descriptor.XmlIdentifiers; import org.apache.tomcat.util.digester.Digester; +import org.apache.tomcat.util.http.Method; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -304,7 +305,7 @@ SecurityCollection collection = new SecurityCollection(); collection.setName("dummy"); collection.addPatternDecoded("/*"); - collection.addMethod("DELETE"); + collection.addMethod(Method.DELETE); sc.addCollection(collection); webXmlDefaultFragment.addSecurityConstraint(sc); diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java 2026-01-23 19:33:36.000000000 +0000 @@ -21,6 +21,8 @@ import org.junit.Assert; import org.junit.Test; +import org.apache.tomcat.util.descriptor.web.Constants; + public class TestCookieProcessorGeneration { @Test @@ -244,6 +246,19 @@ rfc6265.setPartitioned(true); Assert.assertEquals("foo=bar; Secure; HttpOnly; Partitioned", rfc6265.generateHeader(cookie, null)); + + rfc6265.setPartitioned(false); + cookie.setAttribute(Constants.COOKIE_PARTITIONED_ATTR, "true"); + + Assert.assertEquals("foo=bar; Secure; HttpOnly; Partitioned", rfc6265.generateHeader(cookie, null)); + + cookie.setAttribute(Constants.COOKIE_PARTITIONED_ATTR, "false"); + + Assert.assertEquals("foo=bar; Secure; HttpOnly", rfc6265.generateHeader(cookie, null)); + + cookie.setAttribute(Constants.COOKIE_PARTITIONED_ATTR, ""); + + Assert.assertEquals("foo=bar; Secure; HttpOnly", rfc6265.generateHeader(cookie, null)); } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestCookieProcessorGenerationHttp.java tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestCookieProcessorGenerationHttp.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestCookieProcessorGenerationHttp.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestCookieProcessorGenerationHttp.java 2026-01-23 19:33:36.000000000 +0000 @@ -44,7 +44,9 @@ // No file system docBase required Context ctx = getProgrammaticRootContext(); ctx.setCookieProcessor(new Rfc6265CookieProcessor()); - Tomcat.addServlet(ctx, "test", new CookieServlet("\u0120")); + Map cookies = new HashMap<>(); + cookies.put("Test", "\u0120"); + Tomcat.addServlet(ctx, "test", new CookieServlet(cookies)); ctx.addServletMappingDecoded("/test", "test"); tomcat.start(); @@ -68,19 +70,50 @@ private static final long serialVersionUID = 1L; - private final String cookieValue; + private final Map cookieNamesValues; - CookieServlet(String cookieValue) { - this.cookieValue = cookieValue; + CookieServlet(Map cookieNamesValues) { + this.cookieNamesValues = cookieNamesValues; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - Cookie cookie = new Cookie("Test", cookieValue); - resp.addCookie(cookie); + for (Map.Entry entry : cookieNamesValues.entrySet()) { + Cookie cookie = new Cookie(entry.getKey(), entry.getValue()); + resp.addCookie(cookie); + } resp.setContentType("text/plain"); resp.getWriter().print("OK"); } } + + + @Test + public void testCaseSensitiveCookie() throws Exception { + Tomcat tomcat = getTomcatInstance(); + // No file system docBase required + Context ctx = getProgrammaticRootContext(); + ctx.setCookieProcessor(new Rfc6265CookieProcessor()); + Map cookies = new HashMap<>(); + cookies.put("aaa", "zzz"); + cookies.put("aAa", "yyy"); + Tomcat.addServlet(ctx, "test", new CookieServlet(cookies)); + ctx.addServletMappingDecoded("/test", "test"); + tomcat.start(); + + Map> headers = new HashMap<>(); + ByteChunk res = new ByteChunk(); + getUrl("http://localhost:" + getPort() + "/test", res, headers); + List cookieHeaders = headers.get("Set-Cookie"); + Assert.assertEquals("There should be two Set-Cookie headers in this test", + 2, cookieHeaders.size()); + // Remove the cookies the client sees from the map that was sent. Should leave the map empty. + for (String cookieHeader : cookieHeaders) { + String[] nv = cookieHeader.split("="); + Assert.assertEquals(2, nv.length); + Assert.assertTrue(nv[1].equals(cookies.remove(nv[0]))); + } + Assert.assertEquals(0, cookies.size()); + } } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestMethod.java tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestMethod.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestMethod.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestMethod.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.http; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +public class TestMethod { + + /* + * Not testing performance. Just checking that there are no errors in the parsing code. + */ + @Test + public void testHttpMethodParsing() { + List methods = Arrays.asList(Method.GET, Method.POST, Method.PUT, Method.PATCH, Method.HEAD, + Method.OPTIONS, Method.DELETE, Method.TRACE, Method.PROPPATCH, Method.PROPFIND, Method.MKCOL, + Method.COPY, Method.MOVE, Method.LOCK, Method.UNLOCK, Method.CONNECT); + + for (String method : methods) { + byte[] bytes = method.getBytes(StandardCharsets.ISO_8859_1); + String result = Method.bytesToString(bytes, 0, bytes.length); + Assert.assertEquals(method, result); + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestMethodPerformance.java tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestMethodPerformance.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestMethodPerformance.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestMethodPerformance.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.http; + +import java.nio.charset.StandardCharsets; + +import org.junit.Test; + +import org.apache.tomcat.util.buf.MessageBytes; + +public class TestMethodPerformance { + + private static final int LOOPS = 6; + private static final int ITERATIONS = 100000000; + + private static final String INPUT = "GET /context-path/servlet-path/path-info HTTP/1.1"; + private static final byte[] INPUT_BYTES = INPUT.getBytes(StandardCharsets.UTF_8); + + private static MessageBytes mb = MessageBytes.newInstance(); + + @Test + public void testGetMethodPerformance() throws Exception { + + for (int j = 0; j < LOOPS; j++) { + long start = System.nanoTime(); + for (int i = 0; i < ITERATIONS; i++) { + mb.setBytes(INPUT_BYTES, 0, 3); + mb.toStringType(); + } + long duration = System.nanoTime() - start; + + if (j > 0) { + System.out.println("MessageBytes conversion took :" + duration + "ns"); + } + } + + for (int j = 0; j < LOOPS; j++) { + long start = System.nanoTime(); + for (int i = 0; i < ITERATIONS; i++) { + String method = Method.bytesToString(INPUT_BYTES, 0, 3); + if (method == null) { + mb.setBytes(INPUT_BYTES, 0, 5); + mb.toStringType(); + } + } + long duration = System.nanoTime() - start; + + if (j > 0) { + System.out.println("Optimized conversion took :" + duration + "ns"); + } + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestRequestUtilSameOrigin.java tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestRequestUtilSameOrigin.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/http/TestRequestUtilSameOrigin.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/http/TestRequestUtilSameOrigin.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,6 +42,8 @@ TesterRequest request2 = new TesterRequest("ws", "example.com", 80); TesterRequest request3 = new TesterRequest("http", "example.com", 443); TesterRequest request4 = new TesterRequest("http", "example.com", 8080); + TesterRequest request5 = new TesterRequest(null, "example.com", 80); + TesterRequest request6 = new TesterRequest("http", null, 8080); parameterSets.add(new Object[] { request1, "http://example.com", Boolean.TRUE }); parameterSets.add(new Object[] { request1, "http://example.com:80", Boolean.TRUE }); @@ -59,6 +61,14 @@ parameterSets.add(new Object[] { request4, "http://example.com:80", Boolean.FALSE }); parameterSets.add(new Object[] { request4, "http://example.com:8080", Boolean.TRUE}); + parameterSets.add(new Object[]{ request5, "http://example.com:80", Boolean.FALSE}); + parameterSets.add(new Object[]{ request5, "://example.com:80", Boolean.FALSE}); + parameterSets.add(new Object[]{ request5, "example.com:80", Boolean.FALSE}); + + parameterSets.add(new Object[]{ request6, "http://example.com:80", Boolean.FALSE}); + parameterSets.add(new Object[]{ request6, "http://:80", Boolean.FALSE}); + parameterSets.add(new Object[]{ request6, "http://", Boolean.FALSE}); + return parameterSets; } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/http/parser/TestTE.java tomcat10-10.1.52/test/org/apache/tomcat/util/http/parser/TestTE.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/http/parser/TestTE.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/http/parser/TestTE.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.http.parser; + +import java.io.StringReader; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +public class TestTE { + + private static final String GZIP = "gzip"; + private static final double Q1_000 = 1; + private static final double Q0_500 = 0.5; + private static final double Q0_050 = 0.05; + + @Test + public void testSimpleTE() throws Exception { + List actual = TE.parse(new StringReader("gzip")); + + Assert.assertEquals(1, actual.size()); + Assert.assertEquals("gzip", actual.get(0).getEncoding()); + Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001); + } + + @Test + public void testComplexTE1() throws Exception { + List actual = TE.parse(new StringReader("gzip;q=0.5")); + + Assert.assertEquals(1, actual.size()); + Assert.assertEquals("gzip", actual.get(0).getEncoding()); + Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001); + } + + @Test + public void testComplexTE2() throws Exception { + List actual = TE.parse(new StringReader("gzip;q=0.5, something;q=0.05")); + + Assert.assertEquals(2, actual.size()); + Assert.assertEquals("gzip", actual.get(0).getEncoding()); + Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001); + } + + @Test + public void testComplexTE3() throws Exception { + List actual = TE.parse(new StringReader("gzip; arg1= val1; arg2 = val2 ; q =0.05")); + + Assert.assertEquals(1, actual.size()); + Assert.assertEquals(GZIP, actual.get(0).getEncoding()); + Assert.assertEquals(Q0_050, actual.get(0).getQuality(), 0.0001); + Assert.assertEquals("val1", actual.get(0).getParameters().get("arg1")); + Assert.assertEquals("val2", actual.get(0).getParameters().get("arg2")); + } + + @Test + public void testMalformed01() throws Exception { + List actual = TE.parse(new StringReader("gzip;q=a,gzip;q=0.5")); + + Assert.assertEquals(1, actual.size()); + Assert.assertEquals(GZIP, actual.get(0).getEncoding()); + Assert.assertEquals(Q0_500, actual.get(0).getQuality(), 0.0001); + } + + @Test + public void testMalformed02() throws Exception { + List actual = TE.parse(new StringReader("gzip,,")); + + Assert.assertEquals(1, actual.size()); + Assert.assertEquals("gzip", actual.get(0).getEncoding()); + Assert.assertEquals(Q1_000, actual.get(0).getQuality(), 0.0001); + } + + @Test + public void testMalformed03() throws Exception { + List actual = TE.parse(new StringReader("gzip;q=1.0a0")); + + Assert.assertEquals(0, actual.size()); + } + +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestClientCertTls13.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestClientCertTls13.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestClientCertTls13.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestClientCertTls13.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,9 +31,9 @@ import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; import org.apache.catalina.connector.Connector; -import org.apache.catalina.core.AprStatus; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.jni.AprStatus; import org.apache.tomcat.util.buf.ByteChunk; import org.apache.tomcat.util.net.openssl.OpenSSLStatus; diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestKeyManagerWrappingFips.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestKeyManagerWrappingFips.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestKeyManagerWrappingFips.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestKeyManagerWrappingFips.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,227 @@ +/* + * 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; + +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.security.Key; +import java.security.KeyStore; +import java.security.KeyStoreSpi; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Enumeration; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactorySpi; +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.X509KeyManager; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +import org.apache.tomcat.util.net.jsse.JSSEUtil; + +/** + * Test case for Bug 64614. + */ +public class TestKeyManagerWrappingFips { + private static final String FIPS_PROVIDER = "FIPS_PROVIDER"; + private static final String NON_FIPS_PROVIDER = "NON_FIPS_PROVIDER"; + private static final String DUMMY_ALGORITHM = "DUMMY_ALGORITHM"; + private static final String KEYSTORE_PROVIDER = "KEYSTORE_PROVIDER"; + private static final String DUMMY_KEYSTORE = "DUMMY_KEYSTORE"; + @After + public void restore() { + DummyKeyStoreSpi.wrappingOccurred = false; + Security.removeProvider(FIPS_PROVIDER); + Security.removeProvider(NON_FIPS_PROVIDER); + Security.removeProvider(KEYSTORE_PROVIDER); + } + + @Test + public void testBug64614_01() throws Exception { + Security.addProvider(new DummyKeyManagerFactoryProvider(FIPS_PROVIDER, "Sun JSSE provider (FIPS mode, crypto provider SunPKCS11-NSSfips", DUMMY_ALGORITHM)); + getKeyManagers(); + Assert.assertFalse(DummyKeyStoreSpi.wrappingOccurred); + } + + @Test + public void testBug64614_02() throws Exception { + Security.addProvider(new DummyKeyManagerFactoryProvider(NON_FIPS_PROVIDER, "Sun JSSE provider", DUMMY_ALGORITHM)); + getKeyManagers(); + Assert.assertTrue(DummyKeyStoreSpi.wrappingOccurred); + } + private void getKeyManagers() throws Exception { + Security.addProvider(new DummyKeyStoreProvider(KEYSTORE_PROVIDER, "", DUMMY_KEYSTORE)); + SSLHostConfig hostConfig = new SSLHostConfig(); + hostConfig.setKeyManagerAlgorithm(DUMMY_ALGORITHM); + SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(hostConfig, SSLHostConfigCertificate.Type.UNDEFINED); + + File keystoreFile = File.createTempFile("keystore", ".jks"); + + certificate.setCertificateKeystoreProvider(KEYSTORE_PROVIDER); + certificate.setCertificateKeystoreType(DUMMY_KEYSTORE); + certificate.setCertificateKeystoreFile(keystoreFile.getAbsolutePath()); + new JSSEUtil(certificate).getKeyManagers(); + + if (!keystoreFile.delete()) { + keystoreFile.deleteOnExit(); + } + } + + private static final class DummyKeyStoreProvider extends Provider { + private static final long serialVersionUID = 1L; + + DummyKeyStoreProvider(String name, String info, String algorithm) { + super(name, "", info); + put("KeyStore." + algorithm, DummyKeyStoreSpi.class.getName()); + } + } + + public static final class DummyKeyStoreSpi extends KeyStoreSpi { + static volatile boolean wrappingOccurred = false; + @Override + public Key engineGetKey(String s, char[] chars) { + wrappingOccurred = true; + return null; + } + @Override + public Certificate[] engineGetCertificateChain(String s) { + return null; + } + @Override + public Certificate engineGetCertificate(String s) { + return null; + } + @Override + public Date engineGetCreationDate(String s) { + return null; + } + @Override + public void engineSetKeyEntry(String s, Key key, char[] chars, Certificate[] certificates) { + } + @Override + public void engineSetKeyEntry(String s, byte[] bytes, Certificate[] certificates) { + } + @Override + public void engineSetCertificateEntry(String s, Certificate certificate) { + } + @Override + public void engineDeleteEntry(String s) { + } + @Override + public Enumeration engineAliases() { + return new Enumeration<>() { + @Override + public boolean hasMoreElements() { + return true; + } + @Override + public String nextElement() { + return ""; + } + }; + } + @Override + public boolean engineContainsAlias(String s) { + return false; + } + @Override + public int engineSize() { + return 0; + } + @Override + public boolean engineIsKeyEntry(String s) { + return true; + } + @Override + public boolean engineIsCertificateEntry(String s) { + return false; + } + @Override + public String engineGetCertificateAlias(Certificate certificate) { + return ""; + } + @Override + public void engineStore(OutputStream outputStream, char[] chars) { + } + @Override + public void engineLoad(InputStream inputStream, char[] chars) { + } + } + + private static final class DummyKeyManagerFactoryProvider extends Provider { + private static final long serialVersionUID = 1L; + + DummyKeyManagerFactoryProvider(String name, String info, String algorithm) { + super(name, "", info); + put("KeyManagerFactory." + algorithm, DummyKeyManagerFactorySpi.class.getName()); + } + } + + public static final class DummyKeyManagerFactorySpi extends KeyManagerFactorySpi { + @Override + protected void engineInit(KeyStore ks, char[] password) { + } + @Override + protected void engineInit(ManagerFactoryParameters spec) { + } + @Override + protected KeyManager[] engineGetKeyManagers() { + return new KeyManager[] { new X509KeyManager() { + @Override + public String[] getClientAliases(String s, Principal[] principals) { + return new String[0]; + } + + @Override + public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) { + return ""; + } + + @Override + public String[] getServerAliases(String s, Principal[] principals) { + return new String[0]; + } + + @Override + public String chooseServerAlias(String s, Principal[] principals, Socket socket) { + return ""; + } + + @Override + public X509Certificate[] getCertificateChain(String s) { + return new X509Certificate[0]; + } + + @Override + public PrivateKey getPrivateKey(String s) { + return null; + } + } }; + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestSSLHostConfig.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSSLHostConfig.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestSSLHostConfig.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSSLHostConfig.java 2026-01-23 19:33:36.000000000 +0000 @@ -77,6 +77,76 @@ @Test + public void testCipher05() { + SSLHostConfig hc = new SSLHostConfig(); + Cipher c = Cipher.TLS_AES_128_CCM_SHA256; + + // Single TLSv1.3 name - should be filtered out + hc.setCiphers(c.getOpenSSLAlias()); + Assert.assertEquals("", hc.getCiphers()); + } + + + @Test + public void testCipher06() { + SSLHostConfig hc = new SSLHostConfig(); + Cipher c1 = Cipher.TLS_AES_128_CCM_SHA256; + Cipher c2 = Cipher.TLS_RSA_WITH_NULL_MD5; + + // TLSv1.3 then TLSv1.2 - TLSv1.3 name should be filtered out + hc.setCiphers(c1.getOpenSSLAlias() + ":" + c2.getOpenSSLAlias()); + Assert.assertEquals(c2.getOpenSSLAlias(), hc.getCiphers()); + } + + + @Test + public void testCipher07() { + SSLHostConfig hc = new SSLHostConfig(); + Cipher c1 = Cipher.TLS_AES_128_CCM_SHA256; + Cipher c2 = Cipher.TLS_RSA_WITH_NULL_MD5; + + // TLSv1.2 then TLSv1.3 - TLSv1.3 name should be filtered out + hc.setCiphers(c2.getOpenSSLAlias() + ":" + c1.getOpenSSLAlias()); + Assert.assertEquals(c2.getOpenSSLAlias(), hc.getCiphers()); + } + + + @Test + public void testCiphersuite01() { + SSLHostConfig hc = new SSLHostConfig(); + Cipher c = Cipher.TLS_AES_128_CCM_SHA256; + + // Single TLSv1.3 cipher suite name + hc.setCipherSuites(c.getOpenSSLAlias()); + Assert.assertEquals(c.getOpenSSLAlias(), hc.getCipherSuites()); + } + + + @Test + public void testCiphersuite02() { + SSLHostConfig hc = new SSLHostConfig(); + Cipher c1 = Cipher.TLS_AES_128_CCM_SHA256; + Cipher c2 = Cipher.TLS_RSA_WITH_NULL_MD5; + + // TLSv1.3 then TLSv1.2 - TLSv1.2 name should be filtered out + hc.setCipherSuites(c1.getOpenSSLAlias() + ":" + c2.getOpenSSLAlias()); + Assert.assertEquals(c1.getOpenSSLAlias(), hc.getCipherSuites()); + } + + + @Test + public void testCiphersuite03() { + SSLHostConfig hc = new SSLHostConfig(); + Cipher c1 = Cipher.TLS_AES_128_CCM_SHA256; + Cipher c2 = Cipher.TLS_RSA_WITH_NULL_MD5; + + // TLSv1.2 then TLSv1.3 - TLSv1.2 name should be filtered out + hc.setCipherSuites(c2.getOpenSSLAlias() + ":" + c1.getOpenSSLAlias()); + Assert.assertEquals(c1.getOpenSSLAlias(), hc.getCipherSuites()); + } + + + @Test public void testSerialization() throws IOException, ClassNotFoundException { // Dummy OpenSSL command name/value pair String name = "foo"; diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSSLHostConfigCipher.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,161 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.net.ssl.SSLHandshakeException; + +import org.junit.Assert; +import org.junit.Assume; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; + +import org.apache.catalina.Context; +import org.apache.catalina.connector.Connector; +import org.apache.catalina.startup.TesterServlet; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.net.openssl.OpenSSLStatus; + +@RunWith(Parameterized.class) +public class TestSSLHostConfigCipher extends TomcatBaseTest { + + private static final String CIPHER_12_AVAILABLE = "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"; + private static final String CIPHER_12_NOT_AVAILABLE = "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"; + private static final String CIPHER_13_AVAILABLE = "TLS_AES_128_GCM_SHA256"; + private static final String CIPHER_13_NOT_AVAILABLE = "TLS_AES_256_GCM_SHA384"; + + @Parameterized.Parameters(name = "{0}") + public static Collection parameters() { + List parameterSets = new ArrayList<>(); + parameterSets.add(new Object[] { + "JSSE", Boolean.FALSE, "org.apache.tomcat.util.net.jsse.JSSEImplementation"}); + parameterSets.add(new Object[] { + "OpenSSL", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.OpenSSLImplementation"}); + parameterSets.add(new Object[] { + "OpenSSL-FFM", Boolean.TRUE, "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation"}); + + return parameterSets; + } + + @Parameter(0) + public String connectorName; + + @Parameter(1) + public boolean useOpenSSL; + + @Parameter(2) + public String sslImplementationName; + + + @Override + public void setUp() throws Exception { + super.setUp(); + + Tomcat tomcat = getTomcatInstance(); + + // Server-side TLS configuration + TesterSupport.initSsl(tomcat); + TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); + + // Test specific, server-side cipher & protocol configuration + SSLHostConfig sslHostConfig = getSSLHostConfig(); + sslHostConfig.setProtocols("+TLSv1.2+TLSv1.3"); + sslHostConfig.setCiphers(CIPHER_12_AVAILABLE); + sslHostConfig.setCipherSuites(CIPHER_13_AVAILABLE); + + // Simple webapp + Context ctxt = getProgrammaticRootContext(); + Tomcat.addServlet(ctxt, "TesterServlet", new TesterServlet()); + ctxt.addServletMappingDecoded("/*", "TesterServlet"); + } + + + @Test + public void testTls12CipherAvailable() throws Exception { + if ("OpenSSL-FFM".equals(connectorName)) { + // The functionality works, but the two ciphers used are not available + Assume.assumeFalse(OpenSSLStatus.isBoringSSL()); + } + // Client-side TLS configuration + TesterSupport.configureClientSsl(true, new String[] { CIPHER_12_AVAILABLE } ); + + doTest(); + } + + + @Test(expected=SSLHandshakeException.class) + public void testTls12CipherNotAvailable() throws Exception { + if ("OpenSSL-FFM".equals(connectorName)) { + Assume.assumeFalse(OpenSSLStatus.isBoringSSL()); + } + // Client-side TLS configuration + TesterSupport.configureClientSsl(true, new String[] { CIPHER_12_NOT_AVAILABLE } ); + + doTest(); + } + + + @Test + public void testTls13CipherAvailable() throws Exception { + if ("OpenSSL-FFM".equals(connectorName)) { + Assume.assumeFalse(OpenSSLStatus.isBoringSSL()); + } + // Client-side TLS configuration + TesterSupport.configureClientSsl(new String[] { CIPHER_13_AVAILABLE } ); + + doTest(); + } + + + @Test(expected=SSLHandshakeException.class) + public void testTls13CipherNotAvailable() throws Exception { + if ("OpenSSL-FFM".equals(connectorName)) { + // The TLS 1.3 call might not be present + Assume.assumeFalse(OpenSSLStatus.isLibreSSLPre35()); + Assume.assumeFalse(OpenSSLStatus.isBoringSSL()); + } + // Client-side TLS configuration + TesterSupport.configureClientSsl(new String[] { CIPHER_13_NOT_AVAILABLE } ); + + doTest(); + } + + + private void doTest() throws Exception { + Tomcat tomcat = getTomcatInstance(); + + tomcat.start(); + + // Check a request can be made + ByteChunk res = getUrl("https://localhost:" + getPort() + "/"); + Assert.assertEquals("OK", res.toString()); + } + + + private SSLHostConfig getSSLHostConfig() { + Tomcat tomcat = getTomcatInstance(); + Connector connector = tomcat.getConnector(); + return connector.findSslHostConfigs()[0]; + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestSSLHostConfigCompat.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSSLHostConfigCompat.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestSSLHostConfigCompat.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSSLHostConfigCompat.java 2026-01-23 19:33:36.000000000 +0000 @@ -106,7 +106,7 @@ configureHostEC(); // Configure cipher suite that requires an RSA certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"}); doTest(false); @@ -123,7 +123,7 @@ configureHostRSA(); // Configure cipher suite that requires an RSA certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"}); doTest(false); @@ -140,7 +140,7 @@ configureHostEC(); // Configure cipher suite that requires an EC certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}); doTest(false); @@ -157,7 +157,7 @@ configureHostRSA(); // Configure cipher suite that requires an EC certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}); doTest(false); @@ -169,7 +169,7 @@ configureHostRSA(); // Configure cipher suite that requires an RSA certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"}); doTest(false); @@ -181,7 +181,7 @@ configureHostRSA(); // Configure cipher suite that requires an EC certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}); doTest(false); @@ -193,7 +193,7 @@ configureHostRSA(); // Configure cipher suite that requires an EC certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] { "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"}); @@ -207,7 +207,7 @@ configureHostEC(); // Configure cipher suite that requires an RSA certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] {"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"}); doTest(false); @@ -219,7 +219,7 @@ configureHostEC(); // Configure cipher suite that requires an EC certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] {"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}); doTest(false); @@ -231,7 +231,7 @@ configureHostEC(); // Configure cipher suite that requires an RSA certificate on the server - ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(true); clientSSLSocketFactory.setCipher(new String[] { "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"}); @@ -288,7 +288,7 @@ private void doTest(boolean configureClientSsl) throws Exception { if (configureClientSsl) { - TesterSupport.configureClientSsl(); + TesterSupport.configureClientSsl(true); } Tomcat tomcat = getTomcatInstance(); diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestSsl.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSsl.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/TestSsl.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/TestSsl.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,7 +34,10 @@ import javax.net.SocketFactory; import javax.net.ssl.HandshakeCompletedEvent; import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.SNIHostName; +import javax.net.ssl.SNIServerName; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; @@ -51,6 +54,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; +import static org.apache.catalina.startup.SimpleHttpClient.CRLF; import org.apache.catalina.Context; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; @@ -59,11 +63,14 @@ import org.apache.catalina.connector.Connector; import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; +import org.apache.catalina.startup.SimpleHttpClient; import org.apache.catalina.startup.TesterServlet; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.catalina.valves.ValveBase; import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type; +import org.apache.tomcat.util.net.TesterSupport.ClientSSLSocketFactory; import org.apache.tomcat.util.net.openssl.OpenSSLStatus; import org.apache.tomcat.websocket.server.WsContextListener; @@ -252,7 +259,7 @@ Context ctxt = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); ctxt.addApplicationListener(WsContextListener.class.getName()); - TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS, + TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS, false, TesterSupport.JKS_PASS, null, TesterSupport.JKS_KEY_PASS, null); TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); @@ -275,7 +282,7 @@ Context ctxt = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); ctxt.addApplicationListener(WsContextListener.class.getName()); - TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS, + TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_KEYPASS_JKS, false, null, TesterSupport.JKS_PASS_FILE, null, TesterSupport.JKS_KEY_PASS_FILE); TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); @@ -289,6 +296,159 @@ } @Test + public void testSni() throws Exception { + System.setProperty("jsse.enableSNIExtension", "true"); + ClientSSLSocketFactory clientSSLSocketFactory = TesterSupport.configureClientSsl(); + Tomcat tomcat = getTomcatInstance(); + tomcat.getConnector().setProperty("strictSni", "true"); + + File appDir = new File(getBuildDirectory(), "webapps/examples"); + Context ctxt = tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath()); + ctxt.addApplicationListener(WsContextListener.class.getName()); + + TesterSupport.initSsl(tomcat); + + // Add another config for localhost + SSLHostConfig sslHostConfig = new SSLHostConfig(); + SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(sslHostConfig, Type.UNDEFINED); + sslHostConfig.addCertificate(certificate); + certificate.setCertificateKeystoreFile(new File(TesterSupport.LOCALHOST_RSA_JKS).getAbsolutePath()); + certificate.setCertificateKeystorePassword(TesterSupport.JKS_PASS); + sslHostConfig.setHostName("localhost"); + tomcat.getConnector().addSslHostConfig(sslHostConfig); + + // Add another config for foobar + sslHostConfig = new SSLHostConfig(); + certificate = new SSLHostConfigCertificate(sslHostConfig, Type.UNDEFINED); + sslHostConfig.addCertificate(certificate); + certificate.setCertificateKeystoreFile(new File(TesterSupport.LOCALHOST_RSA_JKS).getAbsolutePath()); + certificate.setCertificateKeystorePassword(TesterSupport.JKS_PASS); + sslHostConfig.setHostName("foobar"); + tomcat.getConnector().addSslHostConfig(sslHostConfig); + + TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); + + tomcat.start(); + + // Send SNI and it matches + SSLSocket sslSocket = (SSLSocket) clientSSLSocketFactory.createSocket("localhost", getPort()); + SNIHostName serverName = new SNIHostName("localhost"); + List serverNames = new ArrayList<>(1); + serverNames.add(serverName); + SSLParameters params = sslSocket.getSSLParameters(); + params.setServerNames(serverNames); + sslSocket.setSSLParameters(params); + + Client client = new Client(); + client.setPort(getPort()); + + // @formatter:off + client.setRequest(new String[] { + "GET /examples/servlets/servlet/HelloWorldExample HTTP/1.1" + CRLF + + "Host: localhost" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(sslSocket); + client.processRequest(true); + + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + Assert.assertTrue(client.getResponseBody().contains("")); + client.disconnect(); + client.reset(); + + // Send SNI and it does not match + sslSocket = (SSLSocket) clientSSLSocketFactory.createSocket("localhost", getPort()); + params = sslSocket.getSSLParameters(); + params.setServerNames(serverNames); + sslSocket.setSSLParameters(params); + + // @formatter:off + client.setRequest(new String[] { + "GET /examples/servlets/servlet/HelloWorldExample HTTP/1.1" + CRLF + + "Host: foobar" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(sslSocket); + client.processRequest(true); + + Assert.assertEquals(HttpServletResponse.SC_BAD_REQUEST, client.getStatusCode()); + client.disconnect(); + client.reset(); + + // Send SNI and it does not match, but this goes to the default host which is the same one + tomcat.getConnector().setProperty("defaultSSLHostConfigName", "localhost"); + sslSocket = (SSLSocket) clientSSLSocketFactory.createSocket("localhost", getPort()); + params = sslSocket.getSSLParameters(); + params.setServerNames(serverNames); + sslSocket.setSSLParameters(params); + + // @formatter:off + client.setRequest(new String[] { + "GET /examples/servlets/servlet/HelloWorldExample HTTP/1.1" + CRLF + + "Host: something" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(sslSocket); + client.processRequest(true); + + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + client.disconnect(); + client.reset(); + tomcat.getConnector().setProperty("defaultSSLHostConfigName", "_default_"); + + tomcat.getConnector().setProperty("strictSni", "false"); + + // SNI is not verified + sslSocket = (SSLSocket) clientSSLSocketFactory.createSocket("localhost", getPort()); + params = sslSocket.getSSLParameters(); + params.setServerNames(serverNames); + sslSocket.setSSLParameters(params); + + // @formatter:off + client.setRequest(new String[] { + "GET /examples/servlets/servlet/HelloWorldExample HTTP/1.1" + CRLF + + "Host: foobar" + CRLF + + "Connection: Close" + CRLF + + CRLF + }); + // @formatter:on + client.connect(sslSocket); + client.processRequest(true); + + Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode()); + client.disconnect(); + client.reset(); + + tomcat.getConnector().setProperty("strictSni", "true"); + + // No SNI but this is the default config + tomcat.getConnector().setProperty("defaultSSLHostConfigName", "localhost"); + Assert.assertEquals(HttpServletResponse.SC_OK, + getUrl("https://localhost:" + getPort() + "/examples/servlets/servlet/HelloWorldExample", new ByteChunk(), null)); + + // No SNI and this is not the default config + tomcat.getConnector().setProperty("defaultSSLHostConfigName", "_default_"); + Assert.assertEquals(HttpServletResponse.SC_BAD_REQUEST, + getUrl("https://localhost:" + getPort() + "/examples/servlets/servlet/HelloWorldExample", new ByteChunk(), null)); + + } + + private static final class Client extends SimpleHttpClient { + + @Override + public boolean isResponseBodyOK() { + return true; + } + + } + + @Test public void testClientInitiatedRenegotiation() throws Exception { Tomcat tomcat = getTomcatInstance(); @@ -332,7 +492,7 @@ if (!renegotiationSupported) { Assert.fail("Renegotiation started when it should have failed"); } - } catch (IOException e) { + } catch (IOException ioe) { if (renegotiationSupported) { Assert.fail("Renegotiation failed when it should be supported"); } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/TesterSupport.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/TesterSupport.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/TesterSupport.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/TesterSupport.java 2026-01-23 19:33:36.000000000 +0000 @@ -24,12 +24,24 @@ import java.net.Socket; import java.net.UnknownHostException; import java.security.KeyStore; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.Principal; import java.security.PrivateKey; +import java.security.cert.CertPathValidator; +import java.security.cert.Certificate; import java.security.cert.CertificateException; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.PKIXRevocationChecker; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CertSelector; import java.security.cert.X509Certificate; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import javax.net.ssl.CertPathTrustManagerParameters; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; @@ -87,6 +99,12 @@ public static final String LOCALHOST_EC_KEY_PEM = SSL_DIR + "localhost-ec-key.pem"; public static final String LOCALHOST_RSA_CERT_PEM = SSL_DIR + "localhost-rsa-cert.pem"; public static final String LOCALHOST_RSA_KEY_PEM = SSL_DIR + "localhost-rsa-key.pem"; + public static final String DB_INDEX = SSL_DIR + "index.db"; + public static final String OCSP_RESPONDER_RSA_CERT = SSL_DIR + "ocsp-responder-rsa-cert.pem"; + public static final String OCSP_RESPONDER_RSA_KEY = SSL_DIR + "ocsp-responder-rsa-key.pem"; + public static final String LOCALHOST_CRL_RSA_JKS = SSL_DIR + "localhost-crl-rsa.jks"; + public static final String CLIENT_CRL_JKS = SSL_DIR + "user2-crl.jks"; + public static final String CLIENT_CRL_LONG_JKS = SSL_DIR + "user3-crl-long.jks"; public static final boolean TLSV13_AVAILABLE; public static final String ROLE = "testrole"; @@ -110,10 +128,21 @@ } public static void initSsl(Tomcat tomcat) { - initSsl(tomcat, LOCALHOST_RSA_JKS, null, null, null, null); + // By default, use JSSE JSSE trust + initSsl(tomcat, false); } - protected static void initSsl(Tomcat tomcat, String keystore, + public static void initSsl(Tomcat tomcat, boolean opensslTrust) { + // By default, use valid JSSE configuration + initSsl(tomcat, LOCALHOST_RSA_JKS, opensslTrust); + } + + public static void initSsl(Tomcat tomcat, String keystore, boolean opensslTrust) { + // TLS material for tests uses default password + initSsl(tomcat, keystore, opensslTrust, null, null, null, null); + } + + protected static void initSsl(Tomcat tomcat, String keystore, boolean opensslTrust, String keystorePass, String keystorePassFile, String keyPass, String keyPassFile) { Connector connector = tomcat.getConnector(); @@ -135,7 +164,11 @@ } sslHostConfig.setSslProtocol("tls"); certificate.setCertificateKeystoreFile(new File(keystore).getAbsolutePath()); - sslHostConfig.setTruststoreFile(new File(CA_JKS).getAbsolutePath()); + if (opensslTrust) { + sslHostConfig.setCaCertificateFile(new File(CA_CERT_PEM).getAbsolutePath()); + } else { + sslHostConfig.setTruststoreFile(new File(CA_JKS).getAbsolutePath()); + } if (keystorePassFile != null) { certificate.setCertificateKeystorePasswordFile(new File(keystorePassFile).getAbsolutePath()); } @@ -150,10 +183,10 @@ } } - protected static KeyManager[] getUser1KeyManagers() throws Exception { + protected static KeyManager[] getUserKeyManagers(String keyStore) throws Exception { KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); - kmf.init(getKeyStore(CLIENT_JKS), JKS_PASS.toCharArray()); + kmf.init(getKeyStore(keyStore), JKS_PASS.toCharArray()); KeyManager[] managers = kmf.getKeyManagers(); KeyManager manager; for (int i=0; i < managers.length; i++) { @@ -168,23 +201,74 @@ } protected static TrustManager[] getTrustManagers() throws Exception { - TrustManagerFactory tmf = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm()); - tmf.init(getKeyStore(CA_JKS)); + return getTrustManagers(false); + } + + protected static TrustManager[] getTrustManagers(boolean enableOcsp) throws Exception { + KeyStore trustStore = getKeyStore(CA_JKS); + TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX"); + if (enableOcsp) { + Set trustAnchors = getTrustAnchorsFromKeystore(trustStore); + PKIXBuilderParameters pkix = new PKIXBuilderParameters(trustAnchors, new X509CertSelector()); + PKIXRevocationChecker revocationChecker = + (PKIXRevocationChecker) CertPathValidator.getInstance("PKIX").getRevocationChecker(); + revocationChecker.setOptions(EnumSet.of(PKIXRevocationChecker.Option.NO_FALLBACK)); + pkix.addCertPathChecker(revocationChecker); + tmf.init(new CertPathTrustManagerParameters(pkix)); + } else { + tmf.init(trustStore); + } return tmf.getTrustManagers(); } + private static Set getTrustAnchorsFromKeystore(KeyStore keyStore) throws KeyStoreException { + Set trustAnchors = new HashSet<>(); + Enumeration aliases = keyStore.aliases(); + while (aliases.hasMoreElements()) { + String alias = aliases.nextElement(); + Certificate certificate = keyStore.getCertificate(alias); + if (certificate instanceof X509Certificate) { + trustAnchors.add(new TrustAnchor((X509Certificate) certificate, null)); + } + } + return trustAnchors; + } + public static ClientSSLSocketFactory configureClientSsl() { + return configureClientSsl(false, null, false, CLIENT_JKS); + } + + public static ClientSSLSocketFactory configureClientSsl(boolean forceTls12) { + return configureClientSsl(forceTls12, null, false, CLIENT_JKS); + } + + public static ClientSSLSocketFactory configureClientSsl(String[] ciphers) { + return configureClientSsl(false, ciphers, false, CLIENT_JKS); + } + + public static ClientSSLSocketFactory configureClientSsl(boolean forceTls12, String[] ciphers) { + return configureClientSsl(forceTls12, ciphers, false, CLIENT_JKS); + } + + public static ClientSSLSocketFactory configureClientSsl(boolean enableOcsp, String keyStore) { + return configureClientSsl(false, null, enableOcsp, keyStore); + } + + public static ClientSSLSocketFactory configureClientSsl(boolean forceTls12, String[] ciphers, boolean enableOcsp, + String keyStore) { ClientSSLSocketFactory clientSSLSocketFactory = null; try { SSLContext sc; - if (TLSV13_AVAILABLE) { - sc = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_3); + if (TLSV13_AVAILABLE && !forceTls12) { + sc = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_3); } else { sc = SSLContext.getInstance(Constants.SSL_PROTO_TLSv1_2); } - sc.init(getUser1KeyManagers(), getTrustManagers(), null); + sc.init(getUserKeyManagers(keyStore), getTrustManagers(enableOcsp), null); clientSSLSocketFactory = new ClientSSLSocketFactory(sc.getSocketFactory()); + if (ciphers != null) { + clientSSLSocketFactory.setCipher(ciphers); + } javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(clientSSLSocketFactory); } catch (Exception e) { e.printStackTrace(); diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/ca-cert.pem tomcat10-10.1.52/test/org/apache/tomcat/util/net/ca-cert.pem --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/ca-cert.pem 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/ca-cert.pem 2026-01-23 19:33:36.000000000 +0000 @@ -1,38 +1,39 @@ -----BEGIN CERTIFICATE----- -MIIGpzCCBI+gAwIBAgIJAL51xu6EZW62MA0GCSqGSIb3DQEBCwUAMIGTMQswCQYD -VQQGEwJVUzELMAkGA1UECBMCTUExEjAQBgNVBAcTCVdha2VmaWVsZDEnMCUGA1UE -ChMeVGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFj -aGUgVG9tY2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4X -DTE3MDgwODEwMzYzNloXDTI3MDgwNjEwMzYzNlowgZMxCzAJBgNVBAYTAlVTMQsw -CQYDVQQIEwJNQTESMBAGA1UEBxMJV2FrZWZpZWxkMScwJQYDVQQKEx5UaGUgQXBh -Y2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21jYXQg -UE1DMR4wHAYDVQQDExVBcGFjaGUgVG9tY2F0IFRlc3QgQ0EwggIiMA0GCSqGSIb3 -DQEBAQUAA4ICDwAwggIKAoICAQCuokXuzdQ8HToRVcL07AdxHW9WmaMpcPWb/vyQ -dcuha+JXJ9Wu+d7fpxppeuxnjDmDCZNo0kimI7nYIEDMd3WVen75aoMZnQ7+vN/G -ZQXxzSPz2vzTZyEETAqs7DsGwO5CK2y5sWKl57+QCz/N+xM7EwOyNkmt+7xI1eQ+ -z2sUNLRMK7abom8nm/wVftGAXIiribmTqukoxjr8dpEDg77VCy9eqe6kcil6Fvnr -mYrJqmrwzGldUlw4jqHl1IJnJ5z281vzzQ0U5ULeiuBpDGXcOHoaH8zYxilBVpPu -RDRBOcX17e5NouZtDTFemkJq5ns3PDt+WjJvuYNSELLBbnP+S1V6mt+MU9PsF6Td -lVZZxxFD9hPYqAzymwJGzTKbE8juZruQswL4iftyELmLPjIsetVtXifsUNay6CfD -r5sN6r+KLrhJWUqhii2mH1jx4cLmlf308TOc80TldvvI9cfrb596954cEE+7dlaU -vnRbBAeVNHNHl5e68fvwpKgtvQhtg1rZ2w1foSkAyyNRkYrUZKe4ztUx9E2w9qIm -3OkZyMcPTKYkBVahR6K1bCo69uaUrxY4NaYlPfKdJmGfio/J2WGdqLq9na4iHRyY -pb5zKvYmH9cNpmn5V42yhmX7tjMJzUyWw8KxXpE/qEVB2wl11wNguEL8CaZy+3u0 -iaCqbQIDAQABo4H7MIH4MB0GA1UdDgQWBBQA8phNISwAPECbhPTeKvAm7jIOnzCB -yAYDVR0jBIHAMIG9gBQA8phNISwAPECbhPTeKvAm7jIOn6GBmaSBljCBkzELMAkG -A1UEBhMCVVMxCzAJBgNVBAgTAk1BMRIwEAYDVQQHEwlXYWtlZmllbGQxJzAlBgNV -BAoTHlRoZSBBcGFjaGUgU29mdHdhcmUgRm91bmRhdGlvbjEaMBgGA1UECxMRQXBh -Y2hlIFRvbWNhdCBQTUMxHjAcBgNVBAMTFUFwYWNoZSBUb21jYXQgVGVzdCBDQYIJ -AL51xu6EZW62MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAAJx4rzn -rtrcic/F0keS2BSazERFmRlSOlHUsREk+6fq35h7ktnijBHsPXyLEPa5w9x5qJZf -a2zFGiiiHqRBiX1E8JhKR6jjcX3D3VODfLomXWInHgEDcwNNcnvspG0RUX2jUh7m -p1i1c32r1s51P0AEB4zmT6KZ7gAZThqwtkpf6FQXmKdVXQFbf8EP0+6HFpHhV4lc -Ee4tDGJnc8X59Yzhu2rg8tF8OmNwcccTXthCH4I/4wymbw6YLg/B/V7AXH1/lui3 -B15MKabYgZOU3TeOmQ9sqFPztekEKe+sE3Mvdf90Fh4EBZCENWULGUJE9uVJuT8S -2WVGOMmIkDlMP0t8Wnb3gMwUzhGyWp2FjzixVg8vS85ZE5wX4kGPD6nx+cAPDKrd -j3TCdr0VHoxVoGkzvijDjf6+aNhHp87VYSOZDQh1ToNgDFHum362iXt7n+ppu3u4 -LDG3c1ztmUjgGrki+bQvnVyeYSprNWO1houo7xvZ61gWtzo1jwvcOwU0NxWtQMAg -NLZeketZSAL2834Xhkj1tjP2HT5HffkYbg6QRWKPYk/vBUKU40VilDCXf2ieOR9A -UtbcjjB5dRbR0CTnbwu33XeuhqobhaaAbp9gGt71WnOZpKIrkvVG3Z+YLpotRiYd -cl3dVVqvg/CTCpwd/VOOAmW1ynLpflLR8rH/ +MIIGwjCCBKqgAwIBAgIUb23jgHGhJDQJaz0iHUfKwRV5z5kwDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWlu +Z3RvbjEnMCUGA1UEChMeVGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRow +GAYDVQQLExFBcGFjaGUgVG9tY2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNh +dCBUZXN0IENBMB4XDTI1MTIwNTE5MTIzOFoXDTM1MTIwMzE5MTIzOFowgZQxCzAJ +BgNVBAYTAlVTMQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUG +A1UEChMeVGhlIEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFB +cGFjaGUgVG9tY2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENB +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqeIoIFXsR468dJCUYnNX +E4DLI9J2jLIz/T1Jiq/tA9XCzA+XFqf3tlZdXLsfQhDLg0lf+wHqQfSvbQ0Gcatn +Iw8BIvA7yq6PDKsO4cjQ2EdrkTn2JD7hJW5uUB9Je95TUUbKuNxOatihwS4DNU2J +aoI2R2iUGasXsawVR+4qj1UJByawlsGHxFCqhVoaUKM/fKXK7LllbyHsRxuZTgab +Spmvj2jcMxyeDN2ZjlBdsrSg+8FpYvnPKzHpdFiA6d4pxU6QJbeDeWPQ6B64yWi1 +eImFvilqZQoQ3RR/1gDjoFuLCdWK7mAvfaSXFzax1YK3D/MiCowTZ8M0iWmGqMg1 +QF6luhPL+5p8Q2/KooE3CZol0IpCUGr3cQs3FC1deL5ySNMYniXQMgxGqVE3x0i1 +Wlf5bItGY0Ha9vc+s4vmnRZxW4O+fXiuG9OXS5XjfWGAjzkJ6wXgFRJKbWDsSPkR +Sw9j9SAsLE6cycY66eMl94QdOl6KRXVyeaLHJbRMBux7WvDXYS/eWKFHjmPYNKq6 +btIB7ELTqoMuwTAWj83bdkQgMac5fsTozKduM+C63sSg3mhmyy6yVgCX1eECqPNk +N4K4oWdhQvKYvCp1n1EySAupZqrxudBLp/LDHRfGh7gfAZgP8hxRaoBqh2u0H8Fh +RwSAML8zEeRH/si3G7LfXzECAwEAAaOCAQgwggEEMB0GA1UdDgQWBBSsh9yLPYrb +iUh+bzscxnCSCRdv+TCB1AYDVR0jBIHMMIHJgBSsh9yLPYrbiUh+bzscxnCSCRdv ++aGBmqSBlzCBlDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkRFMRMwEQYDVQQHEwpX +aWxtaW5ndG9uMScwJQYDVQQKEx5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRp +b24xGjAYBgNVBAsTEUFwYWNoZSBUb21jYXQgUE1DMR4wHAYDVQQDExVBcGFjaGUg +VG9tY2F0IFRlc3QgQ0GCFG9t44BxoSQ0CWs9Ih1HysEVec+ZMAwGA1UdEwQFMAMB +Af8wDQYJKoZIhvcNAQELBQADggIBAA/DyqPhxRAluY1e1S9L2SkkrcYePjb033Wl +ZmMVdXvf6LZtpjquNz81ThNoX5yfgV6CawDRw7COI4dAU3HOLVtOeKybm11Jq1BH +AWmlZVQIOplgnitoEoZi8brxEpSgeyRGLKHYaW1YxshPRC5tVDKsbyYBtvmTaBtK +dMirc36cEDkSfEe2dq+MkHgzHyyvySqTSlI/MH7CKQYBjGg2D+Gcle/BNrK02wef +Rol9M3R3skDfV/P0NFmrfFi4B/cXt/EFxm1thMsQ6ofd0GUMGgv6fJKN1AVDihx6 +sQvbn712+54ilkcWMa3gW5L/BIxcDS+ZZiO/7T2tZ2d3tcz86gYbFkiQbWelnTmn +FBSHno9PDgxRWZzqauKqlsczAsz5qLf8zF+GDubEcxvOgEf0wQ/vPwtSb2R4egb+ +3Vn+InqDy9/78ug497KV842jxQy2G6wZLND0q81H04t8NYQopbX4DI0hS5BJnKuV +TM3rh4C/hNXs8kUpjusqnnV8nbBpab2R1YzEOefhgtTFdk/dHI7a5+6BKNSK55yn +hDqgemD5p+5A6FLu3M0RDgbTZ1WdrtYDvQZrAMB10mrV0YDoDgnEzGYEc/PQwkYF +gOeqb97mOdjf9YUJli4S4XLkZ5zn/IitEQRbnKen2VE60qIgW/UMYKTiwuSZtRaW +gxBAeoi3 -----END CERTIFICATE----- Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/org/apache/tomcat/util/net/ca.jks and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/ca.jks differ diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/index.db tomcat10-10.1.52/test/org/apache/tomcat/util/net/index.db --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/index.db 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/index.db 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,7 @@ +V 271205191323Z 1000 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=localhost +R 271205191327Z 251205192921Z 1001 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=localhost +V 271205191331Z 1002 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=user1 +R 271205191335Z 251205192924Z 1003 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=user2 +V 271205191540Z 1004 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=localhost +V 271205193355Z 1005 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=OCSP Responder +R 271210104820Z 251210105017Z 1006 unknown /C=US/ST=DE/L=Wilmington/O=The Apache Software Foundation/OU=Apache Tomcat PMC/CN=user3 diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/index.db.attr tomcat10-10.1.52/test/org/apache/tomcat/util/net/index.db.attr --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/index.db.attr 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/index.db.attr 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1 @@ +unique_subject = no diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-crl-rsa-cert.pem tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-crl-rsa-cert.pem --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-crl-rsa-cert.pem 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-crl-rsa-cert.pem 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,108 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4097 (0x1001) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA + Validity + Not Before: Dec 5 19:13:27 2025 GMT + Not After : Dec 5 19:13:27 2027 GMT + Subject: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:aa:f2:ee:01:dd:21:fd:4b:f6:1e:9f:a1:a6:65: + 42:71:1c:db:cf:d7:47:6d:20:81:63:01:c7:6c:a2: + 5a:2e:17:0d:5d:d9:60:78:e5:d9:2a:9a:c0:2d:1c: + 2d:24:9d:b3:f6:26:a9:93:80:86:ad:0e:b0:06:4e: + 64:dc:11:da:70:a0:eb:47:aa:9d:39:3e:10:1c:b5: + fe:bc:ca:b2:5b:b8:3f:0c:a8:d4:b3:cb:9e:dd:f5: + 19:0e:dc:83:34:54:1d:17:b9:0d:2e:5d:4c:31:d7: + 9b:64:e9:40:89:de:77:75:ad:6e:86:3e:46:15:88: + ed:48:88:bb:a2:77:a5:05:4a:33:33:3e:bb:cb:7b: + f7:00:9b:eb:4e:2b:f5:4a:59:07:ff:18:5a:a1:49: + c2:25:48:21:03:55:64:da:d7:75:0f:e8:55:a8:61: + 39:ca:af:09:64:c2:c1:67:4b:ec:95:df:66:50:2d: + 39:45:d6:4c:8d:07:a8:3d:d1:fc:3c:46:76:92:2f: + 1a:b0:27:f0:0c:e8:19:2b:c6:b4:ad:62:c4:c6:21: + 7c:23:17:c4:13:a4:0e:7d:d3:05:3b:0b:43:ab:43: + 32:88:12:67:91:1b:01:e4:98:90:fc:67:88:d7:8b: + 8e:6b:f0:2a:59:62:d1:dc:24:38:28:8c:b7:e9:1b: + 7b:27 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Netscape Comment: + OpenSSL Generated Certificate + X509v3 Subject Key Identifier: + CB:A6:D2:18:07:CE:1A:E1:A7:F1:DC:FB:4C:A0:2C:85:AE:A5:40:A2 + X509v3 Authority Key Identifier: + AC:87:DC:8B:3D:8A:DB:89:48:7E:6F:3B:1C:C6:70:92:09:17:6F:F9 + Authority Information Access: + OCSP - URI:http://127.0.0.1:8888 + X509v3 Subject Alternative Name: + DNS:localhost, IP Address:127.0.0.1 + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 11:8b:03:aa:50:38:53:88:71:66:01:2f:d5:ab:82:49:ba:4d: + 45:5a:20:b6:f7:6e:1f:ca:77:ce:f7:93:85:ec:4d:da:ba:17: + 71:dc:30:7b:7f:55:35:07:88:a2:4a:47:06:9c:8c:1b:a0:33: + 5f:46:dd:c9:c6:f5:0e:27:48:6e:1f:0b:a0:e8:17:e4:16:3b: + d0:e3:0b:56:4f:5c:5f:b7:ae:fb:da:46:13:0b:fc:e8:3d:72: + 7e:ab:2d:19:4d:6c:09:8b:32:51:9a:7a:b8:49:70:7e:ce:ce: + 78:7f:95:9c:3a:8e:05:cc:29:af:0b:de:42:b8:cd:5e:56:a8: + 2f:78:ea:19:67:ed:a7:59:c0:ce:0a:4c:50:46:67:9a:6e:72: + 6b:fd:ae:00:a6:2f:9a:93:97:39:58:06:b4:05:a3:78:a9:b6: + df:66:22:0d:ae:5b:fd:e8:37:78:25:ac:2e:5a:a0:65:56:3e: + 83:04:bf:98:96:30:e1:11:f9:da:3b:6a:97:c4:77:28:d6:d7: + 46:27:a4:05:67:2e:f5:b8:d4:2e:c7:00:39:49:08:9b:a7:58: + 21:48:4c:fb:8f:dd:99:69:ea:59:bc:41:96:fd:1a:29:1a:fe: + 53:16:6a:2b:ac:92:51:18:ac:85:d9:7d:d5:77:ad:60:63:98: + 47:2c:52:ec:85:ef:ad:ff:b0:58:d7:12:7b:6f:70:2b:93:2a: + 92:f8:99:3c:15:7a:26:d4:79:16:05:93:9a:f4:01:bc:e6:9b: + a0:31:c3:d7:12:23:47:12:81:30:02:06:0c:13:ca:38:4c:63: + 96:2d:17:41:53:06:8b:c5:a3:8c:c3:96:b7:e2:b5:85:49:f7: + cd:46:01:21:ff:4f:d1:65:be:37:1b:84:7c:79:a6:e1:a8:a8: + d7:8c:63:34:35:45:d5:42:10:03:0f:60:fa:52:c5:39:03:26: + b1:41:6c:b9:53:3c:cf:8d:5d:fa:02:ac:0a:7a:04:27:28:df: + 33:b2:01:69:d8:3e:72:38:57:60:d3:a8:47:5f:98:55:8c:55: + 15:36:58:6e:c7:fc:65:a5:f8:c7:7f:1a:8c:de:b7:79:21:50: + 98:76:69:8a:b3:48:b0:1d:b2:91:fd:25:8f:bd:59:17:81:22: + bc:67:32:d5:fb:e6:c3:cd:3c:b3:79:6f:cd:1c:4a:b0:04:13: + 3c:af:80:53:7c:f0:c6:28:f3:37:3e:36:4d:65:ef:38:dc:00: + 5f:88:7e:04:34:75:66:fa:ba:6c:1f:76:b2:55:bb:21:60:fe: + b8:48:f8:ac:cb:6e:16:2f:75:87:44:d0:c3:6d:d5:6a:e1:e6: + b9:32:7e:39:a6:e6:d0:7f +-----BEGIN CERTIFICATE----- +MIIFZjCCA06gAwIBAgICEAEwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT +MQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhl +IEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9t +Y2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4XDTI1MTIw +NTE5MTMyN1oXDTI3MTIwNTE5MTMyN1owgYgxCzAJBgNVBAYTAlVTMQswCQYDVQQI +EwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhlIEFwYWNoZSBT +b2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9tY2F0IFBNQzES +MBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAqvLuAd0h/Uv2Hp+hpmVCcRzbz9dHbSCBYwHHbKJaLhcNXdlgeOXZKprALRwt +JJ2z9iapk4CGrQ6wBk5k3BHacKDrR6qdOT4QHLX+vMqyW7g/DKjUs8ue3fUZDtyD +NFQdF7kNLl1MMdebZOlAid53da1uhj5GFYjtSIi7onelBUozMz67y3v3AJvrTiv1 +SlkH/xhaoUnCJUghA1Vk2td1D+hVqGE5yq8JZMLBZ0vsld9mUC05RdZMjQeoPdH8 +PEZ2ki8asCfwDOgZK8a0rWLExiF8IxfEE6QOfdMFOwtDq0MyiBJnkRsB5JiQ/GeI +14uOa/AqWWLR3CQ4KIy36Rt7JwIDAQABo4HLMIHIMAkGA1UdEwQCMAAwLAYJYIZI +AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW +BBTLptIYB84a4afx3PtMoCyFrqVAojAfBgNVHSMEGDAWgBSsh9yLPYrbiUh+bzsc +xnCSCRdv+TAxBggrBgEFBQcBAQQlMCMwIQYIKwYBBQUHMAGGFWh0dHA6Ly8xMjcu +MC4wLjE6ODg4ODAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcN +AQELBQADggIBABGLA6pQOFOIcWYBL9Wrgkm6TUVaILb3bh/Kd873k4XsTdq6F3Hc +MHt/VTUHiKJKRwacjBugM19G3cnG9Q4nSG4fC6DoF+QWO9DjC1ZPXF+3rvvaRhML +/Og9cn6rLRlNbAmLMlGaerhJcH7Oznh/lZw6jgXMKa8L3kK4zV5WqC946hln7adZ +wM4KTFBGZ5pucmv9rgCmL5qTlzlYBrQFo3iptt9mIg2uW/3oN3glrC5aoGVWPoME +v5iWMOER+do7apfEdyjW10YnpAVnLvW41C7HADlJCJunWCFITPuP3Zlp6lm8QZb9 +Gika/lMWaiusklEYrIXZfdV3rWBjmEcsUuyF763/sFjXEntvcCuTKpL4mTwVeibU +eRYFk5r0Abzmm6Axw9cSI0cSgTACBgwTyjhMY5YtF0FTBovFo4zDlrfitYVJ981G +ASH/T9FlvjcbhHx5puGoqNeMYzQ1RdVCEAMPYPpSxTkDJrFBbLlTPM+NXfoCrAp6 +BCco3zOyAWnYPnI4V2DTqEdfmFWMVRU2WG7H/GWl+Md/Gozet3khUJh2aYqzSLAd +spH9JY+9WReBIrxnMtX75sPNPLN5b80cSrAEEzyvgFN88MYo8zc+Nk1l7zjcAF+I +fgQ0dWb6umwfdrJVuyFg/rhI+KzLbhYvdYdE0MNt1Wrh5rkyfjmm5tB/ +-----END CERTIFICATE----- diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-crl-rsa-key.pem tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-crl-rsa-key.pem --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-crl-rsa-key.pem 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-crl-rsa-key.pem 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCq8u4B3SH9S/Ye +n6GmZUJxHNvP10dtIIFjAcdsolouFw1d2WB45dkqmsAtHC0knbP2JqmTgIatDrAG +TmTcEdpwoOtHqp05PhActf68yrJbuD8MqNSzy57d9RkO3IM0VB0XuQ0uXUwx15tk +6UCJ3nd1rW6GPkYViO1IiLuid6UFSjMzPrvLe/cAm+tOK/VKWQf/GFqhScIlSCED +VWTa13UP6FWoYTnKrwlkwsFnS+yV32ZQLTlF1kyNB6g90fw8RnaSLxqwJ/AM6Bkr +xrStYsTGIXwjF8QTpA590wU7C0OrQzKIEmeRGwHkmJD8Z4jXi45r8CpZYtHcJDgo +jLfpG3snAgMBAAECggEAGwmoPlA454SjrU1HNneujhBn7dQZXne1LzEhVxvIkqCf +wxe2C/kio3vtaFUcCL4OsCCGUqeR5X48tgqhJjOGrqis/d1M17hquPfKDhcrJZmb +E0gCuFs8ydNRvsl+0QB6x31DyfEEs59r2waPaB7xGwIuyHnCAPbyvLWvo68zwQWs +q3YF8Z03wPoSVxx9IrIsi01vaZGYREXmsNEX5krvd+Ku2JsQJ9NreduMg/gtegbB +snN9MpjbydRWnwql4YzOCPaaGlQltrUFixKw6InZJRkKaSri49gEseaiSqwx31mY +RvnWtyZfq1qRkcQm+NC9WO3cyCH38Um1egQmVpmguQKBgQDUlfAuAotMDN9aFnQ5 +DUviMyVy+ea1SDvJ3TnoaplZ4vp9pmwX5B2RPz8MzHKVLhll9h6GjAIXZuLT2Fed +OyGUhM0Cd//+5esp0dHMKQFdzVMrhn8eGPPPBReEZtILbi3ulRJI6nsagAmKnLLN +bik5fbcsc1KhtIUzGFbrw9ThWQKBgQDN3DTFrxP50XLc1hfs3OjP3OqZ7mqMyVXq +JFJ4JNiFCFk/Jhqz83IuxrzoWGVmtnDjj9sdN/TsClG337xD9L3OgpS1yjjbzhi5 +wYG4cCjNxpqis097LFHqZDBBSZ9d3SWsdENqSWPbWOVLZQKrwR239m3mjCipSewI +JRnX+1cwfwKBgQCrySoYFAg6gWBvrRtoCv2aIZiOi7DKJz/hRPsJfDLFtyySIszQ +lY7rE3/AuOmS8XimszdBpJwACy0a4YUTUng1Swdbilr8wRDCb4Ioh65J/aTK1Fme +ma1TShsR7ACqKfPGCwKGl6y44mRTdYLrjKyVf6horBxG/dhxTKbYyBwbqQKBgQDE +vQPuPXFg2ivlI/L/muwg46eN704DONOUDpyGV+hZ022/rUHt4uaoD6UwhHJ8ZDWl +NbGZzgWTpBUPHpMFexv/BcrrpdULNH9q36WCyCYm6vyUK7v1Ipky4gdADgVxpk0/ +8GkRZgw58E5K7MFNtiUZ0DieEis2BwC9k/+L65gbLwKBgG87Ag/C0un6LYMHI8gT +DyqrsjARdlwVeyL864p3GHQbfGeQ2VTxSriEPHwSc2H1FtRsFo06P98DBjVW1xDD +MPY8PD9MZ347rnNi1lcG42moxY6ywYesRwKaDPC98PgvaXzmm81wSyjOerwDQubi +UQ6bYhVLe3BdVfFI8N8kSRzu +-----END PRIVATE KEY----- Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-crl-rsa.jks and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-crl-rsa.jks differ diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-ec-cert.pem tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-ec-cert.pem --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-ec-cert.pem 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-ec-cert.pem 2026-01-23 19:33:36.000000000 +0000 @@ -1,22 +1,22 @@ Certificate: Data: Version: 3 (0x2) - Serial Number: 4110 (0x100e) + Serial Number: 4100 (0x1004) Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=MA, L=Wakefield, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA + Issuer: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA Validity - Not Before: Feb 17 16:05:19 2023 GMT - Not After : Feb 16 16:05:19 2025 GMT + Not Before: Dec 5 19:15:40 2025 GMT + Not After : Dec 5 19:15:40 2027 GMT Subject: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=localhost Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: - 04:38:6d:73:27:ea:ab:41:cc:7b:ac:36:18:94:ab: - 1f:de:f3:aa:35:f4:ef:8e:2e:60:39:63:5c:08:75: - cf:86:aa:4d:ef:40:85:16:40:d1:d5:a5:36:2c:25: - 05:20:8d:f5:ff:2b:a9:cf:5d:4b:b9:e1:b0:d4:9e: - 81:be:2a:cb:8d + 04:4a:67:a8:52:9a:a4:a8:54:e0:0a:26:01:7b:c8: + 20:fc:99:99:09:b9:ce:a4:11:bd:99:0f:9d:73:b3: + 82:ef:9f:a9:73:31:bf:e5:f2:10:b2:b4:91:82:e1: + d5:9e:91:4b:e4:f2:ce:87:e1:a6:ee:6b:b5:68:39: + c0:c0:9a:e7:5d ASN1 OID: prime256v1 NIST CURVE: P-256 X509v3 extensions: @@ -25,62 +25,66 @@ Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: - A8:26:CE:0D:E5:8E:35:2E:AC:B0:5B:E1:89:42:FA:64:26:F3:4C:69 + AD:35:5C:21:09:CA:97:51:8F:CC:F2:33:B0:86:80:DF:73:4F:FD:73 X509v3 Authority Key Identifier: - 00:F2:98:4D:21:2C:00:3C:40:9B:84:F4:DE:2A:F0:26:EE:32:0E:9F + AC:87:DC:8B:3D:8A:DB:89:48:7E:6F:3B:1C:C6:70:92:09:17:6F:F9 + Authority Information Access: + OCSP - URI:http://127.0.0.1:8888 Signature Algorithm: sha256WithRSAEncryption Signature Value: - 40:ba:a4:ff:c2:df:92:55:88:e4:8e:35:9e:28:28:ce:f4:63: - f0:da:a2:3e:f4:af:68:60:fb:f3:d4:d8:9d:d4:d3:54:3e:dc: - 33:e4:24:74:0f:4b:af:fa:b5:bd:56:5c:dd:0d:86:7f:99:b7: - fc:84:8c:88:b9:f5:50:f1:8f:ea:3c:7a:95:a6:6c:5e:20:9a: - 2d:a9:b5:35:d6:f4:37:23:1a:79:ce:e3:40:23:79:09:b4:08: - fc:22:3e:c4:d3:66:70:84:06:e8:54:21:b2:7a:22:ed:78:d3: - 9f:8f:da:92:2d:1c:0a:95:3f:b2:fe:18:50:60:73:ea:2f:8d: - cc:e8:73:52:16:38:76:ba:9c:2c:85:2c:a6:3d:30:fd:f8:a0: - da:6b:24:d8:a0:ad:d0:5c:d3:d3:a2:13:47:49:d8:49:1b:0d: - 56:fd:4a:4d:62:b7:32:f0:97:23:22:ea:a3:68:5a:c7:f3:56: - dc:bf:95:82:fe:8b:ff:10:f5:79:1a:e5:07:12:ca:46:75:42: - 0d:b2:2b:2e:26:39:72:08:0f:53:f4:d7:de:75:da:c3:a1:69: - 32:0e:eb:94:63:3f:7d:81:13:cf:c2:a6:ae:8f:02:3f:0d:a9: - 99:68:e1:ff:b1:17:c8:94:58:57:ae:cb:08:9e:88:1e:17:1d: - e3:8b:f7:a9:f5:fc:d5:0d:87:bd:6f:f3:26:b1:6b:cb:62:59: - d8:4d:2d:21:40:57:77:b2:53:05:4f:2c:b7:f2:f8:57:d5:93: - 85:00:1f:97:e3:6b:d0:05:1e:8e:4a:4f:02:98:3f:69:37:75: - 64:37:07:aa:d6:b3:00:bc:8f:52:e9:c9:8e:98:82:f5:6a:d8: - c2:94:dc:5f:2c:31:09:15:6a:62:05:6d:4c:9d:14:e8:a0:f4: - ae:f5:14:f3:61:28:13:55:bf:22:50:78:23:4f:80:34:7e:f2: - b0:89:e2:fa:6b:d2:00:b1:aa:71:a7:2f:1c:04:b9:1e:39:6c: - c5:fc:10:8a:4c:7f:d1:16:74:87:45:f2:11:a9:71:12:0f:2b: - 0e:d7:20:16:a7:a1:8a:36:a9:bc:2c:0d:b9:eb:e8:5b:bc:75: - e2:3b:84:28:3f:c0:c3:7d:87:99:e0:aa:42:ef:66:73:5f:ec: - be:14:c0:70:cf:46:23:c2:bf:ee:c1:c7:af:28:99:e2:09:23: - 4e:de:b5:be:d6:44:1c:fa:b0:77:ee:b3:7a:b4:a6:d7:e8:24: - a4:10:50:cb:94:2d:94:0e:52:19:d1:d3:88:b8:36:35:2b:c3: - af:82:a2:f8:4c:55:b2:6e:42:65:80:00:74:17:53:af:7a:f9: - 60:59:b7:3e:82:bc:2d:00 + 9a:74:81:f5:9d:87:bc:47:ea:33:a9:ef:d4:6b:6a:de:77:4e: + fc:41:c4:44:e7:ca:02:63:8b:a6:4f:b3:ed:14:44:8d:3f:29: + 3b:ee:2f:30:2b:31:bc:2f:88:64:ce:e7:4e:77:d7:d2:94:c6: + 31:98:ea:b8:c3:0c:23:e9:cd:00:99:2f:bd:41:bb:49:5d:ae: + f0:65:c7:b8:1b:c3:07:58:ea:ed:0e:ff:22:be:7b:0a:77:15: + 62:49:e0:7c:39:6a:9d:02:7d:b3:6d:94:eb:14:e6:75:1f:8a: + 40:cd:88:13:bd:42:03:49:18:ae:11:43:d5:f1:98:c5:a0:87: + 18:33:ae:84:c6:65:8f:61:89:be:54:d9:52:02:80:59:30:42: + df:02:d0:e5:44:9c:f5:36:28:3b:26:5c:a3:28:89:d2:b9:a3: + c7:2a:99:d3:86:b5:fd:13:6d:d7:f9:5d:7e:88:35:af:19:9e: + 4e:9c:8b:48:92:15:62:47:c7:c4:1e:25:4e:d5:8b:96:1f:7e: + cc:55:10:5f:82:1e:3f:b7:c7:ba:00:38:59:55:53:59:3c:f9: + 0b:2a:ab:d4:bf:0e:84:2b:71:61:be:14:4d:12:c1:8d:ab:8b: + 14:d7:af:b2:b4:6a:7e:b6:23:64:a2:7b:11:6b:ec:6b:1a:64: + ea:49:f0:a7:9f:d0:c9:47:71:fa:01:8a:3b:be:b9:6d:4a:d9: + 58:25:1a:a8:f6:88:35:e5:d4:98:53:48:62:f9:24:db:87:21: + bb:67:5f:b7:d1:60:0f:34:f5:63:f9:52:fd:f1:33:cb:5f:79: + 16:c8:e5:e4:c1:79:0c:d9:76:b4:44:56:ff:e7:86:a5:9f:dd: + 72:c6:15:4f:ed:6e:2d:1a:54:61:b3:5c:c7:26:10:98:9f:7e: + 77:0b:80:4c:a9:d7:bd:28:4c:3c:3b:05:67:e1:af:6d:1b:26: + d5:24:96:5f:01:69:84:37:1e:f2:30:3e:9d:e9:8a:fd:0f:28: + 56:87:d1:28:8f:32:5d:c9:96:f0:ec:14:b1:34:b9:0d:63:16: + 1b:26:e5:76:33:12:fe:52:f5:49:17:2d:14:06:fc:3f:4e:81: + b2:02:7d:e7:16:ae:62:d2:0f:1c:df:a1:50:a3:ff:3e:69:d5: + 7f:e7:c4:e7:6a:b8:93:b2:d8:99:9b:72:30:e9:e0:62:d6:c3: + c1:39:a0:1b:30:37:63:6e:b0:0f:47:20:71:8a:20:56:2f:2d: + a6:54:35:ee:72:38:fc:e2:bb:67:60:4f:f7:b8:79:cb:57:6e: + f3:7c:b3:55:03:01:41:29:48:ca:f6:a9:3a:4a:6a:26:cb:f7: + 21:54:fa:70:31:3d:95:d0 -----BEGIN CERTIFICATE----- -MIIESTCCAjGgAwIBAgICEA4wDQYJKoZIhvcNAQELBQAwgZMxCzAJBgNVBAYTAlVT -MQswCQYDVQQIEwJNQTESMBAGA1UEBxMJV2FrZWZpZWxkMScwJQYDVQQKEx5UaGUg -QXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21j -YXQgUE1DMR4wHAYDVQQDExVBcGFjaGUgVG9tY2F0IFRlc3QgQ0EwHhcNMjMwMjE3 -MTYwNTE5WhcNMjUwMjE2MTYwNTE5WjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgM -AkRFMRMwEQYDVQQHDApXaWxtaW5ndG9uMScwJQYDVQQKDB5UaGUgQXBhY2hlIFNv -ZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsMEUFwYWNoZSBUb21jYXQgUE1DMRIw -EAYDVQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ4bXMn -6qtBzHusNhiUqx/e86o19O+OLmA5Y1wIdc+Gqk3vQIUWQNHVpTYsJQUgjfX/K6nP -XUu54bDUnoG+KsuNo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu -U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUqCbODeWONS6ssFvh -iUL6ZCbzTGkwHwYDVR0jBBgwFoAUAPKYTSEsADxAm4T03irwJu4yDp8wDQYJKoZI -hvcNAQELBQADggIBAEC6pP/C35JViOSONZ4oKM70Y/Daoj70r2hg+/PU2J3U01Q+ -3DPkJHQPS6/6tb1WXN0Nhn+Zt/yEjIi59VDxj+o8epWmbF4gmi2ptTXW9DcjGnnO -40AjeQm0CPwiPsTTZnCEBuhUIbJ6Iu1405+P2pItHAqVP7L+GFBgc+ovjczoc1IW -OHa6nCyFLKY9MP34oNprJNigrdBc09OiE0dJ2EkbDVb9Sk1itzLwlyMi6qNoWsfz -Vty/lYL+i/8Q9Xka5QcSykZ1Qg2yKy4mOXIID1P019512sOhaTIO65RjP32BE8/C -pq6PAj8NqZlo4f+xF8iUWFeuywieiB4XHeOL96n1/NUNh71v8yaxa8tiWdhNLSFA -V3eyUwVPLLfy+FfVk4UAH5fja9AFHo5KTwKYP2k3dWQ3B6rWswC8j1LpyY6YgvVq -2MKU3F8sMQkVamIFbUydFOig9K71FPNhKBNVvyJQeCNPgDR+8rCJ4vpr0gCxqnGn -LxwEuR45bMX8EIpMf9EWdIdF8hGpcRIPKw7XIBanoYo2qbwsDbnr6Fu8deI7hCg/ -wMN9h5ngqkLvZnNf7L4UwHDPRiPCv+7Bx68omeIJI07etb7WRBz6sHfus3q0ptfo -JKQQUMuULZQOUhnR04i4NjUrw6+CovhMVbJuQmWAAHQXU696+WBZtz6CvC0A +MIIEfzCCAmegAwIBAgICEAQwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT +MQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhl +IEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9t +Y2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4XDTI1MTIw +NTE5MTU0MFoXDTI3MTIwNTE5MTU0MFowgYgxCzAJBgNVBAYTAlVTMQswCQYDVQQI +DAJERTETMBEGA1UEBwwKV2lsbWluZ3RvbjEnMCUGA1UECgweVGhlIEFwYWNoZSBT +b2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLDBFBcGFjaGUgVG9tY2F0IFBNQzES +MBAGA1UEAwwJbG9jYWxob3N0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESmeo +UpqkqFTgCiYBe8gg/JmZCbnOpBG9mQ+dc7OC75+pczG/5fIQsrSRguHVnpFL5PLO +h+Gm7mu1aDnAwJrnXaOBrzCBrDAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1P +cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUrTVcIQnKl1GP +zPIzsIaA33NP/XMwHwYDVR0jBBgwFoAUrIfciz2K24lIfm87HMZwkgkXb/kwMQYI +KwYBBQUHAQEEJTAjMCEGCCsGAQUFBzABhhVodHRwOi8vMTI3LjAuMC4xOjg4ODgw +DQYJKoZIhvcNAQELBQADggIBAJp0gfWdh7xH6jOp79Rrat53TvxBxETnygJji6ZP +s+0URI0/KTvuLzArMbwviGTO505319KUxjGY6rjDDCPpzQCZL71Bu0ldrvBlx7gb +wwdY6u0O/yK+ewp3FWJJ4Hw5ap0CfbNtlOsU5nUfikDNiBO9QgNJGK4RQ9XxmMWg +hxgzroTGZY9hib5U2VICgFkwQt8C0OVEnPU2KDsmXKMoidK5o8cqmdOGtf0Tbdf5 +XX6INa8Znk6ci0iSFWJHx8QeJU7Vi5YffsxVEF+CHj+3x7oAOFlVU1k8+Qsqq9S/ +DoQrcWG+FE0SwY2rixTXr7K0an62I2SiexFr7GsaZOpJ8Kef0MlHcfoBiju+uW1K +2VglGqj2iDXl1JhTSGL5JNuHIbtnX7fRYA809WP5Uv3xM8tfeRbI5eTBeQzZdrRE +Vv/nhqWf3XLGFU/tbi0aVGGzXMcmEJiffncLgEyp170oTDw7BWfhr20bJtUkll8B +aYQ3HvIwPp3piv0PKFaH0SiPMl3JlvDsFLE0uQ1jFhsm5XYzEv5S9UkXLRQG/D9O +gbICfecWrmLSDxzfoVCj/z5p1X/nxOdquJOy2JmbcjDp4GLWw8E5oBswN2NusA9H +IHGKIFYvLaZUNe5yOPziu2dgT/e4ectXbvN8s1UDAUEpSMr2qTpKaibL9yFU+nAx +PZXQ -----END CERTIFICATE----- diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-ec-key.pem tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-ec-key.pem --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-ec-key.pem 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-ec-key.pem 2026-01-23 19:33:36.000000000 +0000 @@ -2,7 +2,7 @@ BggqhkjOPQMBBw== -----END EC PARAMETERS----- -----BEGIN EC PRIVATE KEY----- -MHcCAQEEIKJoMeaqKfUg2NywCJdy61UF8pk3kAO73SICW8i1/9PaoAoGCCqGSM49 -AwEHoUQDQgAEOG1zJ+qrQcx7rDYYlKsf3vOqNfTvji5gOWNcCHXPhqpN70CFFkDR -1aU2LCUFII31/yupz11LueGw1J6BvirLjQ== +MHcCAQEEIF9PqIbcIMlMOKkaAID2L1ULbJApzzOj3WlBYuyZQqaaoAoGCCqGSM49 +AwEHoUQDQgAESmeoUpqkqFTgCiYBe8gg/JmZCbnOpBG9mQ+dc7OC75+pczG/5fIQ +srSRguHVnpFL5PLOh+Gm7mu1aDnAwJrnXQ== -----END EC PRIVATE KEY----- Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-ec.jks and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-ec.jks differ diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-rsa-cert.pem tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa-cert.pem --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-rsa-cert.pem 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa-cert.pem 2026-01-23 19:33:36.000000000 +0000 @@ -1,35 +1,35 @@ Certificate: Data: Version: 3 (0x2) - Serial Number: 4109 (0x100d) + Serial Number: 4096 (0x1000) Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=MA, L=Wakefield, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA + Issuer: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA Validity - Not Before: Feb 17 16:03:54 2023 GMT - Not After : Feb 16 16:03:54 2025 GMT + Not Before: Dec 5 19:13:23 2025 GMT + Not After : Dec 5 19:13:23 2027 GMT Subject: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=localhost Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: - 00:f2:2e:c8:d3:25:64:2f:76:ae:51:35:45:e4:7a: - 5e:2a:ff:01:21:d7:08:5a:ec:2a:cb:03:7d:da:4d: - 10:6e:74:83:e1:e9:79:6f:48:bd:d7:90:46:55:ab: - df:b7:c1:dd:3a:a6:c5:ae:fd:c0:9f:8c:1e:fb:b3: - 3b:b0:9a:d6:fb:6e:05:51:e7:25:d8:ae:f8:5c:fa: - 56:c2:7b:c6:5e:5b:bd:8a:6d:e9:46:ec:db:f1:8d: - 0b:26:1a:ff:9a:30:27:06:63:a1:d8:16:40:b1:d8: - 15:59:61:a5:7a:6f:1a:6a:e3:11:05:54:9d:25:3c: - 0d:fa:6c:8b:9a:6a:93:6f:9f:c9:15:2a:73:9c:cc: - c7:2c:5b:b2:5f:fd:ad:5d:66:4a:5d:75:f0:ed:e1: - 61:e6:0a:96:4b:a3:a8:71:d7:9b:cb:04:d0:f2:e1: - 2a:1a:f1:42:67:53:f9:ee:00:97:80:f8:47:fb:4b: - 6e:01:63:9c:cc:41:5b:25:75:b8:ca:ca:b9:f7:1b: - 52:bf:a7:68:c7:eb:a2:f4:ff:7a:9b:5b:bc:fd:fa: - cf:9b:15:3a:33:32:75:f7:ca:40:42:f9:a8:2d:eb: - cb:b7:a0:64:0d:89:08:1c:2e:38:56:45:24:4f:ad: - 94:19:e2:35:43:2c:fe:31:dc:38:b4:c1:10:f6:a1: - 6a:75 + 00:b1:da:2e:50:5c:5b:22:db:84:61:5e:e4:9d:e1: + a4:24:6c:92:d3:96:5a:77:52:c8:f7:cf:16:b5:60: + 80:a7:15:0e:12:75:1a:bc:3f:0b:54:38:96:f6:2f: + 0a:38:6c:21:68:da:13:49:a2:7b:7c:80:c7:aa:69: + fe:e3:8c:b1:ce:73:e6:5b:80:e2:36:89:49:5b:03: + ce:76:47:bf:35:20:ee:c6:78:df:62:aa:d1:d7:bf: + 91:b0:fc:aa:fd:7f:59:3f:8a:51:9f:7d:59:0f:e1: + d6:dc:7b:16:80:62:3f:6c:c2:2c:1b:a5:05:1e:40: + 90:60:20:e1:c3:6d:3f:ff:1c:73:84:92:86:59:03: + e5:4a:f6:d6:dc:e6:6e:68:1f:05:5f:f7:21:a9:fc: + 5e:e6:fc:8b:c7:36:34:18:fc:d5:70:21:54:5a:f1: + 10:c9:24:c8:08:92:2c:91:13:54:b1:d9:59:70:80: + 39:9f:8c:90:ad:c9:d2:b8:bd:a3:28:20:3f:67:35: + a2:d9:20:e2:3f:2e:be:3d:88:d2:3b:7d:3b:47:44: + 79:53:e2:52:98:72:13:ae:a2:9d:fe:68:bd:36:8c: + 2e:43:b1:3b:cd:50:35:3d:b5:f7:2f:43:ea:1e:c9: + d1:10:ac:e8:24:c1:8c:ee:e6:d0:c2:06:de:14:2d: + 1a:89 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: @@ -37,69 +37,72 @@ Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: - 5D:45:8F:DA:02:13:CC:A4:01:75:0C:BB:72:E2:3C:EA:FB:AA:A9:09 + 70:34:99:AD:29:E3:27:9D:BD:97:72:50:20:98:D2:97:B3:6C:AD:A1 X509v3 Authority Key Identifier: - 00:F2:98:4D:21:2C:00:3C:40:9B:84:F4:DE:2A:F0:26:EE:32:0E:9F + AC:87:DC:8B:3D:8A:DB:89:48:7E:6F:3B:1C:C6:70:92:09:17:6F:F9 + Authority Information Access: + OCSP - URI:http://127.0.0.1:8888 X509v3 Subject Alternative Name: DNS:localhost, IP Address:127.0.0.1 Signature Algorithm: sha256WithRSAEncryption Signature Value: - 0c:21:e7:d7:33:bb:6c:65:01:91:54:fa:0b:1c:c5:c9:83:51: - 02:03:a9:e7:c4:dc:30:4e:90:a8:73:8f:a8:ca:4e:2c:5a:ef: - 87:43:a2:de:3f:db:21:ea:60:7c:e4:a1:d4:75:66:3a:e9:44: - af:e0:4a:2c:4e:59:bc:1f:af:5c:8c:8a:ed:06:ee:56:0a:cf: - 55:08:6a:51:90:ce:cf:cc:b9:22:95:3a:91:78:a5:84:96:4b: - 2b:a0:bf:7d:72:f7:01:b5:a1:ca:62:04:2e:73:d5:2c:30:fa: - 98:e3:89:35:9d:e6:d1:b2:79:19:55:dc:52:56:99:84:9f:4a: - f0:16:d0:27:9b:12:13:55:9c:fd:f1:60:fa:21:79:58:69:cd: - 4f:0a:a4:5f:1c:9a:2f:fb:10:2c:94:95:13:54:3c:08:61:fd: - 43:8b:de:1f:c8:0b:c6:c5:92:3e:97:f8:c3:56:8b:98:7e:23: - dd:73:c8:24:6d:39:e8:5b:eb:c9:15:60:dd:1e:d7:9a:62:bb: - 34:28:27:f5:59:de:8e:4a:d9:0f:3f:1c:95:39:c3:ab:82:d9: - 02:a0:d7:c4:85:39:f1:24:96:3a:e4:7a:cd:06:6b:93:dd:86: - 47:00:e6:2b:c8:f2:c4:d7:56:87:f2:25:2c:df:f8:21:62:86: - 19:b4:54:07:c2:4a:a8:f2:2e:0a:5b:9b:a6:51:31:c6:97:32: - 1b:e9:13:08:79:23:3b:f8:a4:28:79:3f:d0:76:0d:04:94:16: - 47:01:02:1d:44:ad:d4:54:22:30:a1:b9:f7:75:ff:c8:a1:91: - 78:f0:57:00:92:72:a3:45:81:10:10:77:4c:41:37:56:7a:a4: - 97:b2:1d:5f:b4:d2:67:00:25:1a:d4:08:18:a4:d8:4b:e6:3e: - 42:ac:f9:6b:72:46:bc:83:d7:aa:0f:e8:da:c5:1c:ce:55:1c: - 8b:d8:f2:3f:d4:e1:a0:f0:c0:d5:54:c1:7b:ac:3a:b8:2a:57: - 37:cf:55:2a:ce:c4:18:1d:97:b0:26:5b:b8:f4:bd:7f:5c:91: - 94:fa:fc:74:aa:e7:15:d1:2c:a9:12:bb:65:93:fe:39:62:82: - 50:d1:9d:dc:6f:e5:45:82:c0:fe:96:66:4a:5a:cf:55:82:56: - 92:20:12:8c:4b:da:35:f1:73:d9:02:cc:38:ab:1f:5e:60:56: - 9a:41:75:8e:a2:2e:33:25:3c:28:79:63:de:3b:06:75:26:9f: - 2e:b5:5b:24:32:00:48:5f:af:ee:5d:91:3f:24:42:6b:c8:22: - 76:41:00:b7:99:d8:05:4a:7a:50:44:bd:6f:d6:b0:fc:26:9d: - 1b:c1:8c:03:c8:26:79:e6 + 77:af:4a:b3:09:d8:9b:43:5a:73:78:10:ba:31:ad:a5:06:50: + 49:5d:14:f7:7b:1c:38:f0:3a:9b:67:b3:cb:71:52:7a:df:67: + fa:e1:c1:8b:18:dc:4e:bf:21:6d:58:b1:5f:df:2d:f1:ee:62: + a1:b6:78:dd:e2:c5:f2:4f:0b:9d:3d:cc:88:c7:26:8d:f0:41: + 02:ad:bd:e0:29:21:f9:0c:0a:07:cf:38:a4:f1:db:87:f7:e5: + ed:55:59:b5:34:7d:b4:39:70:ca:81:7a:bb:20:fa:2e:85:47: + 72:9c:35:f8:f2:e1:57:f9:9e:5c:9c:76:92:50:6a:c9:52:e9: + a0:d3:4d:41:03:f7:4f:bb:fc:5c:b8:aa:2c:7d:10:ff:3f:6c: + 82:e0:60:b8:d5:58:66:de:e0:db:30:b7:bf:3c:09:b9:3c:5c: + 36:cc:c6:d3:aa:58:9f:90:fe:e7:eb:5b:62:e2:66:a5:88:ec: + dd:2b:64:d1:25:08:e6:0a:49:91:5f:ac:1f:35:8d:2c:a2:71: + de:01:18:b0:0e:13:3d:cd:83:92:67:62:63:8a:63:bb:eb:8d: + f9:b7:95:b0:b6:a8:9c:14:8c:0b:00:34:8b:65:af:b7:6c:b9: + 1b:94:e2:54:3f:ee:87:0f:81:a9:3f:61:10:5f:ee:4d:42:b5: + 67:77:30:2e:04:4f:ad:5d:4e:de:e3:20:b3:8f:ca:46:98:7d: + 97:91:1d:4c:04:d9:bb:ae:02:3d:6f:55:dd:39:64:f3:53:86: + b6:4a:6a:eb:4a:7a:46:4e:0f:3b:44:8e:c2:46:c6:4b:07:1e: + b1:4e:8a:1c:fd:6a:06:55:2e:88:c6:85:8c:9b:c2:ea:90:e1: + 0b:d9:b1:af:2d:80:7f:b6:a2:c7:7a:63:7d:0a:cd:60:f6:46: + ea:40:3d:7d:4e:89:3e:0b:5d:22:8f:b7:3a:3a:38:18:26:a3: + 76:e9:cf:18:bc:c1:3c:8b:06:74:b6:f2:a8:a9:b4:cf:e1:96: + 54:bd:87:21:1d:20:b6:25:d1:d9:c6:fa:94:25:c6:6e:2b:1e: + a1:51:e3:31:ff:43:77:bd:e0:4c:6a:c2:24:97:43:99:f6:cb: + dd:3f:63:31:88:af:07:ef:e0:2b:3b:8e:3b:80:26:27:a6:c3: + 58:95:53:fa:39:14:59:42:3d:3a:e5:02:ff:0d:5b:e0:63:ad: + 93:72:8e:1d:55:c2:f9:49:7d:5c:cb:35:d2:11:b4:86:2d:26: + 61:74:1e:4d:e6:c7:97:e7:99:8b:fc:91:41:c8:9f:b8:ec:3e: + a3:f9:18:b5:2a:2e:1d:ee:2d:b6:cd:8f:14:be:b8:1c:6d:fd: + c3:59:61:66:87:e8:31:82 -----BEGIN CERTIFICATE----- -MIIFMjCCAxqgAwIBAgICEA0wDQYJKoZIhvcNAQELBQAwgZMxCzAJBgNVBAYTAlVT -MQswCQYDVQQIEwJNQTESMBAGA1UEBxMJV2FrZWZpZWxkMScwJQYDVQQKEx5UaGUg -QXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21j -YXQgUE1DMR4wHAYDVQQDExVBcGFjaGUgVG9tY2F0IFRlc3QgQ0EwHhcNMjMwMjE3 -MTYwMzU0WhcNMjUwMjE2MTYwMzU0WjCBiDELMAkGA1UEBhMCVVMxCzAJBgNVBAgT -AkRFMRMwEQYDVQQHEwpXaWxtaW5ndG9uMScwJQYDVQQKEx5UaGUgQXBhY2hlIFNv -ZnR3YXJlIEZvdW5kYXRpb24xGjAYBgNVBAsTEUFwYWNoZSBUb21jYXQgUE1DMRIw -EAYDVQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQDyLsjTJWQvdq5RNUXkel4q/wEh1wha7CrLA33aTRBudIPh6XlvSL3XkEZVq9+3 -wd06psWu/cCfjB77szuwmtb7bgVR5yXYrvhc+lbCe8ZeW72KbelG7NvxjQsmGv+a -MCcGY6HYFkCx2BVZYaV6bxpq4xEFVJ0lPA36bIuaapNvn8kVKnOczMcsW7Jf/a1d -ZkpddfDt4WHmCpZLo6hx15vLBNDy4Soa8UJnU/nuAJeA+Ef7S24BY5zMQVsldbjK -yrn3G1K/p2jH66L0/3qbW7z9+s+bFTozMnX3ykBC+agt68u3oGQNiQgcLjhWRSRP -rZQZ4jVDLP4x3Di0wRD2oWp1AgMBAAGjgZgwgZUwCQYDVR0TBAIwADAsBglghkgB -hvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYE -FF1Fj9oCE8ykAXUMu3LiPOr7qqkJMB8GA1UdIwQYMBaAFADymE0hLAA8QJuE9N4q -8CbuMg6fMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsF -AAOCAgEADCHn1zO7bGUBkVT6CxzFyYNRAgOp58TcME6QqHOPqMpOLFrvh0Oi3j/b -IepgfOSh1HVmOulEr+BKLE5ZvB+vXIyK7QbuVgrPVQhqUZDOz8y5IpU6kXilhJZL -K6C/fXL3AbWhymIELnPVLDD6mOOJNZ3m0bJ5GVXcUlaZhJ9K8BbQJ5sSE1Wc/fFg -+iF5WGnNTwqkXxyaL/sQLJSVE1Q8CGH9Q4veH8gLxsWSPpf4w1aLmH4j3XPIJG05 -6FvryRVg3R7XmmK7NCgn9VnejkrZDz8clTnDq4LZAqDXxIU58SSWOuR6zQZrk92G -RwDmK8jyxNdWh/IlLN/4IWKGGbRUB8JKqPIuClubplExxpcyG+kTCHkjO/ikKHk/ -0HYNBJQWRwECHUSt1FQiMKG593X/yKGRePBXAJJyo0WBEBB3TEE3Vnqkl7IdX7TS -ZwAlGtQIGKTYS+Y+Qqz5a3JGvIPXqg/o2sUczlUci9jyP9ThoPDA1VTBe6w6uCpX -N89VKs7EGB2XsCZbuPS9f1yRlPr8dKrnFdEsqRK7ZZP+OWKCUNGd3G/lRYLA/pZm -SlrPVYJWkiASjEvaNfFz2QLMOKsfXmBWmkF1jqIuMyU8KHlj3jsGdSafLrVbJDIA -SF+v7l2RPyRCa8gidkEAt5nYBUp6UES9b9aw/CadG8GMA8gmeeY= +MIIFZjCCA06gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT +MQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhl +IEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9t +Y2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4XDTI1MTIw +NTE5MTMyM1oXDTI3MTIwNTE5MTMyM1owgYgxCzAJBgNVBAYTAlVTMQswCQYDVQQI +EwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhlIEFwYWNoZSBT +b2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9tY2F0IFBNQzES +MBAGA1UEAxMJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAsdouUFxbItuEYV7kneGkJGyS05Zad1LI988WtWCApxUOEnUavD8LVDiW9i8K +OGwhaNoTSaJ7fIDHqmn+44yxznPmW4DiNolJWwPOdke/NSDuxnjfYqrR17+RsPyq +/X9ZP4pRn31ZD+HW3HsWgGI/bMIsG6UFHkCQYCDhw20//xxzhJKGWQPlSvbW3OZu +aB8FX/chqfxe5vyLxzY0GPzVcCFUWvEQySTICJIskRNUsdlZcIA5n4yQrcnSuL2j +KCA/ZzWi2SDiPy6+PYjSO307R0R5U+JSmHITrqKd/mi9NowuQ7E7zVA1PbX3L0Pq +HsnREKzoJMGM7ubQwgbeFC0aiQIDAQABo4HLMIHIMAkGA1UdEwQCMAAwLAYJYIZI +AYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQW +BBRwNJmtKeMnnb2XclAgmNKXs2ytoTAfBgNVHSMEGDAWgBSsh9yLPYrbiUh+bzsc +xnCSCRdv+TAxBggrBgEFBQcBAQQlMCMwIQYIKwYBBQUHMAGGFWh0dHA6Ly8xMjcu +MC4wLjE6ODg4ODAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcN +AQELBQADggIBAHevSrMJ2JtDWnN4ELoxraUGUEldFPd7HDjwOptns8txUnrfZ/rh +wYsY3E6/IW1YsV/fLfHuYqG2eN3ixfJPC509zIjHJo3wQQKtveApIfkMCgfPOKTx +24f35e1VWbU0fbQ5cMqBersg+i6FR3KcNfjy4Vf5nlycdpJQaslS6aDTTUED90+7 +/Fy4qix9EP8/bILgYLjVWGbe4Nswt788Cbk8XDbMxtOqWJ+Q/ufrW2LiZqWI7N0r +ZNElCOYKSZFfrB81jSyicd4BGLAOEz3Ng5JnYmOKY7vrjfm3lbC2qJwUjAsANItl +r7dsuRuU4lQ/7ocPgak/YRBf7k1CtWd3MC4ET61dTt7jILOPykaYfZeRHUwE2buu +Aj1vVd05ZPNThrZKautKekZODztEjsJGxksHHrFOihz9agZVLojGhYybwuqQ4QvZ +sa8tgH+2osd6Y30KzWD2RupAPX1OiT4LXSKPtzo6OBgmo3bpzxi8wTyLBnS28qip +tM/hllS9hyEdILYl0dnG+pQlxm4rHqFR4zH/Q3e94ExqwiSXQ5n2y90/YzGIrwfv +4Cs7jjuAJiemw1iVU/o5FFlCPTrlAv8NW+BjrZNyjh1VwvlJfVzLNdIRtIYtJmF0 +Hk3mx5fnmYv8kUHIn7jsPqP5GLUqLh3uLbbNjxS+uBxt/cNZYWaH6DGC -----END CERTIFICATE----- Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-rsa-copy1.jks and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa-copy1.jks differ diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-rsa-key.pem tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa-key.pem --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-rsa-key.pem 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa-key.pem 2026-01-23 19:33:36.000000000 +0000 @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDyLsjTJWQvdq5R -NUXkel4q/wEh1wha7CrLA33aTRBudIPh6XlvSL3XkEZVq9+3wd06psWu/cCfjB77 -szuwmtb7bgVR5yXYrvhc+lbCe8ZeW72KbelG7NvxjQsmGv+aMCcGY6HYFkCx2BVZ -YaV6bxpq4xEFVJ0lPA36bIuaapNvn8kVKnOczMcsW7Jf/a1dZkpddfDt4WHmCpZL -o6hx15vLBNDy4Soa8UJnU/nuAJeA+Ef7S24BY5zMQVsldbjKyrn3G1K/p2jH66L0 -/3qbW7z9+s+bFTozMnX3ykBC+agt68u3oGQNiQgcLjhWRSRPrZQZ4jVDLP4x3Di0 -wRD2oWp1AgMBAAECggEAdjst0prmAp/rcPzTfrvYnh/48wbR7uVt7MdrGPhXZb0f -J1p+bLwz5flqxOdQJBsSQFBroMcD00AqXhG9ubWV75BiK35vhQ3CdU3HrlGT4bvi -2CJZaMU4iP+k6jjzANiyLJXWTLJsagZGQekASrnTyUvKIvKQdz2Oy2tTahLOoIaL -kgf+6sjZR5ipyN1kOwxtONDG5LvFT82MeOr4+efJ6i7R5sDSMXoEydBSULMTmLw8 -e0cpefttP0ugvI6wmUDOCUd+sPbOG2JQpRVuFsYcbqgpb3J/sVa8UoaC6ho+Ga6V -EHKGhFF79LWs1SoTAFxD3B8Ex84v9/Lw23BY6DOw7wKBgQD9cSd5A0fY9UTxrdeL -1okltuz29wOsBBgKuiSPGuhh2OxzEUYowvE+uHoNcXXF9aSQY4ZElVe6nkqndaGx -u0c3T6XbDgsX3ROxdanM1My0zsjRB/9wfdStUnpbDAh8mmnzFi/UBRjNvRtuK+Yw -DbJa3gRq/ely4VDTbVI78UZBiwKBgQD0oInbxrFhtBc/8AKapep5rNonF4zkaPa5 -RLpMShDu/z1UEi0SHsjWuodEvti1pRvZjfipHm0EtCXSXdrPvlk5i35pz83Hryt+ -VJY0W2w2W6+pzZh/TQyKX3o4GJBHqmbU48Y93C7KYvcLawLByBYVGb3RQdXkIwV7 -borCFdSD/wKBgHPPUAU5HYyjTxTMplENAHCObSvQ5gmleUL1mh0WY5fKowlVnGSl -580MSSF5diK2Q7OC9ujCAWuSR3TMCM4JUNqSd6xod2M5L3WI2cVye/QnBZaW+/9w -UE2/AZA3KG2ftchRnBtbFOF2h2FJiA7vQ0/IYeZuDk3jbaafLtObRcpPAoGAI+Sq -VaUBweQ6xRbiSu6tSM0j9ThvY9RC30xGSYtUkLzCsuM32cDKlzcnQd7+zSg8vd8m -7SmZtWb5ZbR/tpunW9or4jqrZvE84HxozaXcat1pGPEhvwv/Gw6HUFPKV7mY/QOu -wsTb2LZMqVA6R1SkaNQuFdYjHCZHV6UQlwm89OECgYAOJQO+vKlZm1ZKwFcpVvy+ -F9AOZiy9/ByjTgh0lzsoPEbgDQTtLnNjjoHn73hZXY2+NEDQLNhpbYyucP2qDCCp -ArF4HpOA+g2FOzNGUKP8bZ5mIUMMQpCtyBO877Jr1IVzf8jlAXnMil/lshNkhLRl -APsZ5D8Qm68GqPuDQMoNqA== +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCx2i5QXFsi24Rh +XuSd4aQkbJLTllp3Usj3zxa1YICnFQ4SdRq8PwtUOJb2Lwo4bCFo2hNJont8gMeq +af7jjLHOc+ZbgOI2iUlbA852R781IO7GeN9iqtHXv5Gw/Kr9f1k/ilGffVkP4dbc +exaAYj9swiwbpQUeQJBgIOHDbT//HHOEkoZZA+VK9tbc5m5oHwVf9yGp/F7m/IvH +NjQY/NVwIVRa8RDJJMgIkiyRE1Sx2VlwgDmfjJCtydK4vaMoID9nNaLZIOI/Lr49 +iNI7fTtHRHlT4lKYchOuop3+aL02jC5DsTvNUDU9tfcvQ+oeydEQrOgkwYzu5tDC +Bt4ULRqJAgMBAAECggEAAqX0dvsIodFhzimzbSpMuCdZnObktgPd60UpnYpJLz5k +fTjQc/X3lVt9zffN2KC7yILhOiTTiZ7eSHiT+AUNN/YTdIJL7cuu7Hj+SZ1QYbMr +lpYlV2PNRH+vAShpjp4KzWsMpyZJehMmEKStHjzGgEZxWIdBqOSFgA3Tgzf5Q/NS +OiWNgarUkwVGLDRFH99WWbVmAJEnJeB9pxEQllqb/5AkgeiDD3Mx+BwNgO5GRu4u +z4BwB6InA4e6T+vBhnPra3qxaS3bPfna1P2Gz7jsH8xOHFt51hIVMCzeVj0/wOWL +Ie7YinLbCAhIa5YD8F1LTvrzCGuJn+a8Rlt6AXPYDQKBgQD1O0plltNO/3y8WN1i +c3Ox7sQfTsTj5ZFoW4U6kbAIPF5ByKTBAXdWHWxUwBxoZA+4lgYPEvdwXWPG+VXy +JXAFbR2/JQO/yROKnodB4O73WZitt81PIHdFP7myEeN5QSjnYSvd6lIiaxe178BM +qyO/85GgcBf/o3MAQib0UCwXHQKBgQC5qXZjspZd8nC33NP6A6HYawjkrZeVYG8S +qPgT0ctmsb1EpihK+wU1iAJzECZuTSgoZtcRPL/N17/xgSuwk6nTUJyOdQwnmVP+ ++Cfz7lcvx8Dg9FdrmjiEjFHnAYzaZ4bJP5Tub0tsUJRE/7YbhPlvlzaD95USLMKB +IHzAQWx5XQKBgHgsmgS2qM6pvQK/uZ2pXiTwEQQWob3cnik50EwnYNBoZPhvzu0W +Ptjgilnt2v39KwcV3do9PSy/V0oGneuQFRlTo6QsC25Mp1ri3P2XsQNd0Mgwrlf8 +XPZ+iA2PXp3pJJZetBSH48AiIvhxiRcJNve18MNiqyAHhS+3O0e2kiSxAoGAL3uY +rKzK6iIME+nlSMbPCKNvNdTztJ9iKNqP/7mjFJOWfU0ldu+2CFfNkJHr0j/nalXK +4TyxLTrleyV3AATz5Phz4bcrsaD0K3xZ83fcUnr66E11Yi6iD7w3YiYyWNUrUqLx +Ov25w2zkTrU7ZNRgWtrIdX3HYUuTPyUI4r6YuH0CgYA+Qs6ZexX1Xys6qBbO2OiR +6+eRItL12doGDDCO7tqtscf8S3VGSLk29lfwipyz6YhO46yRP2FypqVHYYhwvYuW +ubXGUUZhzI3JBvqI0Lrtwt0KJ2QUXzfqZqO1cvKe3+gooywmemvzg5s1jZ+/Ys3b +rQR0KWEn04xhcDMyTr/A9Q== -----END PRIVATE KEY----- Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/org/apache/tomcat/util/net/localhost-rsa.jks and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/localhost-rsa.jks differ diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/OcspBaseTest.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/OcspBaseTest.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/OcspBaseTest.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/OcspBaseTest.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,186 @@ +/* + * 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.ocsp; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileLock; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import jakarta.servlet.http.HttpServletResponse; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.buf.ByteChunk; +import org.apache.tomcat.util.net.SSLHostConfig; +import org.apache.tomcat.util.net.TesterSupport; +import org.apache.tomcat.util.net.TesterSupport.SimpleServlet; +import org.apache.tomcat.util.net.openssl.OpenSSLStatus; + +public class OcspBaseTest extends TomcatBaseTest { + + private static final File lockFile = new File("test/org/apache/tomcat/util/net/ocsp/ocsp-responder.lock"); + private static FileLock lock = null; + + @BeforeClass + public static void obtainOcspResponderLock() throws IOException { + @SuppressWarnings("resource") + FileOutputStream fos = new FileOutputStream(lockFile); + lock = fos.getChannel().lock(); + } + + @AfterClass + public static void releaseOcspResponderLock() throws IOException { + // Should not be null be in case obtaining the lock fails, avoid a second error. + if (lock != null) { + lock.release(); + } + } + + + @Parameters(name = "{0} with OpenSSL trust {2}") + public static Collection parameters() { + List parameterSets = new ArrayList<>(); + parameterSets.add(new Object[] { "JSSE", Boolean.FALSE, Boolean.FALSE, + "org.apache.tomcat.util.net.jsse.JSSEImplementation"}); + parameterSets.add(new Object[] { "OpenSSL", Boolean.TRUE, Boolean.TRUE, + "org.apache.tomcat.util.net.openssl.OpenSSLImplementation" }); + parameterSets.add(new Object[] { "OpenSSL", Boolean.TRUE, Boolean.FALSE, + "org.apache.tomcat.util.net.openssl.OpenSSLImplementation" }); + parameterSets.add(new Object[] { "OpenSSL-FFM", Boolean.TRUE, Boolean.TRUE, + "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" }); + parameterSets.add(new Object[] { "OpenSSL-FFM", Boolean.TRUE, Boolean.FALSE, + "org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation" }); + + return parameterSets; + } + + @Parameter(0) + public String connectorName; + + @Parameter(1) + public boolean useOpenSSL; + + @Parameter(2) + public boolean useOpenSSLTrust; + + @Parameter(3) + public String sslImplementationName; + + @Override + public void setUp() throws Exception { + super.setUp(); + Tomcat tomcat = getTomcatInstance(); + TesterSupport.configureSSLImplementation(tomcat, sslImplementationName, useOpenSSL); + } + + + protected void doTest(boolean clientCertValid, boolean serverCertValid, ClientCertificateVerification verifyClientCert, + boolean verifyServerCert) throws Exception { + doTest(clientCertValid, serverCertValid, verifyClientCert, verifyServerCert, null); + } + + + protected void doTest(boolean clientCertValid, boolean serverCertValid, ClientCertificateVerification verifyClientCert, + boolean verifyServerCert, Boolean softFail) throws Exception { + + if ("OpenSSL-FFM".equals(connectorName)) { + Assume.assumeFalse(OpenSSLStatus.isBoringSSL() || OpenSSLStatus.isLibreSSLPre35()); + } + Assume.assumeFalse(!useOpenSSLTrust && verifyClientCert == ClientCertificateVerification.OPTIONAL_NO_CA); + + Tomcat tomcat = getTomcatInstance(); + + // No file system docBase required + Context ctx = tomcat.addContext("", null); + + Tomcat.addServlet(ctx, "simple", new SimpleServlet()); + ctx.addServletMappingDecoded("/simple", "simple"); + + if (serverCertValid) { + TesterSupport.initSsl(tomcat, useOpenSSLTrust); + } else { + TesterSupport.initSsl(tomcat, TesterSupport.LOCALHOST_CRL_RSA_JKS, useOpenSSLTrust); + } + SSLHostConfig sslHostConfig = tomcat.getConnector().findSslHostConfigs()[0]; + switch (verifyClientCert) { + case DEFAULT: + sslHostConfig.setCertificateVerification("required"); + break; + case DISABLED: + sslHostConfig.setOcspEnabled(false); + sslHostConfig.setCertificateVerification("required"); + break; + case ENABLED: + sslHostConfig.setOcspEnabled(true); + sslHostConfig.setCertificateVerification("required"); + break; + case OPTIONAL_NO_CA: + sslHostConfig.setOcspEnabled(true); + sslHostConfig.setCertificateVerification("optionalNoCA"); + break; + + } + + if (clientCertValid) { + TesterSupport.configureClientSsl(verifyServerCert, TesterSupport.CLIENT_JKS); + } else { + TesterSupport.configureClientSsl(verifyServerCert, TesterSupport.CLIENT_CRL_JKS); + } + + if (softFail != null) { + sslHostConfig.setOcspSoftFail(softFail.booleanValue()); + } + + /* + * Use shorter timeout to speed up test. + * + * Note: JSSE timeout set earlier as it requires setting a system property that is read once in a static + * initializer. + */ + if (useOpenSSLTrust) { + sslHostConfig.setOcspTimeout(1000); + } + + tomcat.start(); + + int rc = getUrl("https://localhost:" + getPort() + "/simple", new ByteChunk(), false); + + // If the TLS handshake fails, the test won't get this far. + Assert.assertEquals(HttpServletResponse.SC_OK, rc); + } + + + protected enum ClientCertificateVerification { + DEFAULT, + ENABLED, + OPTIONAL_NO_CA, + DISABLED + } + +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TestOcspEnabled.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,121 @@ +/* + * 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.ocsp; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.net.ssl.SSLHandshakeException; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class TestOcspEnabled extends OcspBaseTest { + + private static TesterOcspResponder ocspResponder; + + @BeforeClass + public static void startOcspResponder() { + ocspResponder = new TesterOcspResponder(); + try { + ocspResponder.start(); + } catch (IOException ioe) { + ocspResponder = null; + } + } + + + @AfterClass + public static void stopOcspResponder() { + if (ocspResponder != null) { + ocspResponder.stop(); + ocspResponder = null; + } + } + + + @Parameters(name = "{0} with OpenSSL trust {2}: clientOk {4}, serverOk {5}, verifyClient {6}, verifyServer {7}") + public static Collection parameters() { + List parameterSets = new ArrayList<>(); + Collection baseData = OcspBaseTest.parameters(); + + for (Object[] base : baseData) { + for (Boolean clientCertValid : booleans) { + for (Boolean serverCertValid : booleans) { + for (ClientCertificateVerification verifyClientCert : ClientCertificateVerification.values()) { + boolean useOpenSSLTrust = ((Boolean) base[2]).booleanValue(); + if (verifyClientCert == ClientCertificateVerification.OPTIONAL_NO_CA && !useOpenSSLTrust || + verifyClientCert == ClientCertificateVerification.ENABLED && !clientCertValid.booleanValue()) { + continue; + } + for (Boolean verifyServerCert : booleans) { + Boolean handshakeFailureExpected; + if (!serverCertValid.booleanValue() && verifyServerCert.booleanValue()) { + handshakeFailureExpected = Boolean.TRUE; + } else { + handshakeFailureExpected = Boolean.FALSE; + } + parameterSets.add(new Object[] { base[0], base[1], base[2], base[3], clientCertValid, + serverCertValid, verifyClientCert, verifyServerCert, handshakeFailureExpected}); + } + } + } + } + } + return parameterSets; + } + + @Parameter(4) + public boolean clientCertValid; + + @Parameter(5) + public boolean serverCertValid; + + @Parameter(6) + public ClientCertificateVerification verifyClientCert; + + @Parameter(7) + public boolean verifyServerCert; + + @Parameter(8) + public boolean handshakeFailureExpected; + + @Test + public void test() throws Exception { + Assume.assumeNotNull(ocspResponder); + try { + doTest(clientCertValid, serverCertValid, verifyClientCert, verifyServerCert); + if (handshakeFailureExpected) { + Assert.fail("Handshake did not fail when expected to do so."); + } + } catch (SSLHandshakeException e) { + if (!handshakeFailureExpected) { + Assert.fail("Handshake failed when not expected to do so."); + } + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TestOcspSoftFail.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TestOcspSoftFail.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TestOcspSoftFail.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TestOcspSoftFail.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.net.ocsp; + +import java.net.SocketException; + +import javax.net.ssl.SSLHandshakeException; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * Only looking to test the Tomcat (server) side configuration of soft fail. + */ +@RunWith(Parameterized.class) +public class TestOcspSoftFail extends OcspBaseTest { + + @Test + public void testNoResponderDefaultSoftFail() throws Exception { + // Default behaviour should be the same for all configurations and equivalent to enabled. + doTest(false, false, ClientCertificateVerification.ENABLED, false, null); + } + + + @Test + public void testNoResponderWithSoftFail() throws Exception { + doTest(false, false, ClientCertificateVerification.ENABLED, false, Boolean.TRUE); + } + + + @Test(expected = SSLHandshakeException.class) + public void testNoResponderWithoutSoftFail() throws Exception { + try { + doTest(false, false, ClientCertificateVerification.ENABLED, false, Boolean.FALSE); + } catch (SocketException se) { + // NIO2 may throw a SocketException rather than a SSLHandshakeException + if (getTomcatInstance().getConnector().getProtocolHandlerClassName().contains("Nio2")) { + throw new SSLHandshakeException(se.getMessage()); + } else { + throw se; + } + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TestOcspTimeout.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TestOcspTimeout.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TestOcspTimeout.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TestOcspTimeout.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,73 @@ +/* + * 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.ocsp; + +import java.net.SocketException; +import java.net.SocketTimeoutException; + +import javax.net.ssl.SSLHandshakeException; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +/* + * The timeout for reading an OCSP response is 15s by default for both JSSE and OpenSSL. + */ +@RunWith(Parameterized.class) +public class TestOcspTimeout extends OcspBaseTest { + + private static TesterOcspResponderNoResponse ocspResponder; + + @BeforeClass + public static void startOcspResponder() { + /* + * Use shorter timeout to speed up test. + * + * Note: OpenSSL timeout set later as it requires access to SSLHostConfig. + */ + System.setProperty("com.sun.security.ocsp.readtimeout", "1000ms"); + ocspResponder = new TesterOcspResponderNoResponse(); + ocspResponder.start(); + } + + + @AfterClass + public static void stopOcspResponder() { + ocspResponder.stop(); + ocspResponder = null; + } + + + @Test + public void testTimeoutWithSoftFail() throws Exception { + doTest(false, false, ClientCertificateVerification.ENABLED, false, Boolean.TRUE); + } + + + @Test(expected = SSLHandshakeException.class) + public void testTimeoutWithoutSoftFail() throws Exception { + try { + doTest(false, false, ClientCertificateVerification.ENABLED, false, Boolean.FALSE); + } catch (SocketTimeoutException | SocketException e) { + // May throw a SocketTimeoutException or SocketException rather than a SSLHandshakeException + throw new SSLHandshakeException(e.getMessage()); + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponder.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponder.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponder.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponder.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,125 @@ +/* + * 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.ocsp; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.Assert; + +import org.apache.tomcat.util.net.TesterSupport; + +/* + * The OpenSSL ocsp tool is great, but it does generate a lot of output. That output needs to be swallowed, else the + * process will freeze when the output buffers (stdout and stderr) are full. + * + * There is a command line option to redirect stdout (which could be redirected to /dev/null), but there is no option to + * redirect stderr. Therefore, this class uses a couple of dedicated threads to read stdout and stderr. By default, the + * output is ignored, but it can be dumped to Java's stdout/stderr if required for debugging purposes. + */ +public class TesterOcspResponder { + + private static List ocspArgs = Arrays.asList("ocsp", "-port", "8888", "-text", "-index", + TesterSupport.DB_INDEX, "-CA", TesterSupport.CA_CERT_PEM, "-rkey", TesterSupport.OCSP_RESPONDER_RSA_KEY, + "-rsigner", TesterSupport.OCSP_RESPONDER_RSA_CERT, "-nmin", "60"); + + private Process p; + + public void start() throws IOException { + if (p != null) { + throw new IllegalStateException("Already started"); + } + + String openSSLPath = System.getProperty("tomcat.test.openssl.path"); + String openSSLLibPath = null; + if (openSSLPath == null || openSSLPath.length() == 0) { + openSSLPath = "openssl"; + } else { + // Explicit OpenSSL path may also need explicit lib path + // (e.g. Gump needs this) + openSSLLibPath = openSSLPath.substring(0, openSSLPath.lastIndexOf('/')); + openSSLLibPath = openSSLLibPath + "/../:" + openSSLLibPath + "/../lib:" + openSSLLibPath + "/../lib64"; + } + List cmd = new ArrayList<>(); + cmd.add(openSSLPath); + cmd.addAll(ocspArgs); + + ProcessBuilder pb = new ProcessBuilder(cmd.toArray(new String[0])); + + if (openSSLLibPath != null) { + Map env = pb.environment(); + String libraryPath = env.get("LD_LIBRARY_PATH"); + if (libraryPath == null) { + libraryPath = openSSLLibPath; + } else { + libraryPath = libraryPath + ":" + openSSLLibPath; + } + env.put("LD_LIBRARY_PATH", libraryPath); + } + + p = pb.start(); + + redirect(new BufferedReader(new InputStreamReader(p.getInputStream())) , System.out, true); + redirect(new BufferedReader(new InputStreamReader(p.getErrorStream())), System.err, true); + + Assert.assertTrue(p.isAlive()); + } + + public void stop() { + if (p == null) { + throw new IllegalStateException("Not started"); + } + p.destroy(); + + try { + if (!p.waitFor(30, TimeUnit.SECONDS)) { + throw new IllegalStateException("Failed to stop"); + } + } catch (InterruptedException e) { + throw new IllegalStateException("Interrupted while waiting to stop", e); + } + } + + + private void redirect(final Reader r, final PrintStream os, final boolean swallow) { + /* + * InputStream will close when process ends. Thread will exit once stream closes. + */ + new Thread( () -> { + char[] cbuf = new char[1024]; + try { + int read; + while ((read = r.read(cbuf)) > 0) { + if (!swallow) { + os.print(new String(cbuf, 0, read)); + } + } + } catch (IOException ignore) { + // Ignore + } + + }).start(); + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponderNoResponse.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponderNoResponse.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponderNoResponse.java 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp/TesterOcspResponderNoResponse.java 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.tomcat.util.net.ocsp; + +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; + +import javax.net.ServerSocketFactory; + +import org.junit.Assert; + +/* + * An OCSP responder that swallows any input received and never responds. Use to test timeouts. + */ +public class TesterOcspResponderNoResponse { + + private ServerRunnable sr; + + public void start() { + if (sr != null) { + throw new IllegalStateException("Already started"); + } + + sr = new ServerRunnable(); + Thread t = new Thread(sr); + t.start(); + + Assert.assertTrue(sr.isAlive()); + } + + public void stop() { + if (sr == null) { + throw new IllegalStateException("Not started"); + } + sr.stop(); + } + + + private static class ServerRunnable implements Runnable { + + private volatile boolean alive = true; + private volatile ServerSocket serverSocket; + + @Override + public void run() { + try { + serverSocket = ServerSocketFactory.getDefault().createServerSocket(); + serverSocket.bind(new InetSocketAddress("localhost", 8888)); + + while (alive) { + Socket socket = serverSocket.accept(); + Thread t = new Thread(new SwallowRunnable(socket)); + t.start(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + public void stop() { + try { + serverSocket.close(); + alive = false; + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + public boolean isAlive() { + return alive; + } + } + + + private static class SwallowRunnable implements Runnable { + + private final Socket socket; + + SwallowRunnable(Socket socket) { + this.socket = socket; + } + + @Override + public void run() { + byte[] buf = new byte[1024]; + try (InputStream os = socket.getInputStream()) { + // Read until the client closes the socket + while (os.read(buf) > 0) { + // Ignore any data read + } + } catch (IOException ignore) { + // Ignore + } + } + } +} diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp-responder-rsa-cert.pem tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp-responder-rsa-cert.pem --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp-responder-rsa-cert.pem 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp-responder-rsa-cert.pem 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,105 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4101 (0x1005) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=Apache Tomcat Test CA + Validity + Not Before: Dec 5 19:33:55 2025 GMT + Not After : Dec 5 19:33:55 2027 GMT + Subject: C=US, ST=DE, L=Wilmington, O=The Apache Software Foundation, OU=Apache Tomcat PMC, CN=OCSP Responder + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9b:ae:b1:40:e5:e4:84:b6:c3:ec:93:cf:23:63: + 51:51:7f:bd:d3:64:1d:e0:23:bb:33:44:f1:47:c6: + f5:d2:4d:2a:13:2a:f4:4c:d2:44:13:56:03:73:16: + 30:98:be:66:b1:fb:3c:e9:9c:5a:61:25:b8:47:ef: + 72:04:ad:ab:6a:f8:83:fe:00:a6:c0:8b:a3:ef:34: + 09:4f:13:e6:fa:96:d3:2b:24:db:e3:d1:4a:40:50: + 5f:9f:8f:31:fa:de:42:0d:e6:a2:4a:7a:e6:a6:48: + 5a:97:04:b3:b9:bd:52:2e:2f:e9:02:27:1b:8f:bf: + 2b:19:28:e8:96:eb:29:d0:6e:39:0b:d9:bf:2a:d2: + 3e:65:a4:e0:87:30:ab:26:09:01:ea:4a:a7:a2:38: + 09:0e:f5:d3:b8:66:0a:cc:17:61:72:fb:a9:5e:e4: + 98:ff:30:e4:93:11:f7:6e:13:f3:76:f6:f4:44:b7: + a8:2a:79:b3:4f:59:d8:67:b7:c0:4c:cd:50:57:d2: + 09:65:f5:5e:2d:3b:e7:29:bf:e3:11:ff:37:1d:ad: + f2:cf:3b:ff:f7:49:d1:1a:05:1d:2b:0e:59:fe:fe: + e6:6a:5d:73:7f:0b:8c:b3:6d:c3:65:2a:93:f8:87: + b7:fd:c2:4a:e3:b8:d5:e6:55:8b:b3:e5:0e:4f:f4: + fa:a7 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Digital Signature + X509v3 Extended Key Usage: + OCSP Signing + X509v3 Subject Key Identifier: + FA:23:45:E2:94:8E:6A:88:B8:72:B1:53:EA:2A:AE:B9:39:F8:6A:C8 + X509v3 Authority Key Identifier: + AC:87:DC:8B:3D:8A:DB:89:48:7E:6F:3B:1C:C6:70:92:09:17:6F:F9 + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 3b:39:47:9e:28:f1:c0:46:eb:a8:de:0e:44:63:4d:3c:f4:59: + c1:a4:2a:eb:6a:e5:50:76:ea:76:f7:a9:ca:a2:cb:70:91:c9: + d3:c5:fd:d6:c9:86:3d:ad:f2:b8:a7:e4:b1:93:09:44:eb:27: + 30:01:85:13:db:0a:62:f0:0f:fa:59:de:af:93:1a:9e:df:07: + da:b8:ec:19:c1:c9:56:f9:a6:8e:4e:03:ae:e0:f9:d8:d2:b7: + d2:94:4a:70:95:b7:a8:7f:42:b2:1d:43:77:3a:9e:f8:76:93: + 4b:3a:48:09:5a:8b:3b:2c:38:97:ed:27:6b:d5:31:0b:f0:3d: + a9:dc:d4:0b:96:55:07:41:59:e1:1f:68:70:47:04:6a:11:a4: + 81:e7:7d:75:08:15:9f:ca:20:20:69:fe:ef:50:3f:cf:02:0b: + 96:4a:9e:e7:ce:08:07:8a:c0:93:a6:aa:a9:91:53:ac:5e:80: + 3b:bd:6f:cb:d2:7c:e3:9b:cd:ff:b0:12:ec:9a:71:48:e1:01: + 96:21:f4:ee:bb:f8:1d:99:77:00:68:ef:e3:bc:5d:1d:58:bc: + 91:88:51:39:93:b1:91:88:5c:d0:9d:0f:87:0b:0d:04:ca:be: + 79:05:b9:42:ae:b3:62:df:7a:02:d7:d5:4e:a8:27:8f:0b:b8: + 4c:aa:d5:07:a6:e4:65:b0:13:78:cd:3a:3b:10:58:49:13:d7: + 74:88:76:8a:77:a4:d4:24:01:61:fe:0b:46:fb:4e:15:3a:fa: + 2b:c3:ca:10:9c:5f:2b:f5:33:21:93:16:be:a4:c5:bd:a4:80: + 88:74:2a:1e:09:d4:2a:c6:af:ed:be:46:7f:b8:d7:ad:a6:e0: + 8f:92:ae:2b:8a:97:ca:9c:fb:21:29:48:f1:38:98:09:f9:2b: + 55:53:7c:99:a5:23:21:58:35:1a:36:15:67:79:80:3f:fc:94: + 60:94:2b:78:a5:f1:81:b4:51:5c:08:1a:50:24:21:da:0b:cc: + db:d7:3c:7d:d8:2c:b1:93:9d:f4:94:bb:fe:37:ad:8d:3f:06: + 9d:83:b0:e5:f2:ef:e4:88:75:e9:50:3a:f3:ef:aa:e4:00:54: + ad:1d:cd:a7:6b:ec:0e:b7:31:92:82:da:0d:4b:c3:27:ee:7a: + a8:f4:e0:a9:0a:f1:40:61:a0:09:a5:fb:24:f9:68:34:28:a9: + 1b:b6:5a:bd:aa:3a:c4:b8:89:92:ba:92:9c:81:f8:a8:ff:5a: + 0c:ef:af:97:b4:86:09:71:e8:13:28:1c:89:16:6f:43:de:1b: + 2f:1d:16:9f:37:7f:9c:5f:4e:4a:a8:22:4b:77:9e:f6:94:f8: + d9:05:4b:bc:a6:aa:f7:6e +-----BEGIN CERTIFICATE----- +MIIFFDCCAvygAwIBAgICEAUwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT +MQswCQYDVQQIEwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhl +IEFwYWNoZSBTb2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9t +Y2F0IFBNQzEeMBwGA1UEAxMVQXBhY2hlIFRvbWNhdCBUZXN0IENBMB4XDTI1MTIw +NTE5MzM1NVoXDTI3MTIwNTE5MzM1NVowgY0xCzAJBgNVBAYTAlVTMQswCQYDVQQI +EwJERTETMBEGA1UEBxMKV2lsbWluZ3RvbjEnMCUGA1UEChMeVGhlIEFwYWNoZSBT +b2Z0d2FyZSBGb3VuZGF0aW9uMRowGAYDVQQLExFBcGFjaGUgVG9tY2F0IFBNQzEX +MBUGA1UEAxMOT0NTUCBSZXNwb25kZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCbrrFA5eSEtsPsk88jY1FRf73TZB3gI7szRPFHxvXSTSoTKvRM0kQT +VgNzFjCYvmax+zzpnFphJbhH73IEratq+IP+AKbAi6PvNAlPE+b6ltMrJNvj0UpA +UF+fjzH63kIN5qJKeuamSFqXBLO5vVIuL+kCJxuPvysZKOiW6ynQbjkL2b8q0j5l +pOCHMKsmCQHqSqeiOAkO9dO4ZgrMF2Fy+6le5Jj/MOSTEfduE/N29vREt6gqebNP +Wdhnt8BMzVBX0gll9V4tO+cpv+MR/zcdrfLPO//3SdEaBR0rDln+/uZqXXN/C4yz +bcNlKpP4h7f9wkrjuNXmVYuz5Q5P9PqnAgMBAAGjdTBzMAwGA1UdEwEB/wQCMAAw +DgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMJMB0GA1UdDgQWBBT6 +I0XilI5qiLhysVPqKq65OfhqyDAfBgNVHSMEGDAWgBSsh9yLPYrbiUh+bzscxnCS +CRdv+TANBgkqhkiG9w0BAQsFAAOCAgEAOzlHnijxwEbrqN4ORGNNPPRZwaQq62rl +UHbqdvepyqLLcJHJ08X91smGPa3yuKfksZMJROsnMAGFE9sKYvAP+lner5Mant8H +2rjsGcHJVvmmjk4DruD52NK30pRKcJW3qH9Csh1Ddzqe+HaTSzpICVqLOyw4l+0n +a9UxC/A9qdzUC5ZVB0FZ4R9ocEcEahGkged9dQgVn8ogIGn+71A/zwILlkqe584I +B4rAk6aqqZFTrF6AO71vy9J845vN/7AS7JpxSOEBliH07rv4HZl3AGjv47xdHVi8 +kYhROZOxkYhc0J0PhwsNBMq+eQW5Qq6zYt96AtfVTqgnjwu4TKrVB6bkZbATeM06 +OxBYSRPXdIh2inek1CQBYf4LRvtOFTr6K8PKEJxfK/UzIZMWvqTFvaSAiHQqHgnU +Ksav7b5Gf7jXrabgj5KuK4qXypz7ISlI8TiYCfkrVVN8maUjIVg1GjYVZ3mAP/yU +YJQreKXxgbRRXAgaUCQh2gvM29c8fdgssZOd9JS7/jetjT8GnYOw5fLv5Ih16VA6 +8++q5ABUrR3Np2vsDrcxkoLaDUvDJ+56qPTgqQrxQGGgCaX7JPloNCipG7Zavao6 +xLiJkrqSnIH4qP9aDO+vl7SGCXHoEygciRZvQ94bLx0Wnzd/nF9OSqgiS3ee9pT4 +2QVLvKaq924= +-----END CERTIFICATE----- diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp-responder-rsa-key.pem tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp-responder-rsa-key.pem --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/ocsp-responder-rsa-key.pem 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/ocsp-responder-rsa-key.pem 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCbrrFA5eSEtsPs +k88jY1FRf73TZB3gI7szRPFHxvXSTSoTKvRM0kQTVgNzFjCYvmax+zzpnFphJbhH +73IEratq+IP+AKbAi6PvNAlPE+b6ltMrJNvj0UpAUF+fjzH63kIN5qJKeuamSFqX +BLO5vVIuL+kCJxuPvysZKOiW6ynQbjkL2b8q0j5lpOCHMKsmCQHqSqeiOAkO9dO4 +ZgrMF2Fy+6le5Jj/MOSTEfduE/N29vREt6gqebNPWdhnt8BMzVBX0gll9V4tO+cp +v+MR/zcdrfLPO//3SdEaBR0rDln+/uZqXXN/C4yzbcNlKpP4h7f9wkrjuNXmVYuz +5Q5P9PqnAgMBAAECggEAALCpC3sZZLc0rh9Hm0YM7boNutqmQUCXS1ZiZWmN3GVI +KvaVR6Pk9lr6v+9YvsyVQvO0pOpzAhw7MWJv1HJ6oIpCd4VEN/VGgEBwTB/v9atm +ezn6GYvrctaIXfoyBAUpMMuVa5QY4qLOd/3m2AttSlQfCcnnlt8Hj2B9i3G2aTya +EgwH9GKBoQ0FIMP3Znytx2YEj2dMk2wxApafNhxpZ302JZgvQG163UovQeGfJXvN +d0xwtA8Xyzuv+/l1Bx8KyD0dYjeA6++khJ/oDNkM1+JKfN6qRzIhcNYWZAx6Au78 +tJwhBuUVqpbZnPkMXwyFjnAMvzSRcJrGIHdrIyPsWQKBgQDIgbcgiECHldkdrIUF +3qKluX8g8mLTGts/wXG20iY730KThAnbxJ0ORPD5S3HZDSKZkG31ohoZHH7YT9iC +0+pnu4bEhqtfPttXIK9q3PQh6i5jhiENc2x0NvJ0nUg8DkVqVmnmC3eIVIHfnnUU +PDhe0GjHC02TegBFXfOFz3LDSQKBgQDGxRa4tKtB/0BeJCu4RD7pHuikt+NUKS7n +4F3QBpyIzchjImIlHbutFwIbmUQ6C5TzEcgnTDAlJadBmljZYemaX5RN1UEPulPT +NvSOdGdxyhJmKpFX+umUbypAcpKC6+EElaOuCUphMDOISIyooKUtJqWUXTVgO60x +2vh94BHebwKBgGpIL/0DnEkCikLrdtukpsx35kZdlTrXio5iCNfizzd/Ybf2Do+6 +yZGNw7oxXpiyGLwTzeWdVn4nF6mrsVWv5Rm1UnuL8v0awYOOpM1db98mVg6VQ6Hw +o/V6Rsy/rlF/MxZp1dqGC4dtXCZfxSnDvhGsIU4Y1LcuvUQHyBUO4INZAoGAPATV +GI7NS49Uk1iervREfsXrf5lbFlWdKT0RdrgYWiKxCGYgdo6k1d4lr8m21UQcBM1w +v5T80Kqu91swyusKy6dis6HaFHKxzwxACifR+IUIyzq9SnCkSULL4kv2O3wYJPc+ +RlXd1LzmQpeNiXmAhsKtqr/+VwGOCYjSEcgv/RcCgYBfUrsTG3dgWUVKviFzS63R +nb0T9yKZij79HkIM0bROpaU5atTgQa/iwvSktw8mHGuJkjQZ6BqVpKHbDOq/Y/zB +NihYIlf4WUPlQVr/sBLzfTtaxBKcnuVm1d8igKK3qQ0XuH5jk0gD6tzaU4ySTJ+P +ldxvbWraHRcPzn08xJ6+UA== +-----END PRIVATE KEY----- diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/openssl/TestOpenSSLConf.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/openssl/TestOpenSSLConf.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/openssl/TestOpenSSLConf.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/openssl/TestOpenSSLConf.java 2026-01-23 19:33:36.000000000 +0000 @@ -39,11 +39,11 @@ import org.apache.catalina.LifecycleListener; import org.apache.catalina.connector.Connector; import org.apache.catalina.core.AprLifecycleListener; -import org.apache.catalina.core.AprStatus; import org.apache.catalina.core.OpenSSLLifecycleListener; import org.apache.catalina.core.StandardServer; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.jni.AprStatus; import org.apache.tomcat.jni.SSLContext; import org.apache.tomcat.util.net.SSLHostConfig; import org.apache.tomcat.util.net.TesterSupport; diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java tomcat10-10.1.52/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java --- tomcat10-10.1.34/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java 2026-01-23 19:33:36.000000000 +0000 @@ -46,28 +46,26 @@ String versionString = null; try { versionString = executeOpenSSLCommand("version"); - } catch (IOException e) { + } catch (IOException ioe) { versionString = ""; } - if (versionString.startsWith("OpenSSL 3.4.")) { - // Note: Gump currently tests 11.x with OpenSSL HEAD which is current 3.4.x + if (versionString.startsWith("OpenSSL 4.0.")) { + VERSION = 40000; + } else if (versionString.startsWith("OpenSSL 3.6.")) { + VERSION = 30600; + } else if (versionString.startsWith("OpenSSL 3.5.")) { + VERSION = 30500; + } else if (versionString.startsWith("OpenSSL 3.4.")) { VERSION = 30400; } else if (versionString.startsWith("OpenSSL 3.3.")) { + // Note: Gump currently tests 10.x with OpenSSL 3.3.x VERSION = 30300; - } else if (versionString.startsWith("OpenSSL 3.2.")) { - VERSION = 30200; - } else if (versionString.startsWith("OpenSSL 3.1.")) { - VERSION = 30100; } else if (versionString.startsWith("OpenSSL 3.0.")) { VERSION = 30000; - } else if (versionString.startsWith("OpenSSL 1.1.1")) { - // LTS - // Supported until at least 2023-09-11 - // Note: Gump currently tests 9.x and earlier with OpenSSL 1.1.1[x] - VERSION = 10101; - // Note: Release branches 1.1.0 and earlier are no longer supported by - // the OpenSSL team so these tests don't support them either. } else { + // Note: 3.2.x and 3.1.x are no longer supported by OpenSSL + // Note: Release branches 1.1.1 and earlier are no longer supported by + // the OpenSSL team so these tests don't support them either. VERSION = -1; } @@ -394,7 +392,7 @@ public void run() { try { IOTools.flow(is, baos); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/org/apache/tomcat/util/net/user1.jks and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/user1.jks differ Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/org/apache/tomcat/util/net/user2-crl.jks and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/user2-crl.jks differ Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/org/apache/tomcat/util/net/user3-crl-long.jks and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/org/apache/tomcat/util/net/user3-crl-long.jks differ diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestPerMessageDeflate.java 2026-01-23 19:33:36.000000000 +0000 @@ -42,7 +42,7 @@ List> preferences = new ArrayList<>(); preferences.add(parameters); - PerMessageDeflate perMessageDeflate = PerMessageDeflate.negotiate(preferences, true); + PerMessageDeflate perMessageDeflate = PerMessageDeflate.build(preferences, true); perMessageDeflate.setNext(new TesterTransformation()); ByteBuffer bb1 = ByteBuffer.wrap("A".getBytes(StandardCharsets.UTF_8)); @@ -65,7 +65,7 @@ * https://bz.apache.org/bugzilla/show_bug.cgi?id=65317 */ @Test - public void testMessagePartThatFillsBufffer() throws IOException { + public void testMessagePartThatFillsBuffer() throws IOException { // Set up the extension using defaults List parameters = Collections.emptyList(); @@ -73,7 +73,7 @@ preferences.add(parameters); // Set up the compression and sending of the message. - PerMessageDeflate perMessageDeflateTx = PerMessageDeflate.negotiate(preferences, true); + PerMessageDeflate perMessageDeflateTx = PerMessageDeflate.build(preferences, true); perMessageDeflateTx.setNext(new TesterTransformation()); byte[] data = new byte[8192]; @@ -88,7 +88,7 @@ MessagePart compressedPart = compressedParts.get(0); // Set up the decompression and process the received message - PerMessageDeflate perMessageDeflateRx = PerMessageDeflate.negotiate(preferences, true); + PerMessageDeflate perMessageDeflateRx = PerMessageDeflate.build(preferences, true); perMessageDeflateRx.setNext(new TesterTransformation(compressedPart.getPayload())); ByteBuffer received = ByteBuffer.allocate(8192); @@ -112,7 +112,7 @@ preferences.add(parameters); // Set up the compression and sending of the message. - PerMessageDeflate perMessageDeflateTx = PerMessageDeflate.negotiate(preferences, true); + PerMessageDeflate perMessageDeflateTx = PerMessageDeflate.build(preferences, true); perMessageDeflateTx.setNext(new TesterTransformation()); List uncompressedParts = new ArrayList<>(); @@ -135,7 +135,7 @@ MessagePart compressedPart1 = compressedParts.get(0); // Set up the decompression and process the received message - PerMessageDeflate perMessageDeflateRx = PerMessageDeflate.negotiate(preferences, true); + PerMessageDeflate perMessageDeflateRx = PerMessageDeflate.build(preferences, true); perMessageDeflateRx.setNext(new TesterTransformation(compressedPart1.getPayload())); ByteBuffer received = ByteBuffer.allocate(8192); diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/TestWebSocketFrameClient.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestWebSocketFrameClient.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/TestWebSocketFrameClient.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestWebSocketFrameClient.java 2026-01-23 19:33:36.000000000 +0000 @@ -77,7 +77,7 @@ Session wsSession = wsContainer.connectToServer(TesterProgrammaticEndpoint.class, clientEndpointConfig, new URI("ws://localhost:" + getPort() + TesterFirehoseServer.PATH)); CountDownLatch latch = new CountDownLatch(TesterFirehoseServer.MESSAGE_COUNT); - BasicText handler = new BasicText(latch); + BasicText handler = new BasicText(latch, TesterFirehoseServer.MESSAGE); wsSession.addMessageHandler(handler); wsSession.getBasicRemote().sendText("Hello"); @@ -87,11 +87,7 @@ // if the right number of messages arrived handler.getLatch().await(TesterFirehoseServer.WAIT_TIME_MILLIS, TimeUnit.MILLISECONDS); - Queue messages = handler.getMessages(); - Assert.assertEquals(TesterFirehoseServer.MESSAGE_COUNT, messages.size()); - for (String message : messages) { - Assert.assertEquals(TesterFirehoseServer.MESSAGE, message); - } + Assert.assertEquals(TesterFirehoseServer.MESSAGE_COUNT, handler.getMessageCount()); } @Test diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java 2026-01-23 19:33:36.000000000 +0000 @@ -32,6 +32,8 @@ import javax.net.ssl.TrustManagerFactory; import jakarta.websocket.ClientEndpointConfig; +import jakarta.websocket.CloseReason; +import jakarta.websocket.CloseReason.CloseCodes; import jakarta.websocket.ContainerProvider; import jakarta.websocket.MessageHandler; import jakarta.websocket.Session; @@ -285,11 +287,9 @@ Assert.fail("There are [" + openConnectionCount + "] connections still open"); } - // Set a short session close timeout (milliseconds) - wsSession.getUserProperties().put( - org.apache.tomcat.websocket.Constants.SESSION_CLOSE_TIMEOUT_PROPERTY, Long.valueOf(2000)); - // Close the client session. - wsSession.close(); + // Cast so we can force the session to be closed quickly. + CloseReason cr = new CloseReason(CloseCodes.CLOSED_ABNORMALLY, ""); + ((WsSession) wsSession).doClose(cr, cr, true); } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/TestWsSessionSuspendResume.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestWsSessionSuspendResume.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/TestWsSessionSuspendResume.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/TestWsSessionSuspendResume.java 2026-01-23 19:33:36.000000000 +0000 @@ -108,8 +108,8 @@ public void onClose(Session session, CloseReason closeReason) { try { session.close(); - } catch (IOException e) { - e.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); } } @@ -137,7 +137,7 @@ session.getBasicRemote().sendText(messages.toString()); messages.clear(); ((WsSession) session).resume(); - } catch (IOException e) { + } catch (IOException ioe) { Assert.fail(); } } else { diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/TesterEchoServer.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterEchoServer.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/TesterEchoServer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterEchoServer.java 2026-01-23 19:33:36.000000000 +0000 @@ -65,10 +65,10 @@ public void echoTextMessage(Session session, String msg, boolean last) { try { session.getBasicRemote().sendText(msg, last); - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -79,10 +79,10 @@ public void echoBinaryMessage(Session session, ByteBuffer msg, boolean last) { try { session.getBasicRemote().sendBinary(msg, last); - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -96,10 +96,10 @@ public void echoTextMessage(Session session, String msg) { try { session.getBasicRemote().sendText(msg); - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -110,10 +110,10 @@ public void echoBinaryMessage(Session session, ByteBuffer msg) { try { session.getBasicRemote().sendBinary(msg); - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -130,10 +130,10 @@ public void echoTextMessage(Session session, String msg) { try { session.getBasicRemote().sendText(msg); - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -144,10 +144,10 @@ public void echoBinaryMessage(Session session, ByteBuffer msg) { try { session.getBasicRemote().sendBinary(msg); - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -164,10 +164,10 @@ public void echoTextMessage(Session session, String msg) { try { session.getBasicRemote().sendText(msg); - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -178,10 +178,10 @@ public void echoBinaryMessage(Session session, ByteBuffer msg) { try { session.getBasicRemote().sendBinary(msg); - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -198,11 +198,11 @@ session.getBasicRemote().getSendWriter(); // Simulate an error throw new RuntimeException(); - } catch (IOException e) { + } catch (IOException ioe) { // Should not happen try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -216,10 +216,10 @@ public void echoTextMessage(Session session, String msg) { try { session.getBasicRemote().sendText(msg); - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/TesterFirehoseServer.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterFirehoseServer.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/TesterFirehoseServer.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterFirehoseServer.java 2026-01-23 19:33:36.000000000 +0000 @@ -41,7 +41,7 @@ public static final String MESSAGE; public static final int MESSAGE_SIZE = 1024; public static final int WAIT_TIME_MILLIS = 300000; - public static final int SEND_TIME_OUT_MILLIS = 5000; + public static final int SEND_TIME_OUT_MILLIS = 2000; public static final String PATH = "/firehose"; diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/TesterMessageCountClient.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterMessageCountClient.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/TesterMessageCountClient.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterMessageCountClient.java 2026-01-23 19:33:36.000000000 +0000 @@ -22,6 +22,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; import jakarta.websocket.ClientEndpoint; import jakarta.websocket.CloseReason; @@ -144,6 +145,7 @@ public static class BasicText extends BasicHandler { private final String expected; + private final AtomicInteger messageCount; public BasicText(CountDownLatch latch) { this(latch, null); @@ -152,6 +154,11 @@ public BasicText(CountDownLatch latch, String expected) { super(latch); this.expected = expected; + if (expected == null) { + messageCount = null; + } else { + messageCount = new AtomicInteger(0); + } } @Override @@ -162,11 +169,20 @@ if (!expected.equals(message)) { throw new IllegalStateException("Expected: [" + expected + "]\r\n" + "Was: [" + message + "]"); } + messageCount.incrementAndGet(); } if (getLatch() != null) { getLatch().countDown(); } } + + public int getMessageCount() { + if (expected == null) { + return getMessages().size(); + } else { + return messageCount.get(); + } + } } public static class SleepingText implements MessageHandler.Whole { diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/TesterWsClientAutobahn.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterWsClientAutobahn.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/TesterWsClientAutobahn.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/TesterWsClientAutobahn.java 2026-01-23 19:33:36.000000000 +0000 @@ -153,10 +153,10 @@ if (session.isOpen()) { session.getBasicRemote().sendText(msg, last); } - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -168,10 +168,10 @@ if (session.isOpen()) { session.getBasicRemote().sendBinary(bb, last); } - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/server/TestSlowClient.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/server/TestSlowClient.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/server/TestSlowClient.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/server/TestSlowClient.java 2026-01-23 19:33:36.000000000 +0000 @@ -19,6 +19,8 @@ import java.net.URI; import jakarta.websocket.ClientEndpointConfig; +import jakarta.websocket.CloseReason; +import jakarta.websocket.CloseReason.CloseCodes; import jakarta.websocket.ContainerProvider; import jakarta.websocket.MessageHandler; import jakarta.websocket.Session; @@ -34,6 +36,7 @@ import org.apache.tomcat.websocket.TesterFirehoseServer; import org.apache.tomcat.websocket.TesterMessageCountClient.TesterProgrammaticEndpoint; import org.apache.tomcat.websocket.WebSocketBaseTest; +import org.apache.tomcat.websocket.WsSession; public class TestSlowClient extends WebSocketBaseTest { @@ -61,19 +64,26 @@ // Trigger the sending of the messages from the server wsSession.getBasicRemote().sendText("start"); - // Wait for server to close connection (it shouldn't) - // 20s should be long enough even for the slowest CI system. May need to - // extend this if not. + /* + * Wait for server to experience a write timeout. This should take TesterFirehoseServer.SEND_TIME_OUT_MILLIS + * plus however long it takes for the network buffer to fill up which should be a few milliseconds. An + * additional 10s should be enough even for the slowest CI system. + * + * As soon as the server has experienced the write timeout, send the session close message from the client and + * close the network connection. + */ + Thread.sleep(TesterFirehoseServer.SEND_TIME_OUT_MILLIS); int count = 0; - while (wsSession.isOpen() && count < 200) { - Thread.sleep(100); + while (wsSession.isOpen() && TesterFirehoseServer.Endpoint.getErrorCount() == 0 && count < 200) { + Thread.sleep(50); count++; } + Assert.assertTrue(TesterFirehoseServer.Endpoint.getErrorCount() > 0); Assert.assertTrue(wsSession.isOpen()); - // Set a short session close timeout (milliseconds) - wsSession.getUserProperties().put( - org.apache.tomcat.websocket.Constants.SESSION_CLOSE_TIMEOUT_PROPERTY, Long.valueOf(2000)); - wsSession.close(); + + // Cast so we can force the session and the socket to be closed quickly. + CloseReason cr = new CloseReason(CloseCodes.CLOSED_ABNORMALLY, ""); + ((WsSession) wsSession).doClose(cr, cr, true); // BZ 64848 (non-container thread variant) // Confirm there are no waiting processors diff -Nru tomcat10-10.1.34/test/org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServerDeadlock.java tomcat10-10.1.52/test/org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServerDeadlock.java --- tomcat10-10.1.34/test/org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServerDeadlock.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServerDeadlock.java 2026-01-23 19:33:36.000000000 +0000 @@ -51,7 +51,7 @@ /* * https://bz.apache.org/bugzilla/show_bug.cgi?id=66508 * - * If the client sends a close while the server waiting for the client before sending the rest of a message, the + * If the client sends a close while the server is waiting for the client before sending the rest of a message, the * processing of the close from the client can hang until the sending of the message times out. * * This is packaged in a separate class to allow test specific parameterisation. diff -Nru tomcat10-10.1.34/test/webapp/WEB-INF/classes/bug69623-a.mdd tomcat10-10.1.52/test/webapp/WEB-INF/classes/bug69623-a.mdd --- tomcat10-10.1.34/test/webapp/WEB-INF/classes/bug69623-a.mdd 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/WEB-INF/classes/bug69623-a.mdd 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1 @@ +This is a test file for https://bz.apache.org/bugzilla/show_bug.cgi?id=69623 \ No newline at end of file Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/webapp/WEB-INF/lib/bug69623-lib.jar and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/webapp/WEB-INF/lib/bug69623-lib.jar differ diff -Nru tomcat10-10.1.34/test/webapp/WEB-INF/web.xml tomcat10-10.1.52/test/webapp/WEB-INF/web.xml --- tomcat10-10.1.34/test/webapp/WEB-INF/web.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/WEB-INF/web.xml 2026-01-23 19:33:36.000000000 +0000 @@ -28,6 +28,11 @@ required. + + JSP-nonstandard + /jsp/generator/nonstandard/* + + jsp org.apache.jasper.servlet.JspServlet @@ -41,6 +46,29 @@ + modificationTestInterval + 0 + + 3 + + + + JSP-nonstandard + org.apache.jasper.servlet.JspServlet + + fork + false + + + xpoweredBy + false + + + useNonstandardTagOptimizations + c:set,c:remove + + + modificationTestInterval 0 diff -Nru tomcat10-10.1.34/test/webapp/bug6nnnn/bug69508.jsp tomcat10-10.1.52/test/webapp/bug6nnnn/bug69508.jsp --- tomcat10-10.1.34/test/webapp/bug6nnnn/bug69508.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/bug6nnnn/bug69508.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,64 @@ +<%-- + 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. +--%> + + + + + + + + + + + + + + + + + +<%-- + Verify expression support in page and param value. + --%> +<% + String initCommand = request.getParameter("init"); + if (initCommand != null) { + String relativeUrl = "../echo-params.jsp?param3=" + initCommand; + String absoluteUrl = "/echo-params.jsp?param3=" + initCommand + "Abs"; + String init_param = initCommand+"_param"; + String init_param_value_abs=initCommand+"Abs"; + %> + + + + + + + + + <% + } +%> +<%-- +Following cases without jsp:param +--%> + + + + + + \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/bug6nnnn/bug69635.jsp tomcat10-10.1.52/test/webapp/bug6nnnn/bug69635.jsp --- tomcat10-10.1.34/test/webapp/bug6nnnn/bug69635.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/bug6nnnn/bug69635.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,18 @@ +<%-- + 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. +--%> +<%@page import="org.apache.catalina.authenticator.DigestAuthenticator.AuthDigest"%> +

    01-${ AuthDigest.values().stream().count() }

    diff -Nru tomcat10-10.1.34/test/webapp/jsp/encoding/README.txt tomcat10-10.1.52/test/webapp/jsp/encoding/README.txt --- tomcat10-10.1.34/test/webapp/jsp/encoding/README.txt 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/encoding/README.txt 2026-01-23 19:33:36.000000000 +0000 @@ -16,7 +16,7 @@ ================================================================================ A number of the test files in this directory specify conflicting encoding -in the BOM and and in the XML prolog. The rules for determining the actual +in the BOM and in the XML prolog. The rules for determining the actual encoding used in the file are as follows: 1. If there is a BOM, use the encoding defined by the BOM. diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/lambda.jsp tomcat10-10.1.52/test/webapp/jsp/generator/lambda.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/lambda.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/lambda.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,23 @@ +<%-- + 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. +--%> +<%@ page contentType="text/plain; charset=UTF-8" %> +<% + Runnable r = () -> System.out.println("Lambda OK"); + r.run(); + java.util.function.Consumer c = System.out::print; + c.accept("Method Reference OK"); +%> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-01.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-01.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-01.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-01.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,26 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + + + + + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-02.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-02.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-02.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-02.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,26 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + + + + + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-03.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-03.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-03.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-03.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,26 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + + + + + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-04.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-04.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-04.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-04.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,26 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + + + + + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-05.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-05.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/remove-05.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/remove-05.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,26 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + + + + + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-01.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-01.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-01.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-01.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,22 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-02.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-02.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-02.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-02.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,22 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-03.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-03.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-03.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-03.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,22 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-04.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-04.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-04.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-04.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,22 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-05.jsp tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-05.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/nonstandard/set-05.jsp 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/nonstandard/set-05.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,22 @@ +<%-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> +<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %> + +pageContext value=<%= pageContext.getAttribute("testVar") %> +request value=<%= request.getAttribute("testVar") %> +session value=<%= session.getAttribute("testVar") %> +application value=<%= application.getAttribute("testVar") %> \ No newline at end of file diff -Nru tomcat10-10.1.34/test/webapp/jsp/generator/setproperty-01.jsp tomcat10-10.1.52/test/webapp/jsp/generator/setproperty-01.jsp --- tomcat10-10.1.34/test/webapp/jsp/generator/setproperty-01.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/generator/setproperty-01.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -16,7 +16,7 @@ --%> - jsp:setProeprty test cases + jsp:setProperty test cases diff -Nru tomcat10-10.1.34/test/webapp/jsp/pageContext1.jsp tomcat10-10.1.52/test/webapp/jsp/pageContext1.jsp --- tomcat10-10.1.34/test/webapp/jsp/pageContext1.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/test/webapp/jsp/pageContext1.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,7 @@ } else { pageContext.include("/jsp/pageContext2.jsp"); } - } catch (IOException e) { + } catch (IOException ioe) { out.println("OK"); return; } catch (Throwable t) { Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/test/webresources/dir1.jar and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/test/webresources/dir1.jar differ diff -Nru tomcat10-10.1.34/test-profiles.properties.default tomcat10-10.1.52/test-profiles.properties.default --- tomcat10-10.1.34/test-profiles.properties.default 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/test-profiles.properties.default 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,100 @@ +# ----------------------------------------------------------------------------- +# 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. +# ----------------------------------------------------------------------------- +# test-profiles.properties +# +# This file contains test profile pattern definitions for ant test targets. +# These patterns are used by the test profile system in build.xml. +# +# To add a new profile: +# 1. Add the property: test.profile.myprofile=pattern +# 2. Add a condition in build.xml's set-profile-pattern macro +# +# ----------------------------------------------------------------------------- + +# Smoke test profile +test.profile.smoke=\ +org/apache/catalina/authenticator/TestFormAuthenticatorA.java,\ +org/apache/catalina/authenticator/TestSSOnonLoginAndBasicAuthenticator.java,\ +org/apache/catalina/core/TestAsyncContextStateChanges.java,\ +org/apache/catalina/core/TestStandardContextResources.java,\ +org/apache/catalina/core/TestStandardWrapper.java,\ +org/apache/catalina/loader/TestVirtualContext.java,\ +org/apache/catalina/mapper/TestMapperWebapps.java,\ +org/apache/catalina/nonblocking/TestNonBlockingAPI.java,\ +org/apache/catalina/servlets/TestDefaultServletEncodingPassThroughBom.java,\ +org/apache/catalina/servlets/TestDefaultServletEncodingWithBom.java,\ +org/apache/catalina/servlets/TestDefaultServletEncodingWithoutBom.java,\ +org/apache/catalina/servlets/TestDefaultServletIfMatchRequests.java,\ +org/apache/catalina/servlets/TestDefaultServlet.java,\ +org/apache/catalina/servlets/TestDefaultServletOptions.java,\ +org/apache/catalina/servlets/TestWebdavServletOptionsFile.java,\ +org/apache/catalina/startup/TestContextConfig.java,\ +org/apache/catalina/startup/TestHostConfigAutomaticDeploymentA.java,\ +org/apache/catalina/startup/TestHostConfigAutomaticDeploymentB.java,\ +org/apache/catalina/startup/TestHostConfigAutomaticDeploymentC.java,\ +org/apache/catalina/valves/rewrite/TestRewriteValve.java,\ +org/apache/catalina/valves/TestStuckThreadDetectionValve.java,\ +org/apache/coyote/ajp/TestAbstractAjpProcessor.java,\ +org/apache/coyote/http11/filters/TestChunkedInputFilter.java,\ +org/apache/coyote/http11/TestHttp11InputBufferCRLF.java,\ +org/apache/coyote/http11/TestHttp11InputBuffer.java,\ +org/apache/coyote/http11/TestHttp11Processor.java,\ +org/apache/coyote/http2/TestAsync.java,\ +org/apache/coyote/http2/TestHttp2ConnectionTimeouts.java,\ +org/apache/coyote/http2/TestHttp2Limits.java,\ +org/apache/coyote/http2/TestHttp2Section_6_8.java,\ +org/apache/coyote/http2/TestStreamQueryString.java,\ +org/apache/el/TestELInJsp.java,\ +org/apache/jasper/compiler/TestCompiler.java,\ +org/apache/jasper/compiler/TestEncodingDetector.java,\ +org/apache/jasper/compiler/TestGenerator.java,\ +org/apache/jasper/compiler/TestJspConfig.java,\ +org/apache/jasper/compiler/TestJspDocumentParser.java,\ +org/apache/jasper/compiler/TestValidator.java,\ +org/apache/jasper/optimizations/TestELInterpreterTagSetters.java,\ +org/apache/jasper/optimizations/TestStringInterpreterTagSetters.java,\ +org/apache/jasper/runtime/TestCustomHttpJspPage.java,\ +org/apache/jasper/runtime/TestJspContextWrapper.java,\ +org/apache/jasper/runtime/TestJspRuntimeLibrary.java,\ +org/apache/jasper/runtime/TestPageContextImpl.java,\ +org/apache/jasper/servlet/TestTldScanner.java,\ +org/apache/naming/resources/TestWarDirContext.java,\ +org/apache/naming/TestEnvEntry.java,\ +org/apache/tomcat/util/net/TestClientCert.java,\ +org/apache/tomcat/util/net/TestCustomSslTrustManager.java,\ +org/apache/tomcat/util/net/TestSSLHostConfigCompat.java,\ +org/apache/tomcat/util/net/TestSsl.java,\ +org/apache/tomcat/websocket/server/TestSlowClient.java,\ +org/apache/tomcat/websocket/server/TestWsRemoteEndpointImplServerDeadlock.java,\ +org/apache/tomcat/websocket/TestWebSocketFrameClientSSL.java,\ +org/apache/tomcat/websocket/TestWsWebSocketContainer.java,\ +org/apache/tomcat/websocket/TestWsWebSocketContainerTimeoutServer.java,\ +org/apache/catalina/tribes/group/interceptors/TestTcpFailureDetector.java + +# Component test profiles +test.profile.catalina=**/catalina/**/*Test*.java +test.profile.coyote=**/coyote/**/*Test*.java + +# Performance test profile +test.profile.performance=**/*Performance.java + +# Tribes test profile +test.profile.tribes=**/tribes/**/*Test*.java + +# Build utility test profile: Tests for build tools (normally excluded) +# Note: These tests depend on classes not in output JARs and are excluded by default +test.profile.buildutil=**/buildutil/**/*Test*.java diff -Nru tomcat10-10.1.34/webapps/ROOT/asf-logo-wide.svg tomcat10-10.1.52/webapps/ROOT/asf-logo-wide.svg --- tomcat10-10.1.34/webapps/ROOT/asf-logo-wide.svg 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/ROOT/asf-logo-wide.svg 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru tomcat10-10.1.34/webapps/docs/META-INF/context.xml tomcat10-10.1.52/webapps/docs/META-INF/context.xml --- tomcat10-10.1.34/webapps/docs/META-INF/context.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/META-INF/context.xml 2026-01-23 19:33:36.000000000 +0000 @@ -16,6 +16,6 @@ limitations under the License. --> - + diff -Nru tomcat10-10.1.34/webapps/docs/aio.xml tomcat10-10.1.52/webapps/docs/aio.xml --- tomcat10-10.1.34/webapps/docs/aio.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/aio.xml 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,6 @@ Advanced IO and Tomcat - Remy Maucherat diff -Nru tomcat10-10.1.34/webapps/docs/appdev/deployment.xml tomcat10-10.1.52/webapps/docs/appdev/deployment.xml --- tomcat10-10.1.34/webapps/docs/appdev/deployment.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/appdev/deployment.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Craig R. McClanahan Deployment diff -Nru tomcat10-10.1.34/webapps/docs/appdev/index.xml tomcat10-10.1.52/webapps/docs/appdev/index.xml --- tomcat10-10.1.34/webapps/docs/appdev/index.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/appdev/index.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,26 +23,12 @@ &project; - Craig R. McClanahan Table of Contents -
    - -

    This manual includes contributions from many members of the Tomcat Project -developer community. The following authors have provided significant content: -

    -
    - -
    - -

    The information presented is divided into the following sections:

    diff -Nru tomcat10-10.1.34/webapps/docs/appdev/installation.xml tomcat10-10.1.52/webapps/docs/appdev/installation.xml --- tomcat10-10.1.34/webapps/docs/appdev/installation.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/appdev/installation.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ &project; - Craig R. McClanahan - Yoav Shapira Installation diff -Nru tomcat10-10.1.34/webapps/docs/appdev/introduction.xml tomcat10-10.1.52/webapps/docs/appdev/introduction.xml --- tomcat10-10.1.34/webapps/docs/appdev/introduction.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/appdev/introduction.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Craig R. McClanahan Introduction diff -Nru tomcat10-10.1.34/webapps/docs/appdev/processes.xml tomcat10-10.1.52/webapps/docs/appdev/processes.xml --- tomcat10-10.1.34/webapps/docs/appdev/processes.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/appdev/processes.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Craig R. McClanahan Development Processes @@ -45,7 +44,7 @@

    The task descriptions below do not assume any particular source code control system but simply identify when and what source code control tasks are typically -performed. You will need to idenitfy the appropriate source code control +performed. You will need to identify the appropriate source code control commands for your system.

    diff -Nru tomcat10-10.1.34/webapps/docs/appdev/sample/index.html tomcat10-10.1.52/webapps/docs/appdev/sample/index.html --- tomcat10-10.1.34/webapps/docs/appdev/sample/index.html 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/appdev/sample/index.html 2026-01-23 19:33:36.000000000 +0000 @@ -18,7 +18,6 @@ - Sample Application diff -Nru tomcat10-10.1.34/webapps/docs/appdev/sample/src/mypackage/Hello.java tomcat10-10.1.52/webapps/docs/appdev/sample/src/mypackage/Hello.java --- tomcat10-10.1.34/webapps/docs/appdev/sample/src/mypackage/Hello.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/appdev/sample/src/mypackage/Hello.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,8 +30,6 @@ * execute servlets. In the web application deployment descriptor, * this servlet must be mapped to correspond to the link in the * "index.html" file. - * - * @author Craig R. McClanahan <Craig.McClanahan@eng.sun.com> */ public final class Hello extends HttpServlet { diff -Nru tomcat10-10.1.34/webapps/docs/appdev/source.xml tomcat10-10.1.52/webapps/docs/appdev/source.xml --- tomcat10-10.1.34/webapps/docs/appdev/source.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/appdev/source.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Craig R. McClanahan Source Organization diff -Nru tomcat10-10.1.34/webapps/docs/apr.xml tomcat10-10.1.52/webapps/docs/apr.xml --- tomcat10-10.1.34/webapps/docs/apr.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/apr.xml 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,6 @@ Apache Portable Runtime (APR) based Native library for Tomcat - Remy Maucherat diff -Nru tomcat10-10.1.34/webapps/docs/architecture/index.xml tomcat10-10.1.52/webapps/docs/architecture/index.xml --- tomcat10-10.1.34/webapps/docs/architecture/index.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/architecture/index.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Yoav Shapira Table of Contents @@ -37,12 +36,8 @@ contributions from several tomcat developers:

    diff -Nru tomcat10-10.1.34/webapps/docs/architecture/overview.xml tomcat10-10.1.52/webapps/docs/architecture/overview.xml --- tomcat10-10.1.34/webapps/docs/architecture/overview.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/architecture/overview.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Yoav Shapira Architecture Overview diff -Nru tomcat10-10.1.34/webapps/docs/architecture/requestProcess/11_nio.plantuml tomcat10-10.1.52/webapps/docs/architecture/requestProcess/11_nio.plantuml --- tomcat10-10.1.34/webapps/docs/architecture/requestProcess/11_nio.plantuml 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/architecture/requestProcess/11_nio.plantuml 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,97 @@ +@startuml + +' 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. + +hide footbox +skinparam style strictuml + +activate Acceptor +participant NioEndpoint +participant ServerSocket +participant SocketChannel +activate Poller +participant Selector +participant Executor +activate Worker + +group Acceptor Loop +Acceptor -> NioEndpoint ++: serverSocketAccept() +NioEndpoint -> ServerSocket ++: accept() +note right of ServerSocket + This is where Tomcat + waits for incoming + connections +end note +ServerSocket -->> SocketChannel **: +return +return + +Acceptor -> NioEndpoint ++: setSocketOptions() +NioEndpoint -> Poller ++: register() +return +return +end + +group Poller Loop (simplified) +Poller -> Poller ++: events() +Poller -> SocketChannel ++: register(OP_READ) +return +return + +Poller -> Selector ++: select() +return + +Poller -> Poller ++: processKey() +Poller -> NioEndpoint ++: processSocket() +NioEndpoint -> NioEndpoint ++: createSocketProcessor() +NioEndpoint -->> SocketProcessor **: +NioEndpoint -> Executor ++: execute() +Executor -> Worker ++: run() +return +return +return +return +return +end + +Worker -> SocketProcessor ++: run() +SocketProcessor -> SocketChannel ++: handshake() +note right of SocketChannel + TLS handshake +end note +return +SocketProcessor -> ConnectionHandler ++: process() +ConnectionHandler -> Protocol ++: createProcessor() +Protocol -->> Processor **: +activate Processor +Processor -->> CoyoteRequest **: +Processor -->> CoyoteResponse **: +return +return +ConnectionHandler -> Processor ++: process() +Processor -> Processor ++: service() +note right of Processor + More detail of this + processing in the + protocol specific + diagrams +end note +return +return +return +return + +@enduml \ No newline at end of file Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/webapps/docs/architecture/requestProcess/11_nio.png and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/11_nio.png differ diff -Nru tomcat10-10.1.34/webapps/docs/architecture/requestProcess/21_http11.plantuml tomcat10-10.1.52/webapps/docs/architecture/requestProcess/21_http11.plantuml --- tomcat10-10.1.34/webapps/docs/architecture/requestProcess/21_http11.plantuml 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/architecture/requestProcess/21_http11.plantuml 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,38 @@ +@startuml + +' 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. + +hide footbox +skinparam style strictuml + +activate Processor + +Processor -> InputBuffer ++: parseRequestLine() +return +Processor -> Processor ++: prepareRequestProtocol() +return +Processor -> InputBuffer ++: parseHeaders() +return +Processor -> Processor ++: prepareRequest() +return +Processor -> CoyoteAdapter ++: service() +note right of CoyoteAdapter + Servlet request processing + happens here +end note +return + +@enduml \ No newline at end of file Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/webapps/docs/architecture/requestProcess/21_http11.png and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/21_http11.png differ diff -Nru tomcat10-10.1.34/webapps/docs/architecture/requestProcess/31_synchronous.plantuml tomcat10-10.1.52/webapps/docs/architecture/requestProcess/31_synchronous.plantuml --- tomcat10-10.1.34/webapps/docs/architecture/requestProcess/31_synchronous.plantuml 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/architecture/requestProcess/31_synchronous.plantuml 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,109 @@ +@startuml + +' 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. + +hide footbox +skinparam style strictuml + +participant CoyoteRequest +participant CoyoteResponse +activate CoyoteAdapter + +CoyoteAdapter -> Connector ++: createRequest() +return + +CoyoteAdapter -> Connector ++: createResponse() +return + +CoyoteAdapter -> Request ++: setRequest() +return +CoyoteAdapter -> Response ++: setResponse() +return +CoyoteAdapter -> CoyoteRequest ++: setNote() +return +CoyoteAdapter -> CoyoteResponse ++: setNote() +return + +CoyoteAdapter -> CoyoteAdapter ++: postParseRequest() +return + +CoyoteAdapter -> Connector ++: getService() +return +CoyoteAdapter -> Service ++: getContainer() +return +CoyoteAdapter -> Engine ++: getPipeline() +return +CoyoteAdapter -> "StandardPipeline\n(Engine)" ++: getFirst() +return +CoyoteAdapter -> StandardEngineValve ++: invoke() +StandardEngineValve -> Request ++: getHost() +return +StandardEngineValve -> Host ++: getPipeline() +return +StandardEngineValve -> "StandardPipeline\n(Host)" ++: getFirst() +return +StandardEngineValve -> AccessLogValve ++: invoke() +AccessLogValve -> AccessLogValve ++: getNext() +return +AccessLogValve -> ErrorReportValve ++: invoke() +ErrorReportValve -> ErrorReportValve ++: getNext() +return +ErrorReportValve -> StandardHostValve ++: invoke() +StandardHostValve -> Request ++: getContext() +return +StandardHostValve -> Context ++: bind() +return +StandardHostValve -> Context ++: getPipeline() +return +StandardHostValve -> "StandardPipeline\n(Context)" ++: getFirst() +return +StandardHostValve -> StandardContextValve ++: invoke() +StandardContextValve -> Request ++: getWrapper() +return +StandardContextValve -> Wrapper ++: getPipeline() +return +StandardContextValve -> "StandardPipeline\n(Wrapper)" ++: getFirst() +return +StandardContextValve -> StandardWrapperValve ++: invoke() +StandardWrapperValve -> Wrapper ++: allocate() +return +StandardWrapperValve -> ApplicationFilterFactory ++: createFilterChain +ApplicationFilterFactory --> FilterChain **: +return +StandardWrapperValve -> FilterChain ++: doFilter() +FilterChain -> "Filter\nA" ++: doFilter() +"Filter\nA" -> FilterChain ++: doFilter() +FilterChain -> "Filter\nB" ++: doFilter() +"Filter\nB" -> FilterChain ++: doFilter() +FilterChain -> Servlet ++: service() +||| +return +return +return +return +return +return +StandardWrapperValve -> FilterChain ++: release() +return +StandardWrapperValve -> Wrapper ++: deallocate() +return +return +return +return +return +return +return +@enduml \ No newline at end of file Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/webapps/docs/architecture/requestProcess/31_synchronous.png and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/31_synchronous.png differ diff -Nru tomcat10-10.1.34/webapps/docs/architecture/requestProcess/41_basic.plantuml tomcat10-10.1.52/webapps/docs/architecture/requestProcess/41_basic.plantuml --- tomcat10-10.1.34/webapps/docs/architecture/requestProcess/41_basic.plantuml 1970-01-01 00:00:00.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/architecture/requestProcess/41_basic.plantuml 2026-01-23 19:33:36.000000000 +0000 @@ -0,0 +1,78 @@ +@startuml + +' 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. + +hide footbox +skinparam style strictuml + +participant MimeHeaders +participant CoyoteRequest +participant Request +activate ErrorReportValve + +ErrorReportValve -> StandardHostValve ++: invoke() +StandardHostValve -> Request ++: getContext() +return +StandardHostValve -> Context ++: bind() +return +StandardHostValve -> Context ++: getPipeline() +return +StandardHostValve -> "StandardPipeline\n(Context)" ++: getFirst() +return +StandardHostValve -> BasicAuthenticator ++: invoke() +BasicAuthenticator -> Context ++: getRealm() +return +BasicAuthenticator -> LockoutRealm ++: findSecurityContraints() +return +BasicAuthenticator -> LockoutRealm ++: hasUserDataPermission() +return +BasicAuthenticator -> BasicAuthenticator ++: doAuthenticate() +BasicAuthenticator -> Request ++: getCoyoteRequest() +return +BasicAuthenticator -> CoyoteRequest ++: getMimeHeaders() +return +BasicAuthenticator -> MimeHeaders ++: getValue("authorization") +return +BasicAuthenticator --> BasicCredentials **: +BasicAuthenticator -> BasicCredentials ++: getUserName() +return +BasicAuthenticator -> BasicCredentials ++: getPassword() +return +BasicAuthenticator -> Context ++: getRealm() +return +BasicAuthenticator -> LockoutRealm ++: authenticate() +LockoutRealm -> UserDatabaseRealm ++: authenticate() +UserDatabaseRealm -> UserDatabaseRealm ++: getPassword(username) +UserDatabaseRealm -> CredentialHandler ++: matches() +return +UserDatabaseRealm -> UserDatabaseRealm ++: getPrincipal() +return +return +return +return +return +BasicAuthenticator -> Realm ++: hasResourcePermission() +return +BasicAuthenticator -> BasicAuthenticator ++: getNext() +return +BasicAuthenticator -> StandardContextValve ++: invoke() +note right of StandardContextValve + Standard Servlet request + processing continues from + this point +end note + +@enduml \ No newline at end of file Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/webapps/docs/architecture/requestProcess/41_basic.png and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/41_basic.png differ Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/webapps/docs/architecture/requestProcess/authentication-process.png and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/authentication-process.png differ Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/webapps/docs/architecture/requestProcess/request-process.png and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/docs/architecture/requestProcess/request-process.png differ diff -Nru tomcat10-10.1.34/webapps/docs/architecture/requestProcess.xml tomcat10-10.1.52/webapps/docs/architecture/requestProcess.xml --- tomcat10-10.1.34/webapps/docs/architecture/requestProcess.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/architecture/requestProcess.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,52 +23,81 @@ &project; - Yoav Shapira Request Process Flow - -
    - +

    -This page describes the process used by Tomcat to handle -an incoming request. This process is largely defined by -the Servlet Specification, which outlines the order -of events that must take place. +This page describes how requests flow through Tomcat during processing. Requests +start processing in an endpoint, then get passed to the protocol and then the +Coyote adapter which is the common entry point for all servlet request +processing.

    - -

    -TODO +The UML diagrams do not show every detail of Tomcat's internal processing. They +are intended to show the key elements of the processing chain.

    -
    +
    - +

    -A UML sequence diagram of the request process is available -here. +UML diagrams have been created for each Endpoint implementation to show how +requests flow from the endpoint to the protocol.

    -A UML sequence diagram of the authentication process is available -here. +Diagram 1.1 shows how requests received via +an NIO based endpoint are processed upto the point they reach the processor +instance.

    - - - -

    -The Servlet Specification provides many opportunities for -listening in (using Listeners) or modifying (using Filters) -the request handling process even before the request arrives -at the servlet that will handle it. +NIO2 - TBD.

    +
    -
    +
    +

    +UML diagrams have been created for each protocol to show how requests flow from +the processor instance to the CoyoteAdapter. +

    +

    +Diagram 2.1 shows how HTTP/0.9, HTTP/1.0 +and HTTP/1.1 requests are processed. +

    +

    +HTTP/2 - TBD. +

    +

    +AJP - TBD. +

    +
    +
    +

    +Diagram 3.1 shows synchronous +Servlet requests are processed. +

    +

    +Asynchronous - TBD. +

    +
    +

    +Diagram 4.1 shows how BASIC +authentication is processed. +

    +

    +DIGEST - TBD. +

    +

    +FORM - TBD. +

    +

    +Jakarta Authentication (JASPIC) - TBD. +

    +
    diff -Nru tomcat10-10.1.34/webapps/docs/architecture/startup.xml tomcat10-10.1.52/webapps/docs/architecture/startup.xml --- tomcat10-10.1.34/webapps/docs/architecture/startup.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/architecture/startup.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Yoav Shapira Startup @@ -56,7 +55,7 @@ received, the Server object is stopped and then destroyed. The JVM then exits.

    -Diagram 2 shows how Tomcat initalizes +Diagram 2 shows how Tomcat initializes the objects created by the Digester in the previous step and when additional key objects are created. A Server may have several Services although it typically only has one. Each Service may have multiple Connectors. A Connector instance is diff -Nru tomcat10-10.1.34/webapps/docs/balancer-howto.xml tomcat10-10.1.52/webapps/docs/balancer-howto.xml --- tomcat10-10.1.34/webapps/docs/balancer-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/balancer-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,6 @@ &project; - Yoav Shapira - Remy Maucherat - Andy Oliver Load Balancer How-To diff -Nru tomcat10-10.1.34/webapps/docs/cdi.xml tomcat10-10.1.52/webapps/docs/cdi.xml --- tomcat10-10.1.34/webapps/docs/cdi.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/cdi.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,7 @@ &project; - CDI 2, JAX-RS and dependent libraries support + CDI, JAX-RS and dependent libraries support @@ -42,11 +42,11 @@

    -
    +

    - CDI 2 support is provided by the modules/owb optional module. - It packages the Apache OpenWebBeans project and allows adding CDI 2 support + CDI support is provided by the modules/owb optional module. + It packages the Apache OpenWebBeans project and allows adding CDI support to the Tomcat container. The build process of the module uses Apache Maven, and is not available as a binary bundle as it is built using a number of publicly available JARs. @@ -83,7 +83,7 @@ It packages the Apache CXF project and allows adding JAX-RS support to individual webapps. The build process of the module uses Apache Maven, and is not available as a binary bundle as it is built using a number of - publicly available JARs. The support depends on CDI 2 support, which should + publicly available JARs. The support depends on CDI support, which should have previously been installed at either the container or webapp level.

    @@ -99,7 +99,7 @@

    - If the CDI 2 support is available at the container + If the CDI support is available at the container level, the JAR can also be placed in the Tomcat lib folder, but in that case the CXF Servlet declaration must be individually added in each webapp as needed (it is normally loaded by the web fragment that is @@ -108,79 +108,6 @@ desired root path where JAX-RS resources will be available.

    -

    - The webapp as a whole should be processed by the Tomcat migration tool for - Jakarta EE. -

    - -
    - -
    - -

    - ASF artifacts are available that implement Eclipse Microprofile - specifications using CDI 2 extensions. Once the CDI 2 and JAX-RS support - is installed, they will be usable by individual webapps. -

    - -

    - The following implementations are available (reference: - org.apache.tomee.microprofile.TomEEMicroProfileListener) as - Maven artifacts which must be added to the webapp /WEB-INF/lib - folders: -

      -
    • Configuration: - Maven artifact: - org.apache.geronimo.config:geronimo-config - CDI extension class: - org.apache.geronimo.config.cdi.ConfigExtension -
    • -
    • Fault Tolerance: - Maven artifact: - org.apache.geronimo.safeguard:safeguard-parent - CDI extension class: - org.apache.safeguard.impl.cdi.SafeguardExtension -
    • -
    • Health: - Maven artifact: - org.apache.geronimo:geronimo-health - CDI extension class: - org.apache.geronimo.microprofile.impl.health.cdi.GeronimoHealthExtension -
    • -
    • Metrics: - Maven artifact: - org.apache.geronimo:geronimo-metrics - CDI extension class: - org.apache.geronimo.microprofile.metrics.cdi.MetricsExtension -
    • -
    • OpenTracing: - Maven artifact: - org.apache.geronimo:geronimo-opentracing - CDI extension class: - org.apache.geronimo.microprofile.opentracing.microprofile.cdi.OpenTracingExtension -
    • -
    • OpenAPI: - Maven artifact: - org.apache.geronimo:geronimo-openapi - CDI extension class: - org.apache.geronimo.microprofile.openapi.cdi.GeronimoOpenAPIExtension -
    • -
    • Rest client: - Maven artifact: - org.apache.cxf:cxf-rt-rs-mp-client - CDI extension class: - org.apache.cxf.microprofile.client.cdi.RestClientExtension -
    • -
    • JSON Web Tokens: - Note: Fore reference only, unusable outside Apache TomEE; - Maven artifact: - org.apache.tomee:mp-jwt - CDI extension class: - org.apache.tomee.microprofile.jwt.cdi.MPJWTCDIExtension -
    • -
    -

    -
    diff -Nru tomcat10-10.1.34/webapps/docs/changelog.xml tomcat10-10.1.52/webapps/docs/changelog.xml --- tomcat10-10.1.34/webapps/docs/changelog.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/changelog.xml 2026-01-23 19:33:36.000000000 +0000 @@ -104,7 +104,1554 @@ They eventually become mixed with the numbered issues (i.e., numbered issues do not "pop up" wrt. others). --> -
    +
    + + + + 69936: Fix bug in previous fix for Tomcat Native crashes on + shutdown that triggered a significant memory leak. Patch provided by + Wes. (markt) + + + +
    +
    + + + + 69623: Additional fix for the long standing regression that + meant that calls to ClassLoader.getResource().getContent() + failed when made from within a web application with resource caching + enabled if the target resource was packaged in a JAR file. (markt) + + + Pull request 923: Avoid adding multiple CSRF tokens to a URL in + the CsrfPreventionFilter. (schultz) + + + 69918: Ensure request parameters are correctly parsed for + HTTP/2 requests when the content-length header is not set. (dsoumis) + + + Enable minimum and recommended Tomcat Native versions to be set + separately for Tomcat Native 1.x and 2.x. Update the minimum and + recommended versions for Tomcat Native 1.x to 1.3.4. Update the minimum + and recommended versions for Tomcat Native 2.x to 2.0.12. (markt) + + + Add a new ssoReauthenticationMode to the Tomcat provided + Authenticators that provides a per Authenticator override of the SSO + Valve requireReauthentication attribute. (markt) + + + Ensure URL encoding errors in the Rewrite Valve trigger an exception + rather than silently using a replacement character. (markt) + + + 69932: Fix request end access log pattern regression, + which would log the start time of the request instead. (remm) + + + + + + + Avoid possible NPEs when using a TLS enabled custom connector. (remm) + + + Improve warnings when setting ciphers lists in the FFM code, mirroring + the tomcat-native changes. (remm) + + + 69910: Dereference TLS objects right after closing a socket + to improve memory efficiency. (remm) + + + Relax the JSSE vs OpenSSL configuration style checks on + SSLHostConfig to reflect the existing implementation that + allows one configuration style to be used for the trust attributes and a + different style for all the other attributes. (markt) + + + Better warning message when OpenSSLConf configuration + elements are used with a JSSE TLS implementation. (markt) + + + When using OpenSSL via FFM, don't log a warning about missing CA + certificates unless CA certificates were configured and the + configuration failed. (markt) + + + For configuration consistency between OpenSSL and JSSE TLS + implementations, TLSv1.3 cipher suites included in the + ciphers attribute of an SSLHostConfig are now + always ignored (previously they would be ignored with OpenSSL + implementations and used with JSSE implementations) and a warning is + logged that the cipher suite has been ignored. (markt) + + + Add the ciphersuite attribute to + SSLHostConfig to configure the TLSv1.3 cipher suites. + (markt) + + + Add OCSP support to JSSE based TLS connectors and make the use of OCSP + configurable per connector for both JSSE and OpenSSL based TLS + implementations. Align the checks performed by OpenSSL with those + performed by JSSE. (markt) + + + Add support for soft failure of OCSP checks with soft failure support + disabled by default. (markt) + + + Add support for configuring the verification flags passed to + OCSP_basic_verify when using an OpenSSL based TLS + implementation. (markt) + + + Fix OpenSSL FFM code compatibility with LibreSSL versions below 3.5. + (remm) + + + Prevent concurrent release of OpenSSLEngine resources and + the termination of the Tomcat Native library as it can cause crashes + during Tomcat shutdown. (markt) + + + + + + + 69333: Correct a regression in the previous fix for + 69333 and ensure that reuse() or + release() is always called for a tag. (markt) + + + + + + + 62814: Document that human-readable names may be used for + mapSendOptions and align documentation with + channelSendOptions. Based on pull request 929 by + archan0621. (markt) + + + + + + + 69920: When attempting to write to a closed + Writer or OutputStream obtained from a + WebSocket session, throw an IOException rather than an + IllegalStateExcpetion as required by Writer + and strongly suggested by OutputStream. (markt) + + + + + + + Update the internal fork of Commons Pool to 2.13.1. (markt) + + + Update the internal fork of Commons DBCP to 2.14.0. (markt) + + + Update Commons Daemon to 1.5.1. (markt) + + + Update ByteBuddy to 1.18.3. (markt) + + + Update UnboundID to 7.0.4. (markt) + + + Update Checkstyle to 12.3.1. (markt) + + + Improvements to French translations. (markt) + + + Improvements to Japanese translations provided by tak7iji. (markt) + + + Improvements to Chinese translations provided by Yang. vincent.h and + yong hu. (markt) + + + Update Tomcat Native to 2.0.12. (markt) + + + Add property "gpg.sign.files" to optionally disable release artefact + signing with GPG. (rjung) + + + +
    +
    + + + + Add test.silent property to suppress JUnit console output + during test execution. Useful for cleaner console output when running + tests with multiple threads. (csutherl) + + + + + + + 69871: Increase log level to INFO for missing configuration + for the rewrite valve. (remm) + + + Add log warnings for additional Host appBase suspicious + values. (remm) + + + Remove hard dependency on tomcat-jni.jar for catalina.jar. + org.apache.catalina.Connector no longer requires + org.apache.tomcat.jni.AprStatus to be present. (markt) + + + Add the ability to use a custom function to generate the client + identifier in the CrawlerSessionManagerValve. This is only + available programmatically. Pull request 902 by Brian Matzon. + (markt) + + + Change the SSO reauthentication behaviour for SPNEGO authentication so + that a normal SPNEGO authentication is performed if the SSL Valve is + configured with reauthentication enabled. This is so that the delegated + credentials will be available to the web application. (markt) + + + + + + + Don't log an incorrect certificate KeyStore location when + creating a TLS connector if the KeyStore instance has been + set directly on the connector. (markt) + + + HTTP/0.9 only allows GET as the HTTP method. (remm) + + + Add strictSni attribute on the Connector to + allow matching the SSLHostConfig configuration associated + with the SNI host name to the SSLHostConfig configuration + matched from the HTTP protocol host name. Non matching configurations + will cause the request to be rejected. The attribute default value is + true, enabling the matching. (remm) + + + + + + + 69877: Catch IllegalArgumentException when processing URIs + when creating the classpath to handle invalid URIs. (remm) + + + Fix populating the classpath with the webapp classloader repositories. + (remm) + + + + + + + Correct a regression introduced in 10.1.45 that broke some clustering + configurations. (markt) + + + + + + + Manager: Fix abrupt truncation of the HTML and JSON complete server + status output if one or more of the web applications failed to start. + (schultz) + + + Manager: Include web application state in the HTML and JSON complete + server status output. (markt) + + + Documentation: Expand the documentation to better explain when OCSP is + supported and when it is not. (markt) + + + + + + + 64083: If the underlying connection has been closed, don't + add it to the pool when it is returned. Pull request 235 by + Alex Panchenko. (markt) + + + + + + + Add test profile system for selective test execution. Profiles can be + specified via -Dtest.profile=<name> to run specific + test subsets without using patterns directly. Profile patterns are + defined in test-profiles.properties. (csutherl) + + + Update file extension to media type mappings to align with the current + list used by the Apache Web Server (httpd). (markt) + + + Update the packaged version of the Tomcat Migration Tool for Jakarta EE + to 1.0.10. (markt) + + + Update Commons Daemon to 1.5.0. (markt) + + + Update Byte Buddy to 1.18.2. (markt) + + + Update Checkstyle to 12.2.0. (markt) + + + Improvements to Spanish translations provided by White Vogel. (markt) + + + Improvements to French translations. (remm) + + + Improvements to Japanese translations provided by tak7iji. (markt) + + + +
    +
    + + + + When generating the class path in the Loader, re-order the check on + individual class path components to avoid a potential + NullPointerException. Identified by Coverity Scan. (markt) + + + Fix SSL socket factory configuration in the JNDI realm. Based on pull + request 915 by Joshua Rogers. (remm) + + + Add an attribute, digestInRfc3112Order, to + MessageDigestCredentialHandler to control the order in + which the credential and salt are digested. By default, the current, + non-RFC 3112 compliant, order of salt then credential will be used. This + default will change in Tomcat 12 to the RFC 3112 compliant order of + credential then salt. (markt) + + + + + + + Graceful failure for OCSP on BoringSSL in the FFM code. (remm) + + + Fix use of deferAccept attribute in JMX, since it is + normally only removed in Tomcat 11. (remm) + + + 69866: Fix a memory leak when using a trust store with the + OpenSSL provider. Pull request 912 by aogburn. (markt) + + + Fix potential crash on shutdown when a Connector depends on the Tomcat + Native library. (markt) + + + Fix AJP message length check. Pull request 916 by Joshua + Rogers. (remm) + + + + + + + 69862: Avoid NPE unwrapping Servlet exception which would + hide some exception details. Patch submitted by Eric Blanquer. (remm) + + + + + + + Update the internal fork of Apache Commons BCEL to 6.11.0. (markt) + + + Update to Byte Buddy 1.17.8. (markt) + + + Update to Checkstyle 12.1.1. (markt) + + + Update to Jacoco 0.8.14. (markt) + + + Update to SpotBugs 4.9.8. (markt) + + + Update to JSign 7.4. (markt) + + + Update Maven Resolver Ant Tasks to 1.6.0. (rjung) + + + Improvements to French translations. (remm) + + + Improvements to Japanese translations provided by tak7iji. (markt) + + + +
    +
    + + + + Log warnings when the SSO configuration does not comply with the + documentation. (remm) + + + Deprecate the RemoteAddrFilter and + RemoteAddrValve in favour of the + RemoteCIDRFilter and RemoteCIDRValve. (markt) + + + 69837: Fix corruption of the class path generated by the + Loader when running on Windows. (markt) + + + Reject requests that map to invalid Windows file names earlier. (markt) + + + 69839: Ensure that changes to session IDs (typically after + authentication) are promulgated to the SSO Valve to ensure that SSO + entries are fully clean-up on session expiration. Patch provided by Kim + Johan Andersson. (markt) + + + Fix a race condition in the creation of the storage location for the + FileStore. (markt) + + + + + + + 69848: Fix copy/paste errors in 10.1.47 that meant DELETE + requests received via the AJP connector were processed as OPTIONS + requests and PROPFIND requests were processed as TRACE. (markt) + + + Various OCSP processing issues in the OpenSSL FFM code. (dsoumis) + + + + + + + 69845: When using permessage-deflate with Java + 25 onwards, handle the underlying Inflater and/or + Deflater throwing IllegalStateException + when closed rather than NullPointerException as they do in + Java 24 and earlier. (markt) + + + +
    +
    + + + + HTTP methods are case-sensitive so always use case sensitive comparisons + when comparing HTTP methods. (markt) + + + 69814: Ensure that HttpSession.isNew() returns + false once the client has joined the session. (markt) + + + Further performance improvements for ParameterMap. (jengebr/markt) + + + Refactor access log time stamps to be based on the Instant + request processing starts. (markt) + + + Fix a case-sensitivity issue in the trailer header allow list. (markt) + + + Be proactive in cleaning up temporary files after a failed multi-part + upload rather than waiting for GC to do it. (markt) + + + + + + + Add specific certificate selection code for TLS 1.3 supporting post + quantum cryptography. Certificates defined with type + MLDSA will be selected depending on the TLS client hello. + (remm) + + + Add groups attribute on SSLHostConfig + allowing to restrict which groups can be enabled on the SSL engine. + (remm) + + + Optimize the conversion of HTTP method from byte form to String form. + (markt) + + + Store HTTP request headers using the original case for the header name + rather than forcing it to lower case. (markt) + + + + + + + Prevent the channel configuration (sender, receiver, membership service) + from being changed unless the channel is fully stopped. (markt) + + + + + + + Documentation. Clarify the purpose of the maxPostSize + attribute of the Connector element. (markt) + + + Avoid NPE in manager webapp displaying certificate information. (remm) + + + + + + + Update Byte Buddy to 1.17.7. (markt) + + + Update Checkstyle to 11.1.0. (markt) + + + Update SpotBugs to 4.9.6. (markt) + + + Update Jsign to 7.2. (markt) + + + Improvements to Russian translations provided by usmazat. (markt) + + + Improvements to French translations. (remm) + + + Improvements to Japanese translations provided by tak7iji. (markt) + + + Minor refactoring in JULI loggers. Patch provided by minjund. (schultz) + + + +
    +
    + + + + Change the digest used to calculate strong ETags (if enabled) for the + default Servlet from SHA-1 to SHA-256 to align with the recommendation + in RFC 9110 that hash functions used to generate strong ETags should be + collision resistant. (markt) + + + Correct a regression in the fix for 69781 that broke + FileStore. (markt) + + + + + + + Add hybrid PQC support to OpenSSL, based on code from + mod_ssl. Using this OpenSSL specific code path, + additional PQC certificates defined with type MLDSA are + added to contexts which use classic certificates. (jfclere/remm) + + + +
    +
    + + + + Remove a number of unnecessary packages from the catalina-deployer.jar. + (markt) + + + 69781: Fix concurrent access issues in the session + FileStore implementation that were causing lost sessions + when the store was used with the PersistentValve. Based on + pull request 882 by Aaron Ogburn. (markt) + + + Fix handling of QSA and QSD flags in + RewriteValve. (markt) + + + + + + + Ensure keys are handed out to OpenSSL even if PEMFile + fails to process it, with appropriate logging. (remm) + + + Add new ML-DSA key algorithm to PEMFile + and improve reporting when reading a key fails. (remm) + + + Fix possible early timeouts for network operations caused by a spurious + wake-up of a waiting thread. Found by Coverity Scan. (markt) + + + + + + + Handle spurious wake-ups during leader election for + NonBlockingCoordinator. (markt) + + + Handle spurious wake-ups during sending of messages by + RpcChannel. (markt) + + + + + + + Review logging and include the full stack trace and exception message + by default rather then just the exception message when logging an error + or warning in response to an exception. (markt) + + + Add escaping to log formatters to align with JSON formatter. (markt) + + + Update Checkstyle to 11.0.0. (markt) + + + +
    +
    + + + + Fix bloom filter population for archive indexing when using a packed + WAR containing one or more JAR files. (markt) + + + + + + + 69748: Add missing call to set keep-alive timeout when using + HTTP/1.1 following an async request, which was present for AJP. + (remm/markt) + + + 69762: Fix possible overflow during HPACK decoding of + integers. Note that the maximum permitted value of an HPACK decoded + integer is Integer.MAX_VALUE. (markt) + + + Update the HTTP/2 overhead documentation - particularly the code + comments - to reflect the deprecation of the PRIORITY frame + and clarify that a stream reset always triggers an overhead increase. + (markt) + + + 69762: Additional overflow fix for HPACK decoding of + integers. Pull request 880 by Chenjp. (markt) + + + + + + + Add enableStatistics configuration attribute for the + DeltaManager, defaulting to true. (remm) + + + + + + + Align the WebSocket extension handling for WebSocket client connections + with WebSocket server connections. The WebSocket client now only + includes an extension requested by an endpoint in the opening handshake + if the WebSocket client supports that extension. (markt) + + + + + + + Manager and Host Manager. Provide the Manager and Host Manager web + applications with a dedicated favicon file rather than using the one + from the ROOT web application which might not be present or may + represent something entirely different. Pull requests 876 + and 878 by Simon Arame. + + + + + + + Update Checkstyle to 10.26.1. (markt) + + + Improvements to French translations. (remm) + + + Improvements to Japanese translations by tak7iji. (markt) + + + +
    +
    + + + + Ensure application configured welcome files override the defaults when + configuring an embedded web application programmatically. (markt) + + + Allow the default servlet to set the content length when the content + length is known, no content has been written and a Writer + is being used. (markt) + + + 69717: Correct a regression in the fix for CVE-2025-49125 + that prevented access to PreResources and PostResources when mounted + below the web application root with a path that was terminated with a + file separator. (remm/markt) + + + 69731: Fix an issue that meant that the value of + maxParameterCount applied was smaller than intended for + multipart uploads with non-file parts when the parts were processed + before query string parameters. (markt) + + + Align size tracking for multipart requests with FileUpload's use of + long. (schultz) + + + + + + + 69710: Increase the default for maxPartCount + from 10 to 50. Update the documentation to + provide more details on the memory requirements to support multi-part + uploads while avoiding a denial of service risk. (markt) + + + 69713: Correctly handle an HTTP/2 data frame that includes + padding when the headers include a content-length. (remm/markt) + + + Correctly collect statistics for HTTP/2 requests and avoid counting one + request multiple times. Based on pull request 868 by + qingdaoheze. (markt) + + + Fix JMX value for keepAliveCount on the endpoint. Also add + the value of useVirtualThreads in JMX. (remm) + + + 69728: Remove incorrect warning when HTTP/2 is used with + optional certificate verification and improve the warnings when a web + application tries to use CLIENT-CERT with either HTTP/2 or a JSSE + implementation of TLS 1.3. (markt) + + + When setting the initial HTTP/2 connection limit, apply those limits + earlier. (markt) + + + + + + + Remove IMPL_OBJ_START from EL grammar for + IDENTIFIER. (markt) + + + Remove the INSTANCEOF and FUNCTIONSUFFIX + definitions from the EL grammar as both are unused. (markt) + + + + + + + Documentation. Provide more explicit guidance regarding the security + considerations for enabling write access to the web application via + WebDAV, HTTP PUT requests or similar. (markt) + + + Documentation. Add a section on reverse proxies to the security + considerations page. (markt) + + + + + + + Update UnboundID to 7.0.3. (markt) + + + Update Checkstyle to 10.25.1. (markt) + + + Improvements to French translations. (remm) + + + Improvements to Japanese translations provided by tak7iji. (markt) + + + +
    +
    + + + + Add support for the java:module namespace which mirrors + the java:comp namespace. (markt) + + + Support parsing of multiple path parameters separated by ; + in a single URL segment. Based on pull request 860 by Chenjp. + (markt) + + + Added support for limiting the number of parameters in HTTP requests through + the new ParameterLimitValve. The valve allows configurable + URL-specific limits on the number of parameters. (dsoumis) + + + 69699: Encode redirect URL used by the rewrite valve with + the session id if appropriate, and handle cross context with different + session configuration when using rewrite. (remm) + + + 863: Add support for comments at the end of lines in text + rewrite map files to align behaviour with Apache httpd. Pull request + provided by Chenjp. (markt) + + + 69706: Fix saved request serialization issue in FORM + introduced when allowing infinite session timeouts. (remm) + + + Expand the path checks for Pre-Resources and Post-Resources mounted at a + path within the web application. (markt) + + + + + + + 861: Refactor TaskQueue to use the new interface + RetryableQueue which enables better integration of custom + Executors which provide their own + BlockingQueue implementation. Pull request provided by + Paulo Almeida. (markt) + + + Provide finer grained control of multi-part request processing via two + new attributes on the Connector element. + maxPartCount limits the total number of parts in a + multi-part request and maxPartHeaderSize limits the size of + the headers provided with each part. Add support for these new + attributes to the ParameterLimitValve. (markt) + + + + + + + 69696: Mark the JSP wrapper for reload after a failed + compilation. (remm) + + + + + + + 69694: Improve error reporting of deployment tasks done + using the manager webapp when a copy operation fails. (remm) + + + + + + + Add thread name to webappClassLoader.stackTraceRequestThread message. + Patch provided by Felix Zhang. (schultz) + + + Update Tomcat Native to 2.0.9. (markt) + + + Update the internal fork of Apache Commons FileUpload to 1.6.0 + (2025-06-05). (markt) + + + Update EasyMock to 5.6.0. (markt) + + + Update Checkstyle to 10.25.0. (markt) + + + Use the full path when the installer for Windows sets calls + icacls.exe to set file permissions. (markt) + + + Improvements to Japanese translations provided by tak7iji. (markt) + + + +
    +
    + + + + Fix use of SSS in SimpleDateFormat + pattern for AccessLogValve. (rjung) + + + Process possible path parameters rewrite production in the rewrite + valve. (remm) + + + 69588: Enable allowLinking to be set on + PreResources, JarResources and + PostResources. If not set explicitly, the setting will be + inherited from the Resources. (markt) + + + 69633: Add support for Filters using context root mappings. + (markt) + + + 69643: Optimize directory listing for large amount of files. + Patch submitted by Loic de l'Eprevier. (remm) + + + 843: Fix off by one validation logic for partial PUT ranges + and associated test case. Submitted by Chenjp. (remm) + + + Replace the unused buffer in + org.apache.catalina.connector.InputBuffer with a static, + zero length buffer. (markt) + + + Refactor GCI servlet to access resources via the + WebResource API. (markt) + + + 69662: Report name in exception message when a naming lookup + failure occurs. Based on code submitted by Donald Smith. (remm) + + + Ensure that the FORM authentication attribute + authenticationSessionTimeout works correctly when sessions + have an infinite timeout when authentication starts. (markt) + + + Provide a content type based on file extension when web application + resources are accessed via a URL. (markt) + + + + + + + Refactor the SavedRequestInputFilter so the buffered data + is used directly rather than copied. (markt) + + + + + + + 69635: Add support to jakarta.el.ImportHandler + for resolving inner classes. (markt) + + + 842Add support for optimized execution of c:set and c:remove tags, when + activated via JSP servlet param useNonstandardTagOptimizations. + (jengebr) + + + Fix an edge case compilation bug for JSP and tag files on case + insensitive file systems that was exposed by the test case for + 69635. (markt) + + + + + + + 68876: Documentation. Update the UML diagrams for server + start-up, request processing and authentication using PlantUML and + include the source files for each diagram. (markt) + + + + + + + Set sun.io.useCanonCaches in service.bat + Based on pull request 841 by Paul Lodge. (remm) + + + Update Jacoco to 0.8.13. (remm) + + + Explicitly set the locale to be used for Javadoc. For official releases, + this locale will be English (US) to support reproducible builds. (schultz) + + + Update Byte Buddy to 1.17.5. (markt) + + + Update Checkstyle to 10.23.1. (markt) + + + Update file extension to media type mappings to align with the current + list used by the Apache Web Server (httpd). (markt) + + + Improvements to French translations. (remm) + + + Improvements to Japanese translations provided by tak7iji. (markt) + + + +
    +
    + + + + Return 400 if the amount of content sent for a partial PUT is + inconsistent with the range that was specified. (remm) + + + Add a new RateLimiter implementation, + org.apache.catalina.util.ExactRateLimiter, that can be used + with org.apache.catalina.filters.RateLimitFilter to provide + rate limit based on the exact values configured. Based on pull request + 794 by Chenjp. (markt) + + + Fix parsing of the time-taken token in the + ExtendedAccessLogValve. (remm) + + + Fix invocation of the FFM OpenSSL code for setting a SSL engine and + FIPS mode. (remm) + + + 69600: Add IPv6 local addresses (RFC 4193 and RFC 4291) to + the default internal proxies for the RemoteIpFilter and RemoteIpValve. + (markt) + + + 69615: Improve integration with the not found class resources + cache for users who are using a custom web application class loader + and/or using reflection to dynamically add external repositories to the + web application class loader. (markt) + + + Add a new initialisation parameter to the Default servlet - + allowPostAsGet - which controls whether a direct request + (i.e. not a forward or an include) for a static resource using the POST + method will be processed as if the GET method had been used. If not + allowed, the request will be rejected. The default behaviour of + processing the request as if the GET method had been used is unchanged. + (markt) + + + 69623: Correct a long standing regression that meant that + calls to ClassLoader.getResource().getContent() failed when + made from within a web application with resource caching enabled. + (markt) + + + 69634: Avoid NPE on JsonErrorReportValve. + (remm) + + + Add missing throwable stack trace to + JsonErrorReportValve equivalent to the one from + ErrorReportValve. (remm) + + + Improve the handling of %nn URL encoding in the + RewriteValve and document how %nn URL encoding may be used + with rewrite rules. (markt) + + + Fix a potential exception when calling + WebappClassLoaderBase.getResource(""). (markt) + + + + + + + 69607: Allow failed initialization of MD5. Based on code + submitted by Shivam Verma. (remm) + + + 69614: HTTP/2 priority frames with an invalid priority field + value should be ignored. (markt) + + + Improve handling of unexpected errors during HTTP/2 processing. (markt) + + + Add missing code to process an OpenSSL profile, such as + PROFILE=SYSTEM, using FFM. (remm) + + + Simplify the process of using a custom SSLContext for an HTTPS enabled + connector. Based on pull request 805 by Hakky54. (markt) + + + + + + + Replace custom URL encoding provided by the JSP runtime library with + calls to java.net.URLEncoder.encode(). (markt) + + + Add compiler using the Java Compiler API, supporting + exploded web applications. The compilerClassName to use is + org.apache.jasper.compiler.JavaCompiler. (remm) + + + Add support for specifying Java 25 (with the value 25) as + the compiler source and/or compiler target for JSP compilation. If used + with an Eclipse JDT compiler version that does not support these values, + a warning will be logged and the default will be used. + (markt) + + + + + + + Fix resetting cross context sessions in the + ReplicationValve. (remm) + + + + + + + Documentation. Add a link to the Log4j documentation that describes how + to use Log4j rather than JULI for Tomcat's internal logging. (markt) + + + Documentation. Document the runtime attributes available to web + applications via the Request or the ServletContext. Based on pull + request 832 by usmazat. (markt) + + + + + + + Revert JSign to 6.0 to avoid a file locking issue. (markt) + + + Update to NSIS 3.11. (markt) + + + Update to ByteBuddy 1.17.4. (markt) + + + Update to Checkstyle 10.21.4. (markt) + + + Update to SpotBugs to 4.9.3. (markt) + + + Improvements to French translations. (remm) + + + Improvements to Japanese translations provided by tak7iji. (markt) + + + +
    +
    +
    +
    + + + + 69602: Fix regression in releases from 12-2024 that were too + strict and rejected weak etags in the If-Range header with + a 400 response. Instead will consider it as a failed match since strong + etags are required for If-Range. (remm) + + + +
    +
    + + + + When looking up class loader resources by resource name, the resource + name should not start with '/'. If the resource name does start with + '/', Tomcat is lenient and looks it up as if the '/' was not present. + When the web application class loader was configured with external + repositories and names starting with '/' were used for lookups, it was + possible that cached 'not found' results could effectively hide lookup + results using the correct resource name. (markt) + + + Enable the JNDIRealm to validate credentials provided to + HttpServletRequest.login(String username, String password) + when the realm is configured to use GSSAPI authentication. (markt) + + + Fix a bug in the JRE compatibility detection that incorrectly identified + Java 19 and Java 20 as supporting Java 21 features. (markt) + + + Improve the checks for exposure to and protection against CVE-2024-56337 + so that reflection is not used unless required. The checks for whether + the file system is case sensitive or not have been removed. (markt) + + + Add support for logging the connection ID (as returned by + ServletRequest.getServletConnection().getConnectionId()) + with the AccessLogValve and + ExtendedAccessLogValve. Based on pull request 814 + by Dmole. (markt) + + + Avoid scenarios where temporary files used for partial PUT would not + be deleted. (remm) + + + + + + + 69575: Avoid using compression if a response is already + compressed using compress, deflate or + zstd. (remm) + + + Use Transfer-Encoding for compression rather than + Content-Encoding if the client submits a TE + header containing gzip. (remm) + + + Fix a race condition in the handling of HTTP/2 stream reset that could + cause unexpected 500 responses. (markt) + + + + + + + 69598: Add detection of service account token changes to the + KubernetesMembershipProvider implementation and reload the + token if it changes. Based on a patch by Miroslav Jezbera. (markt) + + + + + + + Add makensis as an option for building the Installer for + Windows on non-Windows platforms. (rjung/markt) + + + Update Byte Buddy to 1.17.1. (markt) + + + Update Checkstyle to 10.21.3. (markt) + + + Update SpotBugs to 4.9.1. (markt) + + + Update JSign to 7.1. (markt) + + + Improvements to French translations. (remm) + + + Improvements to Japanese translations by tak7iji. (markt) + + + +
    +
    + + + + 69576: Avoid possible failure initializing + JreCompat due to uncaught exception introduced for the + check for CVE-2024-56337. (remm) + + + + + + + Add org.apache.juli.JsonFormatter to format log as one + line JSON documents. (remm) + + + +
    +
    + + + + Add tableName configuration on the + DataSourcePropertyStore that may be used by the WebDAV + Servlet. (remm) + + + Improve HTTP If headers processing according to RFC 9110. Based on pull + request 796 by Chenjp. (remm/markt) + + + Allow readOnly attribute configuration on the + Resources element and allow configure the + readOnly attribute value of the main resources. The + attribute value will also be used by the default and WebDAV Servlets. + (remm) + + + 69285: Optimise the creation of the parameter map for + included requests. Based on sample code and test cases provided by John + Engebretson. (markt) + + + 69527: Avoid rare cases where a cached resource could be set + with 0 content length, or could be evicted immediately. (remm) + + + Fix possible edge cases (such as HTTP/1.0) with trying to detect + requests without body for WebDAV LOCK and PROPFIND. (remm) + + + 69528: Add multi-release JAR support for the + bloom archiveIndexStrategy of the + Resources. (remm) + + + Improve checks for WEB-INF and META-INF in + the WebDAV servlet. Based on a patch submitted by Chenjp. (remm) + + + Remove unused session to client map from + CrawlerSessionManagerValve. Submitted by Brian Matzon. + (remm) + + + Add a check to ensure that, if one or more web applications are + potentially vulnerable to CVE-2024-56337, the JVM has been configured to + protect against the vulnerability and to configure the JVM correctly if + not. Where one or more web applications are potentially vulnerable to + CVE-2004-56337 and the JVM cannot be correctly configured or it cannot + be confirmed that the JVM has been correctly configured, prevent the + impacted web applications from starting. (markt) + + + When using the WebDAV servlet with serveSubpathOnly set to + true, ensure that the destination for any requested WebDAV + operation is also restricted to the sub-path. (markt) + + + Generate an appropriate Allow HTTP header when the Default + servlet returns a 405 (method not allowed) response in response to a + DELETE request because the target resource cannot be + deleted. Pull request 802 provided by Chenjp. (markt) + + + Refactor creation of RequestDispatcher instances so that + the processing of the provided path is consistent with normal request + processing. (markt) + + + Add encodedReverseSolidusHandling and + encodedSolidusHandling attributes to Context to provide + control over the handling of the path used to created a + RequestDispatcher. (markt) + + + Handle a potential NullPointerException after an + IOException occurs on a non-container thread during + asynchronous processing. (markt) + + + Enhance lifecycle of temporary files used by partial PUT. (remm) + + + + + + + Don't log warnings for registered HTTP/2 settings that Tomcat does not + support. These settings are now silently ignored. (markt) + + + Avoid a rare NullPointerException when recycling the + Http11InputBuffer. (markt) + + + Lower the log level to debug for logging an invalid socket channel when + processing poller events for the NIO Connector as this may occur in + normal usage. (markt) + + + Clean-up references to the HTTP/2 stream once request processing has + completed to aid GC and reduce the size of the HTTP/2 recycled request + and response cache. (markt) + + + Add a new Connector configuration attribute, + encodedReverseSolidusHandling, to control how + %5c sequences in URLs are handled. The default behaviour is + unchanged (decode) keeping in mind that the + allowBackslash attribute determines how the decoded + URI is processed. (markt) + + + 69545: Improve CRLF skipping for the available + method of the ChunkedInputFilter. (remm) + + + Improve the performance of repeated calls to getHeader(). + Pull request 813 provided by Adwait Kumar Singh. (markt) + + + 69559: Ensure that the Java 24 warning regarding the use of + sun.misc.Unsafe::invokeCleaner is only reported by the JRE + when the code will be used. (markt) + + + + + + + 69508: Correct a regression in the fix for 69382 + that broke JSP include actions if both the page attribute and the body + contained parameters. Pull request 803 provided by Chenjp. + (markt) + + + Update the identifier validation in the Expression Language parser to + reflect that, as of Java 9, _ is also a Java keyword and + may not be used as an identifier. (markt) + + + 69521: Update the EL Parser to allow the full range of valid + characters in an EL identifier as defined by the Java Language + Specification. (markt) + + + 69532: Optimise the creation of + ExpressionFactory instances. Patch provided by John + Engebretson. (markt) + + + + + + + Documentation. Expand the description of the security implications of + setting mapperContextRootRedirectEnabled and/or + mapperDirectoryRedirectEnabled to true. + (markt) + + + Documentation. Better document the default for the + truststoreProvider attribute of a + SSLHostConfig element. (markt) + + + + + + + Update to Commons Daemon 1.4.1. (markt) + + + Update the packaged version of the Tomcat Migration Tool for Jakarta EE + to 1.0.9. (markt) + + + Update the internal fork of Commons Pool to 2.12.1. (markt) + + + Update Byte Buddy to 1.16.1. (markt) + + + Update UnboundID to 7.0.2. (markt) + + + Update Checkstyle to 10.21.2. (markt) + + + Update SpotBugs to 4.9.0. (markt) + + + Improvements to French translations. (remm) + + + Improvements to Chinese translations by leeyazhou. (markt) + + + Improvements to Japanese translations by tak7iji. (markt) + + + +
    +
    @@ -324,7 +1871,7 @@
    -
    +
    Fix release build issue. @@ -1312,6 +2859,10 @@ + 69844: Close the connection with a protocol error if the + server sends masked frames. (markt) + + 68884: Reduce the write timeout when writing WebSocket close messages for abnormal closes. The timeout defaults to 50 milliseconds and may be controlled using the @@ -5814,6 +7365,9 @@ Update the packaged version of the Tomcat Native Library to 1.2.30. Also update the minimum recommended version to 1.2.30. (markt) + + Update bnd to 7.2.0. (markt) +
    diff -Nru tomcat10-10.1.34/webapps/docs/class-loader-howto.xml tomcat10-10.1.52/webapps/docs/class-loader-howto.xml --- tomcat10-10.1.34/webapps/docs/class-loader-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/class-loader-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ &project; - Craig R. McClanahan - Yoav Shapira Class Loader How-To @@ -143,7 +141,7 @@
  • catalina-storeconfig.jar — Optional. Generation of XML configuration files from current state.
  • catalina-tribes.jar — Optional. Group communication - package used by the high availabaility package.
  • + package used by the high availability package.
  • ecj-*.jar — Optional. Eclipse JDT Java compiler used to compile JSPs to Servlets.
  • el-api.jar — Optional. EL 5.0 API.
  • diff -Nru tomcat10-10.1.34/webapps/docs/cluster-howto.xml tomcat10-10.1.52/webapps/docs/cluster-howto.xml --- tomcat10-10.1.34/webapps/docs/cluster-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/cluster-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ &project; - Filip Hanik - Peter Rossbach Clustering/Session Replication How-To @@ -74,7 +72,7 @@ The following is the default cluster configuration:

    + channelSendOptions="async"> + channelSendOptions="use_ack,sync"> + mapSendOptions="use_ack,sync"/> + +]> + + + &project; + + + Runtime attributes + + + + +
    + +
    + + +
    +

    The following attributes are made available to web applications at runtime. + Many of these attributes provide access to Tomcat internals. Attributes that + expose Tomcat internals may be explicitly requested by name but are often + deliberately not included when requesting a list of available attributes.

    +
    + +
    +

    Request attributes are made available to web applications via a call to + ServletRequest.getAttribute(String).

    + + +

    The Servlet specification defined attribute for the name of the cipher + suite being used on an SSL connection.

    +
    + +

    The Servlet specification defined attribute for the key size being used + on an SSL connection.

    +
    + +

    The Servlet specification defined attribute for the name of the + security protocol (e.g. TLSv1.3) being used on a secured connection.

    +
    + +

    The Servlet specification defined attribute for the session ID being + used for an SSL connection.

    +
    + +

    A Tomcat specific extension to the Servlet specification that provides + the session manager being used for an SSL connection.

    +
    + +

    The Servlet specification defined attribute for the array of + X509Certificate objects representing the certificate chain presented by + our client, if any.

    +
    + +

    Does the processing chain (Servlet, Filters, Valves) for the current + request support asynchronous processing? Each component in the chain must + support asynchronous processing for this to be true.

    +
    + +

    The name of the target Servlet for the current named dispatch, if + any.

    +
    + +

    The current jakarta.servlet.DispatcherType for the + request.

    +
    + +

    The current path (Servlet Info + Path Info) to which the request has + been dispatched.

    +
    + +

    The request attribute that is set to the value of + Boolean.TRUE if the RemoteIpFilter determines that this + request was submitted via a secure channel.

    +
    + +

    The request attribute that is set to Boolean.TRUE if some + request parameters have been ignored during request parameters parsing. It + can happen, for example, if there is a limit on the total count of + parseable parameters, or if parameter cannot be decoded, or any other + error happened during parameter parsing.

    +
    + +

    The reason that the parameter parsing failed.

    +
    + +

    The GSS credential for the currently authenticated user if they have + successfully authenticated using SPNEGO authentication.

    +
    + +

    The request attribute set by the RemoteIpFilter, RemoteIpValve (and may + be set by other similar components) that identifies the connection peer IP + address.

    +
    + +

    The request attribute set by the RemoteIpFilter, RemoteIpValve (and may + be set by other similar components) that identifies for the connector the + remote IP address claimed to be associated with this request when a + request is received via one or more proxies. It is typically provided via + the X-Forwarded-For HTTP header.

    +
    + +

    The request attribute that is set to the value of + Boolean.TRUE by the RemoteIpFilter, RemoteIpValve (and other + similar components) that identifies a request which been forwarded via one + or more proxies.

    +
    + +

    The request attribute that can be used by a servlet to pass to the + connector the end offset (not included) of the part of a file that is to + be served by sendfile. The value should be Long. To serve the + complete file the value should be equal to the length of the file.

    +
    + +

    The request attribute that can be used by a servlet to pass to the + connector the name of the file that is to be served by sendfile. The value + should be a String that is the canonical path of the file to + be served.

    +
    + +

    The request attribute that can be used by a servlet to pass to the + connector the start offset of the part of a file that is to be served by + sendfile. The value should be a Long. To serve the complete + file the value should be Long.valueOf(0).

    +
    + +

    The request attribute that is set to the value of + Boolean.TRUE if connector processing this request supports + the use of sendfile.

    +
    +
    +
    + +
    +

    ServletContext attributes are made available to web applications via a call + to ServletContext.getAttribute(String).

    + + +

    The CredentialHandler (if any) associated with the Realm (if any) + that has been explicitly associated with the Context. Realms associated + with parent containers (Hosts or Engines) are not considered.

    +
    + +

    The alternate deployment descriptor for this web application.

    +
    + +

    The class path for our application class loader (as an object of type + String), delimited with the appropriate path delimiter for the + platform.

    +
    + +

    The WebResourceRoot which is associated with the context.

    +
    + +

    The web application version string (the text that appears after ## + when parallel deployment is configured).

    +
    + +

    The InstanceManager used to create Servlets, Filters, Listeners etc. + for the web application.

    +
    + +

    The JarScanner instance used to scan the web application for + annotations, TLDs, web fragments and similar features.

    +
    + +

    The utility executor for this Context.

    +
    +
    +
    + +
    diff -Nru tomcat10-10.1.34/webapps/docs/config/server.xml tomcat10-10.1.52/webapps/docs/config/server.xml --- tomcat10-10.1.34/webapps/docs/config/server.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/config/server.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Craig R. McClanahan The Server Component @@ -87,7 +86,7 @@ -

    The number of threads this Service will use for +

    The number of threads this Server will use for various utility tasks, including recurring ones. The special value of 0 will result in the value of Runtime.getRuntime().availableProcessors() being diff -Nru tomcat10-10.1.34/webapps/docs/config/service.xml tomcat10-10.1.52/webapps/docs/config/service.xml --- tomcat10-10.1.34/webapps/docs/config/service.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/config/service.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Craig R. McClanahan The Service Component diff -Nru tomcat10-10.1.34/webapps/docs/config/valve.xml tomcat10-10.1.52/webapps/docs/config/valve.xml --- tomcat10-10.1.34/webapps/docs/config/valve.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/config/valve.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Craig R. McClanahan The Valve Component @@ -337,6 +336,8 @@ remote (client) port (xxx=remote)

  • %{xxx}t write timestamp at the end of the request formatted using the enhanced SimpleDateFormat pattern xxx
  • +
  • %{xxx}L write an identifier associated with the request where the only valid value for + xxx is c for connection.
  • %{xxx}T write time taken to process the request using unit xxx where valid units are ns for nanoseconds, us for microseconds, ms for milliseconds, fracsec for fractional seconds, or s for whole seconds. @@ -477,6 +478,7 @@
    • x-H(authType): getAuthType
    • x-H(characterEncoding): getCharacterEncoding
    • +
    • x-H(connectionId): getServletConnection().getConnectionId
    • x-H(contentLength): getContentLength
    • x-H(locale): getLocale
    • x-H(protocol): getProtocol
    • @@ -665,6 +667,11 @@ Remote CIDR Valve, Remote IP Valve, HTTP Connector configuration.

      + +

      Note: This Valve is deprecated and will be removed in + Tomcat 12. Use Remote CIDR Valve + instead.

      + @@ -1193,7 +1200,7 @@ Internal proxies that appear in the remoteIpHeader will be trusted and will not appear in the proxiesHeader value. If not specified the default value of - 10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|100\.6[4-9]{1}\.\d{1,3}\.\d{1,3}|100\.[7-9]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.1[0-1]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.12[0-7]{1}\.\d{1,3}\.\d{1,3}|172\.1[6-9]{1}\.\d{1,3}\.\d{1,3}|172\.2[0-9]{1}\.\d{1,3}\.\d{1,3}|172\.3[0-1]{1}\.\d{1,3}\.\d{1,3}|0:0:0:0:0:0:0:1 + 10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|169\.254\.\d{1,3}\.\d{1,3}|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|100\.6[4-9]{1}\.\d{1,3}\.\d{1,3}|100\.[7-9]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.1[0-1]{1}\d{1}\.\d{1,3}\.\d{1,3}|100\.12[0-7]{1}\.\d{1,3}\.\d{1,3}|172\.1[6-9]{1}\.\d{1,3}\.\d{1,3}|172\.2[0-9]{1}\.\d{1,3}\.\d{1,3}|172\.3[0-1]{1}\.\d{1,3}\.\d{1,3}|0:0:0:0:0:0:0:1|::1|fe[89ab]\p{XDigit}:.*|"f[cd]\p{XDigit}{2}+:.* will be used.

      @@ -1365,7 +1372,9 @@

      The Single Sign On Valve is utilized when you wish to give users the ability to sign on to any one of the web applications associated with your virtual host, and then have their identity recognized by all other - web applications on the same virtual host.

      + web applications on the same virtual host. The SSO Valve caches the + authenticated Principal and authentication type and provides them to all web + applications.

      See the Single Sign On special feature on the Host element for more information.

      @@ -1387,12 +1396,21 @@

      Default false. Flag to determine whether each request needs to be - reauthenticated to the security Realm. If "true", this - Valve uses cached security credentials (username and password) to - reauthenticate to the Realm each request associated - with an SSO session. If "false", the Valve can itself authenticate - requests based on the presence of a valid SSO cookie, without - rechecking with the Realm.

      + reauthenticated to the security Realm. This is required + if the Realm or the authentication process provides additional + information (beyond added an authenticated Principal) to the request + that is required by a web application.

      +

      If "true", this Valve uses cached security credentials (username and + password) to reauthenticate to the Realm each request + associated with an SSO session where the web application is configured + with BASIC or FORM authentication. Web applications using DIGEST, SPNEGO + or CLIENT-CERT authentication will reauthenticate using the standard + authentication process for the authenticator. Therefore, it only makes + sense to use the SSO Valve with this attribute set to "true" if there + are two or more web applications using BASIC or FORM.

      +

      If "false", the Valve can itself authenticate requests based on the + presence of a valid SSO cookie, without rechecking with the + Realm.

      @@ -1448,7 +1466,7 @@ it appears to be a CORS preflight request; it is mapped to a web application that has the CORS Filter enabled; and the request matches the URLPatterns for the CORS - fitler mapper. + filter mapper. always means that all requests that appear to be CORS preflight requests will bypass authentication. If not set, the default value is never.

      @@ -1563,6 +1581,28 @@ If not specified, the default value is false.

      + +

      Provides a per Authenicator override of the SSO Valve attribute + requireReauthentication. It has the following options:

      +
      +
      default
      +
      Reauthentication behaviour depends on the SSO Valve configuration + and the authentictaor type.
      +
      principal
      +
      The authenticator will look first for a cached Principal. If none + is found, the authenticator will look for a cached user name and + password and attempt to use them to reauthenticate. If that fails, + a normal authentication will be performed.
      +
      password
      +
      The authenticator will look for a cached user name and password + and attempt to use them to reauthenticate. If that fails, a normal + authentication will be performed.
      +
      full
      +
      Only a normal authentication will be performed. The SSO Valve is + effectively ignored.
      +
      +
      +

      Controls whether leading and/or trailing whitespace is removed from the parsed credentials. If not specified, the default value is @@ -1616,7 +1656,7 @@ it appears to be a CORS preflight request; it is mapped to a web application that has the CORS Filter enabled; and the request matches the URLPatterns for the CORS - fitler mapper. + filter mapper. always means that all requests that appear to be CORS preflight requests will bypass authentication. If not set, the default value is never.

      @@ -1753,6 +1793,28 @@ If not specified, the default value is false.

      + +

      Provides a per Authenicator override of the SSO Valve attribute + requireReauthentication. It has the following options:

      +
      +
      default
      +
      Reauthentication behaviour depends on the SSO Valve configuration + and the authentictaor type.
      +
      principal
      +
      The authenticator will look first for a cached Principal. If none + is found, the authenticator will look for a cached user name and + password and attempt to use them to reauthenticate. If that fails, + a normal authentication will be performed.
      +
      password
      +
      The authenticator will look for a cached user name and password + and attempt to use them to reauthenticate. If that fails, a normal + authentication will be performed.
      +
      full
      +
      Only a normal authentication will be performed. The SSO Valve is + effectively ignored.
      +
      +
      +

      Should the URI be validated as required by RFC2617? If not specified, the default value of true will be used. This should @@ -1799,7 +1861,7 @@ it appears to be a CORS preflight request; it is mapped to a web application that has the CORS Filter enabled; and the request matches the URLPatterns for the CORS - fitler mapper. + filter mapper. always means that all requests that appear to be CORS preflight requests will bypass authentication. If not set, the default value is never.

      @@ -1910,6 +1972,28 @@ If not specified, the default value is false.

      + +

      Provides a per Authenicator override of the SSO Valve attribute + requireReauthentication. It has the following options:

      +
      +
      default
      +
      Reauthentication behaviour depends on the SSO Valve configuration + and the authentictaor type.
      +
      principal
      +
      The authenticator will look first for a cached Principal. If none + is found, the authenticator will look for a cached user name and + password and attempt to use them to reauthenticate. If that fails, + a normal authentication will be performed.
      +
      password
      +
      The authenticator will look for a cached user name and password + and attempt to use them to reauthenticate. If that fails, a normal + authentication will be performed.
      +
      full
      +
      Only a normal authentication will be performed. The SSO Valve is + effectively ignored.
      +
      +
      +
      @@ -1948,7 +2032,7 @@ it appears to be a CORS preflight request; it is mapped to a web application that has the CORS Filter enabled; and the request matches the URLPatterns for the CORS - fitler mapper. + filter mapper. always means that all requests that appear to be CORS preflight requests will bypass authentication. If not set, the default value is never.

      @@ -2026,6 +2110,28 @@ specified, the platform default provider will be used.

      + +

      Provides a per Authenicator override of the SSO Valve attribute + requireReauthentication. It has the following options:

      +
      +
      default
      +
      Reauthentication behaviour depends on the SSO Valve configuration + and the authentictaor type.
      +
      principal
      +
      The authenticator will look first for a cached Principal. If none + is found, the authenticator will look for a cached user name and + password and attempt to use them to reauthenticate. If that fails, + a normal authentication will be performed.
      +
      password
      +
      The authenticator will look for a cached user name and password + and attempt to use them to reauthenticate. If that fails, a normal + authentication will be performed.
      +
      full
      +
      Only a normal authentication will be performed. The SSO Valve is + effectively ignored.
      +
      +
      + @@ -2064,7 +2170,7 @@ it appears to be a CORS preflight request and the web application the request maps to has the CORS Filter enabled; and the request matches the URLPatterns for the CORS - fitler mapper. + filter mapper. means that all requests that appear to be CORS preflight requests will bypass authentication. If not set, the default value is never.

      @@ -2204,6 +2310,28 @@ If not specified, the default value is false.

      + +

      Provides a per Authenicator override of the SSO Valve attribute + requireReauthentication. It has the following options:

      +
      +
      default
      +
      Reauthentication behaviour depends on the SSO Valve configuration + and the authentictaor type.
      +
      principal
      +
      The authenticator will look first for a cached Principal. If none + is found, the authenticator will look for a cached user name and + password and attempt to use them to reauthenticate. If that fails, + a normal authentication will be performed.
      +
      password
      +
      The authenticator will look for a cached user name and password + and attempt to use them to reauthenticate. If that fails, a normal + authentication will be performed.
      +
      full
      +
      Only a normal authentication will be performed. The SSO Valve is + effectively ignored.
      +
      +
      +

      Controls if the user' delegated credential will be stored in the user Principal. If available, the delegated credential will be @@ -2675,6 +2803,54 @@ + + + +

  • + +
    + + + +

    The Parameter Limit Valve is used to limit the number of parameters allowed in HTTP requests + overriding the Connector's value. The valve can be configured with specific limits for certain URL patterns. + Requests exceeding the defined parameter limits will result in an HTTP 400 Bad Request error.

    + +
    + + + +

    The Parameter Limit Valve supports the following + configuration attributes:

    + + + + +

    Java class name of the implementation to use. This MUST be set to + org.apache.catalina.valves.ParameterLimitValve.

    +
    + + +

    A file consisting of line-separated URL patterns and their respective parameter limits. + Each entry should follow the format urlPattern=limit. + The valve will apply the limit defined for a URL pattern when a request matches that pattern. + If no pattern matches, the Connector's limit will be used. + For example: + + /api/.*=100 + /admin/.*=50 + /upload/.*=30,5,1024 + + Default value: parameter_limit.config. + It must be placed in the Host configuration folder or in the WEB-INF folder of the web application. +

    +

    If a single integer is provided, it is used for maxParameterCount. If three integers are + provided, they are applied to maxParameterCount, maxPartCount and + maxPartHeaderSize respectively. +

    +
    + +
    diff -Nru tomcat10-10.1.34/webapps/docs/connectors.xml tomcat10-10.1.52/webapps/docs/connectors.xml --- tomcat10-10.1.34/webapps/docs/connectors.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/connectors.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Remy Maucherat Connectors How To diff -Nru tomcat10-10.1.34/webapps/docs/default-servlet.xml tomcat10-10.1.52/webapps/docs/default-servlet.xml --- tomcat10-10.1.34/webapps/docs/default-servlet.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/default-servlet.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Tim Funk Default Servlet Reference @@ -150,17 +149,20 @@ directory listing will be shown. - You may also customize your directory listing by directory by - configuring localXsltFile. This must be a file in the - directory where the listing will take place to with a - .xsl or .xslt extension. This overrides - globalXsltFile and contextXsltFile. If this - value is present but a file does not exist, then - contextXsltFile will be used. If - contextXsltFile does not exist, then - globalXsltFile will be used. If - globalXsltFile does not exist, then the default - directory listing will be shown. +

    You may also customize your directory listing by directory by configuring + localXsltFile. This must be a file in the directory where the + listing will take place to with a .xsl or .xslt + extension. This overrides globalXsltFile and + contextXsltFile. If this value is present but a file does not + exist, then contextXsltFile will be used. If + contextXsltFile does not exist, then + globalXsltFile will be used. If globalXsltFile + does not exist, then the default directory listing will be shown.

    +

    Any localXsltFile is both a Tomcat configuration file and + part of the web application. As per the Tomcat security model, such files + are assumed to be trusted. Write access to this file should, like write + access to any Tomcat configuration file, be limited to trusted users. This + incudes users with remote access via WebDAV, PUT or similar.

    Input buffer size (in bytes) when reading @@ -209,10 +211,17 @@ Should the server list all directories before all files. [false] - Should the server treat an HTTP PUT request with a Range header as a - partial PUT? Note that while RFC 7233 clarified that Range headers only - valid for GET requests, RFC 9110 (which obsoletes RFC 7233) now allows - partial puts. [true] + Should the server treat an HTTP PUT request with a Content-Range header + as a partial PUT? Note that while RFC 7231 clarified that such a PUT + with a Content-Range header field is a bad request, RFC 9110 + (which obsoletes RFC 7231) now allows partial PUT. [true] + + + Controls whether a direct request (i.e. not a forward or an include) for + a static resource using the POST method will be processed as if the GET + method had been used. If not allowed, the request will be rejected. The + default behaviour of processing the request as if the GET method had + been used is unchanged. [true]
    diff -Nru tomcat10-10.1.34/webapps/docs/deployer-howto.xml tomcat10-10.1.52/webapps/docs/deployer-howto.xml --- tomcat10-10.1.34/webapps/docs/deployer-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/deployer-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Allistair Crossley Tomcat Web Application Deployment diff -Nru tomcat10-10.1.34/webapps/docs/developers.xml tomcat10-10.1.52/webapps/docs/developers.xml --- tomcat10-10.1.34/webapps/docs/developers.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/developers.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ &project; - Remy Maucherat - Yoav Shapira Tomcat Developers diff -Nru tomcat10-10.1.34/webapps/docs/graal.xml tomcat10-10.1.52/webapps/docs/graal.xml --- tomcat10-10.1.34/webapps/docs/graal.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/graal.xml 2026-01-23 19:33:36.000000000 +0000 @@ -61,7 +61,7 @@

    Download the Tomcat Stuffed module from https://github.com/apache/tomcat/tree/10.1.x/modules/stuffed. - For convinience, an env property can be set: + For convenience, an env property can be set: export TOMCAT_STUFFED=/absolute...path...to/stuffed The build process now requires both Apache Ant and Maven.

    diff -Nru tomcat10-10.1.34/webapps/docs/html-manager-howto.xml tomcat10-10.1.52/webapps/docs/html-manager-howto.xml --- tomcat10-10.1.34/webapps/docs/html-manager-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/html-manager-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Glenn L. Nielsen Tomcat Web Application Manager How To diff -Nru tomcat10-10.1.34/webapps/docs/images/asf-logo.svg tomcat10-10.1.52/webapps/docs/images/asf-logo.svg --- tomcat10-10.1.34/webapps/docs/images/asf-logo.svg 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/images/asf-logo.svg 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru tomcat10-10.1.34/webapps/docs/index.xml tomcat10-10.1.52/webapps/docs/index.xml --- tomcat10-10.1.34/webapps/docs/index.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/index.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,6 @@ &project; - Craig R. McClanahan - Remy Maucherat - Yoav Shapira Documentation Index diff -Nru tomcat10-10.1.34/webapps/docs/introduction.xml tomcat10-10.1.52/webapps/docs/introduction.xml --- tomcat10-10.1.34/webapps/docs/introduction.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/introduction.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Robert Slifka Introduction @@ -115,7 +114,7 @@ instances with single CATALINA_HOME location share one set of .jar files and binary files, you can easily upgrade the files to newer version and have the change propagated to all Tomcat instances - using the same CATALIA_HOME directory. + using the same CATALINA_HOME directory.
  • Avoiding duplication of the same static .jar files. @@ -227,7 +226,7 @@

    For advanced configuration information, see the - + RUNNING.txt file.

    @@ -275,7 +274,6 @@ the answer was right in front of you all along!
  • Tomcat FAQ
  • Tomcat WIKI
  • -
  • Tomcat FAQ at jGuru
  • Tomcat mailing list archives - numerous sites archive the Tomcat mailing lists. Since the links change over time, clicking here will search Google. diff -Nru tomcat10-10.1.34/webapps/docs/jasper-howto.xml tomcat10-10.1.52/webapps/docs/jasper-howto.xml --- tomcat10-10.1.34/webapps/docs/jasper-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/jasper-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ &project; - Glenn L. Nielsen - Peter Rossbach Jasper 2 JSP Engine How To diff -Nru tomcat10-10.1.34/webapps/docs/jndi-datasource-examples-howto.xml tomcat10-10.1.52/webapps/docs/jndi-datasource-examples-howto.xml --- tomcat10-10.1.34/webapps/docs/jndi-datasource-examples-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/jndi-datasource-examples-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,10 +23,6 @@ &project; - Les Hughes - David Haraburda - Glenn Nielsen - Yoav Shapira JNDI Datasource How-To diff -Nru tomcat10-10.1.34/webapps/docs/jndi-resources-howto.xml tomcat10-10.1.52/webapps/docs/jndi-resources-howto.xml --- tomcat10-10.1.34/webapps/docs/jndi-resources-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/jndi-resources-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ &project; - Craig R. McClanahan - Yoav Shapira JNDI Resources How-To @@ -329,7 +327,7 @@

    If the bean property is of type String, the BeanFactory will call the property setter using the provided property value. If the - bean property type is a primitive or a primitive wrapper, the the + bean property type is a primitive or a primitive wrapper, the BeanFactory will convert the value to the appropriate primitive or primitive wrapper and then use that value when calling the setter. Some beans have properties with types that cannot automatically be converted @@ -427,7 +425,7 @@

    1. Database schema
    -

    The database shema for the user database is flexible. It can be the same +

    The database schema for the user database is flexible. It can be the same as the schema used for the DataSourceRealm, with only a table for users (user name, password), and another one listing the roles associated with each user. To support the full UserDatabase @@ -739,7 +737,7 @@ $CATALINA_HOME/lib location only.

    -
    5. Install a compatible implementaion
    +
    5. Install a compatible implementation

    Select and download a compatible implementation.

    @@ -754,23 +752,6 @@

    For the additional JAR to be visible to Tomcat, it is necessary for the Tomcat instance to be restarted.

    - -
    Example Application
    - -

    The /examples application included with Tomcat contains - an example of utilizing this resource factory. It is accessed via the - "JSP Examples" link. The source code for the servlet that actually - sends the mail message is in - /WEB-INF/classes/SendMailServlet.java.

    - -

    WARNING - The default configuration assumes that there - is an SMTP server listing on port 25 on localhost. If this is - not the case, edit the - <Context> element for - this web application and modify the parameter value for the - mail.smtp.host parameter to be the host name of an SMTP server - on your network.

    - diff -Nru tomcat10-10.1.34/webapps/docs/logging.xml tomcat10-10.1.52/webapps/docs/logging.xml --- tomcat10-10.1.34/webapps/docs/logging.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/logging.xml 2026-01-23 19:33:36.000000000 +0000 @@ -25,8 +25,6 @@ Logging in Tomcat - Allistair Crossley - Yoav Shapira @@ -49,9 +47,11 @@ To configure Tomcat to use an alternative logging framework for its internal logging, follow the instructions provided by the alternative logging framework for redirecting logging for applications that use - java.util.logging. Keep in mind that the alternative logging - framework will need to be capable of working in an environment where - different loggers with the same name may exist in different class loaders. + java.util.logging. Links to some of these instructions are + provided towards the end of this page. Keep in mind that the alternative + logging framework will need to be capable of working in an environment + where different loggers with the same name may exist in different class + loaders.

    @@ -352,7 +352,7 @@ 3manager.org.apache.juli.AsyncFileHandler.encoding = UTF-8 java.util.logging.ConsoleHandler.level = ALL -java.util.logging.ConsoleHandler.formatter = java.util.logging.OneLineFormatter +java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter java.util.logging.ConsoleHandler.encoding = UTF-8 ############################################################ @@ -389,7 +389,7 @@ org.apache.juli.AsyncFileHandler.encoding = UTF-8 java.util.logging.ConsoleHandler.level = ALL -java.util.logging.ConsoleHandler.formatter = java.util.logging.OneLineFormatter +java.util.logging.ConsoleHandler.formatter = org.apache.juli.OneLineFormatter java.util.logging.ConsoleHandler.encoding = UTF-8]]> @@ -425,5 +425,12 @@

  • +
    + +

    The Log4j project provides instructions for using Log4J for Tomcat's internal logging.

    + +
    diff -Nru tomcat10-10.1.34/webapps/docs/manager-howto.xml tomcat10-10.1.52/webapps/docs/manager-howto.xml --- tomcat10-10.1.34/webapps/docs/manager-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/manager-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Craig R. McClanahan Manager App How-To @@ -77,8 +76,8 @@ docBase="${catalina.home}/webapps/manager"> - + ]]> @@ -205,13 +204,13 @@

    In addition to the password restrictions, access to the Manager web application can be restricted by the remote IP address or host -by adding a RemoteAddrValve or RemoteHostValve. +by adding a RemoteCIDRValve or RemoteHostValve. See valves documentation for details. Here is an example of restricting access to the localhost by IP address:

    - + ]]>
    diff -Nru tomcat10-10.1.34/webapps/docs/maven-jars.xml tomcat10-10.1.52/webapps/docs/maven-jars.xml --- tomcat10-10.1.34/webapps/docs/maven-jars.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/maven-jars.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Filip Hanik Apache Tomcat - Using Tomcat libraries with Maven diff -Nru tomcat10-10.1.34/webapps/docs/mbeans-descriptors-howto.xml tomcat10-10.1.52/webapps/docs/mbeans-descriptors-howto.xml --- tomcat10-10.1.34/webapps/docs/mbeans-descriptors-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/mbeans-descriptors-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Amy Roh MBeans Descriptors How To diff -Nru tomcat10-10.1.34/webapps/docs/proxy-howto.xml tomcat10-10.1.52/webapps/docs/proxy-howto.xml --- tomcat10-10.1.34/webapps/docs/proxy-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/proxy-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Craig R. McClanahan Proxy Support How-To diff -Nru tomcat10-10.1.34/webapps/docs/realm-howto.xml tomcat10-10.1.52/webapps/docs/realm-howto.xml --- tomcat10-10.1.34/webapps/docs/realm-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/realm-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,9 +23,6 @@ &project; - Craig R. McClanahan - Yoav Shapira - Andrew R. Jaquith Realm Configuration How-To diff -Nru tomcat10-10.1.34/webapps/docs/rewrite.xml tomcat10-10.1.52/webapps/docs/rewrite.xml --- tomcat10-10.1.34/webapps/docs/rewrite.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/rewrite.xml 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,6 @@ &project; - Remy Maucherat The rewrite Valve @@ -56,6 +55,28 @@ +
    + +

    The URL presented to the rewrite valve is the same URL used for request + mapping with any literal '%', ';' and/or + '?' characters encoded in %nn form.

    + +

    A rewrite rule that wishes to insert a literal '%', + ';', '?', '&' or '=' + character should do so in %nn form. Other characters maybe + inserted in either literal or %nn form.

    + +

    This enables the rewrite rules to: +

      +
    • process URLs containing literal '?' characters;
    • +
    • add a query string;
    • +
    • insert a literal '%' character without it being confused with + %nn encoding.
    • +
    +

    + +
    +

    The rewrite.config file contains a list of directives which closely @@ -433,7 +454,7 @@

    Note: You can use more than one parameter. These have to be separated by spaces. Parameters can be quoted with ". This enables space characters inside parameters.

    -

    That map instance will be given the the lookup value that is configured in the corresponding RewriteRule by +

    That map instance will be given the lookup value that is configured in the corresponding RewriteRule by calling lookup(String). Your implementation is free to return null to indicate, that the given default should be used, or to return a replacement value.

    diff -Nru tomcat10-10.1.34/webapps/docs/security-howto.xml tomcat10-10.1.52/webapps/docs/security-howto.xml --- tomcat10-10.1.34/webapps/docs/security-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/security-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -169,7 +169,7 @@
  • Do not remove the use of the LockOutRealm which prevents brute force attacks against user passwords.
  • -
  • Configure the RemoteAddrValve +
  • Configure the RemoteCIDRValve in the context.xml file for the management application which limits access to localhost by default. If remote access is required, limit it to specific IP addresses using @@ -178,6 +178,24 @@
  • +
    +

    Web applications are assumed to be trusted. It is not safe to deploy web + applications from untrusted sources.

    + +

    Any application functionality that permits the modification of a web + application (WebDAV, HTTP PUT requests etc.) may impact the security of + either the web application or the Tomcat instance on which it is running. + Such functionality should either be restricted to trusted users or + limited in scope (e.g. via security constraints) such that users with access + to the functionality are unable to imapct the security of either the web + application or the Tomcat instance on which it is running.

    + +

    Consider using the + CORS filter and/or the + CSRF prevention + filter with deployed web applications.

    +
    +

    Enabling the security manager causes web applications to be run in a sandbox, significantly limiting a web application's ability to perform @@ -291,10 +309,27 @@ will interpret as UTF-7 a response containing characters that are safe for ISO-8859-1 but trigger an XSS vulnerability if interpreted as UTF-7.

    +

    The maxParameterCount attribute controls the maximum + total number of request parameters (including uploaded files) obtained + from the query string and, for POST requests, the request body if the + content type is application/x-www-form-urlencoded or + multipart/form-data. Excessive parameters are ignored. If you + want to reject such requests, configure a + FailedRequestFilter.

    + +

    The maxPartCount attribute controls the maximum number + of parts supported for a multipart request. This is limited to 50 by + default to reduce exposure to a DoS attack. The documentation for + maxPartCount provides more details on the memory + requirements for processing multipart requests. Excessive parts may be + ignored depending on how the application processes the request. If you + want to always reject such requests, configure a + FailedRequestFilter.

    +

    The maxPostSize attribute controls the maximum size - of a POST request that will be parsed for parameters. The parameters are - cached for the duration of the request so this is limited to 2 MiB by - default to reduce exposure to a DOS attack.

    + of data from a POST request that will be parsed for request parameters. + The parameters are cached for the duration of the request so this is + limited to 2 MiB by default to reduce exposure to a DoS attack.

    The maxSavePostSize attribute controls the saving of the request body during FORM and CLIENT-CERT authentication and HTTP/1.1 @@ -308,22 +343,9 @@ the FORM authenticator.

    -

    The maxParameterCount attribute controls the maximum - total number of request parameters (including uploaded files) obtained - from the query string and, for POST requests, the request body if the - content type is application/x-www-form-urlencoded or - multipart/form-data. Excessive parameters are ignored. If you - want to reject such requests, configure a - FailedRequestFilter.

    - -

    The xpoweredBy attribute controls whether or not the - X-Powered-By HTTP header is sent with each request. If sent, the value of - the header contains the Servlet and JSP specification versions, the full - Tomcat version (e.g. Apache Tomcat/), the name of - the JVM vendor and - the version of the JVM. This header is disabled by default. This header - can provide useful information to both legitimate clients and attackers. -

    +

    The requiredSecret attribute in AJP connectors + configures shared secret between Tomcat and reverse proxy in front of + Tomcat. It is used to prevent unauthorized connections over AJP protocol.

    The server attribute controls the value of the Server HTTP header. The default value of this header for Tomcat 4.1.x to @@ -351,9 +373,14 @@ proxy (the authenticated user name is passed to Tomcat as part of the AJP protocol) with the option for Tomcat to still perform authorization.

    -

    The requiredSecret attribute in AJP connectors - configures shared secret between Tomcat and reverse proxy in front of - Tomcat. It is used to prevent unauthorized connections over AJP protocol.

    +

    The xpoweredBy attribute controls whether or not the + X-Powered-By HTTP header is sent with each request. If sent, the value of + the header contains the Servlet and JSP specification versions, the full + Tomcat version (e.g. Apache Tomcat/), the name of + the JVM vendor and + the version of the JVM. This header is disabled by default. This header + can provide useful information to both legitimate clients and attackers. +

    @@ -416,6 +443,15 @@ ($CATALINA_BASE/temp by default). This location should be secured with appropriate file permissions - typically read/write for the Tomcat user and no access for other users.

    + +

    When mapperContextRootRedirectEnabled and/or + mapperDirectoryRedirectEnabled are enabled, request + processing will be more efficient but there are security side effects. + First, the existence of a web application or a directory may be confirmed + even though the user does not have access to that directory. Secondly, any + Valves and/or Filters - including those providing security functionality - + will not have an opportunity to process the request.

    +
    @@ -425,7 +461,7 @@ context as required.

    Any administrative application should be protected by a - RemoteAddrValve (this Valve is also available as a Filter). + RemoteCIDRValve (this Valve is also available as a Filter). The allow attribute should be used to limit access to a set of known trusted hosts.

    @@ -560,6 +596,20 @@ headers it sets unless your application is already setting them. If Tomcat is accessed via a reverse proxy, then the configuration of this filter needs to be co-ordinated with any headers that the reverse proxy sets.

    + +

    The WebDAV servlet enables edit functionality for web application + content. If the WebDAV servlet is enabled, the WebDAV functionality should + be appropriately secured. This should include CORS protection if it is + expected that any legitimate users will access the web application via a + browser.

    + +

    When configuring security constraints, care should be taken if the URL + pattern for one or more constraints covers any segment of the URL that + becomes part of the pathInfo for a servlet and the servlet uses the pathInfo + to identify some other resource (like the default servlet does). In those + circumstances, correct application of the security constraint depends on the + implementation of the Servlet. All servlets included with Tomcat will behave + correctly in this scenario.

    @@ -579,6 +629,23 @@
    +
    +

    All clients, including reverse proxies, are responsible for the + consequences of the data they present to Tomcat.

    + +

    The servlet specification removes path parameters when normalizing + requests. HTTP servers do not normally do this. This creates the possibility + of a client using a /..;a=b/ type sequence in a URI to bypass a + security constraint implemented in the reverse proxy. This possibility can + be avoided with appropriate configuration such as using the setting + mapping=servlet with httpd's mod_proxy.

    + +

    If Tomcat is deployed behind a reverse proxy and that reverse proxy + implements one or more security constraints, it is recommended a defense in + depth approach is taken and Tomcat is secured as if the reverse proxy was + not in use.

    +
    +

    BASIC and FORM authentication pass user names and passwords in clear text. Web applications using these authentication mechanisms with clients diff -Nru tomcat10-10.1.34/webapps/docs/security-manager-howto.xml tomcat10-10.1.52/webapps/docs/security-manager-howto.xml --- tomcat10-10.1.34/webapps/docs/security-manager-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/security-manager-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -24,8 +24,6 @@ &project; - Glenn Nielsen - Jean-Francois Arcand Security Manager How-To diff -Nru tomcat10-10.1.34/webapps/docs/setup.xml tomcat10-10.1.52/webapps/docs/setup.xml --- tomcat10-10.1.34/webapps/docs/setup.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/setup.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Remy Maucherat Tomcat Setup diff -Nru tomcat10-10.1.34/webapps/docs/ssi-howto.xml tomcat10-10.1.52/webapps/docs/ssi-howto.xml --- tomcat10-10.1.34/webapps/docs/ssi-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/ssi-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; -Glenn L. Nielsen SSI How To diff -Nru tomcat10-10.1.34/webapps/docs/ssl-howto.xml tomcat10-10.1.52/webapps/docs/ssl-howto.xml --- tomcat10-10.1.34/webapps/docs/ssl-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/ssl-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,8 +23,6 @@ &project; - Christopher Cain - Yoav Shapira SSL/TLS Configuration How-To @@ -102,7 +100,7 @@ needs to be able to ask about this), but it does not participate in the encryption or decryption itself.

    -

    Tomcat is able to use any of the the cryptographic protocols that are +

    Tomcat is able to use any of the cryptographic protocols that are provided by the underlying environment. Java itself provides cryptographic capabilities through JCE/JCA and encrypted communications capabilities through JSSE. @@ -450,7 +448,7 @@ associated certificate chain in different formats. Additionally, the rules that the Certificate Authorities use for issuing certificates change over time. As a result you may find that the commands given above may need to be modified. If -you require assitance then help is available via the +you require assistance then help is available via the Apache Tomcat users mailing list.

    @@ -458,20 +456,35 @@
    -

    To use Online Certificate Status Protocol (OCSP) with Apache Tomcat, ensure - you have downloaded, installed, and configured the - - Tomcat Native Connector. -Furthermore, if you use the Windows platform, ensure you download the -ocsp-enabled connector.

    +

    Tomcat supports the Online Certificate Status Protocol (OCSP) to verify the +status of client provided certificates for a sub-set of connector +configurations.

    +

    To use OCSP, you require the following:

    • OCSP-enabled certificates
    • -
    • Tomcat with SSL APR connector
    • +
    • Tomcat with an OCSP enabled connector
    • Configured OCSP responder
    +

    OCSP is currently implemented for the following connector configurations:

    + +
      +
    • NIO or NIO2 HTTP connector with the + org.apache.tomcat.util.net.openssl.OpenSSLImplementation, + a Tomcat Native build where OCSP is enabled and using the OpenSSL style + configuration.
    • +
    • NIO or NIO2 HTTP connector with the + org.apache.tomcat.util.net.openssl.panama.OpenSSLImplementation, + and using the OpenSSL style configuration. This uses FFM so Java 22 or + later is also required.
    • +
    + +

    OCSP is not supported if the +org.apache.tomcat.util.net.jsse.JSSEImplementation is used or if +the JSSE configuraton style is used.

    +

    Apache Tomcat requires the OCSP-enabled certificate to have the OCSP responder location encoded in the certificate. The basic OCSP-related diff -Nru tomcat10-10.1.34/webapps/docs/tomcat-docs.xsl tomcat10-10.1.52/webapps/docs/tomcat-docs.xsl --- tomcat10-10.1.34/webapps/docs/tomcat-docs.xsl 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/tomcat-docs.xsl 2026-01-23 19:33:36.000000000 +0000 @@ -52,7 +52,7 @@ - + project.xml @@ -86,7 +86,6 @@ --> - diff -Nru tomcat10-10.1.34/webapps/docs/tribes/introduction.xml tomcat10-10.1.52/webapps/docs/tribes/introduction.xml --- tomcat10-10.1.34/webapps/docs/tribes/introduction.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/tribes/introduction.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Filip Hanik Apache Tribes - Introduction diff -Nru tomcat10-10.1.34/webapps/docs/windows-service-howto.xml tomcat10-10.1.52/webapps/docs/windows-service-howto.xml --- tomcat10-10.1.34/webapps/docs/windows-service-howto.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/docs/windows-service-howto.xml 2026-01-23 19:33:36.000000000 +0000 @@ -23,7 +23,6 @@ &project; - Mladen Turk Windows Service How-To diff -Nru tomcat10-10.1.34/webapps/examples/META-INF/context.xml tomcat10-10.1.52/webapps/examples/META-INF/context.xml --- tomcat10-10.1.34/webapps/examples/META-INF/context.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/META-INF/context.xml 2026-01-23 19:33:36.000000000 +0000 @@ -18,6 +18,6 @@ - + diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/CookieExample.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/CookieExample.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/CookieExample.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/CookieExample.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,8 +30,6 @@ /** * Example servlet showing request headers - * - * @author James Duncan Davidson <duncan@eng.sun.com> */ public class CookieExample extends HttpServlet { diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/HelloWorldExample.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/HelloWorldExample.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/HelloWorldExample.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/HelloWorldExample.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,10 +26,7 @@ /** * The simplest possible servlet. - * - * @author James Duncan Davidson */ - public class HelloWorldExample extends HttpServlet { private static final long serialVersionUID = 1L; diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/LocalStrings_zh_CN.properties tomcat10-10.1.52/webapps/examples/WEB-INF/classes/LocalStrings_zh_CN.properties --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/LocalStrings_zh_CN.properties 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/LocalStrings_zh_CN.properties 2026-01-23 19:33:36.000000000 +0000 @@ -17,7 +17,7 @@ # To edit translations see: https://tomcat.apache.org/getinvolved.html#Translations cookies.cookies=你的浏览器正在发送下面的cookie: -cookies.make-cookie=创建一个发送到你的浏览器的cookie +cookies.make-cookie=创建一个cookie并发送到你的浏览器 cookies.name=名.称: cookies.no-cookies=你的浏览器未发送任何cookie cookies.set=你刚刚将以下cookie发送到你的浏览器: diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/RequestHeaderExample.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/RequestHeaderExample.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/RequestHeaderExample.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/RequestHeaderExample.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,8 +33,6 @@ /** * Example servlet showing request headers - * - * @author James Duncan Davidson <duncan@eng.sun.com> */ public class RequestHeaderExample extends HttpServlet { @@ -73,7 +71,7 @@ // text/html, application/html, etc. if (accept.contains("html")) { - return true; + return false; } } return false; diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/RequestInfoExample.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/RequestInfoExample.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/RequestInfoExample.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/RequestInfoExample.java 2026-01-23 19:33:36.000000000 +0000 @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import java.io.IOException; import java.io.PrintWriter; import java.util.ResourceBundle; @@ -28,10 +27,7 @@ /** * Example servlet showing request information. - * - * @author James Duncan Davidson <duncan@eng.sun.com> */ - public class RequestInfoExample extends HttpServlet { private static final long serialVersionUID = 1L; diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/RequestParamExample.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/RequestParamExample.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/RequestParamExample.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/RequestParamExample.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,10 +27,7 @@ /** * Example servlet showing request headers - * - * @author James Duncan Davidson <duncan@eng.sun.com> */ - public class RequestParamExample extends HttpServlet { private static final long serialVersionUID = 1L; diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/ServletToJsp.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/ServletToJsp.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/ServletToJsp.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/ServletToJsp.java 2026-01-23 19:33:36.000000000 +0000 @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/SessionExample.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/SessionExample.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/SessionExample.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/SessionExample.java 2026-01-23 19:33:36.000000000 +0000 @@ -33,10 +33,7 @@ /** * Example servlet showing request headers - * - * @author James Duncan Davidson <duncan@eng.sun.com> */ - public class SessionExample extends HttpServlet { private static final long serialVersionUID = 1L; diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -34,9 +34,6 @@ /** * Implementation of jakarta.servlet.Filter used to compress * the ServletResponse if it is bigger than a threshold. - * - * @author Amy Roh - * @author Dmitri Valdin */ public class CompressionFilter extends GenericFilter { @@ -168,7 +165,7 @@ } // Are we allowed to compress ? - String s = ((HttpServletRequest)request).getParameter("gzip"); + String s = request.getParameter("gzip"); if ("false".equals(s)) { if (debug > 0) { System.out.println("got parameter gzip=false --> don't compress, just chain filter"); diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java 2026-01-23 19:33:36.000000000 +0000 @@ -26,8 +26,7 @@ import jakarta.servlet.http.HttpServletResponse; /** - * Very Simple test servlet to test compression filter - * @author Amy Roh + * Very Simple test servlet to test compression filter. */ public class CompressionFilterTestServlet extends HttpServlet { diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java 2026-01-23 19:33:36.000000000 +0000 @@ -27,9 +27,6 @@ /** * Implementation of ServletOutputStream that works with * the CompressionServletResponseWrapper implementation. - * - * @author Amy Roh - * @author Dmitri Valdin */ public class CompressionResponseStream extends ServletOutputStream { diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,9 +30,6 @@ /** * Implementation of HttpServletResponseWrapper that works with * the CompressionServletResponseStream implementation.. - * - * @author Amy Roh - * @author Dmitri Valdin */ public class CompressionServletResponseWrapper extends HttpServletResponseWrapper { @@ -188,7 +185,7 @@ stream.close(); } } - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } } diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/examples/FooTag.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/examples/FooTag.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/examples/FooTag.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/examples/FooTag.java 2026-01-23 19:33:36.000000000 +0000 @@ -79,8 +79,8 @@ pageContext.setAttribute("member", atts[i]); i++; return EVAL_BODY_BUFFERED; - } catch (IOException ex) { - throw new JspTagException(ex.toString()); + } catch (IOException ioe) { + throw new JspTagException(ioe.toString()); } } } diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/examples/LogTag.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/examples/LogTag.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/examples/LogTag.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/examples/LogTag.java 2026-01-23 19:33:36.000000000 +0000 @@ -54,8 +54,8 @@ bodyOut.writeOut(bodyOut.getEnclosingWriter()); } return SKIP_BODY; - } catch (IOException ex) { - throw new JspTagException(ex.toString()); + } catch (IOException ioe) { + throw new JspTagException(ioe.toString()); } } } diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/examples/ValuesTag.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/examples/ValuesTag.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/examples/ValuesTag.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/examples/ValuesTag.java 2026-01-23 19:33:36.000000000 +0000 @@ -71,8 +71,8 @@ } else { out.print("-1"); } - } catch (IOException ex) { - throw new JspTagException("IOException: " + ex.toString(), ex); + } catch (IOException ioe) { + throw new JspTagException("IOException: " + ioe.toString(), ioe); } return super.doEndTag(); } diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/filters/ExampleFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,8 +38,6 @@ * subsequently defined filters, and logs the result to the servlet * context log for this application. * - * - * @author Craig McClanahan */ public final class ExampleFilter extends GenericFilter { diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/listeners/ContextListener.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/listeners/ContextListener.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/listeners/ContextListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/listeners/ContextListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,6 @@ * introduced in the 2.3 version of the Servlet API. This listener * merely documents the occurrence of such events in the application log * associated with our servlet context. - * - * @author Craig R. McClanahan */ public final class ContextListener implements ServletContextAttributeListener, ServletContextListener { diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/listeners/SessionListener.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/listeners/SessionListener.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/listeners/SessionListener.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/listeners/SessionListener.java 2026-01-23 19:33:36.000000000 +0000 @@ -29,8 +29,6 @@ * introduced in the 2.3 version of the Servlet API. This listener merely * documents the occurrence of such events in the application log associated * with our servlet context. - * - * @author Craig R. McClanahan */ public final class SessionListener implements ServletContextListener, HttpSessionAttributeListener, HttpSessionListener { diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/num/NumberGuessBean.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/num/NumberGuessBean.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/num/NumberGuessBean.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/num/NumberGuessBean.java 2026-01-23 19:33:36.000000000 +0000 @@ -14,10 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -/* - * Originally written by Jason Hunter, http://www.servlets.com. - */ package num; import java.io.Serializable; diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/util/HTMLFilter.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/util/HTMLFilter.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/util/HTMLFilter.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/util/HTMLFilter.java 2026-01-23 19:33:36.000000000 +0000 @@ -18,9 +18,6 @@ /** * HTML filter utility. - * - * @author Craig R. McClanahan - * @author Tim Tye */ public final class HTMLFilter { diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/validators/DebugValidator.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/validators/DebugValidator.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/validators/DebugValidator.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/validators/DebugValidator.java 2026-01-23 19:33:36.000000000 +0000 @@ -31,8 +31,6 @@ * $CATALINA_HOME/logs/catalina.out). To utilize it, simply * include a taglib directive for this tag library at the top * of your JSP page. - * - * @author Craig McClanahan */ public class DebugValidator extends TagLibraryValidator { @@ -69,7 +67,7 @@ break; } System.out.print((char) ch); - } catch (IOException e) { + } catch (IOException ioe) { break; } } diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/chat/ChatAnnotation.java 2026-01-23 19:33:36.000000000 +0000 @@ -125,12 +125,14 @@ for (ChatAnnotation client : connections) { try { client.sendMessage(msg); - } catch (IOException e) { - log.debug("Chat Error: Failed to send message to client", e); + } catch (IOException ioe) { + if (log.isDebugEnabled()) { + log.debug("Chat Error: Failed to send message to client", ioe); + } if (connections.remove(client)) { try { client.session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } String message = String.format("* %s %s", client.nickname, "has been disconnected."); diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/drawboard/Client.java 2026-01-23 19:33:36.000000000 +0000 @@ -118,7 +118,7 @@ // Ideally, there should be some method that cancels the connection // immediately... session.close(cr); - } catch (IOException e) { + } catch (IOException ignore) { // Ignore } @@ -208,7 +208,7 @@ // immediately... try { session.close(); - } catch (IOException ex) { + } catch (IOException ignore) { // Ignore } } diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/drawboard/Room.java 2026-01-23 19:33:36.000000000 +0000 @@ -201,7 +201,9 @@ ByteArrayOutputStream bout = new ByteArrayOutputStream(); try { ImageIO.write(roomImage, "PNG", bout); - } catch (IOException e) { /* Should never happen */ } + } catch (IOException ignore) { + // Should never happen + } // Send the image as binary message. diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/echo/EchoAnnotation.java 2026-01-23 19:33:36.000000000 +0000 @@ -38,10 +38,10 @@ if (session.isOpen()) { session.getBasicRemote().sendText(msg, last); } - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException ignore) { // Ignore } } @@ -54,10 +54,10 @@ if (session.isOpen()) { session.getBasicRemote().sendBinary(bb, last); } - } catch (IOException e) { + } catch (IOException ioe) { try { session.close(); - } catch (IOException e1) { + } catch (IOException iognore) { // Ignore } } diff -Nru tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java --- tomcat10-10.1.34/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/WEB-INF/classes/websocket/echo/EchoEndpoint.java 2026-01-23 19:33:36.000000000 +0000 @@ -30,17 +30,19 @@ @Override public void onOpen(Session session, EndpointConfig endpointConfig) { RemoteEndpoint.Basic remoteEndpointBasic = session.getBasicRemote(); - session.addMessageHandler(new EchoMessageHandlerText(remoteEndpointBasic)); - session.addMessageHandler(new EchoMessageHandlerBinary(remoteEndpointBasic)); + session.addMessageHandler(new EchoMessageHandlerText(remoteEndpointBasic, session)); + session.addMessageHandler(new EchoMessageHandlerBinary(remoteEndpointBasic, session)); } private static class EchoMessageHandlerText implements MessageHandler.Partial { private final RemoteEndpoint.Basic remoteEndpointBasic; + private final Session session; - private EchoMessageHandlerText(RemoteEndpoint.Basic remoteEndpointBasic) { + private EchoMessageHandlerText(RemoteEndpoint.Basic remoteEndpointBasic, Session session) { this.remoteEndpointBasic = remoteEndpointBasic; + this.session = session; } @Override @@ -49,9 +51,12 @@ if (remoteEndpointBasic != null) { remoteEndpointBasic.sendText(message, last); } - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } catch (IOException ioe) { + try { + session.close(); + } catch (IOException ignore) { + // Ignore + } } } } @@ -60,9 +65,11 @@ implements MessageHandler.Partial { private final RemoteEndpoint.Basic remoteEndpointBasic; + private final Session session; - private EchoMessageHandlerBinary(RemoteEndpoint.Basic remoteEndpointBasic) { + private EchoMessageHandlerBinary(RemoteEndpoint.Basic remoteEndpointBasic, Session session) { this.remoteEndpointBasic = remoteEndpointBasic; + this.session = session; } @Override @@ -71,9 +78,12 @@ if (remoteEndpointBasic != null) { remoteEndpointBasic.sendBinary(message, last); } - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + } catch (IOException ioe) { + try { + session.close(); + } catch (IOException ignore) { + // Ignore + } } } } diff -Nru tomcat10-10.1.34/webapps/examples/jsp/index.html tomcat10-10.1.52/webapps/examples/jsp/index.html --- tomcat10-10.1.34/webapps/examples/jsp/index.html 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/examples/jsp/index.html 2026-01-23 19:33:36.000000000 +0000 @@ -17,7 +17,6 @@ - JSP Examples +

    401 Unauthorized

    diff -Nru tomcat10-10.1.34/webapps/host-manager/WEB-INF/jsp/403.jsp tomcat10-10.1.52/webapps/host-manager/WEB-INF/jsp/403.jsp --- tomcat10-10.1.34/webapps/host-manager/WEB-INF/jsp/403.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/host-manager/WEB-INF/jsp/403.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -27,6 +27,7 @@ A {color : black;}A.name {color : black;} --> +

    403 Access Denied

    diff -Nru tomcat10-10.1.34/webapps/host-manager/WEB-INF/jsp/404.jsp tomcat10-10.1.52/webapps/host-manager/WEB-INF/jsp/404.jsp --- tomcat10-10.1.34/webapps/host-manager/WEB-INF/jsp/404.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/host-manager/WEB-INF/jsp/404.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -28,6 +28,7 @@ A {color : black;}A.name {color : black;} --> +

    404 Not found

    diff -Nru tomcat10-10.1.34/webapps/host-manager/WEB-INF/manager.xml tomcat10-10.1.52/webapps/host-manager/WEB-INF/manager.xml --- tomcat10-10.1.34/webapps/host-manager/WEB-INF/manager.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/host-manager/WEB-INF/manager.xml 2026-01-23 19:33:36.000000000 +0000 @@ -24,7 +24,7 @@ privileged="true" antiResourceLocking="false" > - + diff -Nru tomcat10-10.1.34/webapps/host-manager/images/asf-logo.svg tomcat10-10.1.52/webapps/host-manager/images/asf-logo.svg --- tomcat10-10.1.34/webapps/host-manager/images/asf-logo.svg 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/host-manager/images/asf-logo.svg 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/webapps/host-manager/images/favicon.ico and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/host-manager/images/favicon.ico differ diff -Nru tomcat10-10.1.34/webapps/manager/META-INF/context.xml tomcat10-10.1.52/webapps/manager/META-INF/context.xml --- tomcat10-10.1.34/webapps/manager/META-INF/context.xml 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/META-INF/context.xml 2026-01-23 19:33:36.000000000 +0000 @@ -18,7 +18,7 @@ - + diff -Nru tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/401.jsp tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/401.jsp --- tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/401.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/401.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -27,6 +27,7 @@ A {color : black;}A.name {color : black;} --> +

    401 Unauthorized

    diff -Nru tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/403.jsp tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/403.jsp --- tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/403.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/403.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -27,6 +27,7 @@ A {color : black;}A.name {color : black;} --> +

    403 Access Denied

    diff -Nru tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/404.jsp tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/404.jsp --- tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/404.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/404.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -28,6 +28,7 @@ A {color : black;}A.name {color : black;} --> +

    404 Not found

    diff -Nru tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/connectorCerts.jsp tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/connectorCerts.jsp --- tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/connectorCerts.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/connectorCerts.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -32,9 +32,10 @@ - + Configured certificate chains per Connector +

    Configured certificate chains per Connector

    @@ -68,7 +69,7 @@ -
    +

    diff -Nru tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/connectorCiphers.jsp tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/connectorCiphers.jsp --- tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/connectorCiphers.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/connectorCiphers.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -32,9 +32,10 @@ - + Configured ciphers per Connector +

    Configured ciphers per Connector

    @@ -68,7 +69,7 @@ - +

    diff -Nru tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/connectorTrustedCerts.jsp tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/connectorTrustedCerts.jsp --- tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/connectorTrustedCerts.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/connectorTrustedCerts.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -32,9 +32,10 @@ - + Trusted certificates per Connector +

    Trusted certificates per Connector

    @@ -68,7 +69,7 @@ - +

    diff -Nru tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/sessionDetail.jsp tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/sessionDetail.jsp --- tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/sessionDetail.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/sessionDetail.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -50,10 +50,10 @@ - - + Sessions Administration: details for <%= currentSessionId %> + <% if (currentHttpSession == null) { %> @@ -100,7 +100,7 @@ - +
    @@ -147,7 +147,7 @@ %> - +
    @@ -173,7 +173,7 @@ <% } // endif%> - +

    diff -Nru tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/sessionsList.jsp tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/sessionsList.jsp --- tomcat10-10.1.34/webapps/manager/WEB-INF/jsp/sessionsList.jsp 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/WEB-INF/jsp/sessionsList.jsp 2026-01-23 19:33:36.000000000 +0000 @@ -41,10 +41,10 @@ - - + Sessions Administration for <%= JspHelper.escapeXml(cn.getDisplayName()) %> +

    Sessions Administration for <%= JspHelper.escapeXml(cn.getDisplayName()) %>

    @@ -58,7 +58,7 @@
    <%= JspHelper.escapeXml(request.getAttribute("error")) %>
    <%= JspHelper.escapeXml(request.getAttribute("message")) %>
    - +
    Active HttpSessions information "/> @@ -146,7 +146,7 @@
    -
    +

    diff -Nru tomcat10-10.1.34/webapps/manager/images/asf-logo.svg tomcat10-10.1.52/webapps/manager/images/asf-logo.svg --- tomcat10-10.1.34/webapps/manager/images/asf-logo.svg 2024-12-05 16:07:56.000000000 +0000 +++ tomcat10-10.1.52/webapps/manager/images/asf-logo.svg 2026-01-23 19:33:36.000000000 +0000 @@ -1,4 +1,4 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file Binary files /srv/release.debian.org/tmp/ib2hL8uWRA/tomcat10-10.1.34/webapps/manager/images/favicon.ico and /srv/release.debian.org/tmp/D9Zf8FssVG/tomcat10-10.1.52/webapps/manager/images/favicon.ico differ